今天同事小英童鞋问了我一个问题: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__
实现原型链。
从上图咱们能够找出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.__proto__ == Function.prototype; Foo.__proto__.__proto__ == Function.prototype.__proto__; Foo.__proto__.__proto__.__proto__ == Function.prototype.__proto__.__proto__ == Object.prototype.__proto__ == null;
构造函数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到此本文结束,欢迎提问和指正。写原创文章不易,若本文对你有帮助,请点赞、推荐和关注做者支持。