Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jawil opened this issue Apr 19, 2017 · 25 comments
Open

Comments

@jawil
Copy link
Owner

jawil commented Apr 19, 2017

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

什么是函数(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
Copy link
Owner Author

jawil commented Apr 19, 2017

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

@mqyqingfeng
Copy link

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

@chenshiforever
Copy link

chenshiforever commented May 30, 2017

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

@Ghohankawk
Copy link

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

@ghost
Copy link

ghost commented Nov 23, 2017

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

@zhoubhin
Copy link

@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,想到的是既是对象又是函数,那该如何去用?

@iyuki22
Copy link

iyuki22 commented Nov 5, 2018

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
Copy link

GHolk commented Nov 6, 2018

我是認為 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 的原因,
文章長度幾乎加倍……。

@iyuki22
Copy link

iyuki22 commented Nov 15, 2018

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

@aiyogg
Copy link

aiyogg commented Nov 23, 2018

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

@hezhongfeng
Copy link

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

@aiyogg
Copy link

aiyogg commented Nov 23, 2018

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

你好你好~ 😃
hezfblog 很赞!

@hezhongfeng
Copy link

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

你好你好~ 😃
hezfblog 很赞!

加个好友啊?老年刀狗
130128931

@aiyogg
Copy link

aiyogg commented Nov 23, 2018

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

你好你好~ 😃
hezfblog 很赞!

加个好友啊?老年刀狗
130128931

👌

@Jancat
Copy link

Jancat commented Feb 22, 2019

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

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

@zhangenming
Copy link

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

@panchengtao
Copy link

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

@ftchan
Copy link

ftchan commented Nov 18, 2019

我的理解是这样:

  • 普通对象的原型根源是 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 就可以共享对方的原型方法。

@BenjaminFF
Copy link

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

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

@luo-boss
Copy link

luo-boss commented Sep 9, 2020

你好

@foolishway
Copy link

foolishway commented Oct 22, 2021

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

@cheungkin24
Copy link

cheungkin24 commented Feb 28, 2023

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

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

@mango-csl
Copy link

mango-csl commented Feb 28, 2023 via email

@chuanHH
Copy link

chuanHH commented Feb 28, 2023 via email

@xiaoyan13
Copy link

xiaoyan13 commented Dec 31, 2023

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

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

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests