4

If the JavaScript new operator was a function, how would it be defined?

function _new(fn) {
    // What goes here?
}
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
jrdmcgr
  • 1,681
  • 3
  • 13
  • 13
  • 1
    @raam86 no, it's an operator! And this is actually a really good question... – Alnitak Oct 21 '13 at 13:54
  • You could mimic it's behavior using this method. – Ian Brindley Oct 21 '13 at 13:54
  • @Alnitak that's totally true! – raam86 Oct 21 '13 at 13:54
  • I know what the new operator does as defined on [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new), but I'm looking for technical accuracy. The article on MDN has been flagged as needing a technical review. – jrdmcgr Oct 21 '13 at 13:54
  • I was going to write an answer but I don't think I'm the best person to do so. To save some time for others, here is the ECMA-262 reference: http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.2 – James Donnelly Oct 21 '13 at 13:55
  • What do you think it's definition should be? Have you tried something? – Dexygen Oct 21 '13 at 13:58
  • @JamesDonnelly: thanks for the link. That's what I was looking for, although I'd still like to see it written as a JS function. – jrdmcgr Oct 21 '13 at 13:58
  • Something like this? [http://jsfiddle.net/2qhE2/](http://jsfiddle.net/2qhE2/) – Ian Brindley Oct 21 '13 at 13:58
  • 4
    This question is off-topic because it is not practical. The `new` operator Is part of the language. Use it. There is no reason to reimplement it. – Raymond Chen Oct 21 '13 at 13:59
  • 1
    @RaymondChen I disagree - an answer to this question would probably also indirectly answer the question I've had in my own mind about how you would invoke `new` with an array of parameters. – Alnitak Oct 21 '13 at 14:01
  • @IanBrindley your fiddle doesn't create any new object nor set `this` to the newly created object inside the constructor. – Alnitak Oct 21 '13 at 14:02
  • @Alnitak consider it pseudo code. – Ian Brindley Oct 21 '13 at 14:04
  • If you replace `new` with `+` in the question, this question does not become any more answerable. If you simply want to understand how the `new` operator works, then ask *that* question instead. – zzzzBov Oct 21 '13 at 14:09
  • 2
    Alas, this question is a dupe: http://stackoverflow.com/questions/10428603/simulate-the-new-operator-in-javascript – jrdmcgr Oct 21 '13 at 14:16
  • 1
    Quantas 94 Heavy, Johathan Lonowski, and Alnitak had no trouble discerning the meaning of my question and all provided great answers. The question is simple, and concise, but more importantly it has already been asked on SO. If you don't like the question, then mark it as a dupe instead of putting it on hold. – jrdmcgr Oct 21 '13 at 15:16
  • Dr. Axel Rauschmayer implements the new operator in JS: http://www.2ality.com/2014/01/new-operator.html – jrdmcgr Jan 21 '14 at 16:31

4 Answers4

3

There are a few limitations owing to the capabilities of user code: native constructors such as new Date and new RegExp and bound function cannot be shimmed properly using this function.

If you're looking for the relevant specification sections, they are §11.2.2 and §13.2.2.

Anyway, here goes:

// 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;
}

About the typeof cases, that is for the edge case when host objects return a typeof value which is not object or function. By testing for anything they can't be, this allows us to test for it properly instead of relying on them being native objects.

Note that this is for at least ES5 -- the only way to shim Object.create in ES3 or older environments would be to use the exact thing that we're currently trying to emulate, which defeats the point of doing so.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
3

Had this same curiosity recently.

There is an issue with some of the steps being internal tasks, which simply can't be reimplemented within language.

And, over my original take, Qantas has a good point about using typeof as a faux Type() -- that it should be exclusive to support custom host object types.

But, with that, this is as close as I could manage. Though, it requires that Object.create() be available (so, 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;
};

Example:

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"

And, the annotated version, with steps from 11.2.2 new Operator and 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;
};
Community
  • 1
  • 1
Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
2

This appears to be the minimum necessary, albeit not 100% conformant with the language definition and without error checking. It's ES5 only. There is a shim for Object.create, but by necessity it must call new making it rather pointless in this context!

function _new(T, args) {
    var o = Object.create(T.prototype);
    var res = o.constructor.apply(o, args);
    return (typeof res === 'object') ? res : o;
}

with usage:

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);

The test of the return value of the call to the constructor is necessary because a constructor does not have to return this - if the constructor returns nothing the this is implicit.

See http://jsfiddle.net/uQpUv/

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • If `prototype` is undefined, then it won't work as per the specification. – Qantas 94 Heavy Oct 21 '13 at 14:11
  • @Qantas94Heavy yeah, that's what I meant about "minimum necessary". It's pretty hard to create a function that doesn't have a `prototype`, though - you have to call `Object.create(null)`, AFAICR. – Alnitak Oct 21 '13 at 14:12
  • `res ? res : o` doesn't conform to standards, but the concept is close. – zzzzBov Oct 21 '13 at 14:12
  • @Alnitak, no actually, there's a lot more nuance, for example, in `function A() { return 1 }; var b = new A();`, `b` will be an object, not the value `1`. – zzzzBov Oct 21 '13 at 14:15
  • @zzzzBov ah, yes - so it would be a `typeof` test, then :( Anyhow, I did say this was the _minimum_ possible - I've not tried to make it 100% conformant with the ES spec. – Alnitak Oct 21 '13 at 14:18
-3

Haters gonna hate, but something like this?

Obviously this is just a round about alias of new but with scope to extend it's potential with the arguments.

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"});
Ian Brindley
  • 2,197
  • 1
  • 19
  • 28