深入探究 Function & Object 鸡蛋问题 - 黄庆的博客

引言

这篇文章深入探究下 Function.__proto__ === Function.prototype 引起的鸡生蛋蛋生鸡问题,并在这个过程中深入了解 Object.prototype. Function.prototype. function Object . function Function 之间的关系。

Object.prototype

Object.prototype 表示 Object 的原型对象,其 [[Prototype]] 属性是 null,访问器属性 __proto__ 暴露了一个对象的内部 [[Prototype]]Object.prototype 并不是通过 Object 函数创建的,为什么呢?看如下代码

function Foo() {
  this.value = "foo";
}
let f = new Foo();
f.__proto__ === Foo.prototype;
// true

实例对象的 __proto__指向构造函数的 prototype,即 f.__proto__ 指向 Foo.prototype,但是 Object.prototype.__proto__null,所以 Object.prototype 并不是通过 Object 函数创建的,那它如何生成的?其实 Object.prototype 是浏览器底层根据 ECMAScript 规范创造的一个对象。

Object.prototype 就是原型链的顶端(不考虑 null 的情况下),所有对象继承了它的 toString 等方法和属性。

Function.prototype

Function.prototype 对象是一个函数(对象),其 [[Prototype]] 内部属性值指向内建对象 Object.prototypeFunction.prototype 对象自身没有 valueOf 属性,其从 Object.prototype 对象继承了valueOf 属性。

Function.prototype[[Class]] 属性是 Function,所以这是一个函数,但又不大一样。为什么这么说呢?因为我们知道只有函数才有 prototype 属性,但并不是所有函数都有这个属性,因为 Function.prototype 这个函数就没有。

Function.prototype;
// ? () { [native code] }

Function.prototype.prototype;
// undefined

当然你会发现下面这个函数也没有 prototype 属性。

let fun = Function.prototype.bind();
// ? () { [native code] }

fun.prototype;
// undefined

为什么没有呢,我的理解是 Function.prototype 是引擎创建出来的函数,引擎认为不需要给这个函数对象添加 prototype 属性,不然 Function.prototype.prototype… 将无休无止并且没有存在的意义。

function Object

Object 作为构造函数时,其 [[Prototype]] 内部属性值指向 Function.prototype,即

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

使用new Object() 创建新对象时,这个新对象的 [[Prototype]] 内部属性指向构造函数的 prototype 属性,对应上图就是 Object.prototype

当然也可以通过对象字面量等方式创建对象。

  • 使用对象字面量创建的对象,其 [[Prototype]] 值是 Object.prototype
  • 使用数组字面量创建的对象,其 [[Prototype]] 值是 Array.prototype
  • 使用 function f(){} 函数创建的对象,其 [[Prototype]] 值是 Function.prototype
  • 使用 new fun() 创建的对象,其中 fun 是由 JavaScript 提供的内建构造器函数之一(Object, Function, Array, Boolean, Date, Number, String 等等),其 [[Prototype]] 值是 fun.prototype
  • 使用其他 JavaScript 构造器函数创建的对象,其 [[Prototype]] 值就是该构造器函数的 prototype 属性。
let o = { a: 1 };
// 原型链: o ---> Object.prototype ---> null

let a = ["yo", "whadup", "?"];
// 原型链: a ---> Array.prototype ---> Object.prototype ---> null

function f() {
  return 2;
}
// 原型链: f ---> Function.prototype ---> Object.prototype ---> null

var fun = new Function();
// 原型链: fun ---> Function.prototype ---> Object.prototype ---> null

function Foo() {
  return {};
}
let foo = new Foo();
// 原型链: foo ---> Foo.prototype ---> Object.prototype ---> null

function Function

Function 构造函数是一个函数对象,其 [[Class]] 属性是 FunctionFunction[[Prototype]] 属性指向了 Function.prototype,即

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

Function & Object 鸡蛋问题

我们看下面这段代码

Object instanceof Function; // true
Function instanceof Object; // true

Object instanceof Object; // true
Function instanceof Function; // true

Object 构造函数继承了 Function.prototype,同时 Function 构造函数继承了 Object.prototype。这里就产生了 鸡和蛋 的问题。为什么会出现这种问题,因为 Function.prototypeFunction.__proto__ 都指向 Function.prototype

// Object instanceof Function     即
Object.__proto__ === Function.prototype; // true

// Function instanceof Object     即
Function.__proto__.__proto__ === Object.prototype; // true

// Object instanceof Object         即
Object.__proto__.__proto__ === Object.prototype; // true

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

对于 Function.__proto__ === Function.prototype 这一现象有 2 种解释,争论点在于 Function 对象是不是由 Function 构造函数创建的一个实例?

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

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

这里偏向于第二种解释,即先有 Function.prototype 然后有的 function Function() ,所以就不存在鸡生蛋蛋生鸡问题了,把 Function.__proto__ 指向 Function.prototype 是为了保证原型链的完整,让 Function 可以获取定义在 Object.prototype 上的方法。

最后给一个完整的图,看懂这张图原型就没问题了。

内置类型构建过程

JavaScript 内置类型是浏览器内核自带的,浏览器底层对 JavaScript 的实现基于 C/C++,那么浏览器在初始化 JavaScript 环境时都发生了什么?

  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

至此,所有内置类型构建完成。



原文:大专栏  深入探究 Function & Object 鸡蛋问题 - 黄庆的博客

原文地址:https://www.cnblogs.com/chinatrump/p/11615267.html

时间: 2024-10-27 03:23:55

深入探究 Function & Object 鸡蛋问题 - 黄庆的博客的相关文章

「SF-QC」2 TypeClasses - 黄玄的博客

Considerring printing different types with this common idiom: 1 2 3 4 5 6 showBool : bool → string showNat : nat → string showList : {A : Type} (A → string) → (list A) → string showPair : {A B : Type} (A → string) → (B → string) → A * B → string Defi

黄聪:博客园的积分和排名算法探讨,积分是怎么计算的?(转)

我们先来看看现行规则,用公式表示为:-------------------------------------------------------------------BlogScore = BeRead + 10 * BeComment + 50 * CommentBlogScore:博客积分BeRead:个人博客所有随笔和文章的阅读数之和BeComment:个人博客被评论总数Comment: 个人所发表的评论总数---------------------------------------

喜迎学院周年庆,博客好礼送不停,51CTO博客专栏送书送T恤!!!

好礼一:订阅新上专栏<负载均衡高手炼成记>就送自选图书一本. 简介:学习要趁早,你定专栏我送书,前100名(目前仅剩30名额)订阅指定专栏就送图书一本,为早鸟加餐,为梦想增值. 活动地址:学习要趁早,专栏上新,早鸟订阅送图书 好礼二:喜迎学院周年庆,学知识送清凉,订阅专栏送51CTO定制T恤 简介:需在周年庆活动期间(2018年6月26-7月2日23:59分 )有新增订阅专栏,且订阅专栏总量满3个及以上(含历史)就可免费领取51CTO定制T恤一件. 活动地址:提升技能,升级思维,你学知识我送T

flask_route错误:AttributeError: &#39;function&#39; object has no attribute &#39;route&#39;

问题: 路由完全正确,当只有一个名为home的函数处理这个路由时候,下一个路由处理函数,总是提示没有这个rotue属性 Traceback (most recent call last): File "E:/workspace/wei-move/manage.py", line 3, in <module> from app import app File "E:\workspace\wei-move\app\__init__.py", line 10,

函数对象状态(Function Object State)获取 -- 引用传递和for_each()

一般情况下function object默认值传递,无法获取其状态.本文以引用传递和for_each()两种方法获取function object状态. 引用方式传递function object 以引用方式传递function object程序示例: #include <iostream> #include <list> #include <algorithm> #include <iterator> using namespace std; templa

c++ 11学习笔记--Lambda 表达式(对比测试Lambda ,bind,Function Object)

所有c++ coder都应该为这个语法感到高兴,说的直白一点,Lambda 表达式就是函数对象的语法糖. 还是直接看对比栗子吧,抄袭的是msdn的官网 该示例使用 for_each 函数调用中嵌入的 lambda 向控制台打印 vector 对象中的每个元素是偶数还是奇数. 使用lambda #include <algorithm> #include <iostream> #include <vector> using namespace std; int main()

认识js函数对象(Function Object)

认识函数对象(Function Object) 可以用function关键字定义一个函数,对于每个函数可以为其指定一个函数名,通过函 数名来进行调用.这些都是代码给用户的印象,而在JavaScript解释执行的时候,实际上每 个函数都是被维护为一个对象,这就是本小节将要介绍的函数对象(Function Object). 函数对象与其它用户所定义的对象有着本质的区别,这一类对象被称之为内部对象,例 如日期对象(Date).数组对象(Array).字符串对象(String)都是属于内部对象.换句话

function object用法

1 // Generic findMax, with a function object, version #1 2 // Precondition, a.size() > 0 3 #include <iostream> 4 #include <vector> 5 #include <string.h> 6 using namespace std; 7 8 template <class Object, class Comparator> 9 cons

2.在使用&quot;node-xlsx&quot; 模块时报&quot; TypeError: Object function Object() { [native code] } has no method &#39;assign&#39; &quot;

最近做一个关于数据库数据以xls格式导出的功能.由于之前用的"excel-export"模块功能有不是很全.也有可能是我没完全弄明白怎么使用这个模块生成多页的excel文件吧.后来就选用了'node-xlsx'模块来做.但是在使用demo的时候,会报一个TypeError: Object function Object() { [native code] } has no method 'assign的错误.网上查了半天也没找到相关信息.后来终于找到一篇文章.(链接:https://w