JS中的原型链
大家肯定都知道,JavaScript其实和Java并没有什么关系,当时推出JS时,只是为了趁一下当时如日中天Java大哥的热度。JS中的语法和数据结构太简单了,以至于很长一段时间内,JS程序员都被称为脚本员。而被如此嘲讽的原因,是因为当时的JS不存在“类”的这个概念的,更不用说“模组”这种东西了。
一等公民
在JS世界中,人们常说,函数是一等公民(first-class object),但为什么要这么说呢?
举个例子:
> var a = 1
> a.constructor
< ƒ Number() { [native code] }
从上面的列子可以看出,
> var b = new Number(1)
当然,由于
JS的原生值类型包含以下几种
- number
- string
- boolean
- symbol (ES6新增)
- 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
加上
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。