探秘Javascript对象

61 阅读2分钟

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 属性来自其构造函数的原型对象上,并指向构造函数自身。这也表明了,对象皆通过函数构造

对于通过字面量声明BooleanNumber 或 String类型的数据,在读取时,后台会进行下列的处理:

  1. 通过构造函数创建该类型的一个实例对象;
  2. 在实例上进行操作;
  3. 销毁实例。

因此,即便是原始类型的数据,也可以访问对应类型和 Object 原型对象上的属性。值得注意的是,这些数据设置的属性因为后台实例的销毁而无效。实例如下:

var a = 1
console.log(a.toString()) // '1'

a.foo = 'foo'
console.log(a.foo) // undefined