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

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()

上述表格中,可以一行行分析,可以看出

  1. ff是实例对象,所以无原型
  2. 函数的构造器是Function()
  3. Function()也是自己的构造器,其prototype等于__proto__(代码并没有验证这一点,但是可以测试得出此结论)
  4. 普通对象由Object()函数生成,而Object()的构造器是Function()

可能会有几个问题

  1. ff.__proto__foo {},也就是foo.prototype的原型,这是什么?
  2. Function()的原型function()究竟是个什么东西?

解答第一个问题:

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

  • 可以做个小实践,用函数表达式赋值给一个变量,会生成一个匿名函数,检查这个变量的__proto__发现也是Object{}
  • 所以,其实foo就是这个对象的名字,用console.log输出出来就把名字附加上了

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

selfprototypeconstructor__proto__
foo {}undefinedfoo()Object {}
function()undefinedFunction()Object {}
Object {}undefinedObject()null

可以看出:

  1. 函数原型是对象,函数原型的构造器就是函数本身
  2. 从这个表格,也看出了原型链,即:
    • 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)
  3. 原型链的尽头(root)是Object.prototype。所有对象均从Object.prototype继承属性。

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

  • 函数是对象:函数Function的原型链(与foo的原型链一致)上出现了Object{}(Object的原型)
  • 对象由函数生成:Object本身就是一个函数(这从第一个表中Object的constructor就是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对象和原型链

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值