我可以在不使用new关键字的情况下构造JavaScript对象吗?

时间:2009-12-11 15:59:55

标签: javascript inheritance prototype constructor

以下是我想做的事情:

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

有可能吗?我可以通过将b挂钩到其原型链中轻松地使a成为a的实例但是我必须执行new b(),这是我要避免的。我想要的是什么?

更新:我觉得可以明智地使用b.__proto__ = a.prototype。我下班后会做更多的实验。

更新2:以下是您可以获得的最接近的内容,这对我来说已经足够了。感谢所有有趣的答案。

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

更新3 :一旦我添加了必要的b.__proto__ = a.prototype行,我就在blog post on 'power constructors'找到了我想要的内容:

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

15 个答案:

答案 0 :(得分:27)

您可以使用此模式:

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

之后你可以这样做:

var myObj = SomeConstructor();

除了这个(相当陈旧的)答案:您可以使用module pattern来创建对象:

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

这是我使用该模式创建的某些Date功能的jsFiddle

答案 1 :(得分:10)

使用new关键字有什么问题?

无论如何,听起来最好的事情是阅读Javascript继承: http://javascript.crockford.com/inheritance.html

答案 2 :(得分:4)

有人在这个问题上发表了道格拉斯·克罗克福德的一篇文章,它解释了你的要求。

OO Javascript constructor pattern: neo-classical vs prototypal

答案 3 :(得分:3)

如果你想要继承并且new能够工作,那么在一般情况下你不能避免instanceof(根据Crockford与Zoidberg间接链接的文章没有走极端),但是(再次) ),你为什么要或需要?

我能想到你想要避免它的唯一原因是你试图将构造函数传递给另一段不知道它是构造函数的代码。在这种情况下,只需将其包装在工厂函数中:

function b() {
    // ...
}
function makeB() {
    return new b();
}
var c = makeB();

答案 4 :(得分:3)

您具体问题的简单答案是:否。

这有助于您确定避免new的原因。或许其他答案中提到的模式也会有所帮助。但是,它们都不会导致instanceof在测试中返回true。

新操作基本上是: -

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

然而,使用某些内部属性将构造fn附加到对象是有区别的(是的,您可以使用constructor获取它,但设置它不具有相同的效果)。让instanceof按预期工作的唯一方法是使用new关键字。

答案 5 :(得分:2)

你可以在没有new运算符的情况下创建实例(这是Douglas Crockford撰写的一篇很棒的文章http://yuiblog.com/blog/2006/11/13/javascript-we-hardly-new-ya/)。但它不会帮助你处理“instanceof”故事。

答案 6 :(得分:2)

是的,你可以这样做。

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

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

var user1 = User();

这种方法有什么问题吗?

答案 7 :(得分:1)

使instanceof工作的唯一方法是使用new关键字。 instanceof exploits ____proto____由 new 建立。

答案 8 :(得分:0)

可以在不使用new关键字的情况下创建JavaScript对象。

例如,以下函数返回一个对象而不使用新关键字


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

    return obj;

}

答案 9 :(得分:0)

在javascript书签中,您可以使用eval(unescape(" ..."))来创建没有" new"操作者:

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

答案 10 :(得分:0)

对于那些可以通过谷歌(像我一样)找到这个的人,我进一步开发了原始解决方案,以便更好地使用:



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)




答案 11 :(得分:0)

使用Object.create和经典Function.prototype。正确设置原型链,可以保留instanceof关键字的正常运行,而无需使用new关键字。

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

答案 12 :(得分:0)

是的

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

参考:https://gist.github.com/aniltallam/af358095bd6b36fa5d3dd773971f5fb7

答案 13 :(得分:0)

为新操作员填充:

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

答案 14 :(得分:0)

是的。如果您不想使用'new'关键字,则只需使用prototype,然后在构造函数中返回某些内容即可。

使用'new'告诉构造函数:

1)构造函数中的'this'关键字应引用函数本身,而不是父对象(通常),如果在全局范围内声明此函数,则该父对象将是窗口对象。

2)对于新创建的对象/实例上所有失败的查找(找不到obj属性),请检查原始构造函数的prototype属性。

新增

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

没有新内容:

    function Point(x, y) {
       let d = Object.create(Point.prototype);
       this.x = x;
       this.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);

在第一个示例中尝试添加删除“ new”,然后您将看到“ this”,它不再是指构造函数,而是窗口对象。您将为窗口提供x和y属性,这些属性不是您想要的。