对象
ECMA-62将对象定义为一组属性的无序集合
继承
ECMA-62把原型链定义为ECMAScript的主要主要继承方式。
其基本思想是通过原型继承多个引用类型的属性和方法
原型链继承
实现原型链设计如下代码模式:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function (){
return this.property;
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true
原型链继承的问题:
1.原型中包含的引用值,会在所有实例间共享
在使用原型实现继承时,原型实际变成了另一个类型的实例
2.子类型在实例化时不能给父类型的构造函数传参
function Parent(name) {
this.name = name;
}
function Child(name) {
// 在这里,我们无法将参数传递给 Parent 的构造函数
Parent.call(this, name); // 这是一种解决方案,但这不是原型链继承的一部分了。
}
Child.prototype = new Parent(); // 我们无法给 Parent 的构造函数传参
let child = new Child('John');
console.log(child.name); // undefined,因为我们无法在 Child 的构造函数中给 Parent 的构造函数传递参数。
盗用构造函数(对象伪装)
为了解决原型包含引用值导致的继承问题
基本思路:
在子类构造函数中调用父类构造函数
function SuperType(name) {
this.colors = ["red", "blue", "green"];
this.name = name;
}
function SubType() {
SuperType.call(this,"zhangsan") // 还可以传递参数
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(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.asyAge = function (){
console.log(this.age)
}
let instance1 = new SubType("zhangsan",29);
原型式继承
原型式继承适合:你有一个对象,想在它的基础上再创建一个新对象.
function object(o){
function F(){}
F.prototype = o;
return new F()
}
对于基本类型的属性(比如字符串、数字等),当我们在一个对象上设置这个属性的值时,JavaScript 会在该对象自身上创建一个新的属性,如果对象自身没有这个属性的话,而不会去修改原型链上的同名属性。
在ECMA5中添加Object.create()方法 这个方法接受两个参数:
1.作为新对象原型的对象 2.给新对象定义额外属性的对象
原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。 但要记住属性包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的
寄生式继承
思路:创建一个实现继承的函数以某种方式增强对象,然后返回这个对象
function createAnother(original){
let clone = object(original); // 通过调用函数创建一个新对象
clone.sayHi = function (){ // 以某种方式增强这个对象
console.log('hi');
};
return clone;
}
不足:给对象添加函数导致函数难以复用,与构造函数雷士
寄生式组合继承
基本思路:
不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本
组合继承存在效率问题 父类构造函数始终会被调用两次
一次是在创建子类原型时调用 一次是在子类构造函数中调用
本质上,子类的原型最终是要包含超类对象的所有实例属性 子类构造函数只要在执行时重写自己的原型就行了
实现寄生是组合继承的核心逻辑
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;
}
// 创建一个新的对象,该对象的原型是 SuperType 的原型
let prototype = Object.create(SuperType.prototype);
prototype.constructor = SubType; // 增强对象,使其构造函数指向 SubType
// 将新创建的对象赋值给 SubType 的原型
SubType.prototype = prototype;
let instance = new SubType("Bob", 23);
instance.colors.push("black");
console.log(instance.name); // Bob
console.log(instance.age); // 23
console.log(instance.colors); // ["red", "blue", "green", "black"]
instance.sayName(); // "Bob" —— 成功访问到了父类原型中定义的方法