如何将JavaScript的新运算符定义为函数?

时间:2013-10-21 13:52:05

标签: javascript

如果JavaScript new运算符是一个函数,它将如何定义?

function _new(fn) {
    // What goes here?
}

4 个答案:

答案 0 :(得分:3)

由于用户代码的功能,存在一些限制:本地构造函数(如new Datenew 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值不是objectfunction时的边缘情况。通过测试他们不能做的任何事情,这允许我们正确地测试它,而不是依赖它们作为本机对象。

请注意,这至少适用于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 Operator13.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是隐式的。

请参阅http://jsfiddle.net/uQpUv/

答案 3 :(得分:-2)

仇恨会讨厌,但这样的事情呢?

显然,这只是关于new的别名的一轮,但是可以通过参数来扩展它的潜力。

http://jsfiddle.net/2qhE2/3/

function _new(classname, arguments) {
    // other logic
    return new classname(arguments || {});  
}

function car(args){
    alert(args.make);   
}

var MyNewCar = _new(car, {make: "vw"});