JS对象和构造函数
- 对象分为普通对象和函数对象,函数对象都是通过new Function创造出来的
- 函数对象分为普通函数和构造函数,区别如下:
- 作用: 构造函数目的新建实例对象
- 首字母大小: 普通函数用小写,构造函数习惯用大写
- 调用方式:普通函数直接调用,构造函数需要使用new关键字来调用
- 函数中this: 普通函数中的this,在严格模式下指向undefined,非严格模式下指向window对象。构造函数的this则是指向它创建的对象实例。
原型对象
js每定义一个对象后,都会有几个预定义属性,其中函数对象的一个属性就是原型对象prototype, 普通对象没有prototype;
- 原型对象本身是一个普通对象
- 原型对象主要用作继承
- 原型对象拥有公有的属性和方法
原型链
在js 中每个对象(null 除外)都有一个属性__proto__, 该属性指向原型对象,换句话说则是:该对象是从那个原型对象继承属性和方法,我们可以通过此属性来找到,原型链则是基于__proto__来实现的。 当我们访问一个对象的属性或者方法的时候,会先去对象自身找这个属性或者方法,如果没有找到,则会去它的原型对象身上找,如果原型对象身上也没有找到,则会到原型对象的原型对象上去找,以此类推,找到则返回对应的值,如果直到原型对象为null任然没有找到则返回undefined。
function Fn(name) {
this.name = name
}
Fn.__proto__ === Fn.prototype // true
Fn.__proto__.__proto__ === Fn.prototype.__proto__=== Object.prototype // rue
Objcet.prototype.__proto__ === null // true
原型链继承
function Parent() {
this.property = 888;
}
Parent.prototype.getMaxProperty = function() {
return this.property = this.property * 10;
};
function Child() {
this.property = 1;
}
// 继承 Parent
Child.prototype = new Parent();
let child1 = new Child();
child1.getMaxProperty() //10 已继承父级方法
- 问题一 以对象字面量方式修改、创建原型方法会破坏之前的原型链
Child.prototype = new Parent();
//通过对象字面量添加新方法,这会导致上一行无效
Child.prototype = {
doSomething() {
return '工作'
},
com: 'human'
}
let child2 = new Child();
child2.getMaxProperty(); //报错,父级原型链已丢失
child2.doSomething(); // '工作' 新的原型链
child2.com // 'human' 新的原型链
解决办法: 原型对象prototype中constructor属性指向是构造函数本身,同时需改更改属性是否可以枚举。
Child.prototype = {
doSomething() {
return '工作'
},
com: 'human'
}
Object.defineProperty(Child.prototype, 'constructor', {
enumerable: false,
value: Parent
})
- 问题二 原型中包含的引用值会在所有实例间共享
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {}
// 继承 SuperType
SubType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green,black"
- 问题三 子类型在实例化时不能给父类型的构造函数传参
构造函数继承(call&apply)
在子类 构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用 apply()和 call()方法以新创建的对象为上下文执行构造函数。
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
// 继承 SuperType
SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"
- 优点:可以在子类构造函数中向父类构造函数传参
- 缺点:无法继承原型链上的属性与方法
组合继承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age){
// 继承属性
SuperType.call(this, name);
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
console.log(this.age);
};
let instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";
instance1.sayAge(); // 29
let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27
- 缺点: 父类的构造器被调用了两次
寄生组合继承
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
};
new创建一个对象,执行构造函数。 Object.create相当于创建一个对象,但是不执行构造函数。