小议JS原型链、继承

继承是前端面试必问,说到继承,就必须谈一谈原型链。前端

原型链

我面试的时候都会这么回答原型链:js万物皆对象,用var a={} 或 var a = new Object()或者用构造函数的形式:var a = new A()建立一个对象时,该对象不只能够访问它自身的属性,还会根据__proto__属性找到它原型链上的属性,直到找到Object上面的null。若有不贴切,望评论不足之处哈。
更详细的参考mdn: developer.mozilla.org/zh-CN/docs/…java

对于下面的例子:面试

var A = function(){
    this.name="xiaoming";
}
A.prototype.age=9;
var a = new A();
console.log(a.age); //9
复制代码

我参考之前学习java时对实例和Class的图画了一个原型链的图,不太好,可是画图不易,小女子求您一个赞哈。 bash

言归正传,图中长方形表明实例对象a,圆形表明原型,三角形表明构造函数。由图可知:

a.__proto__ === A.prototype; //true
A.prototype.constructor===A; //true
A.prototype.__proto__===Object.prototype; //true
Object.prototype.__proto__===null; //true
复制代码

上方示例能够在看完个人解释以后再回顾一遍。实例和原型之间是经过__proto__属性链接,且是单向的,从实例指向原型原型和构造函数之间链接是双向的,经过constructor和prototype链接,具体见图;原型链上的属性是全部实例共享的,看下面的例子:app

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属性,可看下面代码:post

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那些事ui

代码: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继承B, 经过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);
复制代码

打印结果:

根据a的打印结果,咱们看到nameB和nameA是同一层级,虽然实现了A继承B,可是经过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);
复制代码

打印结果:

观察a的打印结果,彷佛真的解决了上述两个问题,它也引入了一个新的问题: nameB属性有两个,这样形成了资源浪费(存储占用内存)。

原型式继承

先看下面的示例:

function objectCreate(obj){
  function F(){};
  F.prototype = obj;
  return new F();
}
var a=objectCreate(A.prototype);
复制代码

上个例子中,

  1. 使用__proto__A.prototype.__proto__=B.prototype
  2. 使用Object.createA.prototype=Object.create(B.prototype)
相关文章
相关标签/搜索