傻傻分不清的__proto__与prototype

今天同事小英童鞋问了我一个问题:segmentfault

function Foo(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName; 
}
Foo.prototype.logName = function(){
    Foo.combineName();
    console.log(this.fullName);
}
Foo.prototype.combineName = function(){
    this.fullName = `${this.firstName} ${this.lastName}`
}

var foo = new Foo('Sanfeng', 'Zhang');
foo.logName(); // Uncaught TypeError: Foo.combineName is not a function

小英童鞋认为Foo的原型对象是Foo.prototype,因此Foo会继承Foo.prototype的属性,调用Foo.combineName()至关于调用Foo.prototype.combineName(),但结果Foo.combineName()不是一个方法。浏览器

会形成这个问题的缘由必定是由于小英童鞋弄混了原型和继承的一些原理,下面咱们来整理一下原型和继承的相关原理,找出问题的根本缘由。函数

prototype

prototype是一个拥有 [[Construct]] 内部方法的对象才有的属性。this

例如函数,对象的方法,ES6 中的类。注意 ES6 中的箭头函数没有 [[Construct]] 方法,所以没有prototype这个属性,除非你为它添加一个。spa

当建立函数时,JavaScript 会为这个函数自动添加prototype属性,这个属性指向的是一个原型对象Functionname.prototype。咱们能够向这个原型对象添加属性或对象,甚至能够指向一个现有的对象。prototype

__proto__

接下来咱们说说继承,每一个对象都有一个__proto__属性,这个属性是用来标识本身所继承的原型。code

注意: JavaScript 中任意对象都有一个内置属性 [[Prototype]] ,在ES5以前没有标准的方法访问这个内置属性,可是大多数浏览器都支持经过__proto__来访问。如下统一使用__proto__来访问 [[Prototype]],在实际开发中是不能这样访问的。对象

原型链

JavaScript 能够经过prototype__proto__在两个对象之间建立一个关联,使得一个对象就能够经过委托访问另外一个对象的属性和函数。blog

这样的一个关联就是原型链,一个由对象组成的有限对象链,用于实现继承和共享属性。继承

构造函数建立对象实例

JavaScript 函数有两个不一样的内部方法:[[Call]][[Construct]]

若是不经过new关键字调用函数,则执行 [[Call]] 函数,从而直接执行代码中的函数体。

当经过new关键字调用函数时,执行的是 [[Construct]] 函数,它负责建立一个实例对象,把实例对象的__proto__属性指向构造函数的prototype来实现继承构造函数prototype的全部属性和方法,将this绑定到实例上,而后再执行函数体。

模拟一个构造函数:

function createObject(proto) {
    if (!(proto === null || typeof proto === "object" || typeof proto === "function"){
        throw TypeError('Argument must be an object, or null');
    }
    var obj = new Object();
    obj.__proto__ = proto;
    return obj;
}

var foo = createObject(Foo.prototype);

至此咱们了解了prototype__proto__的做用,也了解使用构造函数建立对象实例时这两个属性的指向,如下使用一张图来总结一下如何经过prototype__proto__实现原型链。

proto

从上图咱们能够找出foo对象和Foo函数的原型链:

foo.__proto__ == Foo.prototype;
foo.__proto__.__proto__ == Foo.prototype.__proto__ == Object.prototype;
foo.__proto__.__proto__.__proto__ == Foo.prototype.__proto__.__proto__ == Object.prototype.__proto__ == null;

foo

Foo.__proto__ == Function.prototype;
Foo.__proto__.__proto__ == Function.prototype.__proto__;
Foo.__proto__.__proto__.__proto__ == Function.prototype.__proto__.__proto__ == Object.prototype.__proto__ == null;

class-foo

构造函数Foo的原型链上没有Foo.prototype,所以没法继承Foo.prototype上的属性和方法。而实例foo的原型链上有Foo.prototype,所以foo能够继承Foo.prototype上的属性和方法。

到这里,咱们能够很简单的解答小英童鞋的问题了,在Foo的原型链上没有Foo.prototype,没法继承Foo.prototype上的combineName方法,所以会抛出Foo.combineName is not a function的异常。要想使用combineName方法,能够这样Foo.prototype.combineName.call(this),或者这样this.combineName()this指向实例对象)。

欢迎关注:Leechikit
原文连接:segmentfault.com

到此本文结束,欢迎提问和指正。写原创文章不易,若本文对你有帮助,请点赞、推荐和关注做者支持。

相关文章
相关标签/搜索