高能!typeof Function.prototype 引起的先有 Function 仍是先有 Object 的探讨

来个摸底测试,说出如下每一个表达式的结果浏览器

function F(){};
var o = {};

typeof F;
typeof o;
typeof F.prototype;
typeof o.prototype;
typeof new F;
typeof (new F).prototype;
typeof (new F).__proto__;
typeof F.__proto__;
typeof o.__proto__;
typeof Object;
typeof Function;
typeof (new Function).prototype;
typeof (new Function).__proto__;
typeof (new Object).prototype;
typeof (new Object).__proto__;
typeof Object.prototype;
typeof Object.__proto__;
typeof Function.prototype;
typeof Function.__proto__;

function F(){};
var o = {};
                
typeof F;                          //==> function
typeof o;                          //==> object
typeof F.prototype;                //==> object
typeof o.prototype;                //==> undefinded
typeof new F;                      //==> object
typeof (new F).prototype;          //==> undefined
typeof (new F).__proto__;          //==> object
typeof F.__proto__;                //==> function
typeof o.__proto__;                //==> object
typeof Object;                     //==> function
typeof Function;                   //==> function
typeof (new Function).prototype;   //==> object
typeof (new Function).__proto__;   //==> function
typeof (new Object).prototype;     //==> undefined
typeof (new Object).__proto__;     //==> object
typeof Object.prototype;           //==> object
typeof Object.__proto__;           //==> function
typeof Function.prototype;         //==> function
typeof Function.__proto__;         //==> function

看到这里相信有很多入门不久的同窗已经产生疑惑了 是真的吗 而后在浏览器试过一番发现真是如此。数据结构

解开疑惑以前先回顾些你们都知道的知识点:函数

引用 MDN 关于 对象实例和对象原型对象 的阐述:学习

JavaScript语言的全部对象都是由Object衍生的对象;
全部对象都继承了Object.prototype的方法和属性,尽管它们可能被覆盖。
例如,其它的构造器原型覆盖了constructor属性并提供了其本身的toString方法。
原型对象的更改会传播给全部的对象,除非这些属性和方法在原型链中被再次覆盖。测试

就如咱们常常在各种教科中看到的 全部的实例对象都是 Object 类型的实例this

那么咱们平时都是如何肯定一个对象是不是另外一个类型或对象的实例的呢?spa

对咱们可使用 typeof 关键字 亦或可使用关键字 instanceof 来肯定某个对象是不是指定类型或对象的实例:prototype

typeof {} //object
({}) instanceof Object //true

typeof Date                      //function
Date instanceof Function         //true
typeof Date.prototype            //obejct
Date.prototype instanceof Object //true

然而针对 Object 的 prototype 属性:code

typeof Object.prototype //object

Object.prototype instanceof Object // false

为何,要想搞清楚为何就得明白 instanceof 这个关键字在表达式中发生了什么?对象

在弄清楚 instanceof 以前 还得弄清楚同样东西 就是 new 一个对象到底作了什么:

如 var a = new A(); 认为 “a为A函数的实例对象”

new操做的过程是什么? 能够总结以下:

1.new 建立一个空对象{}
2.而后将A.prototype的引用放置到该对象的原型链上。即a.__proto__指向 A.prototype
3.执行A函数,将A中this指向该对象,执行结束,若是没有return那么默认返回this引用

那么new的其中一个的做用即是把A.prototype的指向添加到了a的原型链中。

至此咱们便知道了以下关系:

a.__proto__ === A.prototype //true

a instanceof A //true

故为什么不得出一个结论:

instanceof 操做符其实就是检查左侧的元素的 __proto__链上有没有右侧类或对象的prototype存在。
同理 当某某某是某某某的实例时 其实也是证实左侧的__proto__链上有右侧类或对象的prototype存在。

细节剖析以下:

1.看右侧的 A 获取其 prototype 获得 A.prototype。
2.看左侧 a 对象的原型链上是否有第一步获得 A.prototype。
1)获取 a.__proto__对象看是否为A.prototype,是则返回 true
2)获取 a.__proto__.__proto__ 对象看是否为A.prototype,是则返回 true
3)重复获取左侧的原型链上的[[Prototype]]特性即__proto__属性进行判断直到为空返回 false。

校验真理,咱们都知道 js 有几大内置类型 这些类型都是 Function 的实例,是 Function 类型:

out(typeof Date)     //Function
out(typeof RegExp)   //Function
out(typeof Number)   //Function
out(typeof Boolean)  //Function
out(typeof String)   //Function
out(typeof Array)    //Function
out(typeof Error)    //Function
//...

out(Date.__proto__  === Function.prototype)    //true
out(RegExp.__proto__ === Function.prototype)   //true
out(Number.__proto__ === Function.prototype)   //true
out(Boolean.__proto__ === Function.prototype)  //true
out(String.__proto__ === Function.prototype)   //true
out(Array.__proto__ === Function.prototype)    //true
out(Error.__proto__ === Function.prototype)    //true
//...

out(Object.getPrototypeOf(Date)  === Function.prototype)    //true
out(Object.getPrototypeOf(RegExp) === Function.prototype)   //true
out(Object.getPrototypeOf(Number) === Function.prototype)   //true
out(Object.getPrototypeOf(Boolean) === Function.prototype)  //true
out(Object.getPrototypeOf(String) === Function.prototype)   //true
out(Object.getPrototypeOf(Array) === Function.prototype)    //true
out(Object.getPrototypeOf(Error) === Function.prototype)    //true
//...

out( Date instanceof Function)    //true
out( RegExp instanceof Function)  //true
out( Number instanceof Function)  //true
out( Boolean instanceof Function) //true
out( String instanceof Function)  //true
out( Array instanceof Function)   //true
out( Error instanceof Function)   //true
//...

回到上述针对 Object 的 prototype 属性疑惑 为何到了 Object 就得不出同样的结果了呢?

咱们都知道每一个函数对象亦或函数类型都会有个 prototype 属性,在其上挂载的方法和属性均可以被该类型实例化出来的对象共享,由于实例化出来的对象拥有 [[Prototype]]特性即 __proto__ 属性,js 即是经过该特性实现原型链机制。

那么这些函数的 prototype 属性对象是否也有本身的[[Prototype]]特性即 __proto__ 属性呢?

out(typeof Date.prototype)    //object
out(typeof RegExp.prototype)  //object
out(typeof Number.prototype)  //object
out(typeof Boolean.prototype) //object
out(typeof String.prototype)  //object
out(typeof Array.prototype)   //object
out(typeof Error.prototype)   //object

out(typeof Object.getPrototypeOf(Date.prototype))        //object
out(typeof Object.getPrototypeOf(RegExp.prototype))      //object
out(typeof Object.getPrototypeOf(Number.prototype))      //object
out(typeof Object.getPrototypeOf(Boolean.prototype))     //object
out(typeof Object.getPrototypeOf(String.prototype))      //object
out(typeof Object.getPrototypeOf(Array.prototype))       //object
out(typeof Object.getPrototypeOf(Error.prototype))       //object

out(Object.getPrototypeOf(Date.prototype) === Object.prototype)    //true
out(Object.getPrototypeOf(RegExp.prototype) === Object.prototype)  //true
out(Object.getPrototypeOf(Number.prototype) === Object.prototype)  //true
out(Object.getPrototypeOf(Boolean.prototype) === Object.prototype) //true
out(Object.getPrototypeOf(String.prototype) === Object.prototype)  //true
out(Object.getPrototypeOf(Array.prototype) === Object.prototype)   //true
out(Object.getPrototypeOf(Error.prototype) === Object.prototype)   //true

能够看出每一个函数对象的 prototype 属性也有本身的[[Prototype]]特性 并且指向的是 Object.prototype

那么是否全部对象都会有直接的[[Prototype]]特性 呢?

out( Object.getPrototypeOf( Object.getPrototypeOf(Date.prototype)))     //null
out( Object.getPrototypeOf( Object.getPrototypeOf(RegExp.prototype)))   //null
out( Object.getPrototypeOf( Object.getPrototypeOf(Number.prototype)))   //null
out( Object.getPrototypeOf( Object.getPrototypeOf(Boolean.prototype)))  //null
out( Object.getPrototypeOf( Object.getPrototypeOf(String.prototype)))   //null
out( Object.getPrototypeOf( Object.getPrototypeOf(Array.prototype)))    //null
out( Object.getPrototypeOf( Object.getPrototypeOf(Error.prototype)))    //null
out( Object.getPrototypeOf( Object.prototype))                          //null

答案是否。

有个例外他就是 Object.prototype

回看前面的 Demo:

Object.prototype instanceof Object // false

从前面的代码输出咱们看到 Object.prototype 对象是没有[[Prototype]]特性的,同时前面咱们也讨论过 instanceof 这个关键字所涉及的具体操做。

咱们能够具体剖析以下:

1.看右侧的 Object 获取其 prototype 获得 Object.prototype。
2.看左侧 Object.prototype 对象的原型链上是否有第一步获得 Object.prototype。
1)获取 Object.prototype.__proto__对象为空直接返回 false。

那么为何全部的对象都有[[Prototype]] 特性,惟独Object.prototype没有呢。答案很简单:既然 JS的继承机制是基于原型链的那总该有个头吧,这个头即是Object.prototype

再来一发:

out( typeof Function)                                          //function
out( typeof Object)                                            //function
out( Object instanceof Function)                               //true
out( Function instanceof Function)                             //true

在学习 JS 的过程当中咱们已经知道全部的数据类型都是 Function 类型的实例,包括 Object 在内,可是咱们都知道全部的对象都是 Object 的实例,这时便引出文章的题目

究竟是先有 Function 仍是先有 Object?

out( Function.__proto__  === Function.prototype)               //true
out( Object.__proto__  === Function.prototype)                 //true
out( Object.getPrototypeOf(Function) === Function.prototype)   //true
out( Object.getPrototypeOf(Object) === Function.prototype)     //true
out( Object.getPrototypeOf(Function) === Function.prototype)   //true
out( Object.getPrototypeOf(Object) === Function.prototype)     //true
out( Object instanceof Function)                               //true 
out( Function instanceof Function)                             //true

从上述代码加上前面得出的结论咱们能够看出

Function 和 Object 的原型链上都有 Function.prototype

咱们再来详细看看 Function.prototype

out( typeof Function.prototype);                // function
out( Function.prototype instanceof Object)      //true

这时候问题升华为

Function.prototype 和 Object.prototype 的关系。

out( Function.prototype.__proto__ === Object.prototype)

这时候关系已经很明了了:

咱们都知道除了 Object.prototype 以外全部对象都会有[[Prototype]]特性 即 __proto__ 属性,故 Function.prototype 也不例外,
Function.prototype 指向的是 Object.prototype

这时候就能够清楚的知道为何说全部类型都是 Function 的实例,同时也是 Object 的实例:

由于全部的类型的[[Prototype]]特性 即 __proto__ 属性均指向的是 Function.prototype ,同时 Function.prototype 的[[Prototype]]特性 即 __proto__ 属性又指向了 Object.prototype 。

故大王是Object.prototype,二王是Function.prototype,其余均是小弟,可是小弟也有等级划分

先接着来看 Function:

out( typeof Function.prototype);                // function
out( typeof Function.__proto__);                // function
out( Function.prototype === Function.__proto__);// true

首先咱们能够看出 Function.prototype 和其余类型的 prototype 属性是 object 类型不同, 是 function 类型
其次 Function.__proto__ 指向了 Function.prototype。

咱们知道当一个类型实例化出对象时,这些对象的便会共享挂载在该类型的 prototype 属性上的 资源,由于这些对象均有[[Prototype]]特性,指向的就是实例化出这些对象的类型的 prototype

从前面的例子能够看到全部的类型的[[Prototype]]特性均指向了 Function.prototype,因此这些类型都具备了使用挂载在Function.prototype上的资源的权利。所以能够看出,当一个对象具备使用挂载在Function.prototype上的资源的权利时,及该对象[[Prototype]]指向 Function.prototype 时表明这个对象是个 Function 实例是一个类型,可以实例化出该类型的对象,固然包括 Function 在内。

又由于全部类型的[[Prototype]]指向 Function.prototype 而 Function.prototype 的[[Prototype]]指向是 Object.prototype,因此这些类型都具备使用挂载在 Object.prototype 上的资源的权利。

那用这些类型实例化出来的对象呢 类型的原型链并不在实例化出来的对象上呀,可是这些实例化出来的对象的[[Protitype]]指向的是其类型的 prototype 属性

往回看前面的例子 能够看到有一段

out(Object.getPrototypeOf(Date.prototype) === Object.prototype)    //true
out(Object.getPrototypeOf(RegExp.prototype) === Object.prototype)  //true
out(Object.getPrototypeOf(Number.prototype) === Object.prototype)  //true
out(Object.getPrototypeOf(Boolean.prototype) === Object.prototype) //true
out(Object.getPrototypeOf(String.prototype) === Object.prototype)  //true
out(Object.getPrototypeOf(Array.prototype) === Object.prototype)   //true
out(Object.getPrototypeOf(Error.prototype) === Object.prototype)   //true
out(Object.getPrototypeOf(Function.prototype) === Object.prototype)   //true

能够看到这些类型的 prototype 属性的[[Protitype]]指向的是 Object.prototype 故至此,全部对象包括类型对象亦或类型实例化出来的对象都有使用挂载在 Object.prototype 上的资源的权利。

到这里全部对象之间的关系就已经清除了,相信已经有很多人已经晕乎了,不要紧 我准备了图(看不太清晰是由于上传后被压缩了):

clipboard.png

固然这里我仍是省略了部分细节 譬如对应类型的 prototype 属性对象均有 constructor 属性指向该类型,以及省略部分类型。

对着这张图从新看一遍本文和文章开头的摸底,相信你会有收获。

那么为何使用 typeof 获取 Object.prototype 会返回 object 呢。

咱们知道浏览器底层对 JS 的实现的是基于 C/C++
经过上图,咱们能够猜想

浏览器在初始化JS 环境时都发生了些什么

1.用 C/C++ 构造内部数据结构建立一个 OP 即(Object.prototype)以及初始化其内部属性但不包括行为。
2.用 C/C++ 构造内部数据结构建立一个 FP 即(Function.prototype)以及初始化其内部属性但不包括行为。
3.将 FP 的[[Prototype]]指向 OP。
4.用 C/C++ 构造内部数据结构建立各类内置引用类型。
5.将各内置引用类型的[[Prototype]]指向 FP。
6.将 Function 的 prototype 指向 FP。
7.将 Object 的 prototype 指向 OP。
8.用 Function 实例化出 OP,FP,以及 Object 的行为并挂载。
9.用 Object 实例化出除 Object 以及 Function 的其余内置引用类型的 prototype 属性对象。
10.用 Function 实例化出除Object 以及 Function 的其余内置引用类型的 prototype 属性对象的行为并挂载。
11.实例化内置对象 Math 以及 Grobal
至此,全部 内置类型构建完成。

如今咱们能够回答为何使用 typeof 获取 Object.prototype 会返回 object 了。
由于咱们在使用 typeof 的时候获得的 object 类型并不彻底表明是 Object 类型实例化出来的对象,有多是底层实现的内部数据结构,包括 null。真正想知道这个类型仍是须要去到当前该类的内部[[Class]]属性,至于如何获取可使用Object的toString方法。

最后的最后,你还对是现有 Function 仍是现有 Object 有想法了吗?

以上均为我的查阅及实践总结的观点。

谢谢~

相关文章
相关标签/搜索