我尝试在这样的代码中用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
这正是“新”运算符的作用吗?还有什么案子可以解决吗?
答案 0 :(得分:9)
11.2.2新运营商# Ⓣ Ⓡ Ⓖ
生产 NewExpression :
new
NewExpression 评估如下:
在任何一种情况下,都会正确遵循所有步骤:
var objPrototype = Object.create(this.prototype); // 1-4 1-5
var instance = this.apply(objPrototype, arguments); // 5 6
兴趣点是2.
The specification for [[construct]]
州:
当Function对象F的[[Construct]]内部方法是 使用可能为空的参数列表调用,执行以下步骤 采取:
- 让obj成为新创建的本机ECMAScript对象
. . .
- 让结果成为调用F的[[Call]]内部属性的结果,提供obj作为此值并提供参数列表 作为args传入[[Construct]]。
- 如果
Type
(结果)是对象,则返回结果。- 返回 obj 。
typeof obj
为"object"
返回null
,而null
不是对象。但是,由于null
是一个假值,您的代码也可以按预期工作:
return (typeof instance === 'object' && instance ) || objPrototype;
答案 1 :(得分:6)
new
运算符使用函数F
和arguments
:new F(arguments...)
。它有三个简单的步骤:
创建类的实例。它是一个空物体
__proto__
属性设置为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
答案 2 :(得分:0)
以下是使用__proto__
方法的替代方法。它符合OP最初的开始......
function New(fn) {
var newObj = Object.create(fn.prototype);
return function() {
fn.apply(newObj, arguments);
return newObj;
};
}
这是一种更清洁的方式,它也通过了原型链测试。
答案 3 :(得分:0)
这里的答案对于标准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
不再适用于每种类型的函数的简单原因。 The previous answer解释new
调用内部语言函数[[Construct]]
来初始化参数。使用.apply
的方法实际上取代了[[Construct]]
中处理的自动对象创建和调用逻辑,而是手动创建对象,然后调用函数,该函数使用它[[Call]]
内部方法而不是[[Construct]]
。
对函数的调用是ES6中更改的一部分。在ES5中,您构建的唯一内容是正常function Foo(){}
值,因此您可以对此进行假设。在ES6中,引入了class Foo {}
语法,并且由类语法创建的构造函数具有更多限制,因此关于ES5的假设不适用。最重要的是,明确禁止ES6类使用[[Call]]
。执行以下操作将引发异常:
class Foo {}
Foo();
这与.call
和.apply
相同。它们不是函数构造函数,它们是函数调用函数。因此,如果您尝试在ES6类上使用它们,它们将抛出异常。
Reflect.construct
通过实际调用[[Construct]]
而不是[[Call]]
来避免这些问题,但是通过可以在没有new
的情况下使用的API公开它