模拟';新';JavaScript中的运算符

模拟';新';JavaScript中的运算符,javascript,Javascript,我试着用如下代码在JavaScript中模拟“new”操作符: Function.method('new', function ( ) { var objPrototype = Object.create(this.prototype); var instance = this.apply(objPrototype, arguments); return instance; }); return (typeof instance === 'object' &&

我试着用如下代码在JavaScript中模拟“new”操作符:

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

    return instance;
});
return (typeof instance === 'object' && instance ) || objPrototype;
但是,为了涵盖所有情况,return语句应该如下所示:

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

    return instance;
});
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

“新”运营商就是这么做的吗?还有什么需要说明的吗?

新的
操作符接受函数
F
参数
新的F(参数…
。它有三个简单的步骤:

  • 创建类的实例。它是一个空对象,具有
    \uuuu proto\uuu
    属性设置为
    F.prototype
    。初始化实例

  • 使用传递的参数调用函数F,并将其设置为 就是这样

  • 返回实例

  • 现在我们了解了新操作符的功能,可以用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
    
    从:

    11.2.2新操作员 生产新表达式:
    new
    NewExpression的计算如下:

  • 设ref为计算NewExpression的结果
  • 设构造函数为(ref)
  • 如果(构造函数)不是对象,则引发异常
  • 如果构造函数未实现[[Construct]]内部方法,则引发异常
  • 返回对构造函数调用[[Construct]]内部方法的结果,但不提供任何参数(即参数的空列表)
  • 生产成员表达式:
    new
    MemberExpression参数 评估结果如下:

  • 设ref为计算MemberExpression的结果
  • 设构造函数为(ref)
  • 让argList作为参数求值的结果,生成参数值的内部列表()
  • 如果(构造函数)不是对象,则引发异常
  • 如果构造函数未实现[[Construct]]内部方法,则引发异常
  • 返回对构造函数调用[[Construct]]内部方法的结果,并提供列表argList作为参数值
  • 在任何一种情况下,均正确遵循所有步骤:

    var objPrototype = Object.create(this.prototype);    // 1-4 1-5
    var instance = this.apply(objPrototype, arguments);  // 5   6
    
    兴趣点是2。
    国家:

    当函数对象F的[[Construct]]内部方法为 使用可能为空的参数列表调用,请执行以下步骤 采取的措施如下:

    • 将obj设为新创建的本机ECMAScript对象。
    • 让result是调用F的[[Call]]内部属性的结果,提供obj作为this值的值并提供参数列表 作为参数传递到[[Construct]]
    • 如果(结果)是对象,则返回结果
    • 返回obj
    typeof obj
    null
    返回
    的“对象”
    ,而
    null
    不是对象。但是,由于
    null
    是一个虚假值,因此您的代码也可以按预期工作:

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

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

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

    这是一种更干净的方法,而且它也通过了原型链测试。

    这里的答案对于标准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
    是作为
    Proxy
    系统的一部分引入ES6的,但它有类似于这种情况的一般用途

    现在这是首选方法的原因很简单,
    .apply
    不再适用于所有类型的函数。说明
    new
    调用内部语言函数
    [[Construct]]
    来初始化参数。使用
    .apply
    的方法实质上取代了
    [[Construct]]
    中处理的自动对象创建和调用逻辑,而是手动创建对象,然后调用函数,该函数使用内部方法而不是
    [[Construct]]

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

    class Foo {}
    Foo();
    
    这与
    .call
    .apply
    的问题相同。它们不是函数构造函数,而是函数调用函数。因此,如果您试图在ES6类上使用它们,它们将抛出异常


    Reflect.construct
    通过实际调用
    [[construct]]
    而不是
    [[Call]]]
    来避免这些问题,而是通过一个无需
    新建
    即可使用的API来公开它。。。使用新的接线员怎么样?这看起来有点像是一个相当旧的轮子的翻版。是的。但我仍然想确切地了解幕后发生了什么。出于教育目的?公平地说…只是玩弄继承可能的JavaScript副本,当涉及到新的操作符时,它与ECMASCript 100%兼容。谢谢你的回答!这个答案忘记了当
    This.prototype
    不是ES对象时的情况。值
    null
    将被
    对象接受。create
    ,尽管它应该使用
    Object.prototype
    。实际上,当构造函数返回一个函数对象时,代码是错误的,而
    typeof
    不接受该对象