如果JavaScript new
运算符是一个函数,它将如何定义?
function _new(fn) {
// What goes here?
}
答案 0 :(得分:3)
由于用户代码的功能,存在一些限制:本地构造函数(如new Date
和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 - 在ES3或旧版环境中填充Object.create
的唯一方法是使用我们目前正在尝试模拟的确切内容,这样做无法实现如此。
答案 1 :(得分:3)
最近有同样的好奇心。
某些步骤是内部任务存在问题,而这些步骤根本无法在语言中重新实现。
而且,在我的original take上,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;
};
答案 2 :(得分:2)
这似乎是最低限度,尽管不是100%符合语言定义且没有错误检查。它只是ES5。 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
是隐式的。
答案 3 :(得分:-2)
显然,这只是关于new
的别名的一轮,但是可以通过参数来扩展它的潜力。
function _new(classname, arguments) {
// other logic
return new classname(arguments || {});
}
function car(args){
alert(args.make);
}
var MyNewCar = _new(car, {make: "vw"});