js的面向对象
一、面向对象
1.认识对象
一.认识对象
对象(object)是“键值对”的集合,表示属性和值的映射关系
属性名(键名,key) 属性值(value) 又可以叫
k:v
对
如果属性名不符合js标识符命名规范,则必须用方括号的写法访问
如果属性名以变量形式存储,则必须使用方括号形式
delete操作符删除某个对象的属性
二.对象的方法
如果某个属性值是函数,则它也被称为对象的方法
方法也是函数,只不过方法是对象的函数属性,打点调用即可
三.对象的深浅克隆
1.浅克隆
浅克隆
:只克隆对象的“表层”,如果对象的某些属性值又是引用类型值,则不进一步克隆它们,只是传递它们的引用,浅克隆需要使用for…in
var obj1 = {
a: 1,
b: 2,
c: [1]
}
var obj2 = {};
for (var k in obj1) {
obj2[k] = obj1[k]
}
obj1.a++;
obj1.c.push(2);
//obj1 {a: 2, b: 2, c:1,2}
//Obj2 {a: 1, b: 2, c:1,2}
console.log(obj1, obj2);
以上案例,由于浅克隆只会克隆对象的第一层,后面对象值如果属于引用类型的话是不会克隆的,则会造成一种藕断丝连的现象,obj1.c和obj2.c指向的是同一个对象,改变里面的对象obj1和obj2都会跟着改变
2.深克隆
深克隆
:克隆对象的全貌,不论对象的属性值是否又是引用类型值,都能将它们实现克隆,
对象的深克隆需要使用递归
var obj3 = {
a: 1,
b: 2,
c: [33, 44, {
m: 55,
n: 66,
p: [77, 88]
}]
}
function deppClone(obj) {
//判断是否为数组
if (Array.isArray(obj)) {
var result = [];
for (var i = 0; i < obj.length; i++) {
result.push(deppClone(obj[i]))
}
} else if (typeof obj === "object") {
var result = {};
for (var k in obj) {
result[k] = deppClone(obj[k])
}
} else {
var result = obj;
}
return result
}
var obj4 = deppClone(obj3)
obj4.c.push(22)
obj4.c[2].a = 5;
console.log(obj4.c[2]); //obj4杜绝了“藕断丝连”现象
console.log(obj4, obj3);
以上案例,深克隆把对象所有里面的值都克隆到了新的对象里面,包括了值是引用类型的,杜绝了与obj3藕断丝连的现象
2.认识函数的上下文
函数中可以使用this关键字,它表示函数的上下文
函数的上下文由调用方式决定的,同一个函数,用不同的形式调用它,函数的上下文也不一样
规则1:对象打点调用它的方法函数,则函数的上下文是这个打点的对象
function fun() {
console.log(this.a + this.b);
}
var obj1 = {
a: 1,
b: 2,
fn: function() {
console.log(this.a + this.b);
}
}
var a = 5;
var b = 4;
var fn = obj1.fn
fn() //9
规则2:圆括号直接调用函数,则函数的上下文是window对象
function fun() {
return this.a + this.b
}
var a = 1;
var b = 2;
var obj = {
a: 3,
b: fun(),
fun: fun
};
var result = obj.fun()
console.log(result); //6
规则3:数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)
function fun() {
arguments[3]();
}
fun('A', 'B', 'C', function() {
console.log(this[1]);
}); //B
规则4:IIFE中的丞数,上下文是window对象
var a = 1;
var obj = {
a: 2,
fun: (function() {
var a = this.a;
return function() {
console.log(a + this.a);
}
})()
};
obj.fun(); //3
规则5:定时器、延时器调用函数,上下文是window对象
var obj = {
a: 1,
b: 2,
fun: function() {
console.log(this.a + this.b);
}
}
var a = 3;
var b = 4;
setTimeout(obj.fun, 2000) //7
setTimeout(function() {
obj.fun() //3
}, 2000)
规则6:事件处理函数的上下文是绑定事件的DOM元素
function setColorToRed() {
//备份上下文
var self = this;
setTimeout(function() {
self.style.backgroundColor = "red"
}, 2000)
}
var box = document.querySelector('.box');
box.addEventListener("click", setColorToRed)
call和apply任意指定this指向
//call和apply能指定函数的上下文
function sum(target, target1) {
console.log(this.a + this.b + target + target1)
}
var obj = {
a: 1,
b: 3,
};
// call要用逗号罗列参数
sum.call(obj, 4, 5);
// apply要把参数写到数组中
sum.apply(obj, [4, 6])
new调用函数
function fun() {
this.a = 3;
this.b = 5;
}
var obj = new fun();
console.log(obj);
3.构造函数
1.用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它
2.构造函数用来”构造新对象”,它内部的语句将为新对象添加若干属性和方法,完成对象的初始化
3.构造函数必须使用new关键字调用,构造函数命名时首字母要大写
构造函数可以类比与类,由类创造实例的过程称为实例化
4.原型和原型链
任何函数都有prototype属性,prototype是英语“原型”的意思
prototype属性值是个对象,它默认拥有constructor属性指回函数
普通函数来说的prototype属性没有任何用处,而构造函数的prototype属性非常有用
构造函数的prototype属性是它实例的原型
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//实例化
var xiaoming = new People('小明', 12, "男");
//测试三角关系是否存在
console.log(xiaoming.__proto__ === People.prototype); //true
People是一个构造函数,People的prototype属性指向的是People.prototype原型,用new创建了一个obj实例,obj实例的原型__proto__指向People.prototype,构造函数创建obj实例的过程叫实例化
原型链查找:实例可以打点访问它的原型的属性和方法
//hasOwnProperty方法可以检查对象是否真正“自己拥有某属性或者方法”
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//实例化
var xiaoming = new People('小明', 12, "男");
xiaoming.hasOwnProperty("sex") //true
//in运算符只能检查某个属性或方法是否可以被对象访问,不能检查是否是自己的属性或方法
"name" in xiaoming //true
把方法直接添加到实例身上的缺点:每个实例和每个实例的方法函数都是内存中不同的函数,造成了内存的浪费,将方法写在prototype上可以解决
function People(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
this.aa = function() {
console.log(1);
}
}
//把方法写在原型上
People.prototype.sayHello = function() {
console.log(this.name + "今年" + this.age + "岁了");
}
People.prototype.growup = function() {
this.age++;
}
var xiaoming = new People("小明", "男", 18);
var xiaohong = new People("小红", "女", 20);
console.log(xiaoming.sayHello === xiaohong.sayHello);
console.log(xiaoming.aa === xiaohong.aa);
xiaoming.growup()
xiaoming.sayHello()
xiaohong.sayHello()
原型链终点
![]()
function People(name, age) {
this.name = name;
this.age = age;
}
var obj = new People();
console.log(obj.__proto__ === People.prototype); //true
console.log(obj.__proto__.__proto__ === Object.prototype); //true
5.继承
继承描述了两个类之间的一种关系,分为父类和子类,子类丰富了父类,让类描述的更加具体细化
实现继承的关键在于:子类必须拥有父类的全部属性和方法,同时子类还应该能定义自己特有的属性和方法,使用js特有的原型链特性来实现继承,是很常用的方法
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.sayHello = function() {
console.log("我叫" + this.name + "今年" + this.age + "岁了");
}
People.prototype.sleep = function() {
console.log(this.name + "开始睡觉,zzzzz");
}
function student(name, age, sex, school, studentId) {
this.name = name;
this.age = age;
this.sex = sex;
this.school = school;
this.studentId = studentId;
}
//实现继承
student.prototype = new People()
student.prototype.study = function() {
console.log(this.name + "正在学习");
};
//重写(override)父类的sayHello
student.prototype.sayHello = function() {
console.log("大家好,我叫" + this.name + "今年" + this.age + "岁了");
}
var xiaoming = new student("小明", 15, "男", "中心校", 14238)
console.log(xiaoming.__proto__ === student.prototype); //true
console.log(xiaoming.__proto__.__proto__ === People.prototype); //true
xiaoming.sayHello() //大家好,我叫小明今年15岁了
xiaoming.study() //小明正在学习
var xiaohong = new People("小红", 14, "女");
xiaohong.sayHello() //我叫小红今年14岁了
xiaohong.study() //xioahong is not defined