JavaScript原型系列(三)Function、Object、null等等的关系和鸡蛋问题

5,994 阅读5分钟

將欲廢之,必固興之;將欲奪之,必固與之。——《道德經》

JavaScript原型系列(一)构造函数、原型和原型链

JavaScript原型系列(二)什么是原型继承

JavaScript原型系列(三)Function、Object、Null等等的关系和鸡蛋问题

简介

JavaScript-prototype

基本上都知道原型链的尽头指向null,那么Function.prototypeObject.prototypenullFunction.prototype.__proto__Object.prototype.__proto__function、object之间的关系是什么,下面慢慢来记录一下。

Object


Object 构造函数创建一个对象包装器。JavaScript中的所有对象都来自 Object;所有对象从Object.prototype继承方法和属性,尽管它们可能被覆盖。

Object 作为构造函数时,其 [[Prototype]] 内部属性值指向 Function.prototype

    Object.__proto__ === Function.prototype; // true

JavaScript-prototype

Object.prototype

Object.prototype 表示 Object 的原型对象,其 [[Prototype]] 属性是 null,访问器属性 __proto__ 暴露了一个对象的内部 [[Prototype]]Object.prototype是浏览器底层根据 ECMAScript 规范创造的一个对象。

object

通过字面量实例化一个object,它的__proto__指向Object.prototype

    var obj = {};
    obj.__proto__ === Object.prototype; // true

而通过new Object实例化一个object,它的__proto__指向Object.prototype

    var obj = new Object;
    obj.__proto__ === Object.prototype; // true

Function


摘录来自ECMAScript 5.1规范

对象类型的成员,标准内置构造器 Function的一个实例,并且可做为子程序被调用。 注: 函数除了拥有命名的属性,还包含可执行代码、状态,用来确定被调用时的行为。函数的代码不限于 ECMAScript。

Function构造函数创建一个新的Function对象。在JavaScript中每个函数实际上都是一个Function对象。

Function.prototype

全局的Function对象没有自己的属性和方法, 但是因为它本身也是函数,所以它也会通过原型链从Function.prototype上继承部分属性和方法。Function.prototype也是一个“函数对象“,其[[prototype]]内部属性值指向Object.prototype

Function.prototype 的 [[Class]] 属性是 Function,所以这是一个函数,但又不大一样。

    Function.prototype; // ƒ () { [native code] }
    Function.prototype.prototype; // undefined

用 Function.prototype.bind 创建的函数对象没有 prototype 属性。

    let foo = Function.prototype.bind();
    foo.prototype; // undefined

Function.prototype 是引擎创建出来的函数,引擎认为不需要给这个函数对象添加 prototype 属性,不然 Function.prototype.prototype… 将无休无止并且没有存在的意义。

Function.prototype.__proto__指向Object.prototype

    Function.prototype.__proto__ === Object.prototype; // true

Function.proto

Function 构造函数是一个函数对象,其 [[Class]] 属性是 FunctionFunction[[Prototype]] 属性指向了 Function.prototype,即

    Function.__proto__ === Function.prototype; // true

JavaScript-prototype

function

实例化一个Function,它的__proto__指向Function.prototype

    function foo () {}
    foo.__proto__ === Function.prototype; // true
    // foo.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null

Object和Function的鸡和蛋的问题


经过上面对ObjectFunction的阐述,延伸出来几个问题如下:

  • 在忽滤null在原型链上时,原型链的尽头(root)是Object.prototype。所有对象均从Object.prototype继承属性。

    JavaScript-prototype

  • Function.prototypeFunction.__proto__为同一对象。

    JavaScript-prototype
    这意味着: Object/Array/String等等构造函数本质上和Function一样,均继承于Function.prototype

  • Function.prototype直接继承root(Object.prototype)。

    // Function.prototype继承了Object.prototype
    Function.prototype.__proto__ === Object.prototype; // true
    Function.prototype instanceof Object; // true
    Function.prototype instanceof Function; // false

    // Object Array Function 等等构造函数继承了Function.prototype
    Function instanceof Function; // true
    Array instanceof Function;  // true
    Object instanceof Function; // true
    Function instanceof Object; // true

通过上面代码知道继承的原型链大致是: Object.prototype(root)<---Function.prototype<---Function|Object|Array...

上面的会出现一个比较奇特的现象如下:

  • 第一问
    Function.__proto__ === Function.prototype;

Function对象是不是由Function构造函数创建的实例?

  • 第二问
    Function instanceof Object; // true
    Object instanceof Function; // true
    Object instanceof Object; // true
    Function instanceof Function; // true

为什么Function instanceof ObjecttrueObject instanceof Function也为true,那么他们到底是什么关系?

解答


先要了解清楚Function.prototypeObject构造函数如下: 回归规范,摘录2点:

  • Function.prototype是个不同于一般函数(对象)的函数(对象)。

The Function prototype object is itself a Function object (its [[Class]] is "Function") that, when invoked, accepts any arguments and returns undefined. The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object (15.2.4). The initial value of the [[Extensible]] internal property of the Function prototype object is true. The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object.

上面的可以总结为:

  • Function.prototype像普通函数一样可以调用,但总是返回undefined

  • 普通函数实际上是Function的实例,即普通函数继承于Function.prototypefunc.__proto__ === Function.prototype

  • Function.prototype继承于Object.prototype,并且没有prototype这个属性。func.prototype是普通对象,Function.prototype.prototypenull

  • 所以,Function.prototype其实是个另类的函数,可以独立于/先于Function产生。

  • Object本身是个(构造)函数,是Function的实例,即Object.__proto__就是Function.prototype

The value of the [[Prototype]] internal property of the Object constructor is the standard built-in Function prototype object. The value of the [[Prototype]] internal property of the Object prototype object is null, the value of the [[Class]] internal property is "Object", and the initial value of the [[Extensible]] internal property is true.

第一问

Function对象是由Function构造函数创建的一个实例?

Yes 的部分: 按照 JavaScript 中“实例”的定义,ab的实例即 a instanceof btrue,默认判断条件就是 b.prototypea 的原型链上。而 Function instanceof Function 为 true,本质上即 Object.getPrototypeOf(Function) === Function.prototype,正符合此定义。

No 的部分: Functionbuilt-in 的对象,也就是并不存在“Function对象由Function构造函数创建”这样显然会造成鸡生蛋蛋生鸡的问题。实际上,当你直接写一个函数时(如 function f() {}x => x),也不存在调用 Function 构造器,只有在显式调用 Function 构造器时(如 new Function('x', 'return x') )才有。

个人偏向先有的Function.prototype,再有的function Function,所有构造函数本质上都是集成于Function.prototype ,所以Function.__proto__ === Function.prototype

第二问

    // Function.__proto__、Function.prototype指向同一个对象,Function.prototype.__proto__指向Object.prototype
    // Function.__proto__ => Function.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null
    Function instanceof Object; // true
    // Object作为构造函数继承自Function.prototype
    // Object.__proto__ => Function.prototype
    Object instanceof Function; // true
    // Object作为构造函数继承自Function.prototype,Function.prototype__proto__指向Object.prototype
    // Object.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype
    Object instanceof Object; // true
    // Function构造函数也是继承自Function.prototype
    // Function.__proto__ => Function.prototype
    Function instanceof Function; // true

总结一下:先有 Object.prototype(原型链顶端),Function.prototype 继承 Object.prototype 而产生,最后,FunctionObject 和其它构造函数继承 Function.prototype 而产生。

总结


  • Object.prototype是浏览器底层根据 ECMAScript 规范创造的一个对象。
  • Function.prototype直接继承的Object.prototype,同样它也是由是引擎创建出来的函数,引擎认为不需要给这个函数对象添加 prototype 属性。Function.prototype.prototypeundefined
  • 先有 Object.prototype(原型链顶端),Function.prototype 继承 Object.prototype 而产生,最后,FunctionObject 和其它构造函数继承 Function.prototype 而产生。

参考

MDN Object.prototype

MDN Function.prototype

从__proto__和prototype来深入理解JS对象和原型链

从探究Function.proto===Function.prototype过程中的一些收获

【进阶5-3期】深入探究 Function & Object 鸡蛋问题