26

Here is what I'd like to do:

function a() {
  // ...
}
function b() {
  //  Some magic, return a new object.
}
var c = b();

c instanceof b // -> true
c instanceof a // -> true
b instanceof a // -> true

Is it possible? I can make b be an instance of a easily by hooking a into its prototype chain but then I have to do new b(), which is what I'm trying to avoid. Is what I want possible?

Update: I feel that it might be possible with judicious use of b.__proto__ = a.prototype. I'm going to experiment more after work.

Update 2: Below is what seems to be the closest you can get, which is good enough for me. Thanks all for the interesting answers.

function a() {
  // ...
}
function b() {
  if (!(this instanceof arguments.callee)) {
    return new arguments.callee();
  }
}
b.__proto__ = a.prototype

var c = b();
c instanceof b // -> true
c instanceof a // -> false
b instanceof a // -> true

Update 3: I found exactly what I wanted in a blog post on 'power constructors', once I added the essential b.__proto__ = a.prototype line:

var object = (function() {
     function F() {}
     return function(o) {
         F.prototype = o;
         return new F();
     };
})();

function a(proto) {
  var p = object(proto || a.prototype);
  return p;
}

function b(proto) {
  var g = object(a(proto || b.prototype));
  return g;
}
b.prototype = object(a.prototype);
b.__proto__ = a.prototype;

var c = b();
c instanceof b // -> true
c instanceof a // -> true
b instanceof a // -> true
a() instanceof a // -> true
pr1001
  • 21,727
  • 17
  • 79
  • 125
  • 16
    I am curious: why do you want to avoid new? – Upperstage Dec 11 '09 at 16:02
  • 1
    I am creating a Javascript version of Scala's case classes: github.com/pr1001/caseclass.js – pr1001 Dec 11 '09 at 16:47
  • 2
    @pr1001: Not seeing why that requires you to work around the `new` keyword. And `__proto__` is not going to help you cross-browser. – T.J. Crowder Dec 12 '09 at 00:09
  • 1
    This is all syntactical sugar, nothing more. However, I think it's an interesting challenge all the same. – pr1001 Dec 12 '09 at 00:32
  • Possible duplicate of [JavaScript: How to create a new instance of a class without using the new keyword?](http://stackoverflow.com/questions/1580863/javascript-how-to-create-a-new-instance-of-a-class-without-using-the-new-keywor) – Damjan Pavlica Sep 28 '16 at 11:53
  • Helpful [source](https://muffinresearch.co.uk/js-create-instances-without-new/) – Boolean_Type Mar 08 '18 at 22:08

13 Answers13

34

You can use this pattern:

function SomeConstructor(){
   if (!(this instanceof SomeConstructor)){
        return new SomeConstructor();
   }
   //the constructor properties and methods here
}

after which you can do:

var myObj = SomeConstructor();

[Edit 2023 (actually: re-edit)] To avoid the new keyword altoghether you may use a factory function. Here's an example. It's what Douglas Crockford calls a class free object oriented solution.

The pattern/idea is used within this small github repository

const Person = (name = 'unknown', age = 0, male = false) => {
  const value = () => `This is ${name}. ${
    !male ? `Her` : `His`} age is ${age} years.`;
    
  return {
    get whoIs() { return value(); },
    toString() { return value(); },
    addAge: years => age += years,
    setAge: newAge => age = newAge,
    rename: newName => name = newName,
  };
}
// usage
const [ jane, charles, mary ] =  [
  Person(`Jane`, 23), 
  Person(`Charles`, 32, 1), 
  Person(`Mary`, 16), ];

jane.addAge(17);
charles.setAge(76);
console.log(`${jane}`);
console.log(`${mary}`);
console.log(charles.whoIs);
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • I just tried this and it looks very promising. Will develop it more later. – pr1001 Dec 11 '09 at 17:55
  • 5
    +1 this is the usual idiom for allowing constructors to work without `new`. – bobince Dec 11 '09 at 20:19
  • 6
    Comment #1: But...but...but...it still uses `new`. I'm not seeing the point here, it's just a factory method that *also* allows the user to use `new` if they want to. That just leads to some members of the team using `new` and others not; a maintenance nightmare. Support one or the other (e.g., by including debug code you remove for production that throws an exception if the author didn't -- or did -- use `new` to call the constructor), but not both. – T.J. Crowder Dec 13 '09 at 14:09
  • 3
    Comment #2: If you want to use this pattern, make sure you use a named function (as in your example) and use the function's name, not `arguments.callee` -- e.g.: `if (!(this instanceof SomeConstructor))`. Using `arguments.callee` will slow down your functions *dramatically* (2-10x, depending on the JavaScript engine), and will throw an exception in JavaScript's new "strict" mode. – T.J. Crowder Dec 13 '09 at 14:11
  • 1
    Sorry, T.J., if I wasn't clear but I was looking for code where the _end user_, not the library creator, avoided using `new`. – pr1001 Dec 14 '09 at 08:13
  • It's an interesting technical problem, but I agree with T.J. that if you want the users of your library to be able to avoid `new` you should create a factory method such that it is the *only* way to create your object, not let the users choose to use `new` if they want to. Ideally allow only either `var x = new MyObject();` or `var x = MakeMyObject();` (or similar). – nnnnnn May 10 '11 at 06:12
5

Yes. If you don't want to use the 'new' keyword, just use prototype, and return something in your constructor function.

Using 'new' just tells the constructor functions that:

  1. The 'this' keyword in the constructor function should reference the function itself, not the parent object (as per usual), which would be the window object if this function was declared in the global scope.

  2. For all failed lookups (obj properties not found) on the newly created object/instance, check the original constructor function's prototype property.

So with new:

    function Point(x, y) {
       this.x = x;
       this.y = y;
    }
    Point.prototype.getDistance = function(otherPoint){
       var Dx = (this.x - otherPoint.x) ** 2;
       var Dy = (this.y - otherPoint.y) ** 2;
       var d = Dx + Dy;
       d = Math.sqrt(d);
       return d
    }
    var pointA = new Point(3, 6);
    var pointB = new Point(5, 8);
    var distanceAB = pointA.getDistance(pointB);

Without new:

    function Point(x, y) {
       let d = Object.create(Point.prototype);
       d.x = x;
       d.y = y;
       return d
    }
    Point.prototype.getDistance = function(otherPoint){
       var Dx = (this.x - otherPoint.x) ** 2;
       var Dy = (this.y - otherPoint.y) ** 2;
       var d = Dx + Dy;
       d = Math.sqrt(d);
       return d
    }
    var pointA = Point(3, 6);
    var pointB = Point(5, 8);
    var distanceAB = pointA.getDistance(pointB);

Try adding removing the 'new' in the the first example, and you'll see the 'this', no longer refers to the constructor function, but the window object instead. You'll be giving your window x and y properties, which proplably isn't what you want.

kennsorr
  • 292
  • 3
  • 20
4

Someone posted an article by douglas crockford in this question, and it explains exactly what your asking.

OO Javascript constructor pattern: neo-classical vs prototypal

Community
  • 1
  • 1
Zoidberg
  • 10,137
  • 2
  • 31
  • 53
  • What an .... *interesting* approach. And darned if it doesn't do exactly that, get rid of `new` (and `this`). A bit extreme, but shows how powerful the language is. – T.J. Crowder Dec 11 '09 at 16:18
  • 1
    instanceof wouldn't return the results desired in the question though. – AnthonyWJones Dec 11 '09 at 16:24
4

The simple answer to your specific question is: no.

It would help you identified why you want to avoid new. Perhaps the patterns alluded to by one of the other answers will help. However none of them result in the instanceof returning true in your tests.

The new operation is essentially:-

var x = (function(fn) { var r = {}; fn.call(r); return r;}(b);

However there is the difference that the constructing fn is attached to the object using some internal property (yes you can get it with constructor but setting it doesn't have the same effect). The only way to get instanceof to work as intended is to use the new keyword.

AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
3

Yes you can do this.

var User = function() {
  var privateMethod = function() {
    alert('hello');
  }

  return {
    sayHello : function() {
      privateMethod();
      this.done = true;
    }
  }
}

var user1 = User();

Is anything wrong with this method?

dgellow
  • 692
  • 1
  • 11
  • 18
3

You can't avoid new in the general case (without going to extremes, as per the Crockford article Zoidberg indirectly linked to) if you want inheritance and instanceof to work, but then (again), why would you want or need to?

The only reason I can think of where you'd want to avoid it is if you're trying to pass a constructor function to another piece of code that doesn't know it's a constructor. In that case, just wrap it up in a factory function:

function b() {
    // ...
}
function makeB() {
    return new b();
}
var c = makeB();
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
2

You can create instances without the new operator (here is a great article written about this by Douglas Crockford http://yuiblog.com/blog/2006/11/13/javascript-we-hardly-new-ya/). But it will not help you with the "instanceof" story.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nemisj
  • 11,562
  • 2
  • 25
  • 23
  • 2
    That's not about general purpose `new` use, it's about specific places you can avoid it, such as with `Object` (use `{}` instead), `Array` (use `[]` instead), and especially `Function`. – T.J. Crowder Dec 11 '09 at 16:06
  • The question was, if it's possible. The article explains that YES it is possible, and it also covers some aspects why it can be useful. – nemisj Dec 11 '09 at 16:10
  • 2
    @nemisj: No, it doesn't. It talks about not using `new` inappropriately (like `new function() { ... }`), *not* about avoiding using it in general. The article doesn't talk about inheritance at all, in fact. – T.J. Crowder Dec 11 '09 at 16:14
  • @T.J. Crowder: My mistake, haven't noticed the idea behind c instance a :) – nemisj Dec 11 '09 at 16:51
1

The only way to get instanceof to work is to use the new keyword. instanceof exploits ____proto____ which is established by new.

Upperstage
  • 3,747
  • 8
  • 44
  • 67
1

By using Object.create and the classical Function.prototype. Setting up the prototype chain properly, you preserve proper functioning of the instanceof keyword without any use of the new keyword.

function A() {
    return Object.create(A.prototype);
}

function B() {
    return Object.create(B.prototype);
}

B.prototype = Object.create(A.prototype);

var c = B();

assert(c instanceof A);
assert(c instanceof B);
rich remer
  • 3,407
  • 2
  • 34
  • 47
0

In a javascript bookmarklet, you could use eval(unescape("...")) to create object without "new" operator:

javascript:xhr=eval(unescape('new\x20XMLHttpRequest();'));alert(xhr);
diyism
  • 12,477
  • 5
  • 46
  • 46
0

For those, who may find this via Google (like me...), I developed the original solution further for a better common usage:

var myObject = function() {}; // hint: Chrome debugger will name the created items 'myObject'

var object = (function(myself, parent) {  
     return function(myself, parent) {
         if(parent){
             myself.prototype = parent;
         }
         myObject.prototype = myself.prototype;
         return new myObject();
     };
})(); 

a = function(arg) {
     var me = object(a);
     me.param = arg;
     return me;
};

b = function(arg) {
    var parent = a(arg),
        me = object(b, parent)
    ;
    return me;
};

var instance1 = a();
var instance2 = b("hi there");

console.log("---------------------------")
console.log('instance1 instance of a: ' +  (instance1 instanceof a))
console.log('instance2 instance of b: ' +  (instance2 instanceof b))
console.log('instance2 instance of a: ' +  (instance2 instanceof a))
console.log('a() instance of a: ' +  (a() instanceof a))
console.log(instance1.param)
console.log(instance2.param)
0

Yes.

function _new(classConstructor, ...args) {
  var obj = Object.create(classConstructor.prototype);
  classConstructor.call(obj, ...args);
  return obj;
}

function test_new() {
  function TestClass(name, location) {
    this._name = name;
    this._location = location;
    this.getName = function() {
      return this._name;
    }
  }

  TestClass.prototype.getLocation = function() {
    return this._location;
  }
  TestClass.prototype.setName = function(newName) {
    this._name = newName;
  }

  const a = new TestClass('anil', 'hyderabad');
  const b = _new(TestClass, 'anil', 'hyderabad');

  const assert = console.assert
  assert(a instanceof TestClass)
  assert(b instanceof TestClass)
  assert(a.constructor.name === 'TestClass');
  assert(b.constructor.name === 'TestClass');
  assert(a.getName() === b.getName());
  assert(a.getLocation() === b.getLocation());
  a.setName('kumar');
  b.setName('kumar');
  assert(a.getName() === b.getName());
  console.log('All is well')
}
test_new()

Ref: https://gist.github.com/aniltallam/af358095bd6b36fa5d3dd773971f5fb7

Anil Tallam
  • 331
  • 4
  • 10
0

Polyfill for new operator:

function sampleClass() {
    this.cname = "sample";
}

sampleClass.prototype.getCname = function() {
    return this.cname;
}

const newPolyfill = function(fname) {
    if (typeof fname !== "function")
    throw Error("can't call new on non-functional input");

    let newFun = {};
    newFun.__proto__ = Object.assign(fname.prototype);
    fname.prototype.constructor();

    for (let key in fname.prototype) {
        if (typeof fname.prototype[key] !== "function") {
        newFun[key] = fname.prototype[key];
        delete newFun.__proto__[key];
        delete fname.__proto__[key];
       }
    }

    return newFun;
}

let newObj = new sampleClass();
console.log("new obj", newObj);
console.log("new cname", newObj.getCname());//sample
let newPolyObj = newPolyfill(sampleClass);
console.log("new poly obj", newPolyObj);
console.log("newPly cname", newPolyObj.getCname());//sample
console.log(newPolyObj instanceof(sampleClass)) // true