今天与各位老哥推心置腹聊一聊js的原型链与继承。说这个之前问一下各位js中怎么创建对象呢?现在告诉大家三种方法
第一种:使用字面量创建
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});
有的老哥会问这是两种,那么我们打开控制台看一下
是否完全一样呢?
第二种方法:使用构造函数创建
var O = function (name) { this.name = name; };
var o3 = new O('o3');(OOP)。
第三种方法:使用Object.create创建对象
var n = {name: 'n'};
var o4 = Object.create(n);
那这几种方法有什么不同呢,我们再来打开控制台看一下
那么大家可以看到o4居然是空的,但是他也有name方法,不信的话各位老哥自己试一下。
那么主要关注o3,他是一个O方法实例出来的一个对象,所以他其实也是O的一个实例,js引擎在加载function的时候会自动给function对象创建一个空的prototype,也就是我们常说的原型对象,说道原型对象又需要说一个constructor构造函数对象,那么原型,实例,构造函数是什么样的关系呢,我先贴一张图
对象呢,有类与实例,js是如何声明一个类与创建实例的呢?大家回想一下。
先说下ES5如何声明一个类,那就是使用构造函数
var Animal = function () {
this.name = 'Animal';
}
function Animal(){
this.name='Animal';
}
这两种写法其实是一个意思,都是声明一个Animal类,因为js中变量定义是弱类型。
ES6中引入了关键字class
class Animal2 {
constructor () {
this.name = 'Animal2';
}
}
借助构造函数实现继承
function Parent1 () {
this.name = 'parent1';
}
Parent1.prototype.say = function () {
};
function Child1 () {
** Parent1.call(this);
this.type = 'child1';
}
首先继承要使父类与子类有一个关联关系,带星号的一行就是关键代码,call与apply会改变this指向,让父类的this指向子类就实现了继承,那么这种方法有什么缺点呢,它没有通过原型链来实现,所以父类原型链上的东西子类无法获取,只能继承父类函数体里的方法。
第二种方法:借助原型链实现继承
function Parent2 () {
this.name = 'parent2';
this.play = [1, 2, 3];
}
function Child2 () {
this.type = 'child2';
}
Child2.prototype = new Parent2();
让子类的prototype指向new出来的父类的对象,通过上面讲的原型链就能明白这种继承方式,他的优点是可以继承父类函数体内的方法和原型链上的方法,缺点就是如果子类有多个实例那么修改其中一个实例就会影响其他实例。我们也是无法接受
从图中可以看出,我们只对s1.play方法进行了修改,s2.play也发生了变化。
那么我们再说一下如何优化
1.组合方式
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
function Child3 () {
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
将构造函数与原型链组合实现继承,就可以避免它们各自出现的问题。那么这种方法还有没有缺点呢?答案是有的,它会执行两次Parent函数,对于性能可能又影响。
2.组合方式优化
function Parent4 () {
this.name = 'parent4';
this.play = [1, 2, 3];
}
function Child4 () {
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype;
var s5 = new Child4();
var s6 = new Child4();
console.log(s5, s6);
console.log(s5 instanceof Child4, s5 instanceof Parent4);
console.log(s5.constructor);
这种方式就避免了重复执行父类函数,那么最后两行代码就说出了这种方式的缺点,s5无法用instanceof判断究竟是谁直接实例,你可是说是Child4毕竟它是Child4直接new出来的,但同样你也可以说是Parent4,因为它的constructor指向Parent4,所以无法用instanceof判断。
3,组合方式优化(2)
function Parent5 () {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5 () {
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
还记得我们在上面的第三种创造对象的方法吧,这种继承方式就会避免
(new Child4).instanceof===Parent;因为Child.prototype指向的不是父类的prototype而是一个Object.create新产生的对象,而这个新产生的对象的prototype指向的是Parent.prototype,就避免了实例的constructor指向父类函数体,实现完美继承。
如果有老哥不理解或者我讲的有问题或者说的有错误 QQ1817169988 微信: Ezreal_Ning 顺便求一下北京前端岗位(可实习),我是应届生呦。