从数值包装对象理解原型链
JavaScript 里的一切都可以被当作对象。原始类型也是可以的。
我们来新建一个数值,新建数值有两种方法:
一个是
var n1 = 1
一个是
var n2 = new Number(1)
两种方案都是表示数值 1,只是 n1 是原始类型的数值,而 n2 是数值包装对象。
var n1 = 1 |
所以它俩的内存图是这样的:
n1 是原始类型所以值是存在栈里面的,而 n2 是对象复杂类型,它是在栈里存了一个内存地址(假设地址为 ADDR 35),并把值 1 存在了堆中地址为 35 的位置。
地址 35 中存的 Number 实例对象,它除了存值为 1 的原始值外,还有一个 __proto__
的对象。
__proto__ 对象
__proto__
对象下包含了所有 Number 实例对象的共有属性。所有 Number 实例对象都会有一个 __proto__
指向同一块内存。
var nn = new Number(0) |
可以看出 nn 的 __proto__
对象下有 constructor 指向到 Number 函数, 然后还有 Number 实例的几个方法:toExponential、toFixed、toLocaleString、toPrecision、toString、valueOf,然后还有对象的 __proto__
。而对象的 __proto__
除了 constructor 函数,还包含实例方法 hasOwnProperty、isPropertypeOf、propertyIsEnumerable、toLocaleString、toString、valueOf。
nn 有自己的 toString、toLocaleString、valueOf,如果我们执行 nn.toString(),执行的就是 Number.prototype.toString 方法,假设 Number 的原型对象没有 toString 方法,就会去原型链的上一级 Object.prototype 里面找 toString,就会执行这个方法,假设 Object.prototype 也没有,JavaScript 引擎会继续往上找,不过发现已经到了原型链的顶端 null 了,于是就返回 undefined。
nn 的共有属性包含对象的共有属性。即 nn.__proto__.__proto__
而 实例对象.__proto__
又对应的对象.prototype
。
这里形成的原型链就是:
nn --.__proto__--> Number.prototype --.__proto__--> Object.prototype --.__proto__--> null
可以得出这样一颗原型链树
实例对象与「对象」的关系
Number.prototype 里 Number 即构造函数(这里的「对象」),构造函数.prototype === 实例对象.__proto__
,Number 的实例对象是 nn,那 Number 又是谁的实例对象了?我们声明一个 Number 包装对象的数值是 var nn = new Number(0)
,Number 是一个函数,可以得知 Number 的构造函数就是 Function。所以我们又可以画一颗树:
最后总结为:
构造函数.prototype.__proto__ === Object.prototype
(除 Object 以外的构造函数,因为Object.prototype.__proto__
是原型链顶点 null)构造函数.__proto__ === Function.prototype
套用一下 比如 构造函数是 Function
Function.prototype.__proto__ === Object.prototype |
又比如构造函数是 Object
Object.prototype.__proto__ === null |