new做了些什么


new操作符,熟悉又陌生,昨晚翻看了一下《Javascript语言精粹》,47页有一段代码说的是 new 执行的过程,代码如下:

Function.method('new',function(){
  var that = Object.beget(this.prototype)
  var other = this.apply(that, arguments)
  return (typeof other === 'object' && other ) || that
})

其中的 methodFunction 原型上的一个方法,方便给 Function 原型上加方法, Object.beget 方法可以理解为返回一个使用 this.prototype 作为其原型的新对象, 这个方法对应的其实是 Object.create

两者的代码如下

if(typeof Object.beget !== 'function') {
  Object.beget = function(o){
    var F = function(){}
    F.prototype = o
    return new F()
  }
}

Function.prototype.method = function(name, func) {
  if(!this.prototype[name]) {
    this.prototype[name] = func
  }
}

老道的那段解释 new 的代码,可以直译为:

  1. 创建一个以当前 Function 原型为原型的新对象 that
  2. that 作为上下文,当前 Function 接收的参数为参数,执行当前 Function ,得结果记为 other
  3. 如果 other 为一个对象返回它,否则返回 that

那么规范里的 new 是按规定是怎么执行的呢?请看这里,摘抄如下:

12.3.3.1 Runtime Semantics: Evaluation

  • NewExpression : new NewExpression
    • Return ? EvaluateNew(NewExpression, empty).
  • MemberExpression : new MemberExpression Arguments
    • Return ? EvaluateNew(MemberExpression, Arguments).

12.3.3.1.1 Runtime Semantics: EvaluateNew(constructProduction, arguments)

The abstract operation EvaluateNew with arguments constructProduction, and arguments performs the following steps:

  1. Assert: constructProduction is either a NewExpression or a MemberExpression.
  2. Assert: arguments is either empty or an Arguments production.
  3. Let ref be the result of evaluating constructProduction.
  4. Let constructor be ? GetValue(ref).
  5. If arguments is empty, let argList be a new empty List.
  6. Else,
    1. Let argList be ArgumentListEvaluation of arguments.
    2. ReturnIfAbrupt(argList).
  7. If IsConstructor(constructor) is false, throw a TypeError exception.
  8. Return ? Construct(constructor, argList).

简单解释下,new有两种,一种是带参数的,一种不带参数的,其中 NewExpressionMemberExpression 我们先跳过,这是两种表达式,它们包含的种类非常多,我们直接看 EvaluateNew 里的过程

  • 首先断定 表达式要么是 NewExpression 要么是 MemberExpression
  • 断定 arguments 要么是 空, 要么是 Arguments production
  • ref 为表达式执行的结果
  • constructorGetValue(ref) 的值
  • 如果 arguments 是空,设 argList 为一个空的 List
  • 不为空
    • argListargumentsArgumentListEvaluation
    • 调用 ReturnIfAbrupt(argList)
  • 调用 IsConstructor(constructor),如果是 false, 抛出一个 TypeError 异常
  • 调用并返回 Construct(constructor, argList)

重点就在最后两步,其中 IsConstructor 首先判断 constructor 是不是一个对象,然后判断它有没有 [[Construct]] 内置方法, 这个内置方法是在函数初始化的时候绑定的,有些内置函数是没有这个属性的,比如 String.prototype.toString 如果对一个没有这个属性 的对象调用 new 就会抛出一个 TypeError 的错误。

最后的 Construct 实际比较复杂,在又一次校验过 constructor 后,会调用 [[construct]] ,这里调用的又分为两种,一种是普通的函数对象,另外一种是 内置的函数对象

重点看一下第一种的情况,实质就是如果是普通的函数,设置执行上下文,执行函数体, 如果有返回值,且返回值是对象,直接返回对象,如果返回 undefined, 抛出 TypeError,最后是返回正常绑定的 this

最后这一点涉及的分支还是有几个,我就不继续对着文档解读了,困。。。

相对于 ES5 真的复杂了太多!

参考:


推荐文章