Open jawil opened 7 years ago
其实深究这种问题开发没多大用处,但是探寻事物的本质却收获不少。
了解了本质,开发的时候很多问题才能解决得更加灵活、优雅。
之前一直以为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而产生。详见: https://github.com/creeperyang/blog/issues/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
我是認為 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
目前想到的二個原因:
所以 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
等等都是构造函数,刀友你好
你好你好~ :smiley:
hezfblog 很赞!
@DOTA2mm 其实
Object
,String
,Number
等等都是构造函数,刀友你好你好你好~ 😃 hezfblog 很赞!
加个好友啊?老年刀狗 130128931
@DOTA2mm 其实
Object
,String
,Number
等等都是构造函数,刀友你好你好你好~ 😃 hezfblog 很赞!
加个好友啊?老年刀狗 130128931
:ok_hand:
Function.__proto__ === Function.prototype
导致 Function.constructor === Function
,即: Function 是它自己的构造函数
Function.prototype() 为什么可以直接执行 执行结果是什么? typeof Function.prototype 结果是function Function.prototype instanceof Function 结构false
所以这就是为什么Javascript是个蛋疼和优雅的东西,各种意义不清的东西,却能强行解释到一起
我的理解是这样:
Object.prototype
,而 Object.prototype
的最终根源是 null
。
Function.prototype
,同时 Function
对象会从自己的原型上获得一些方法,比如 apply
,call
,bind
等等。(具体的可以看MDN)。Object 和 Function 的原型是两条线:
Function -> Function.prototype -> Object.prototype -> null Object -> Function.prototype -> Object.prototype -> null
它们之间没有形成闭环回路,而是一种类似互相继承。因为互相继承,Function 和 Object 就可以共享对方的原型方法。
其实深究这种问题开发没多大用处,但是探寻事物的本质却收获不少。
这里有问题。。。应该是原型
你好
也有此疑问,既然 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
你好,你的来件已收到。
我是黄海川。你的邮件我已经收到,谢谢!
个人理解: 模型: 模型解释:每个函数(是不是构造函数都可,除了箭头)都唯一对应一个原型对象,根据该函数创建的实例指向该原型对象。
设构造函数 Object 的原型对象是 A,构造函数 Function 的原型对象是 B.
规定:默认下,所有的函数的 原型对象 均指向 A。 此规定在原型链顶端存在唯一例外: 函数 Object 的 原型对象 指向 B. 这构成循环: 最后可以发现,这个完整的循环链从下往上,只有 Fucntion 函数没有画出它的proto指向谁。
这是因为,Function 函数是 Function 函数自身的实例。之所以这样设计: 我们假设 Function 是某个函数的实例,那它只能是 Fucntion 的实例了; 我们假设 Funciton 不是任何函数的实例:js 规定任何对象都存在proto,该假设不成立。
在引出下面要阐述的问题答案之前,先深入了解几个重要慨念,之前这些概念也是模模糊糊,从最原始的一步步搞清楚。
什么是函数(function)?
解释定义最好的地方就是它的起源,这里便是ECMAScript规范,我们看看规范怎么定义函数(function)的。
摘录来自ECMAScript 5.1规范的4.3.24小节:
至于什么是实例对象,什么构造器(构造函数)下面的会详细讲,这里我们只引用定义,知道有这么个东西它叫这个名字就够了。
函数使用function 关键字来定义,其后跟随,函数名称标识符、 一对圆括号、一对花括号。
结合一个栗子理解这句话:
上面这几行代码就是一个函数,这个函数是标准内置构造器
Function
的一个实例,因此函数是一个对象。demo
就是这个函数的名字,对象是保存在内存中的,函数名demo
则是指向这个对象的指针。console.log('jawil')
则是可执行代码。对于内存,引用,指针不太明白,没有任何认知的童鞋可以去尝试搜索补习一下这些概念,其实都是一些概念,花点时间理解一下就好了。
在浏览器我们也可以检验一下:
上面只是创建函数的一种方式, JavaScript 中有三种创建形式,分别是: ①声明式
②函数的字面量或叫直接量或称表达式
③以new Function 的形式
上面三种创建方式,第三种
var fn = new Function (arg1 , arg2 ,arg3 ,…, argN , body)
是最直观的, 很容易就联想到fn instanceof Function
,fn
一眼就看出来就是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下一节讲,了解概念尽量多结合栗子加深理解,理解构造函数,首先就要理解上面函数的一个概念和定义。
这种抽象的东西不是很好记忆,我们通过一个示例来说明可能更好了解。
在javascript中,你可以把上面的代码看做一个函数,一个类,一个方法,都没有问题。
其实,在JavaScript中,首先,它是函数,任何一个函数你都可以把它看做是构造函数,它没有明显的特征。那什么时候它就明显了呢?实例化的时候。
当这一句代码结束,你就可以肯定的认为 Person 函数是一个构造函数,因为它 new 了"微醺岁月"。 那么,"微醺岁月" 是什么?"微醺岁月"是一个人,一个实实在在的人,是被构造函数初始化出来的。所以 var jawil 就变成了一个实例。
什么是实例对象,什么是原型对象?
原型
我们看看ECMAScript规范怎么定义构造函数(constructor)的。
摘录来自ECMAScript 5.1规范的4.3.5小节:
首先说一下,只有函数才有prototype(原型)属性。为什么只有函数才有prototype属性?ECMAScript规范就这么定的。
但是不是所有的函数都有prototype属性呢?答案是否定的,这可不一定。我们看个简单的栗子:
用 Function.prototype.bind 创建的函数对象没有 prototype 属性。
那么prototype(原型)到底有啥作用呢?
这些概念简单了解一下,这不是本人要讲的重点,这里一笔带过,不太懂的可以自己去查相关资料补习一下基础。
__proto__
引用《JavaScript权威指南》的一段描述:
翻译出来就是每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法。好啦,既然有这么一个原型对象,那么对象怎么和它对应的?
对象proto属性的值就是它所对应的原型对象:
上面的代码应该已经足够解释清楚
__proto__
了。实例对象
把实例和对象对比来看,或许更容易理解。
实例对象和对象的区别,从定义上来讲:
实例是相对而言,这话怎么理解了,我们看下面两个小栗子比如说:
我们可以说a是Array数组的一个实例;
我们知道Array也是一个函数,虽然他是一个构造函数,只要是函数,从上面的知识点可以知道,Array是Function的一个实例。
通俗的理解这几个的关系:
问题引出
我们知道,Array,Date,Number,String,Boolean,Error甚至Object都是Function的一个实例,那么Function是谁的实例呢?
先看一个简单的小栗子:
再来看看这个:Function 也就是浏览器显示的这个,我们暂且这么类比:
我们再来看看,先暂时忽略后面的:
Person.__proto__=== Function.prototype;
//truePerson函数是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.prototype
和Function.__proto__
都指向Function.prototype
,这就是鸡和蛋的问题怎么出现的一样。在这之前,我一直有个误解就是,认为所有对象就是Object的实例,现在想起来真是Too young,Too simple.
Object.prototype
是对象吗?当然是。
这是object的定义,
Object.prototype
显然是符合这个定义的。但是,Object.prototype并不是Object的实例。 这也很好理解Object.prototype.__proto__
是null。就如同刚才上面区分实例和对象一样,实例都是对象,而对象(比如说Object.prototype)不全是实例。
这已经某种程度上解开了鸡和蛋的问题:Object.prototype是对象,但它不是通过Object函数创建的。Object.prototype谁创建的,它是v8引擎(假设用的都是chrome浏览器)按照ECMAScript规范创造的一个对象。我只能这么给你解释。
未完待续,好累,歇一会儿~
关于这个问题也困扰了我很久,功力不够,无法详细回答,但是经过一番查找和探究,在知乎上看到了这篇回答,引用一下,与大家共同学习。
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 而产生。
参考文章
JavaScript 的语言设计有哪些缺陷? JS 的 new 到底是干什么的? 从proto和prototype来深入理解JS对象和原型链 在JavaScript中,Function构造函数本身也算是Function类型的实例吗? JS中先有Object还是先有Function? JavaScript 世界万物诞生记