Can I construct a JavaScript object without using the new keyword?

江枫思渺然 提交于 2019-12-27 14:14: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

回答1:


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

In addition to this (rather old) answer: you can use a module pattern to create an object:

function Person(name, age, male) {
  name = name || 'unknown';
  age = age || 0;
  function get() {
    return ['This person is called ', name,
            (!male ? ', her' : ', his'),' age is ',
            age].join('');
  }
  function setAge(nwage) {
     age = nwage;
  }
  return Object.freeze({get: get, setAge: setAge});
}
// usage
var jane =  Person('Jane', 23)
   ,charles = Person('Charles', 32, 1)
   ,mary = Person('Mary', 16);

console.log(jane.get()); //=> This person is called Jane, her age is 23
mary.setAge(17);
console.log(mary.get()); //=> This person is called Mary, her age is 17

Here's a jsFiddle for some Date functionallity I created using that pattern.




回答2:


What's wrong with using the new keyword?

At any rate, it sounds like the best thing to do is read up on Javascript inheritance: http://javascript.crockford.com/inheritance.html




回答3:


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




回答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.




回答5:


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



回答6:


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.




回答7:


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?




回答8:


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




回答9:


JavaScript objects can be created without using the new keyword.

For example the following function returns an object without using new keyword


function a(){
    var obj ={};
    obj.name = "hello";
    obj.age = 12;

    return obj;

}



回答10:


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

javascript:xhr=eval(unescape('new\x20XMLHttpRequest();'));alert(xhr);



回答11:


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)



回答12:


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



回答13:


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




回答14:


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


来源:https://stackoverflow.com/questions/1889014/can-i-construct-a-javascript-object-without-using-the-new-keyword

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!