undefined 和 null 不在本文讨论范围。对于其他类型的数据,我们可以说:
- 它们都继承自Object
- 它们都是通过函数构造
继承自 Object
所有的对象都有一个隐藏属性__proto__,这个属性指向将其构造出来的函数的原型对象。即:
obj.__proto__ === Func.prototype
下面是一些实例:
var a = 1
var b = {}
var c = function() {}
var d = new Date()
console.log(a.__proto__ === Number.prototype) // true
console.log(b.__proto__ === Object.prototype) // true
console.log(c.__proto__ === Function.prototype) // true
console.log(d.__proto__ === Date.prototype) // true
对象可以直接访问原型对象上的属性,在上面的实例中,a, b, c, d 可以分别访问 Number, Object, Function, Date 原型对象上定义的属性。同时,通过原型链的延伸,这些对象可以访问更远端的原型对象上的属性。
console.log(a.__proto__.__proto__ === Object.prototype) // true
console.log(b.__proto__.__proto__ === null) // true
console.log(c.__proto__.__proto__ === Object.prototype) // true
console.log(d.__proto__.__proto__ === Object.prototype) // true
可以看到,a, b, c, d 的原型链最终都会指向 Object 的原型对象,也就是它们都继承自Object,而原型链的末端是以 null 结束的,即:
Number.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
Date.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
我们不妨将 Object 原型对象上的属性列举出来,这些属性会被所有的数据所继承:
{
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
}
通过函数构造
函数是一类特殊的对象,它的特殊之处在于,所有的对象都是通过函数构造出来的。
函数创建后,都会有一个原型对象 prototype,这个对象里存在一个属性 constructor,指向这个函数本身。观察以下实例:
var a = 1
var b = {}
var c = function() {}
var d = new Date()
var e = Object
console.log(a.constructor === Number) // true
console.log(b.constructor === Object) // true
console.log(c.constructor === Function) // true
console.log(d.constructor === Date) // true
console.log(e.constructor === Function) // true
一般情况下,对象的 constructor 属性来自其构造函数的原型对象上,并指向构造函数自身。这也表明了,对象皆通过函数构造。
对于通过字面量声明Boolean、Number 或 String类型的数据,在读取时,后台会进行下列的处理:
- 通过构造函数创建该类型的一个实例对象;
- 在实例上进行操作;
- 销毁实例。
因此,即便是原始类型的数据,也可以访问对应类型和 Object 原型对象上的属性。值得注意的是,这些数据设置的属性因为后台实例的销毁而无效。实例如下:
var a = 1
console.log(a.toString()) // '1'
a.foo = 'foo'
console.log(a.foo) // undefined