资讯专栏INFORMATION COLUMN

高能!typeof Function.prototype 引发的先有 Function 还是先有 O

娣辩孩 / 929人阅读

摘要:有个例外他就是。看左侧对象的原型链上是否有第一步得到。将各内置引用类型的指向。用实例化出,,以及的行为并挂载。实例化内置对象以及至此,所有内置类型构建完成。最后的最后,你还对是现有还是现有有想法了吗以上均为个人查阅及实践总结的观点。

来个摸底测试,说出以下每个表达式的结果

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 类型的实例

那么我们平时都是如何确定一个对象是否是另一个类型或对象的实例的呢?

对我们可以使用 typeof 关键字 亦或可以使用关键字 instanceof 来确定某个对象是否是指定类型或对象的实例:

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

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

然而针对 Object 的 prototype 属性:

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 上的资源的权利。

到这里所有对象之间的关系就已经清除了,相信已经有不少人已经晕乎了,没关系 我准备了图(看不太清晰是因为上传后被压缩了):

当然这里我还是省略了部分细节 譬如对应类型的 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 有想法了吗?

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

谢谢~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79706.html

相关文章

  • 如何优雅的理解ECMAScript中的对象

    摘要:标准对象,语义由本规范定义的对象。这意味着虽然有,本质上依然是构造函数,并不能像那样表演多继承嵌套类等高难度动作。不过这里的并不是我们所说的数据类型,而是对象构造函数。 序 ECMAScript is an object-oriented programming language for performing computations and manipulating computat...

    why_rookie 评论0 收藏0
  • JavaScript 原型中的哲学思想

    摘要:而作为构造函数,需要有个属性用来作为以该构造函数创造的实例的继承。 欢迎来我的博客阅读:「JavaScript 原型中的哲学思想」 记得当年初试前端的时候,学习JavaScript过程中,原型问题一直让我疑惑许久,那时候捧着那本著名的红皮书,看到有关原型的讲解时,总是心存疑虑。 当在JavaScript世界中走过不少旅程之后,再次萌发起研究这部分知识的欲望,翻阅了不少书籍和资料,才搞懂...

    sugarmo 评论0 收藏0
  • JavaScript原型链以及Object,Function之间的关系

    摘要:由于一般所有的原型链最终都会指向顶端的,所以它们都是的。好了现在了,成了所有对象原型链的。 JavaScript里任何东西都是对象,任何一个对象内部都有另一个对象叫__proto__,即原型,它可以包含任何东西让对象继承。当然__proto__本身也是一个对象,它自己也有自己的__proto__,这样一级一级向上,就构成了一个__proto__链,即原型链。当然原型链不会无限向上,它有...

    zacklee 评论0 收藏0
  • 原型链是什么?关于原型链中constructor、prototype及__proto__之间关系的认

    摘要:的隐式原型是母,母是由构造函数构造的,但函数的隐式原型又是。。。。可能是考虑到它也是由构造函数生成的吧,所以返回的值也是。 showImg(https://segmentfault.com/img/bVyLk0); 首先,我们暂且把object类型和function类型分开来,因为 function是一个特殊的对象类型,我们这里这是便于区分,把function类型单独拿出来。顺便一提,...

    kaka 评论0 收藏0
  • 讲清楚之 javascript原形

    摘要:构造函数和实例都通过属性指向了原形。代码示例是构造函数的实例的属性与的属性保存的值相等,即他们指向同一个对象原形。 讲清楚之javascript原型 标签: javascript javascript 中原形是一个比较难于理解的概念。javascript 权威指南在原形这一章也花了大量的篇幅进行介绍,也许你已经读过javascript 权威指南,或者已经是读第N篇了,然而这篇文章的目...

    高胜山 评论0 收藏0

发表评论

0条评论

娣辩孩

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<