本文写于 2020 年 5 月 23 日

    首先看一个问题:let obj = { name: '孙悟空' }的原型是谁?

    obj.__proto__

    let arr = [1, 2, 3]的原型是谁?

    arr.__proto__

    Object的原型谁是?

    Object.__proto__

    他们都不是prototype,只是正好__proto__等价于某个函数的prototype

    1. 对象的__proto__属性,就是其构造函数的prototype
    2. 任何函数的__proto__都是Functionprototype——包括Object()函数、Array()函数、Function()函数等等;
    3. Object.prototype是所有对象的直接或者间接原型。

    这里有个问题,如果说,Function()函数是造世的函数,那为什么说Object.prototype是所有对象的直接或者间接原型呢?

    其实很简单,因为Function()只是构造出了Object.prototype(指针),但是并没有构造出——Object.prototype所指向的真正对象。


    让我们从头开始一下:

    当我们的 JS 代码还没运行的时候,JS 环境里已经有了一个 window 对象。

    1. window 对象有一个 Object 属性,window.Object 是一个函数;
    2. window.Object 这个函数有一个重要属性是 prototype
    3. window.Object.prototype 里面有这么几个属性 toString(函数)、valueOf(函数)。

    然后我们写一点代码

    1. var obj = {}
    2. obj.toString()

    这句代码做了啥?

    为什么 objtoString() 属性?我们分明没有给她一个toString()方法呀!

    在 JS 中,这句话大概是让 obj 变量指向一个空对象,这个空对象有个 __proto__ 属性指向 window.Object.prototype

    这样你在调用 obj.toString() 的时候,obj 本身没有 toString,就会去 obj.__proto__ 上面去找 toString

    所以调用 obj.toString 的时候,实际上调用的是 window.Object.prototype.toString

    那么问题又来了:

    window.Object.prototype.toString 是怎么获取 obj 的内容的呢?

    因为 obj.toString() 等价于 obj.toString.call(obj)JS 又偷偷的帮我们做事了!

    同时 obj.toString.call(obj) 等价于 window.Object.prototype.toString.call(obj)

    正是这样,JS 就悄咪咪的完成了 obj.toString()

    再来看一下数组:

    1. var arr = []
    2. arr.push(1) // [1]

    其实和对象是一样的,并且 JS 的数组本来就是对象伪装的

    var arr = [] 会让 arr 指向一个空对象,然后 arr.__proto__ 指向 window.Array.prototype

    (其实 arr 还有一个 length: 0属性)

    所以我们在调用 arr.push 的时候,arr 发现自身没有 push 属性,就前往 arr.__proto__ 上找 push

    因此 arr.push 实际上是 window.Array.prototype.push

    并且:

    • arr.push(1) 等价与 arr.push.call(arr,1)
    • arr.push.call(arr,1) 等价于 window.Array.prototype.push.call(arr, 1)

    看,JavaScript 的原型模式其实很优美很简单!


    反证一下:

    假设我们把 __proto__ 去掉,那么

    1. var obj = {}
    2. obj.toString() // 报错,没有 toString 方法

    所以只能这样声明一个对象:

    1. var obj = {
    2. toString: window.Object.prototype.toString,
    3. valueOf: window.Object.prototype.valueOf
    4. }
    5. obj.toString() // '[object Object]'

    现在知道 __proto__ 帮你省多少代码了吧?

    假设我们删掉 prototype,包括 window.Object.prototypewindow.Array.prototype

    那么 window.Object.prototype.toString 也一并被删除了。

    然后我们基本就没法写代码了……全都得自己重新来做!

    1. var obj = {}
    2. obj.toString() // toString 不存在,因为 toString 没有定义过啊

    prototype 的意义就是把共有属性预先定义好,给之后的对象用。

    总结:

    1. prototype 指向一块内存,这个内存里面有共用属性,而 __proto__ 指向同一块内存;
    2. prototype__proto__ 的不同点在于:prototype构造函数的属性,而 __proto__ 是对象的属性;
    3. 如果没有 prototype,那么共用属性就没有立足之地;
    4. 如果没有 __proto__,那么一个对象就不知道自己的共用属性有哪些。

    (完)