如果 JavaScriptnew
运算符是一个函数,它将如何定义?
function _new(fn) {
// What goes here?
}
由于用户代码的功能,存在一些限制:使用此函数无法正确填充诸如new Date
and和绑定函数之类的本机构造函数。new RegExp
如果您正在寻找相关的规范部分,它们是§11.2.2和§13.2.2。
无论如何,这里是:
// we're assuming that correct arguments are given
function _new(F, args) {
// we could have host objects, make sure edge case not left out
// (typeof could be other than object or function in this case - see bottom)
function Type(arg) {
if (arg === undefined) return 'Undefined';
if (arg === null) return 'Null';
if (arg === false || arg === true) return 'Boolean';
var type = typeof arg;
if (type === 'string') return 'String';
if (type === 'number') return 'Number';
return 'Object';
}
// 1. Let obj be a newly created native ECMAScript object.
// 2. Set all the internal methods of obj as specified in 8.12.
// 3. Set the [[Class]] internal property of obj to "Object".
// 4. Set the [[Extensible]] internal property of obj to true.
// All of the steps above are implicitly completed in steps 6 or 7
var obj;
// 5. Let proto be the value of calling the [[Get]] internal property of F
// with argument "prototype".
var proto = F.prototype;
// 6. If Type(proto) is Object, set the [[Prototype]] internal property of
// obj to proto.
if (Type(proto) === 'Object') obj = Object.create(proto);
// 7. If Type(proto) is not Object, set the [[Prototype]] internal property
// of obj to the standard built-in Object prototype object as described
// in 15.2.4.
else obj = {};
// 8. Let result be the result of calling the [[Call]] internal property of
// F, providing obj as the this value and providing the argument list
// passed into [[Construct]] as args.
var result = F.apply(obj, args);
// 9. If Type(result) is Object then return result.
if (Type(result) === 'Object') return result;
// 10. Return obj.
return obj;
}
关于这些情况,这是针对宿主对象返回的值不是或typeof
的边缘情况。通过测试它们不可能的任何东西,这使我们能够正确地对其进行测试,而不是依赖它们是本机对象。typeof
object
function
请注意,这至少适用于 ES5——Object.create
在 ES3 或更早的环境中填充的唯一方法是使用我们当前尝试模拟的确切内容,这与这样做的意义相悖。
最近也有同样的好奇心。
某些步骤是内部任务存在问题,根本无法在语言中重新实现。
而且,在我最初的看法中,Qantas 有一个关于typeof
用作虚假的好点Type()
——它应该是唯一支持自定义主机对象类型的。
但是,有了这个,这是我能做到的。不过,它要求它Object.create()
是可用的(所以,ES5+)。
Object.new = function (constructor /*, args */) {
function isObject(operand) {
// detect and refuse primitives
var type = typeof operand;
return type !== 'undefined' &&
type !== 'boolean' &&
type !== 'number' &&
type !== 'string' &&
operand !== null;
}
var argList = Array.prototype.slice.call(arguments, 1);
if (typeof constructor !== 'function') {
throw new TypeError((typeof constructor) + ' is not a function');
}
var proto = constructor.prototype;
var obj = Object.create(isObject(proto) ? proto : Object.prototype);
var result = constructor.apply(obj, argList);
return isObject(result) ? result : obj;
};
例子:
function Foo(one, two) {
this.one = one;
this.two = two;
}
var bar = Object.new(Foo, 'a', 'b');
console.log(bar instanceof Foo); // true
console.log(bar.one); // "a"
console.log(bar.two); // "b"
并且,带注释的版本,从11.2.2 new
Operator和13.2.2[[Construct]]
开始:
// 1. Let ref be the result of evaluating MemberExpression.
// 2. Let constructor be GetValue(ref).
Object.new = function (constructor /*, args */) {
function isObject(operand) {
// detect and refuse primitives
var type = typeof operand;
return type !== 'undefined' &&
type !== 'boolean' &&
type !== 'number' &&
type !== 'string' &&
operand !== null;
}
// 3. Let argList be the result of evaluating Arguments, producing an internal list of argument values (11.2.4).
var argList = Array.prototype.slice.call(arguments, 1);
// 4. If Type(constructor) is not Object, throw a TypeError exception.
// 5. If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.
if (typeof constructor !== 'function') {
throw new TypeError((typeof constructor) + ' is not a function');
}
// 6. Return the result of calling the [[Construct]] internal method on constructor, providing the list argList as the argument values.
// For [[Construct]], it gets a bit out of order with current options for internal vs. abstractions.
// 5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".
var proto = constructor.prototype;
// 1. Let obj be a newly created native ECMAScript object.
// 2. Set all the internal methods of obj as specified in 8.12.
// 3. Set the [[Class]] internal property of obj to "Object".
// 4. Set the [[Extensible]] internal property of obj to true.
//
// 6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.
// 7. If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.
var obj = Object.create(isObject(proto) ? proto : Object.prototype);
// 8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
var result = constructor.apply(obj, argList);
// 9. If Type(result) is Object then return result.
// 10. Return obj.
return isObject(result) ? result : obj;
};
这似乎是最低要求,尽管不是 100% 符合语言定义并且没有错误检查。它只是 ES5。有一个 shim Object.create
,但在这种情况下,它必须调用new
使它变得毫无意义!
function _new(T, args) {
var o = Object.create(T.prototype);
var res = o.constructor.apply(o, args);
return (typeof res === 'object') ? res : o;
}
用法:
function A(name) {
this.name = name;
}
A.prototype.hello = function() {
console.log(this);
}
var foo = _new(A, ['foo']);
var bar = _new(A, ['bar']);
console.log(foo.name);
console.log(bar.name);
对构造函数调用的返回值进行测试是必要的,因为构造函数不必这样做return this
——如果构造函数没有返回任何内容,this
则它是隐式的。
讨厌的人会讨厌,但是像这样?
显然,这只是关于别名的一轮,new
但可以通过参数扩展它的潜力。
function _new(classname, arguments) {
// other logic
return new classname(arguments || {});
}
function car(args){
alert(args.make);
}
var MyNewCar = _new(car, {make: "vw"});