模拟 JavaScript 中的“new”运算符

IT技术 javascript
2021-03-04 11:32:31

我试图用这样的代码模拟 JavaScript 中的“new”运算符:

Function.method('new', function ( ) {
    var objPrototype = Object.create(this.prototype);
    var instance = this.apply(objPrototype, arguments);

    return instance;
});

但是,为了涵盖所有情况,return 语句应如下所示:

return (typeof instance === 'object' && instance ) || objPrototype;

现在进行测试:

var SomeClass = function (param1, param2) {
    this.param1 = param1;
    this.param2 = param2;
};

var test1 = String.new('test1'); //in this case, the "instance" variable is an object
var test2 = SomeClass.new('test1', 'test2'); // in this case, the "instance" variable is undefined

这正是'new'运算符所做的吗?有没有需要处理的案例?

4个回答

规范

11.2.2 新的运算符#

产生式NewExpression : new NewExpression的计算方式如下:

  1. ref成为评估NewExpression的结果
  2. 构造函数GetValueref)。
  3. 如果Type( constructor ) 不是 Object,则抛出TypeError异常。
  4. 如果构造函数没有实现 [[Construct]] 内部方法,则抛出TypeError异常。
  5. 返回在constructor上调用 [[Construct]] 内部方法的结果,不提供任何参数(即一个空的参数列表)。

生产MemberExpression : new MemberExpression Arguments 的评估如下:

  1. ref成为评估MemberExpression的结果
  2. 构造函数GetValueref)。
  3. argList是评估Arguments的结果,产生一个内部参数值列表(11.2.4)。
  4. 如果Type( constructor ) 不是 Object,则抛出TypeError异常。
  5. 如果构造函数没有实现 [[Construct]] 内部方法,则抛出TypeError异常。
  6. 返回在constructor上调用 [[Construct]] 内部方法的结果,提供列表argList作为参数值。

在任何一种情况下,都正确遵循所有步骤:

var objPrototype = Object.create(this.prototype);    // 1-4 1-5
var instance = this.apply(objPrototype, arguments);  // 5   6

兴趣点是 2。状态
的规范[[construct]]

当使用可能为空的参数列表调用 Function 对象 F 的 [[Construct]] 内部方法时,将执行以下步骤:

  • 让 obj 成为一个新创建的原生 ECMAScript 对象。
    . . .
  • 令 result 是调用 F 的 [[Call]] 内部属性的结果,提供 obj 作为 this 值并提供传入 [[Construct]] 的参数列表作为 args。
  • 如果Type( result ) 是 Object 则返回result
  • 返回obj

typeof obj返回"object"for null,而null不是对象。但是,由于null是一个假值,您的代码也可以按预期工作:

return (typeof instance === 'object' && instance ) || objPrototype;
这个答案忘记了this.prototype不是 ES-Object 的情况。该值null将被 接受Object.create,但它应该Object.prototype改为使用
2021-04-22 11:32:31
实际上,当构造函数返回一个函数对象时,代码是错误的,它typeof也不能很好地工作。更好的:return (Object(instance) === instance) ? instance : objPrototype)
2021-04-27 11:32:31
希望 JavaScript 在新运算符方面与 ECMASCRipt 100% 兼容。谢谢你的回答!
2021-05-07 11:32:31

new运营商需要一个功能Fargumentsnew F(arguments...)它做了三个简单的步骤:

  1. 创建类的实例。它是一个空对象,其 __proto__属性设置为F.prototype初始化实例。

  2. 使用传递的参数调用函数 F,并将 this 设置为实例。

  3. 返回实例

现在我们了解了 new 运算符的作用,我们可以在 Javascript 中实现它。

    function New (f) {
/*1*/  var n = { '__proto__': f.prototype };
       return function () {
/*2*/    f.apply(n, arguments);
/*3*/    return n;
       };
     }

只是一个小测试,看看它是否有效。

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype = {
  print: function () { console.log(this.x, this.y); }
};

var p1 = new Point(10, 20);
p1.print(); // 10 20
console.log(p1 instanceof Point); // true

var p2 = New (Point)(10, 20);
p2.print(); // 10 20
console.log(p2 instanceof Point); // true
关闭救援!有趣的方法。
2021-05-01 11:32:31

这里的答案对标准 ES5 都是有效的,在编写它们时就已经存在了,但它们不是适用于所有 ES6 上下文的通用解决方案,所以我想扩展它们。简短的回答是问题中的代码:

Function.method('new', function ( ) {
  var objPrototype = Object.create(this.prototype);
  var instance = this.apply(objPrototype, arguments);

  return instance;
});

在标准的 ES6 环境中会更好地实现为

Function.method('new', function ( ) {
  return Reflect.construct(this, arguments);
});

这绝对简化了事情。

Reflect.construct在 ES6 中作为Proxy系统的一部分引入,但它具有像这种情况的一般用途。

这是现在首选方法的原因很简单,.apply它不再适用于所有类型的函数。前面的回答解释了new调用内部语言函数[[Construct]]来初始化参数。使用的方法.apply基本上取代了自动创建对象和调用逻辑[[Construct]],而是手动创建对象,然后调用函数,它使用[[Call]]内部方法而不是[[Construct]].

对函数的调用是 ES6 中更改的一部分。在 ES5 中,您将构建的几乎唯一的东西是一个正常值function Foo(){},因此您可以对此做出假设。在 ES6class Foo {}中引入了语法,并且类语法创建的构造函数有更多的限制,因此对 ES5 所做的假设不适用。最重要的是,明确禁止 ES6 类使用[[Call]]. 执行以下操作将引发异常:

class Foo {}
Foo();

这与.call相同的问题.apply它们不是函数构造函数,而是函数调用函数。所以如果你尝试在 ES6 类上使用它们,它们会抛出异常。

Reflect.construct通过实际调用[[Construct]]和 not 来避免这些问题[[Call]],而是通过可以在没有new.

这是使用该__proto__方法的替代方法。它与 OP 最初开始的方式一致......

function New(fn) {
    var newObj = Object.create(fn.prototype);
    return function() {
        fn.apply(newObj, arguments);
        return newObj;
    };
}

这是一种更简洁的方法,它也通过了原型链测试。

有错别字吗?两个连续的返回语句?
2021-04-22 11:32:31
@nino.porcino 大多数构造函数不返回任何东西......我的意思是我们就是这样编写它们的......因此return fn.apply(...)with 给你undefined......
2021-05-06 11:32:31
关于上述错字,我不得不在我自己的代码中使用但给了我错误,直到我将其更改为“return fn.apply(newObj, arguments);” (而不是“返回 newObj;”)。两者应该是一样的吧?
2021-05-08 11:32:31