JS中的原型链

大家肯定都知道,JavaScript其实和Java并没有什么关系,当时推出JS时,只是为了趁一下当时如日中天Java大哥的热度。JS中的语法和数据结构太简单了,以至于很长一段时间内,JS程序员都被称为脚本员。而被如此嘲讽的原因,是因为当时的JS不存在“类”的这个概念的,更不用说“模组”这种东西了。

一等公民

在JS世界中,人们常说,函数是一等公民(first-class object),但为什么要这么说呢?

举个例子:

    > var a = 1
    > a.constructor
    < ƒ Number() { [native code] }

从上面的列子可以看出,a的值是1,属于Number类型,而以上的代码,等同于

    > var b = new Number(1)

当然,由于a是值类型,而此处b作为引用类型,它们不全等(因为 === 并不是比较值,而是比较内存地址)

JS的原生值类型包含以下几种

  • number
  • string
  • boolean
  • symbol (ES6新增)
  • null
  • undefined

以上的值类型中,除开 null 和 undefined 外,剩下的都可以使用使用函数进行创建。

再举个例子:

    > var a = {}
    > a.constructor
    < ƒ Object() { [native code] }

    > var b = new Object
    > a.constructor === b.constructor
    < true

上面的例子显示出,函数直接参与了对象的构建工作(constructor)。虽然JS并没有实际“类”的概念,但还是可以将之称为构造函数。

除了参与构建JS世界其他部分的工作外,函数自己也是这个世界的一部分。它无处不在,除了作为对象被引用,还经常作为参数被传递,偶尔还客串返回值。它的父类是Object,Object的父类是它。

    > Function.__proto__
    < ƒ () { [native code] }
    > Function.__proto__.__proto__
    < { Object native }

    > Object.__proto__
    < ƒ () { [native code] }
    > Object.__proto__.__proto__
    < { Object native }

_proto_ 和 prototype

讨论JS对象,绕不开的就是 _proto_ 和 prototype,除了 undefined 和 null 以外,JS中所有对象都含有 _proto_ 属性,那么这个属性是干嘛的?

让我们先写一个函数:

    function Person(name) { console.log(`I'm ${name}`) }

单独执行它:

    > Person('EvinK')
      I'm EvinK
    < undefined

执行该函数时,输出了一句话,返回了一个 undefined

加上 new 关键字后,再执行一次:

    function Person(name) {
        console.log(`I'm ${name}`)
        this.name = name
    }

    > var person = new Person('EvinK')
      I'm EvinK
    < Person {name: "EvinK"}

发现了吗?new关键字调用函数时,该函数返回了一个以它自身为原型的对象。同时,在执行__Person__函数的过程中,将挂载于Person作用域下的属性,也一同挂载到生成对象上(函数闭包)。

现在,让我们看下__person__的属性

    Person {name: "EvinK"}
    ->   name: "EvinK"
    ->   __proto__:
         ->   constructor: ƒ Person(name)
         ->   __proto__: Object

除了挂载到身上的name属性外,person__的_proto___属性暴露了它的原型 —— Person函数,而我们亦可以沿着 _proto 属性一直深究下去,会发现,对象person 的原型是 Person函数, 对象person 原型的原型是 Object函数,而再往后面就是null了,也就意味着这条 原型链 终止了。

总结一下,对象中的 ___proto_属性__指向的是自身原型的 prototype属性, 并且依靠条链接层层递进,直到找到最终原型函数Object。