jawil/blog

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

jawil opened this issue · 25 comments

jawil commented

在引出下面要阐述的问题答案之前,先深入了解几个重要慨念,之前这些概念也是模模糊糊,从最原始的一步步搞清楚。

什么是函数(function)?

解释定义最好的地方就是它的起源,这里便是ECMAScript规范,我们看看规范怎么定义函数(function)的。

摘录来自ECMAScript 5.1规范的4.3.24小节:

对象类型的成员,标准内置构造器 Function的一个实例,并且可做为子程序被调用。

注: 函数除了拥有命名的属性,还包含可执行代码、状态,用来确定被调用时的行为。函数的代码不限于 ECMAScript。

至于什么是实例对象,什么构造器(构造函数)下面的会详细讲,这里我们只引用定义,知道有这么个东西它叫这个名字就够了。

函数使用function 关键字来定义,其后跟随,函数名称标识符、 一对圆括号、一对花括号。

结合一个栗子理解这句话:

function demo(){

console.log('jawil');

}

上面这几行代码就是一个函数,这个函数是标准内置构造器 Function的一个实例,因此函数是一个对象。demo就是这个函数的名字,对象是保存在内存中的,函数名demo则是指向这个对象的指针。console.log('jawil')则是可执行代码。

对于内存,引用,指针不太明白,没有任何认知的童鞋可以去尝试搜索补习一下这些概念,其实都是一些概念,花点时间理解一下就好了。

在浏览器我们也可以检验一下:

demo instanceof Function
//true

上面只是创建函数的一种方式, JavaScript 中有三种创建形式,分别是:
①声明式

function fn(){ };      //这种定义的方式,无论在哪儿定义,别处都可以调用 ;

②函数的字面量或叫直接量或称表达式

var fn=function () { }; //此时函数作为表达式存在,调用只能在定义的后面;
//解释一下表达式:凡是将数据和运算符等联系起来有值得式子就是表达式。

③以new Function 的形式

var fn = new Function (arg1 , arg2 ,arg3 ,, argN , body) 
/*Function 构造函数所有的参数都是字符串类型。除了最后一个参数, 其余的参数都作为生成函数的参数即形参。
这里可以没有参数。最后一个参数, 表示的是要创建函数的函数体。

使用Function构造器生成的Function对象是在函数创建时解析的。这比你使用函数声明或者函数表达式(function)
并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的。
*/

上面三种创建方式,第三种var fn = new Function (arg1 , arg2 ,arg3 ,…, argN , body)是最直观的,
很容易就联想到fn instanceof Functionfn一眼就看出来就是Function的实例,但是为什么JavaScript还要创造用function来创建一个函数呢?

答案显而易见,用function创建一个函数更优雅,灵活,书写方便,浏览器看到function时候其实已经帮你做了new Function()这一步了,function和new Function()创建的函数都是Function的一个实例,只是方式不一样,其实本质都是一样。就如同创建一个对象一样,我们可以var obj=new Object(),当然我们也可以var obj={};

我觉得function创建函数只是new Function()创建函数的一个语法糖,不对之处还望指出,反正我是这么理解的。






JavaScript函数的new关键字到底是干什么的?

JS 的 new 到底是干什么的?






什么是构造函数?

我们看看ECMAScript规范怎么定义构造函数(constructor)的。

摘录来自ECMAScript 5.1规范的4.3.4小节:

创建和初始化对象的函数对象。

注:构造器的“prototype”属性值是一个原型对象,它用来实现继承和共享属性。

构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象

这些抽象的东西其实不好讲,不好写,讲一个抽象的概念,又引出好几个抽象的概念,情何以堪,实例对象原型prototype下一节讲,了解概念尽量多结合栗子加深理解,理解构造函数,首先就要理解上面函数的一个概念和定义。

这种抽象的东西不是很好记忆,我们通过一个示例来说明可能更好了解。

function Person(name){
    this.name=name;
}

在javascript中,你可以把上面的代码看做一个函数,一个类,一个方法,都没有问题。

其实,在JavaScript中,首先,它是函数,任何一个函数你都可以把它看做是构造函数,它没有明显的特征。那什么时候它就明显了呢?实例化的时候。

var jawil=new Person('微醺岁月');

当这一句代码结束,你就可以肯定的认为 Person 函数是一个构造函数,因为它 new 了"微醺岁月"。
那么,"微醺岁月" 是什么?"微醺岁月"是一个人,一个实实在在的人,是被构造函数初始化出来的。所以 var jawil 就变成了一个实例。






什么是实例对象,什么是原型对象?

原型

我们看看ECMAScript规范怎么定义构造函数(constructor)的。

摘录来自ECMAScript 5.1规范的4.3.5小节:

为其他对象提供共享属性的对象。

当构造器创建一个对象,为了解决对象的属性引用,该对象会隐式引用构造器的“prototype”属性。通过程序表达式 constructor.prototype 可以引用到构造器的“prototype”属性,并且添加到对象原型里的属性,会通过继承与所有共享此原型的对象共享。另外,可使用 Object.create 内置函数,通过明确指定原型来创建一个新对象。

首先说一下,只有函数才有prototype(原型)属性。为什么只有函数才有prototype属性?ECMAScript规范就这么定的。

但是不是所有的函数都有prototype属性呢?答案是否定的,这可不一定。我们看个简单的栗子:

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

那么prototype(原型)到底有啥作用呢?

prototype对象是实现面向对象的一个重要机制。每个函数也是一个对象,它们对应的类就是Function,每个函数对象都具有一个子对象prototype。Prototype 表示了该函数的原型,prototype表示了一个类的属性的集合。当通过new来生成一个类的对象时,prototype对象的属性就会成为实例化对象的属性。

这些概念简单了解一下,这不是本人要讲的重点,这里一笔带过,不太懂的可以自己去查相关资料补习一下基础。




__proto__

引用《JavaScript权威指南》的一段描述:

Every JavaScript object has a second JavaScript object (or null ,
but this is rare) associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype.

翻译出来就是每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法。好啦,既然有这么一个原型对象,那么对象怎么和它对应的?

对象__proto__属性的值就是它所对应的原型对象:

var one = {x: 1};
var two = new Object();
one.__proto__ === Object.prototype // true
two.__proto__ === Object.prototype // true
one.toString === one.__proto__.toString // true

上面的代码应该已经足够解释清楚__proto__了。




实例对象

把实例和对象对比来看,或许更容易理解。

实例对象和对象的区别,从定义上来讲:

1、实例是类的具象化产品,
2、而对象是一个具有多种属性的内容结构。
3、实例都是对象,而对象(比如说Object.prototype)不全是实例。

实例是相对而言,这话怎么理解了,我们看下面两个小栗子比如说:

var a=new Array();
a instanceof Array//true

我们可以说a是Array数组的一个实例;

function Array(){

[native code]

}
Array instanceof Function//true

我们知道Array也是一个函数,虽然他是一个构造函数,只要是函数,从上面的知识点可以知道,Array是Function的一个实例。

通俗的理解这几个的关系:

JavaScript引擎是个工厂。
最初,工厂做了一个最原始的产品原型。
这个原型叫Object.prototype,本质上就是一组无序key-value存储({})

之后,工厂在Object.prototype的基础上,研发出了可以保存一段“指令”并“生产产品”的原型产品,叫函数。
起名为Function.prototype,本质上就是[Function: Empty](空函数)

为了规模化生产,工厂在函数的基础上,生产出了两个构造器:
生产函数的构造器叫Function,生产kv存储的构造器叫Object。

你在工厂定制了一个产品,工厂根据Object.prototype给你做了一个Foo.prototype。
然后工厂发现你定制的产品很不错。就在Function.prototype的基础上做了一个Foo的构造器,叫Foo。

工厂在每个产品上打了个标签__proto__,以标明这个产品是从哪个原型生产的。
为原型打了个标签constructor,标明哪个构造器可以依照这个原型生产产品。
为构造器打了标签prototype,标明这个构造器可以从哪个原型生产产品。

所以,我觉得先有Function还是Object,就看工厂先造谁了。其实先做哪个都无所谓。因为在你定制之前,他们都做好了。






问题引出

我们知道,Array,Date,Number,String,Boolean,Error甚至Object都是Function的一个实例,那么Function是谁的实例呢?

先看一个简单的小栗子:

function Person(){

...一些自定义的code

}
Person.__proto__ === Function.prototype;//true
Person.prototype instanceof Object;//true

再来看看这个:Function
也就是浏览器显示的这个,我们暂且这么类比:

function Person(){

...一些自定义的code

}

function Function(){

[native code]//系统帮我们写的code

}

我们再来看看,先暂时忽略后面的:

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

Person函数是Function的一个实例。

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

上面说了,Person函数是Function的一个实例,这没有争议,那么这行代码是否可以说Function函数对象是由Function构造函数创建的一个实例?
因为我们普遍的认知就是:实例对象(A)的__proto__属性指向它的构造函数(B)的原型对象(prototype)。

大白话就是:A(儿子)继承了B(父母)的一些特性(prototype)才有了A。所以问题就来了,当A===B的时候,该怎么理解了?这就是今天问题的引出了,下面就要探讨这个问题了。

再来看:
Person.__proto__=== Person.prototype;//false

这个显而易见可以看出,Person函数不是由Person的实例,因为Person是Function的一个实例。






那么问题来了:

Function构造函数的prototype属性和__proto__属性都指向同一个原型,是否可以说Function对象是由Function构造函数创建的一个实例?

Function.prototypeFunction.__proto__都指向Function.prototype,这就是鸡和蛋的问题怎么出现的一样。

在这之前,我一直有个误解就是,认为所有对象就是Object的实例,现在想起来真是Too young,Too simple.

Object.prototype 是对象吗?

当然是。

An object is a collection of properties and has a single prototype object. The prototype may be the null value.

这是object的定义,Object.prototype显然是符合这个定义的。但是,Object.prototype并不是Object的实例。 这也很好理解Object.prototype.__proto__是null。

就如同刚才上面区分实例和对象一样,实例都是对象,而对象(比如说Object.prototype)不全是实例。

这已经某种程度上解开了鸡和蛋的问题:Object.prototype是对象,但它不是通过Object函数创建的。Object.prototype谁创建的,它是v8引擎(假设用的都是chrome浏览器)按照ECMAScript规范创造的一个对象。我只能这么给你解释。

未完待续,好累,歇一会儿~

关于这个问题也困扰了我很久,功力不够,无法详细回答,但是经过一番查找和探究,在知乎上看到了这篇回答,引用一下,与大家共同学习。

在JavaScript中,Function构造函数本身也算是Function类型的实例吗?Function构造函数的prototype属性和__proto__属性都指向同一个原型,是否可以说Function对象是由Function构造函数创建的一个实例?

Yes and No.

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

No 的部分:

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

注意,本质上,a instanceof b 只是一个运算,即满足某种条件就返回 true/false,当我们说 a 是 b 的实例时,也只是表示他们符合某种关系。JS 是一门强大的动态语言,你甚至可以在运行时改变这种关系,比如修改对象的原型从而改变 instanceof 运算的结果。此外,ES6+ 已允许通过 Symbol.hasInstance 来自定义 instanceof 运算。

我知道很多 JS 学习者会迷恋于对象和函数之间的 instanceof 关系,并希望探究到底谁更本源?我当初也在这个问题上浪费了很多时间。但这是一个伪问题。参见:JavaScript 里 Function 也算一种基本类型?以上。






收获

对JavaScript的原型和原型链相比以前有了一个更深刻的认识,同时也对函数,构造函数,实例对象的一些概念有了一个更具体的认知,以前对这些概念都是模模糊糊,没有一个明确的概念,导致在理解一些问题上出现盲点,比如说:function和Function的问题,现在总是认知清楚了,也了解到没有十全十美的语言,任何语言也有它的一些缺陷和漏洞,比如说Function对象是由Function构造函数创建的一个实例?typeof null的返回值是Object的问题,历史的车轮滚滚向前,语言也是向前发展,但愿JavaScript发展越来越好,越来越完善,一统天下😄。

最后感觉还是有疑问,很纠结,越陷越深,慢慢回答自己的问题

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

  1. 先有 Object.prototype,再有 Object,那么先有 Object.prototype 里面的这个 Object 代表的是什么呢?
  1. Function.proto=== Function.prototype;
    Function 构造函数的 prototype 属性和__proto__属性都指向同一个原型,是否可以说 Function 对象是由 Function 构造函数创建的一个实例?
  1. Object instanceof Function // true
    Function instanceof Object // true
    Object 本身是构造函数,继承了 Function.prototype;Function 也是对象,继承了 Object.prototype。感觉成了鸡和蛋的问题了。
  1. 比如说:
    function Person(){}
    Person.prototype 是一个对象,为什么 Function.prototype 却是一个函数呢,当然函数也是一个对象,为什么要这么设计呢?

参考文章

JavaScript 的语言设计有哪些缺陷?
JS 的 new 到底是干什么的?
从__proto__和prototype来深入理解JS对象和原型链
在JavaScript中,Function构造函数本身也算是Function类型的实例吗?
JS中先有Object还是先有Function?
JavaScript 世界万物诞生记

jawil commented

其实深究这种问题开发没多大用处,但是探寻事物的本质却收获不少。

了解了本质,开发的时候很多问题才能解决得更加灵活、优雅。

之前一直以为prototype属性一定是对象,直到发现了有Function.prototype这个东西。实际上呢,这句话‘Function对象是由Function构造函数创建的一个实例’,为了记住原型链里面的关系,按照这句话来记得话是没有毛病的,但是深究Function是一个内置对象也是没有毛病的。我是看见ES6的class的继承回过头来看这里的,因为ES6,里边的一个继承 B.__proto__= A,这里不符合我之前所理解的所有的prototype都是对象这一个理论。纠结了好几天。还有,Object.prototype中的Object到底是哪来的,这个问题我发现也被绕进去了。

我觉的你最后提的4个问题,我也是想知道具体为啥如此,不过以下是我的个人理解,可能有误:
第一个是说Object,这个名词,可能就是你说的v8引擎创建的默认对象,而把它起名为了object
第二个,好像就应该说,为啥说,函数就是对象,等号左边是,Function这个对象,而右边是Function这个函数,同名函数和对象
第三个,就是如下:
Object.proto==Function.prototype,因此,Object是Function的实例,
而Function.proto.proto==Object.prototype,Function是Object的实例
这刚好解释了,为啥,函数是一种特殊的对象,对象又是构造函数产生的,这种死循环
第四个,为啥这么设计我,也不太明白,可能是,其他的都是对象,那么怎么区分函数和对象?因为不说第三条吗?函数就是对象,对象即是函数,要细分的时候,总的有分区吧,
比如,我们一般认为Object,就是个对象,不说函数,而Function默认都是认为函数,不说是对象

Function.__proto__ === Function.prototype,很简单的,函数是Function的实例,每一个实例内部都有一个属性__proto__指向该构造函数的prototype,恰巧Function本身就一个函数 function Function() {}

@jawil @Ghohankawk 这四个问题问得很深奥,啊不,很有深度(手动滑稽)
1、天知道Object.prototype的Object到底指代什么
2、Function.proto===Function.prototype成立。既是也不是。
是:从原型链上看,Function对象的prototype是在Function构造函数的原型链上的,实例的说法成立。
不是:Function是内建对象,如果说Function对象是Function构造函数创建,那Function构造函数又是谁创建的呢?
3、先有Object.prototype(原型链顶端),Function.prototype继承Object.prototype而产生,最后,Function和Object和其它构造函数继承Function.prototype而产生。详见:
creeperyang/blog#9
http://louiszhai.github.io/2015/12/17/prototype/
4、我觉得是为了更好地从字面意思上去区分,看到Object就知道是某个对象,看到Function就知道是某个函数,不然,看到Function,想到的是既是对象又是函数,那该如何去用?

Function.__proto__ === Function.prototype,很简单的,函数是Function的实例,每一个实例内部都有一个属性__proto__指向该构造函数的prototype,恰巧Function本身就一个函数 function Function() {}

每一个实例内部都有一个属性__proto__指向该构造函数的prototype

let a = [];
a.__proto__ === Array.prototype
// true
Array.__proto__ === Function.prototype
// true
Array.prototype.__proto__ === Object.prototype // 实例 Array.prototype
// true
a.constructor === Array
// true
Array.constructor === Function
// true
Array.prototype.constructor === Array  // 构造函数的prototype? --> Object
// true
GHolk commented

我是認為 js runtime 中物件就是有一個 __proto__ 屬性,
在查詢屬性時就會一路用 proto 往上找。
函數可以當作建構函數,只是方便我們建立同類的物件;
並在建構物件時,把 proto 指向建構式的 prototype 屬性。

這些都只是包裝。
Object 與 Object.prototype 沒什麼特殊意義,
他只是提供了一些物件常用的方法,
而那些方法其實都能自己 patch 出來。
所以你大可自己寫一個 MyObject 建構式,
在 MyObject.prototype 上也可以自幹出
多數 Object.prototype 提供的方法。

唯一特例是 Function ,
Function.prototype 不是物件,
而也是函數;我不確定這有什麼意義。
但函數也是一種物件,可以有擁有屬性與原型,
所以 Function.prototype.proto == Object.prototype 。

關於 es6 新增的箭頭函數、generator 、 async function ,
其中箭頭函數的原型也是指向 Function.prototype ,
和一般函數沒有差別。

而 generator 與 async 就有趣了,
他們的建構函數不是公開的全域變數,
要用 (function*(){}).constructor(async function() {}).constructor 來獲取。
(以下稱為 GeneratorFunction 與 AsyncFunction 。)

這二個建構式也都和 Function 一樣能用來動態建構函數,
他們的實例的原型是各自建構子的 .prototype
*.prototype 的原型又指向 Function.prototype ,
所以 instanceof 可以得到正確的結果。

但又一點很吊詭, GeneratorFunction 與 AsyncFunction
原型不是指向 Function.prototype 。
原本 js 中所有函數包括建構函數的原型都是指向 Function.prototype ,
但 GeneratorFunction 與 AsyncFunction 的原型是指向 Function 。
GeneratorFunction.__proto__ == Function

後來我發現這可能是 es6 class 的緣故。
如果用 es6 class 語法繼承現有類別,
雖然子 constructor 仍是函數,
constructor.__proto__ 會指向父建構式。

class SubObject extends Object {
}
SubObject.prototype instanceof Object.prototype // true
SubObject instanceof Object // true
SubObject.entries == Object.entries // true

目前想到的二個原因:

  1. 讓 instanceof 算符可以運作在 class 上,也就是建構函數上。
    比較好看而已。
  2. 讓子類繼承父類的靜態方法。因為原型指向了父類,自然能從子類存取到父類的屬性。

所以 GeneratorFunction 與 AsyncFunction 在內部
應該是用類似 class extends Function 的方法實作的。

結論

我認為 js 中的函數與物件都是一個黑箱,
那些建構式只是其一個很有限的介面,
提供很少的方法讓我們接觸外部。

而函數又比物件更黑,
相比物件只是簡單的 key value 集合,
加上一個 __proto__ 屬性。
基本上是可以自己實現的。

child.get = function (key) {
  if (this.hasOwn(key)) return this.getOwn(key)
  else if (this.parent) return this.parent.get(ket)
  else return undefined  // parent is null

但函數的閉包、this 、yield await 都是透過黑魔法實現的功能,
需要在執行時做很多手腳才能辦到,
不然就是像 babel 要完全重編譯成另一種樣子。


今年早些做了點有趣的功能,像 自己實現 Promise
用 generator 實現 async 函數
所以研究了一下要怎麼為 generator function 加 method 但又不加到所有 function 上,
所以找到了 GeneratorFunction.prototype 這東西,
順便做了點實驗。

(結論是 async 可以透過 generator 實現,promise 可以以原生物件方式實現;
整個 async 與 promise 系列升級,只有 generator 是真正重要不可取代的元件。)

昨天才看到這篇,覺得也是另一種探討方向,
也想把自己的想法與發現寫下來,就寫了。
(發現 AsyncFunction.__proto__ != Function.prototype 。)
沒想到寫到一半忽然想通是 es6 class extends 的原因,
文章長度幾乎加倍……。

Function.__proto__ === Function.prototype,很简单的,函数是Function的实例,每一个实例内部都有一个属性__proto__指向该构造函数的prototype,恰巧Function本身就一个函数 function Function() {}

每一个实例内部都有一个属性__proto__指向该构造函数的prototype

let a = [];
a.__proto__ === Array.prototype
// true
Array.__proto__ === Function.prototype
// true
Array.prototype.__proto__ === Object.prototype // 实例 Array.prototype
// true
a.constructor === Array
// true
Array.constructor === Function
// true
Array.prototype.constructor === Array  // 构造函数的prototype? --> Object
// true
// 1.构造函数创建实例。实例一创造出来就有constructor属性(指向构造函数)和__proto__属性(指向原型对象)
let a = [];
a.constructor === Array // true
a.__proto__ === Array.prototype // true
Array.constructor === Function // true
Array.__proto__ === Function.prototype // true
Function.constructor === Function // true
Function.__proto__ === Function.prototype // true
Object.constructor === Function // true
Object.__proto__ === Function.prototype // true
// 2.构造函数对应有原型对象,有constructor属性(指向构造函数)和__proto__属性(指向原型链上级)
Array.prototype.constructor === Array // true
Array.prototype.__proto__ === Object.prototype //true
Function.prototype.constructor === Function // true
Function.prototype.__proto__ === Object.prototype // true
Object.prototype.constructor === Object //true
Object.prototype.__proto__ === null // true

偶然在控制台输入 Function.prototype发现是函数类型,然后找到了这篇文章。收获良多,感谢博主。

@DOTA2mm
其实Object,String,Number等等都是构造函数,刀友你好

@DOTA2mm
其实Object,String,Number等等都是构造函数,刀友你好

你好你好~ 😃
hezfblog 很赞!

@DOTA2mm
其实Object,String,Number等等都是构造函数,刀友你好

你好你好~ 😃
hezfblog 很赞!

加个好友啊?老年刀狗
130128931

@DOTA2mm
其实Object,String,Number等等都是构造函数,刀友你好

你好你好~ 😃
hezfblog 很赞!

加个好友啊?老年刀狗
130128931

👌

Function.__proto__ === Function.prototype 导致 Function.constructor === Function,即: Function 是它自己的构造函数

引用自 http://www.mollypages.org/tutorials/js.mp

Function.prototype()
为什么可以直接执行 执行结果是什么?
typeof Function.prototype 结果是function
Function.prototype instanceof Function 结构false

所以这就是为什么Javascript是个蛋疼和优雅的东西,各种意义不清的东西,却能强行解释到一起

我的理解是这样:

  • 普通对象的原型根源是 Object.prototype ,而 Object.prototype 的最终根源是 null
  • 函数对象(Array、Number、Date、Object等等、包括 Function)它们的原型都继承于 Function.prototype,同时 Function 对象会从自己的原型上获得一些方法,比如 applycallbind 等等。(具体的可以看MDN)。

Object 和 Function 的原型是两条线:

Function -> Function.prototype -> Object.prototype -> null
Object -> Function.prototype -> Object.prototype -> null

它们之间没有形成闭环回路,而是一种类似互相继承。因为互相继承,Function 和 Object 就可以共享对方的原型方法。

其实深究这种问题开发没多大用处,但是探寻事物的本质却收获不少。

image
这里有问题。。。应该是原型

你好

也有此疑问,既然 Function instanceof Object === true,为什么Function.__proto__ !== Object.prototype ?

也有此疑问,既然 Function instanceof Object === true,为什么Function.__proto__ !== Object.prototype ?

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

个人理解:
模型:
image
模型解释:每个函数(是不是构造函数都可,除了箭头)都唯一对应一个原型对象,根据该函数创建的实例指向该原型对象。

设构造函数 Object 的原型对象是 A,构造函数 Function 的原型对象是 B.

规定:默认下,所有的函数的 原型对象 均指向 A。
此规定在原型链顶端存在唯一例外: 函数 Object 的 原型对象 指向 B.
这构成循环:
image
最后可以发现,这个完整的循环链从下往上,只有 Fucntion 函数没有画出它的__proto__指向谁。

这是因为,Function 函数是 Function 函数自身的实例。之所以这样设计:
我们假设 Function 是某个函数的实例,那它只能是 Fucntion 的实例了;
我们假设 Funciton 不是任何函数的实例:js 规定任何对象都存在__proto__,该假设不成立。
image