继承是前端面试必问,说到继承,就必须谈一谈原型链。
原型链
我面试的时候都会这么回答原型链:js万物皆对象,用var a={} 或 var a = new Object()或者用构造函数的形式:var a = new A()创建一个对象时,该对象不仅可以访问它自身的属性,还会根据__proto__属性找到它原型链上的属性,直到找到Object上面的null
。如有不贴切,望评论不足之处哈。
更详细的参考mdn:
developer.mozilla.org/zh-CN/docs/…
对于下面的例子:
var A = function(){
this.name="xiaoming";
}
A.prototype.age=9;
var a = new A();
console.log(a.age); //9
我参考以前学习java时对实例和Class的图画了一个原型链的图,不太好,但是画图不易,小女子求您一个赞哈。
a.__proto__ === A.prototype; //true
A.prototype.constructor===A; //true
A.prototype.__proto__===Object.prototype; //true
Object.prototype.__proto__===null; //true
上方示例可以在看完我的解释之后再回顾一遍。实例和原型之间是通过__proto__属性连接
,且是单向的,从实例指向原型
;原型和构造函数之间连接是双向的,通过constructor和prototype连接
,具体见图;原型链上的属性是所有实例共享的
,看下面的例子:
var A = function(){
this.name="xiaoming";
}
var a = new A();
A.prototype.age=9;
var b = new A();
console.log(a.age); //9
console.log(b.age); //9
a、b都可以访问A原型链上的属性age。
Function和Object比较特殊,他们既是对象又是函数,两者内部同时含有proto和prototype属性,可看下面代码:
Object.__proto__ === Function.prototype //true
Object.__proto__ === Function.__proto__//true
Object.prototype === Function.prototype.__proto__ // true
Function instanceof Object //true
Object instanceof Function //true
至此,原型链的知识差不多可以理解了,后面介绍继承的几种方式。
原型链继承
既然可以访问原型链的所有属性,那么就可以用原型链的原理实现继承。原型链继承用new的方式(实现A继承B):
A.prototype=new B()
;关于new可以看下我另一篇文章this那些事。
代码:
function B(){
this.nameB='B';
}
B.prototype.nameProto="PROTO";
function A(){
this.nameA="A";
}
A.prototype=new B(); //原型链继承:A继承B
var a=new A();
console.log(a);
打印结果:
通过A构造函数new的示例a不仅可以继承B(可访问nameB),而且可以继承B原型上的属性(nameProto),且是所有实例共享的。
好处:可以继承原型链的属性
。
缺点:无法实现多继承,A继承了B,就无法再继承C
。
构造继承
构造继承就是利用构造函数继承,即改变this指向的方式(call/apply/bind)执行一次构造函数B
,具体可我另一篇文章this那些事。废话不多说,上例子:
function B(){
this.nameB='B';
}
B.prototype.nameProto="PROTO";
function A(){
B.call(this); //A继承B,只举了call的例子,apply、bind类似
this.nameA="A";
}
var a=new A();
console.log(a);
打印结果:
无法继承B原型链上的属性nameProto
,不过它的好处是可以多继承
,可以通过C.call(this)
继承C。
好处:可以多继承
。
缺点:无法继承原型链上的属性
。
组合继承
组合继承就是为了解决原型链继承无法多继承、构造继承无法继承原型链上的属性
的问题而诞生的,将两种方式结合。
function B(){
this.nameB='B';
}
B.prototype.nameProto="PROTO";
function A(){
B.call(this); //构造继承
this.nameA="A";
}
A.prototype=new B(); //原型链继承:A继承B
var a=new A();
console.log(a);
打印结果:
nameB属性有两个
,这样造成了资源浪费(存储占用内存)。
原型式继承
先看下面的示例:
function objectCreate(obj){
function F(){};
F.prototype = obj;
return new F();
}
var a=objectCreate(A.prototype);
上个例子中,
使用__proto__
:A.prototype.__proto__=B.prototype
使用Object.create
:A.prototype=Object.create(B.prototype)