JavaScript 原型链中的__proto__

前端 · 2021-07-13 ·

在 JavaScript 的世界里,一切皆对象,但为了理解原型链相关知识,我们需要将 JavaScipt 的对象分为 对象函数 两大类。在此基础上,JavaScript 的原型链逻辑遵从一下通用规则:

  1. 对象__proto__ 属性,函数prototype 属性
  2. 对象函数 生成
  3. 生成 对象 时,对象__proto__ 属性指向 函数prototype 属性

一. 一般情况

    // 创建空对象时,实际上我们使用 Object 函数来生成对象的
    var o1 = {};
    console.log(o1.__proto__ === Object.prototype); // true

    // 通过使用构造函数 Object() 创建对象
    var o2 = Object();
    console.log(o2.__proto__ === Object.prototype); // true

    // 使用函数创建自定义的对象时
    function Foo(){

    }
    console.log(typeof Foo); // function

    var f1 = new Foo();
    console.log(f1.__proto__ === Foo.prototype) // true

二. 函数对象

既然 JavaScript 里“一切结对象”,那 函数 自然 也是对象的一种,对于 函数 作为 对象 来说,上面的通用规则同样实用:

1.自定义函数

function Foo(){

}
console.log(Foo.__proto__ === Function.prototype) // true

2.Function 函数

// Function 函数本身作为对象时,生成它的函数是它自身!
console.log(Function.__proto__ === Function.prototype) // true

3.Object 函数

console.log(Object.__proto__ === Function.prototype) // true

三. prototype

对象的 __proto__ 属性是从生成它的函数的 prototype 那里得来的,一般函数默认的 prototype 是系统自定生成的一个对象:

>function fn(){}
>typeof fn.prototype
"object"
>fn.prototype
{constructor: ƒ}
    constructor: ƒ fn()
    __proto__: Object

>fn.prototype.constructor === fn
true
>fn.prototype.__proto__ === Object.prototype
true

一般函数默认的 prototype 是一个类型为 "object" 的对象,它有2个属性,constructor__proto__。其中 constructor 属性指向这个函数本身,__proto__ 属性指向 Object.prototype,这说明一般函数的 prototype 属性是由 Object 函数生成的。

四. 非一般函数(Object 函数和 Function 函数)

  1. Object.prototype
>typeof Object.prototype
"object"
>Object.prototype
{
    constructor: ƒ Object()
    hasOwnProperty: ƒ hasOwnProperty()
    isPrototypeOf: ƒ isPrototypeOf()
    propertyIsEnumerable: ƒ propertyIsEnumerable()
    toLocaleString: ƒ toLocaleString()
    toString: ƒ toString()
    valueOf: ƒ valueOf()
    __defineGetter__: ƒ __defineGetter__()
    __defineSetter__: ƒ __defineSetter__()
    __lookupGetter__: ƒ __lookupGetter__()
    __lookupSetter__: ƒ __lookupSetter__()
    get __proto__: ƒ __proto__()
    set __proto__: ƒ __proto__()
}

可以看到 object 函数的 prototype 属性也是一个类型为 "object" 的对象,但是和一般函数的默认的 prototype 属性不一样的是,它多了些方法,这些方法都是 JavaScript 对象的系统默认方法,少了个很重要的属性__proto__,我们试着把它的 __proto__属性输出。

>Object.prototype.__proto__
null

这就是 Object 函数的特殊情况了,Object.prototype.__proto__ === null,这就是 JavaScript 原型链的终点了。

为什么要这样设定呢?

typeof Object.prototype === 'object',说明它(Object.prototype)是一个 Object 对象,那 Object 对象有 Object 函数生成,于是按照通用规则,就可以得出 Object.prototype.__proto__ === Objetc.prototype,但是问题出现了,Object.prototype.__proto__ 属性指向了它自身,这样以 __proto__ 属性构成的原型链就没有终点了,所以为了让原型有终点,在原型的最顶端,JavaScript 规定了 bject.prototype.__proto__ === null。

2.Function 函数

> typeof Function.prototype
"function"

Funtion 函数的 prototype 属性 是一个 "function" 类型的对象

> Function.protype
ƒ () { [native code] }

函数内部是 [native code],也就是系统编译好的二进制代码函数,现在看下 __proto__ 属性:

> Function.prototype.__proto__
{
    constructor: ƒ Object()
    hasOwnProperty: ƒ hasOwnProperty()
    isPrototypeOf: ƒ isPrototypeOf()
    propertyIsEnumerable: ƒ propertyIsEnumerable()
    toLocaleString: ƒ toLocaleString()
    toString: ƒ toString()
    valueOf: ƒ valueOf()
    __defineGetter__: ƒ __defineGetter__()
    __defineSetter__: ƒ __defineSetter__()
    __lookupGetter__: ƒ __lookupGetter__()
    __lookupSetter__: ƒ __lookupSetter__()
    get __proto__: ƒ __proto__()
    set __proto__: ƒ __proto__()
}

和 Object.prototype 一样,验证下

> Function.prototype.__proto__ === Object.prototype
true

按照通用规则,一个 "function" 类型的对象,应该是由 Function 函数生成的,那么 它(Function.prototype)的__proto__ 应该指向 Function.prototype,也就是 Function.prototype.__proto__ === Function.prototype。和 Object 函数同样的问题出现了,指向自身。所以 JavaScript 规定 Function.prototype.__proto__ === Object.prototype。

%