原型链和继承

Posted 小章鱼哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原型链和继承相关的知识,希望对你有一定的参考价值。

1. 原型链

每一个object对象都有自己[[prototype]]属性,它指向自己的原型对象(prototype),该原型对象又有自己的[[prototype]]属性,指向自己的原型对象,层层向上直到一个对象的原型对象是nullnull没有原型,作为这个原型链的最后一节。

访问一个object对象的属性的时候,先去查找对象本身有没有同名的属性,如果没有,就去对象的原型上去找,原型的原型,层层向上查询,直到找到同名的属性,或者直到找到原型链的终点:null

2. __proto__ vs prototype

2.1 __proto__

每一个object对象都有一个[[prototype]]属性,它是一个隐藏属性。它指向对象的原型。在很多浏览器的实现中,把[[prototype]]实现为__proto__

所以,__proto__指向的原型是什么呢?这就由它的构造方法来决定。

对象的三种构造方法:

字面量构造法
var a = 
    name: 'lc'

这个a对象,其__proto__指向Object.prototype

实际上,字面量构造法是一个语法糖,本质上也是依靠构造函数创建的。

var a = Object.create();
a.name = 'lc';
构造函数构造法
function A(name) 
    this.name = name;

var a = new A();

这个a对象,其__proto__指向A.prototype
new内部是这样实现的:

// b = new A()

    var obj = ;
    obj.__proto__ = A.prototype;
    A.call(obj);
    return obj;
Object.create构造法
var b = 
    name: 'lc'

var a = Object.create(a);

这个a对象,其__proto__指向b

Object.create内部是这样实现的:

Object.create = function(b) 
    function fn();
    fn.prototype = b;
    return new fn();

实际上,a也是由new方法创建,只是,a的构造函数fn只是存在了一瞬间。在外部,我们看不到a的构造函数,只能看到它的原型是b

隐式原型

__proto__的作用:可以在访问一个object对象的属性的时候,先去查找对象本身有没有同名的属性,如果没有,就去对象的原型上去找,原型的原型,层层向上查询,直到找到同名的属性,或者直到找到原型链的终点:null。我们称它为隐式原型

我们可以通过Object.getPrototypeOf获得。

2.2 prototype

每一个函数在创建之后都有一个prototype属性,它指向函数的原型对象。

ps: 通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性。

显式原型

prototype的作用:用来实现基于原型的继承与属性的共享。我们把它称之为显式原型

2.3 prototype__proto__关系

a的构造函数是A,a的隐式原型指向A的prototype。

2.4 constructor

所有对象都会从它的原型对象上继承constructor属性,它会指向对象的构造函数。

2.5 画个图吧

3. 继承

3.1 构造函数继承

function A(name) 
    this.name = name;


function B(name) 
    A.call(this, name);


var a = new A('qq')
var b = new B('lc');

缺点: 子类父类没有公用的方法。函数复用无从谈起。

3.2 原型链继承

function A(name) 
    this.name = name;
    this.age = 3;

A.prototype.getName = function() 
  return this.name; 


function B(name) 
    this.name = name;

B.prototype = new A();
B.prototype.setName = function(name) 
    this.name = name;


var b = new B('ls');
b.getName();
b.setName('qq');

缺点: 父类的构造函数的属性变成子类的__proto__下的属性了。
所以,上述代码

b.age   //3

// 实际上,b不应该由age属性

如图,B.prototypeconstructor属性指向A而不是B

假设,两个子类child1child2,两个子类都继承一个父类parent。父类的构造函数下的引用类型,就会被两个child共享。并且共享的是引用值,实例之间的属性会互相干扰。

3.3 组合式继承

function A(name) 
    this.name = name;
    this.age = 3;

A.prototype.getName = function() 
  return this.name; 


function B(name) 
    A.call(this, name);

B.prototype = new A();
B.prototype.setName = function(name) 
    this.name = name;


var b = new B('ls');

上面两种方法的组合。目前比较完美了。子类之间的属性既不会互相影响,又可以实现方法的共享。

缺点: 构造函数被执行了两次,new的时候执行了一次,call的时候执行了一次。

实际上解决这个问题,有一个可以优化的地方:

function A(name) 
    this.name = name;
    this.age = 3;

A.prototype.getName = function() 
  return this.name; 


function B(name) 
    A.call(this, name);

// 去掉这行
- B.prototype = new A();
// 添加这行
+ B.prototype = A.prototype;
B.prototype.setName = function(name) 
    this.name = name;


var b = new B('ls');

这个可以解决构造函数被执行了两次,但是实际上,到组合式继承目前为止,都没有解决子类对象原型constructor指向的问题。

3.4 原型式继承

function A(name) 
    this.name = name;
    this.age = 3;

A.prototype.getName = function() 
  return this.name; 

var a = new A('ls');
var b = Object.create(a);

对象的浅复制。

b.__proto__ = a;

这个思路好清奇,没有了构造函数,原型,怪怪的。

子类不能创建子类的方法,只能继承父类的方法。

3.5 寄生式继承

function A(name) 
    this.name = name;
    this.age = 3;

A.prototype.getName = function() 
  return this.name; 

var a = new A('ls');
function clone(a) 
    var obj = Object.create(a);
    obj.setName = function(name) 
        this.name = name;
    
    return obj;

b = clone(a);

寄生式继承在创建函数内部创建的函数(如上例sayName),不能做到函数复用。

3.6 寄生组合式继承

function A(name) 
    this.name = name;
    this.age = 3;

A.prototype.getName = function() 
  return this.name; 


function B(name) 
    A.call(this, name);

B.prototype = new A();
B.prototype.constructor = B;
B.prototype.setName = function(name) 
    this.name = name;


var b = new B('ls');

组合式继承的更进一步,解决了组合式继承的子类原型constructor不指向子类构造函数的问题。

目前是最好的继承方式。

4.参考

javascript》高级程序设计
https://www.zhihu.com/question/34183746

以上是关于原型链和继承的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript高级原型和继承相关:原型对象函数原型原型链和继承继承的优化对象判断相关方法

原型链和继承

对Javascript的原型,原型链和继承的个人理解

js原型链和继承的理解

关于原型原型链和原型继承的理解

原型 原型链和对象是怎么实现继承的