函数
目前在规范中规定一共有6种函数:
创建函数
- 创建函数一共有三种方式:函数声明、函数表达式、Function构造函数
- 函数声明和函数表达式在语法上的区别就是,函数表达式中的函数名是可选的。在实际使用中需要注意使用函数表达式创建的函数不会有”变量提升“,即必须在使用前声明
- 函数声明提升的几种情况:函数表达式,块级函数
ECMAScript中规定的创建函数的算法
- 普通函数
- FunctionCreate(Normal, FormalParameters, FunctionBody, funcEnv).
- MakeConstructor(closure).
- 箭头函数
- FunctionCreate(Arrow, FormalParameters, FunctionBody, funcEnv).
- 都是通过 FunctionCreate 过程实现函数创建,但是箭头函数不会执行MakeConstructor,所以箭头函数不能用作构造函数,并且prototype属性是在MakeConstructor里定义的,所以箭头函数也没有prototype属性
FunctionCreate方法详细说明
- FunctionAllocate(prototype, functionkind)
- 创建一个ecma函数对象F,同时F.[[FunctionKind]]为functionKind。F.[[Prototype]] to functionPrototype.
- arrow function 不会初始化F.[[Construct]] 属性
- 返回 F
- FunctionInitialize
- 箭头函数的 F.[[ThisMode]] 设置为 lexical.普通函数的是 F.[[ThisMode]] to global
执行函数
- 执行函数即函数调用,从预发上来说是执行一个CallExpression, 其具体执行过程在12.3.4 Function Calls, 简化下就是:
- 先确定this值,如a.b()这种形式,this为a
- 调用F.Call
- 执行函数最终都是执行F.[Call]方法。
- F.[[Call]]执行过程中会执行 OrdinaryCallBindThis ,它的作用就是给当前执行环境绑定this的值,这样在执行函数体的时候,知道到this值,就知道this的值是什么了。在OrdinaryCallBindThis执行的时候,如果[[ThisMode]]为 lexical。则直接return,其他情况会envRec.BindThisValue(thisValue).即给当前执行环境中绑定传递过来的this的值。所以箭头函数内的词法环境记录项里是没有this的。所以只能往上找,即当时定义的词法环境记录项。而且bind、call、apply最后都要调用F.[[Call]],所以对箭头函数都无法生效
方法(Method)
方法的定义
- 从规范定义中看,方法一种有6种:普通方法、Generator方法,Async方法,AsyncGenerator方法,set方法,get方法
- DefineMethod执行过程(必选参数object,prototype为可选参数)
- FunctionCreate(kind, UniqueFormalParameters, FunctionBody, scope, prototype)。创建了一个函数,并且F.[[Prototype]] 为prototype参数
- MakeMethod(closure, object),就是把closure的[[HomeObject]]内置属性设置为object
- FunctionCreate执行时,kind是Method,同样的没有执行MakeConstructor,所以也不能当做构造函数使用
方法的执行
- 方法执行代码例如
a.b()
是一个左值表达式,最终走的还是函数调用的逻辑
类(Class)
- 14.6 Class Definitions
- 给class name在当前词法环境记录项中绑定一个不可变的名字
- 如果有继承,找到继承的class p,则以p.prototype为原型创建对象proto,以 p 为constructorParent
- 如果不是继承,则以Object.prototype为原型创建对象proto,以Function.prototype为constructorParent
- 对constructor以proto和constructorParent为参数执行 DefineMethod 得到函数F,并且F.[[Prototype]] constructorParent
- 对F执行MakeConstructor(F, false, proto)。得到 F.prototype为proto
- 给proto添加属性constructor为F
- 处理classbody代码,如果是static方法,全部以F为参数执行 DefineMethod,不是的话已proto为参数执行 DefineMethod
- 返回F
- 如果是继承的情况,为何要让p成为F的[[prototype]]属性
- 看示例
1
2
3
4
5
6
7
8class a {}
class b extends a{} // 这句会导致 b.__proto__ === a, b.prototype.__proto__ === a.prototype
const c = new a()
c instanceof b // true c.__proto__ === b.prototype
c instanceof a // true c.__proto__.__proto__ === a.prototype
b instanceof Function // true b.__proto__ === a, a.__proto__ === Function.prototype
// 因为 c.__proto__ === b.prototype === Object.create(a.prototype)
// 所以 c.__proto__.__proto__ === b.prototype.__proto__ === a.prototype