您的位置:首页 > Web前端 > JavaScript

javascript中的 prototype, __proto__, constructor 与 原型继承链

2016-09-16 21:44 676 查看

javascript中的 prototype,
__proto__
, constructor 与 原型继承链

综述

三个值的含义

每个对象都有自己的的
__proto__
,这个属性指向自己的构造函数(
constructor
)的
prototype
,即

obj.__proto__ === obj.constructor.prototype


在javascript中只有函数有自己的
prototype


大部分对象并没有自己的
prototype
,因为
prototype
表示实例共享的属性,而只有函数可以生成实例,所以…

这里要格外强调一下constructor

constructor并不是实例自己的属性,每个实例的constructor都是一样的,指向构造函数

constructor是原型的属性,因此如果改动原型(重新给原型赋值),会导致constructor不正确

例如:

function a() {}
function b() {}

b.prototype = new a();

console.log((new a()).constructor); //a()
console.log((new b()).constructor); //a()


那么,这个东西有什么用呢?

答:大部分情况是无用的,具体可参考知乎的讨论JavaScript 中对象的 constructor 属性的作用是什么?

javascript中什么是对象

javascript中所有非字面值都是对象

字面值中函数和对象仍然是对象,可以使用
{} instanceof Object
来验证

注意这里我强调了字面值这个概念,如下

// 文中所有测试环境均为 firefox 48
console.log("" instanceof Object); //false
console.log(null instanceof Object); //false
console.log(undefined instanceof Object); //false
console.log(1 instanceof Object); //false
console.log(true instanceof Object); //false
function foo() {}
console.log(foo instanceof Object); //true
console.log({} instanceof Object); //true


字面值应使用
typeof
去判断类型

instanceof
测试一个对象在其原型链构造函数上是否具有prototype属性。

这与python是不同的,python中一切都是对象,包括字面值

具体可查阅相应的MDN文档

深入探讨

从上述中其实就可以对javascript的原型链略知一二,为了更好的看清各个属性的关系,我们来看看一些数据

// print作用是将每个对象的属性直接用console.log输出
function foo() {}
var ff = new foo();
var o = {};
print([ff, foo, Function, o, Object]);
print([foo.prototype, Function.prototype, Object.prototype]);


整理得出的表格如下:

selfprototypeconstructor
__proto__
foo {}undefinedfoo()foo {}
foo()foo {}Function()function()
Function()function()Function()function()
Object {}undefinedObject()Object {}
Object()Object {}Function()function()
上述表格中,可以一行行分析,可以看出

ff是实例对象,所以无原型

函数的构造器是Function()

Function()也是自己的构造器,其
prototype
等于
__proto__
(代码并没有验证这一点,但是可以测试得出此结论)

普通对象由Object()函数生成,而Object()的构造器是Function()

可能会有几个问题

ff.__proto__
foo {}
,也就是
foo.prototype
的原型,这是什么?

Function()的原型function()究竟是个什么东西?

解答第一个问题:

当然,原型是个普通对象,所以第二行的
foo {}
和第五行的
Object {}
都是同一种对象,那为什么有一个是foo而另一个是Object呢?

可以做个小实践,用函数表达式赋值给一个变量,会生成一个匿名函数,检查这个变量的
__proto__
发现也是Object{}

所以,其实foo就是这个对象的名字,用console.log输出出来就把名字附加上了

我们来直接来看看上述所有原型的这三个属性

selfprototypeconstructor
__proto__
foo {}undefinedfoo()Object {}
function()undefinedFunction()Object {}
Object {}undefinedObject()null
可以看出:

函数原型是对象,函数原型的构造器就是函数本身

从这个表格,也看出了原型链,即:

ff.__proto__(foo {}) --> foo.prototype.__proto__(Object {}) --> foo.prototype.__proto__.__proto__(null)


foo.__proto__(function()) --> foo.__proto__.__proto__(Object {}) --> foo.__proto__.__proto__.__proto__(null)


o.__proto__(Object{}) --> o.__proto__.__proto__(null)


原型链的尽头(root)是Object.prototype。所有对象均从Object.prototype继承属性。

细心的人可能发现,上面出现了一个关于鸡和蛋的问题:

函数是对象:函数Function的原型链(与foo的原型链一致)上出现了Object{}(Object的原型)

对象由函数生成:Object本身就是一个函数(这从第一个表中Object的con
a770
structor就是Function可以看出)

你也可以用代码验证:

console.log(Function instanceof Object); //ture
console.log(Object instanceof Function); //true


那么问题来了,究竟是先有Object还是Function呢?

很多语言里都存在一个鸡和蛋的问题

如何实现可以看知乎的讨论先有Class还是先有Object?,摘文中的前几段

简短答案:“鸡・蛋”问题通常都是通过一种叫“自举”(bootstrap)的过程来解决的。
其实“鸡蛋问题”的根本矛盾就在于假定了“鸡”或“蛋”的其中一个要先进入“完全可用”的状态。而许多现实中被简化为“鸡蛋问题”的情况实际可以在“混沌”中把“鸡”和“蛋”都初始化好,而不存在先后问题;在它们初始化的过程中,两者都不处于“完全可用”状态,而完成初始化后它们就同时都进入了可用状态。
打个比方,番茄炒蛋。并不是要先把番茄完全炒好,然后把鸡蛋完全炒好,然后把它们混起来;而是先炒番茄炒到半熟,再炒鸡蛋炒到半熟,然后把两个半熟的部分混在一起同时炒熟。


什么情况下会出现鸡和蛋的问题呢?

就是声明一个包含所有集合的集合啊!好了,你们知道这是罗素悖论,但并不妨碍PL中这样设计。

Function.prototype

Function.prototype是个不同于一般函数(对象)的函数(对象)(作为一个函数,并没有原型)

ECMAScript Specification的Function.prototype摘录几点:

The initial value of Function.prototype is the standard built-in Function prototype object (15.3.4).
The Function prototype object is itself a Function object (its [[Class]] is "Function") that, when invoked, accepts any arguments and returns undefined.
The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object (15.2.4). The initial value of the [[Extensible]] internal property of the Function prototype object is true.
The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object.
The length property of the Function prototype object is 0.


一张比较经典的图是:



近期,在知乎发现了画得非常好而且全面的图,可以参考此篇文章Javascript的原型链图(原创 知乎首发)

参考文章:

proto和prototype来深入理解JS对象和原型链
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript 对象 继承