JavaScrip继承方案
1、原型链继承
构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。
原型链继承核心: 将父类的实例作为子类的原型。
继承的本质就是复制,即重写原型对象,代之以一个新类型的实例,
function Foo(name){
this.color = ['red','blue','black']
this.name = name
};
Foo.prototype.sayName = function () {
return ('my name is '+this.name)
}
function Car(age){
this.age = age
}
Car.prototype = new Foo('jack')
a = new Car(16)
b = new Car()
b.color.push('white') // 在Car的实例b的color属性上添加了一个white
console.log(a.name,a.age)
console.log(a.color,a.sayName())
我们在打印实例a的color属性意外的发现多出来一个白色,这是因为在原型链的继承中,引用类型会被所有的实例所共享,多个实例对引用类型的操作会被篡改。而不是单独属于自己的一份,
这也是原型链继承存在的问题:
问题1:原型中包含的引用类型属性将被所有实例共享;
问题2:子类在实例化的时候不能给父类构造函数传参;
为此我们想出来构造函数式继承方法
2、借用构造函数继承
基本思想:
借用构造函数的基本思想就是利用call或者apply把父类中通过this指定的属性和方法复制(借用)到子类创建的实例中。 因为this对象是在运行时基于函数的执行环境绑定的。也就是说,在全局中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。 call、apply 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
不知道this的指向的话可以仔细看看这篇关于this的完整讲解
This指向问题的超详解
function SuperType(){
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.getColor = function () {
return this.colors
}
function SubType(){
// 继承了 SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
console.log(instance2.colors); //"red,blue,green"
instance2.getColor() // 报错 TypeError: instance2.getColor is not a function
当我们想要使用父类原型上的方法时报错,发现顺着原型链上去查找,找不到getColor这个方法, 这是因为子类的实例不能继承原型属性/方法
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
缺点: 方法都在构造函数中定义, 只能继承父类的实例属性和方法,不能继承原型属性/方法, 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、组合继承
原理:使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。 结合原型继承和构造函数的继承方法,对之进行优化,达到即可继承原型的方法,引用类型也不会被所有实例所共享。
function Person (name) {
this.name = name;
this.friends = ['小李','小红'];
};
Person.prototype.getName = function () {
return this.name;
};
function Parent (age) {
Person.call(this,'老明'); //第二次调用构造函数
this.age = age;
};
Parent.prototype = new Person('老明'); //这一步也很关键
var result = new Parent(24); // 第一次调用构造函数
console.log(result.name); //老明
result.friends.push("小智"); //
console.log(result.friends); //['小李','小红','小智']
console.log(result.getName()); //老明
console.log(result.age); //24
var result1 = new Parent(25); //通过借用构造函数都有自己的属性,通过原型享用公共的方法
console.log(result1.name); //老明
console.log(result1.friends); //['小李','小红']
优点:
结合原型链和借用构造函数继承两种方法,取长补短。实现了函数复用,又能够保证每个子类不会共享父类的引用类型属性。
缺点:
调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
四. 寄生组合式继承
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype,{}); // 创建对象,创建父类原型的一个副本
prototype.constructor = subType; // 增强对象,弥补因重写原型而失去的默认的constructor 属性
subType.prototype = prototype; // 指定对象,将新创建的对象赋值给子类的原型
}
// 父类初始化实例属性和原型属性
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
// 将父类原型指向子类
inheritPrototype(SubType, SuperType);
// 新增子类原型属性
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("xyc", 23);
var instance2 = new SubType("lxy", 23);
instance1.colors.push("2"); // ["red", "blue", "green", "2"]
instance1.colors.push("3"); // ["red", "blue", "green", "3"]
这个例子的高效率体现在它只调用了一次SuperType 构造函数,并且因此避免了在SubType.prototype 上创建不必要的、多余的属性。于此同时,原型链还能保持不变; 因此,还能够正常使用instanceof 和isPrototypeOf() 这是最成熟的方法,也是现在库实现的方法
五、class
在 es6 中,官方给出了 class 关键字来实现面向对象风格的写法,但本质上是寄生组合式继承的语法糖。
class Person {
constructor(age) {
this.age_ = age;
}
sayAge() {
console.log(this.age_);
}
// 静态方法
static create() {
// 使用随机年龄创建并返回一个 Person 实例
return new Person(Math.floor(Math.random()*100));
}
}
// 继承普通构造函数
class Doctor extends Person {}
const doctor = new Doctor(32);
doctor.sayAge(); // 32
总结
1.ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this)).
2.ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this。
3.ES5的继承时通过原型或构造函数机制来实现。
4.ES6通过class关键字定义类,里面有构造方法,类之间通过extends关键字实现继承。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!