Function 是函数? 是构造函数? 还是函数对象? 在翻阅 ECMA 规范时, 这几个名词也是无缝切换中, 稍不留神就可能跑偏了.
都应该知道, 构造函数是用来打造具有相同属性和行为事物的模具.
在作为构造函数之前, 它首先是个函数, 函数是可以被调用的(这也是区别于其它对象的因素). 它是由一系列语句组成, 并最终返回一个值. 每一个函数被创建的时候, 都有一个prototype属性.
普通函数能够变成构造函数, 那都是 new 将函数的prototype和生成的实例关联起来了. 换句话说, 每个普通函数都有称为构建函数的潜质.
// 常见的构造函数模式
function Man(name) {this.name = name;this.sex = 'man';
}
Man.prototype.sayHi = function() {console.log('hello', this.name)
}
let man_1 = new Man('夜曉宸');// 我是普通函数
function man(name) {console.log('hello', name)
}
// 只要经过 new 调教后, 我也可以被称为构造函数了
let man_2 = new man('夜曉宸');
Markdown部分语法不支持,点此可看原文
Function和内置函数
都说函数是 Javascript 里的头等对象, 这话一点不假. 在之前文章里有提到过, Javascript 里的数据类型除了基本数据类型, 就是复杂数据类型. 而这些复杂数据类型都和 Function 有着密切关系.
Function构造函数
函数的身影在 Javascript 中随处可见, 有造字符串类型的 String 函数, 有造数值类型的 Number 函数, 除了这些内置函数, 还有上面我自己写的 Man 和 man 函数.
Javascript 的作者在写 Javascript 语言的时候, 可能知道有且需要 String, Number 这样的函数处理写数据, 于是有了内置函数这么一说.(纯属杜撰?)
那么内置函数满足不了我们的需求呢, 如何去创建一个函数呢? 其实, 在没有这些内置函数之前, 就有了这么一个东西. 那就是为创建函数而生的函数 —— Function. 等会儿, 有点绕?, 创建函数的函数? 那岂不是把自己也给创建了?? 是的, 没错, 就是这么一个奇葩的存在.
The Function constructor is the %Function% intrinsic object and the initial value of the Functionproperty of the global object. When Functionis called as a function rather than as a constructor, it creates and initializes a new Function object. Thus the function call Function(...)is equivalent to the object creation expression new Function(...)with the same arguments.
所以说, 包括这些内置函数, 它们都是由 Function 构建函数造出来的. 如何去验证函数是不是函数造出来的呢?
new操作符
All ordinary objects have an internal slot called [[Prototype]]. The value of this internal slot is either null or an object and is used for implementing inheritance.
每个普通对象都有一个 ***[[Prototype]]***, 这个属性就是用来实现原型继承用的. 上面?也提到了, 在用构造函数造 new 出实例对象时, 实例对象内部的 [[Prototype]] 会关联到构造函数的 prototype 所指向的对象. new 主要做以下两件事:
- 将实例对象的 [[Prototype]] 关联到构造函数的 prototype 上.
- 将 this 绑定到新创建的实例对象上, 并调用构造函数(就是将函数体里的语句执行一遍).
类似于:
function New(func) {const obj = {};// _proto_ 和 [[Prototype]] 不是同一个东西, 后者是内部使用的.ojb._proto_ = func.prototype;func.call(obj);return obj;
}
Object.getPrototypeOf()
是用来检测指定对象内部 [[Prototype]] 属性的值. 对于上面?的 Man 和 man_1 关系, 试一波.
Object.getPrototypeOf(man_1) === Man.prototype // true;
Function和函数
回头再看看 String, Number, Boolean, Array, Object, Function, RegExp, Date, Error, Symbol 这些内置函数和 Function 构造函数的关系(注意包含了 Function 自身). 这里是把这些内置函数作为由 Function 构造出来的实例对象对待的. (函数也是对象的一子类型)
Object.getPrototypeOf(String) === Function.prototype // trueObject.getPrototypeOf(Number) === Function.prototype // trueObject.getPrototypeOf(Boolean) === Function.prototype // trueObject.getPrototypeOf(Array) === Function.prototype // trueObject.getPrototypeOf(Object) === Function.prototype // trueObject.getPrototypeOf(Function) === Function.prototype // trueObject.getPrototypeOf(RegExp) === Function.prototype // trueObject.getPrototypeOf(Date) === Function.prototype // trueObject.getPrototypeOf(Error) === Function.prototype // trueObject.getPrototypeOf(Symbol) === Function.prototype // trueObject.getPrototypeOf(Man) === Function.prototype // true ???Object.getPrototypeOf(man_1) === Function.prototype // false ???
在之前文章有提及过, Javascript里的对象类型基本上也就是那么多. 以上函数的原型链, 最终会指向 Function.prototype (仅为目前, 后面还有 Object.prototype 和 null ). 这也是解释了为什么任何一个普通函数都可以调用 call , apply 等 Functon.prototype 上的方法.
Function和原型
所有的函数的原型链里都将会出现 Functon.prototype, 由构造函数 new 出来的实例对象的 [[Prototype]] 会关联到构造函数 prototype 上.
let num = new Number(0);Object.getPrototypeOf(num) === Number.prototype // trueObject.getPrototypeOf(Man) === Function.prototype // true ???
到目前为止, 任何函数或对象都可以追溯到 Functon.prototype 和 构造函数的 prototype 上. 那么接下来, 又如何沿着原型链再往后找呢?
The Function prototype object is the intrinsic object %FunctionPrototype%. The Function prototype object is itself a built-in function object.
这句话告诉我们, Function 的原型是一个函数对象. 可我们也知道, 所有的函数都应该来自于 Function. 由此可推导, Function.prototype 内部属性 [[Prototype]] 应指向 Function.prototype. 实际上并不是这样. 同样情况的还有 Array.
Object.getPrototypeOf(Function.prototype) === Function.prototype // false
ECMA 规范也给出了解释, 不知是不是解释这个的.
NOTE The Function prototype object is specified to be a function object to ensure compatibility with ECMAScript code that was created prior to the ECMAScript 2015 specification.
以上推导可忽略. 所以呢,
The value of the [[Prototype]] internal slot of the Function prototype object is the intrinsic object %ObjectPrototype%.
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
其它的构造函数也是一样.
// true String.prototype is an ordinary object.Object.getPrototypeOf(String.prototype) === Object.prototype// true String.prototype is an ordinary object.Object.getPrototypeOf(Number.prototype) === Object.prototype// true String.prototype is an ordinary object.Object.getPrototypeOf(Boolean.prototype) === Object.prototype// true String.prototype is an Array exotic objects.Object.getPrototypeOf(Array.prototype) === Object.prototype// true String.prototype is an ordinary object.Object.getPrototypeOf(Function.prototype) === Object.prototype// true String.prototype is a built-in function object.Object.getPrototypeOf(RegExp.prototype) === Object.prototype// true String.prototype is an ordinary object.Object.getPrototypeOf(Date.prototype) === Object.prototype// true String.prototype is an ordinary object.Object.getPrototypeOf(Error.prototype) === Object.prototype// true String.prototype is an ordinary object.Object.getPrototypeOf(Symbol.prototype) === Object.prototype
所有的函数或对象的原型链都会追溯到 Object.prototype 上, 难道这里就是终点么? 不是的, 还有个 null . 所以, 最终,
Object.getPrototypeOf(Object.prototype) === null // true
总结一波:
-
Function 是构造函数的地方, 构造函数是函数, 内建函数也是函数.
-
被构造出来的实例对象内部属性 [[Prototype]] 会关联到构造函数的 prototype 所指向的对象.
-
prototype chain 的走向为:
instance => constructor.prototype => Object.prototype => null
. (构造函数造出的实例对象走向)function => Function.prototype => Object.prototype => null
.(函数走向)- ...
上图说明一切.