JavaScript:如何创建一个类的新示例而不使用new关键字?

nhjlsmyf  于 2023-02-07  发布在  Java
关注(0)|答案(8)|浏览(181)

我想下面的代码可以把问题解释清楚。

// My class
var Class = function() { console.log("Constructor"); };
Class.prototype = { method: function() { console.log("Method");} }

// Creating an instance with new
var object1 = new Class();
object1.method();
console.log("New returned", object1);

// How to write a factory which can't use the new keyword?
function factory(clazz) {
    // Assume this function can't see "Class", but only sees its parameter "clazz".
    return clazz.call(); // Calls the constructor, but no new object is created
    return clazz.new();  // Doesn't work because there is new() method
};

var object2 = factory(Class);
object2.method();
console.log("Factory returned", object2);
thtygnil

thtygnil1#

更简单、更清洁的方式,没有“工厂”

function Person(name) {
  if (!(this instanceof Person)) return new Person(name);
  this.name = name;
}

var p1 = new Person('Fred');
var p2 = Person('Barney');

p1 instanceof Person  //=> true
p2 instanceof Person  //=> true
qvsjd97n

qvsjd97n2#

这不管用吗?

function factory(class_, ...arg) {
    return new class_(...arg);
}

我不明白为什么你不能用new

oiopk7p5

oiopk7p53#

如果你真的不想使用new关键字,并且你不介意只支持Firefox,你可以自己设置原型,但这并没有什么意义,因为你可以直接使用Dave欣顿的答案。

// This is essentially what the new keyword does
function factory(clazz) {
    var obj = {};
    obj.__proto__ = clazz.prototype;
    var result = clazz.call(obj);
    return (typeof result !== 'undefined') ? result : obj;
};
hujrc8aj

hujrc8aj4#

我想独立于浏览器的解决方案会更好

function empty() {}

function factory(clazz /*, some more arguments for constructor */) {
    empty.prototype = clazz.prototype;
    var obj = new empty();
    clazz.apply(obj, Array.prototype.slice.call(arguments, 1));
    return obj;
}
yhuiod9q

yhuiod9q5#

因为JavaScript没有类,所以让我改写一下您的问题:如何在不使用new关键字的情况下基于现有对象创建新对象?
这里有一个不使用“new”的方法,严格来说它不是“new instance of”,但这是我能想到的唯一不使用“new”的方法(也不使用任何ECMAScript 5特性)。

//a very basic version that doesn't use 'new'
function factory(clazz) {
    var o = {};
    for (var prop in clazz) {
        o[prop] = clazz[prop];
    }
    return o;
};

//test
var clazz = { prop1: "hello clazz" };
var testObj1 = factory(clazz);
console.log(testObj1.prop1);    //"hello clazz"

你可以花点心思来设置原型,但是这样会遇到跨浏览器的问题,我尽量让这个简单一些,你也可以使用“hasOwnProperty”来过滤你要添加到新对象中的属性。
还有其他一些方法可以使用“new”,但却隐藏了它。下面是借用JavaScript: The Good Parts by Douglas Crockford中的Object.create函数的一种方法:

//Another version the does use 'new' but in a limited sense
function factory(clazz) {
    var F = function() {};
    F.prototype = clazz;
    return new F();
};

//Test
var orig = { prop1: "hello orig" };
var testObj2 = factory(orig);
console.log(testObj2.prop1);  //"hello orig"

EcmaScript5有Object.create方法,它可以更好地实现这一点,但只有较新的浏览器(例如IE9,FF4)才支持,但您可以使用polyfill(一些填补漏洞的东西),如ES5 Shim,来获得旧浏览器的实现。(参见John Resig's article on new ES5 features including Object.create)。
在ES5中,您可以这样做:

//using Object.create - doesn't use "new"
var baseObj = { prop1: "hello base" };
var testObj3 = Object.create(baseObj);
console.log(testObj3.prop1);

希望能有所帮助

afdcj2ne

afdcj2ne6#

另一种方式:

var factory = function(clazz /*, arguments*/) {
    var args = [].slice.call(arguments, 1);
    return new function() { 
        clazz.apply(this, args)
    }
}
rvpgvaaj

rvpgvaaj7#

为了更准确地回答这个问题,即如何让myClass()返回new myClass() ...这是不可能的,下面是原因...
您必须这样做,以确保类名存在,并且您正在捕获对myClass()的调用(使用apply功能,来自Proxy/trap/handler-land):

class A {

}
A.prototype.apply = function() {
  return new A();
}
A(); //Error occurs here.

或:

class B {

}
B.apply = function() {
  return new B();
}
B(); //Error occurs here.

这个方法不起作用的原因是,当你试图评估以上两种情况时,会产生以下输出:Uncaught TypeError: class constructors must be invoked with 'new'
因此,JavaScript实际上不允许这样做,因为您已经将其声明为class类型,但是,您当然可以使用一个单独命名的函数为您创建一个新示例,就像answer above中那样,或者更简单:

class A {

}
//Note: a != A.
function a() {
  return new A();
}

或者,解决这个问题的另一种方法是使用class本身,而是使用function或常规JS对象{},就像早期的JavaScript一样。

btqmn9zl

btqmn9zl8#

您还可以使用eval
当然,eval存在安全问题,但是它真的与其他动态示例化不同吗?

await import("/path/to/module") //use this to dynamically load module if you like
let obj = `eval new ${classname}();`

相关问题