讲到继承,就先从创建对象讲起
我们都知道,以构造函数的方式创建对象,所创建的实例对象不能共享属性和方法
以原型链的方式创建对象,所创建的实例对象的属性和方法都是共享的,若某一实例对象修改属性值,会影响所有的实例对象
结合二者的优点与不足,js有一下几种创建对象的方式
创建对象
1.组合使用构造函数和原型
构造函数用于定义实例的属性,而原型模式用于定义方法和共享的属性
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shellby","Court"];
}
Person.prototype = {
constructor:Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas",29,"Softwore Engineer");
var person2 = new Person("Greg",30,"Doctor");
person1.friends.push("Van");
console.log(person1.friends);//["Shellby", "Court", "Van"]
console.log(person2.friends);//["Shellby", "Court"]
console.log(person1.friends === person2.friends) //false
console.log(person1.sayName == person2.sayName) //true
2.动态原型模式
动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
//方法
if(typeof this.sayName != 'function'){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
注意if中的这段代码,这里只在sayName()方法不存在的情况下才会将它添加到原型中。if中的这段代码只会在初次调用构造函数时才会执行,此后,原型已经完成初始化,不需要再做什么修改。这里对原型所做的修改,能够立即在所有实例中得到反映。因此,这种方法可以说是 很完美!
3.寄生构造函数模式
寄生构造函数模式的思想是创建一个函数,该函数的作用不仅仅是封装创建对象的代码,然后再返回新创建的对象
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = o.job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend = new Person("Mary",23,"Teacher");
friend.sayName(); //Mary
console.log(friend instanceof Person);//false
console.log(friend instanceof Object);//true
这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,因此可以使用这个模式。
function SpecialArray(){
//创建数组
var values = new Array();
//添加值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
return this.join("|");
};
//返回数组
return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString()); // red|blue|green
4.稳妥式构造函数模式
所谓稳妥对象,指的是没有公共属性,而且方法也不引用this对象。 稳妥构造函数遵循与寄生函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数
function Person(name,age,job){
//创建要返回的对象
var o = new Object();
//在这里可以定义私有变量和函数
// 添加方法
o.sayName = function(){
alert(name);
}
//返回对象
return o;
}
var friend = Person("Nicholas",29,"Softwore Engineer");
friend.sayName(); //Nicholas
继承
1.原型式继承
核心:将父类的实例作为子类的原型
function SuperType(){
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayColors = function(){
console.log(this.colors);
}
function SubType(){
}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//red,blue,green,black
console.log(instance1.sayColors()); //["red", "blue", "green", "black"]
console.log(instance1 instanceof SuperType);//true
console.log(instance1 instanceof SubType);//true
var instance2 = new SubType();
alert(instance2.colors);//red,blue,green,black
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
- 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的(详细请看附录代码: 示例1)
- 创建子类实例时,无法向父类构造函数传参
推荐指数:★★(3、4两大致命缺陷)
2.借用构造函数继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
通过使用call()和apply()
function SuperType(){
this.name = 'SuperType';
this.colors = ["red","blue","green"];
this.sayHi = function(){
console.log("Hi!"+this.name);
}
}
SuperType.prototype.sayColors = function(){
console.log(this.colors);
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//red,blue,green,black
console.log(instance1 instanceof SuperType);//false
console.log(instance1 instanceof SubType);//true
console.log(instance1.sayHi());//Hi!SuperType
console.log(instance1.sayColors());//报错
var instance2 = new SubType();
alert(instance2.colors);//red,blue,green
传递参数
function SuperType(name){
this.name = name;
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this,"Jerry");
//实例属性
this.age = 24;
}
var instance1 = new SubType();
alert(instance1.name); //Jerry
alert(instance1.age); //24
特点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
推荐指数:★★(缺点3)
3.组合继承
组合继承也叫伪经典继承。指的是将原型链和借用构造函数的技术组合到一起,从而发挥而知之长的一种继承模式
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function SuperType(name){
this.name = name||'SuperType';
this.colors = ["red","blue","green"];
this.sayName = function(){
console.log("Hi! "+this.name);
}
}
SuperType.prototype.sayColors = function(){
console.log(this.colors);
};
function SubType(name,age){
//调用父类构造
SuperType.call(this,name);
this.age = age;
}
//将父类实例作为子类原型
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
console.log(this.age);
};
var instance1 = new SubType("Nicholas",29);
instance1.colors.push("black");
console.log(instance1.colors);//(4) ["red", "blue", "green", "black"]
instance1.sayName();//Hi! Nicholas
instance1.sayAge();//29
console.log(instance1 instanceof SubType); //true
console.log(instance1 instanceof SuperType);//true
var instance2 = new SubType("Jane",23);
console.log(instance2.colors);//(4) ["red", "blue", "green"]
instance2.sayName();//Hi! Jane
instance2.sayAge();//23
特点:
- 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
函数可复用
缺点:调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
推荐指数:★★★★(仅仅多消耗了一点内存)
4.寄生式继承
核心:为父类实例添加新特性,作为子类实例返回
function SuperType(name){
this.name = name||'SuperType';
this.colors = ["red","blue","green"];
this.sayName = function(){
console.log("Hi! "+this.name);
}
}
SuperType.prototype.sayColors = function(){
console.log(this.colors);
};
function SubType(name,age){
//创建父类实例,并添加新特性
var superType = new SuperType();
superType.name = name || 'subtype';
superType.age = age;
return superType;
}
var instance1 = new SubType("Lara",23);
instance1.colors.push("pink");
console.log(instance1.colors);//["red", "blue", "green", "pink"]
console.log(instance1.name); //Lara
console.log(instance1.age); //23
console.log(instance1.sayName()); //Hi! Lara
console.log(instance1.sayColors()); // ["red", "blue", "green", "pink"]
console.log(instance1 instanceof SuperType);//true
console.log(instance1 instanceof SubType);//false
var instance2 = new SubType("Daisy",24);
console.log( instance2.colors); //["red", "blue", "green"]
console.log(instance1.name); //Daisy
console.log(instance1.age); //24
特点:
不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
缺点:实例是父类的实例,不是子类的实例
- 不支持多继承
推荐指数:★★
5.寄生组合式继承
暂时没有理解透彻