Javascript 如何在没有类的情况下生成对象的新实例? 问题:
如何从一个类的实例生成该类的新实例 我发现: 考虑下一个类及其实例:Javascript 如何在没有类的情况下生成对象的新实例? 问题:,javascript,Javascript,如何从一个类的实例生成该类的新实例 我发现: 考虑下一个类及其实例: // Create a new class var Foo = function Foo () { console.log('New instance of FOO!'); }; Foo.prototype.generate = function(first_argument) { this.arr = [1,2,3,4,5]; }; var foo = new Foo(); foo.generate(); co
// Create a new class
var Foo = function Foo () {
console.log('New instance of FOO!');
};
Foo.prototype.generate = function(first_argument) {
this.arr = [1,2,3,4,5];
};
var foo = new Foo();
foo.generate();
console.log(foo.arr); // [1,2,3,4,5]
Foo.prototype.createNew = function() {
return new Foo();
};
var bar = foo.createNew();
var fooCopy = foo.createCopy();
注意,foo
作为foo
的一个实例,我将在示例中使用这个实例。
Object.create
使用Object.create,我可以生成foo实例的新副本,但它们将共享状态数据:
var bar = Object.create(foo);
console.log(bar.arr); // [1,2,3,4,5]
bar.arr.push(6);
console.log(foo.arr); // [1,2,3,4,5,6]
console.log(bar.arr); // [1,2,3,4,5,6]
如果构造函数中有一些逻辑,则不会调用它
原型固有
这类似于对象创建,但它调用构造函数。它仍然存在相同的状态数据问题:
var Bar = function () {
foo.constructor.call(this);
};
Bar.prototype = foo;
var bar = new Bar();
console.log(bar.arr); // [1,2,3,4,5]
bar.arr.push(6);
console.log(foo.arr); // [1,2,3,4,5,6]
console.log(bar.arr); // [1,2,3,4,5,6]
Firefox原型
下面是我想要做的一个例子,但它只适用于Firefox:
// Create Bar class from foo
var Bar = foo.constructor;
Bar.prototype = foo.__proto__; // Firefox only
这里我有一个类栏,它是类Foo的副本,我可以生成新的实例
var bar = new Bar();
console.log(bar.arr); // undefined
bar.generate();
console.log(bar.arr); // [1,2,3,4,5]
有没有其他方法可以实现在所有浏览器中都适用的相同目标?在您的第二个原型示例
bar.prototype=foo
中,您必须创建一个代理函数,这样构造函数方法就不会被调用,并且您可以真正从foo扩展:
var Foo = function() {
console.log('foo init')
};
Foo.prototype.generate = function() {
this.arr = [1, 2, 3, 4];
}
var Bar = function() {
// Call the "constructor" method (Foo) with the correnct context and arguments,
// for this we can use Function.apply(context, array);
// where array can be any array like object as well a an real array:
Foo.apply(this, arguments);
console.log('bar init');
};
// Surrogate function:
var sur = function() {};
// Capture Foo's prototype:
sur.prototype = Foo.prototype;
// Apply the prototype from Foo to Bar's prototype chain, without initializing Foo:
Bar.prototype = new sur;
// Also remember to update the constructor:
Bar.prototype.constructor = Bar;
var f = new Foo(); // foo init;
var b = new Bar(); // foo init, bar init;
f.generate();
f.arr.push(-1);
b.generate();
b.arr.push(-2);
console.log(f.arr); // [1,2,3,4,-1]
console.log(b.arr); // [1,2,3,4,-2]
不要把任何数据放在原型中,只放在函数中
(function() {
var Foo = function() {},
MyNamespace = {
Foo: Foo
};
Foo.prototype = {
constructor: window.Foo,
createCopy: function() { /* ... */ },
createNew: function() { /* ... */ }
};
for (var n in window.Foo.prototype) {
Foo.prototype[n] = window.Foo.prototype[n];
}
// Export it.
window.MyNamespace = MyNamespace;
})();
您可以使用对象实现这一点。创建:
function A() {
}
function B() {
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
为什么不在传统意义上这样做呢?在C++类中,可以复制或从它们创建实例,只需实现复制构造函数。您可以用与C++相同的方式在JavaScript中实现这一点,只是让构造函数检查参数:
function Foo(from) {
if (from && from.prototype && from.prototype.constructor === Foo) {
this.arr = [].concat(from.arr);
}
}
如果确实需要,请在Foo上创建一个copy方法:
Foo.prototype.createCopy = function() {
return new Foo(this);
};
或者只使用构造函数:
var bar = new Foo(foo);
这将创建arr的浅克隆。要执行深克隆,您需要编写一个深克隆方法或使用JavaScript框架中的方法,如YUI
如果需要新实例,请执行以下操作:
// Create a new class
var Foo = function Foo () {
console.log('New instance of FOO!');
};
Foo.prototype.generate = function(first_argument) {
this.arr = [1,2,3,4,5];
};
var foo = new Foo();
foo.generate();
console.log(foo.arr); // [1,2,3,4,5]
Foo.prototype.createNew = function() {
return new Foo();
};
var bar = foo.createNew();
var fooCopy = foo.createCopy();
如果该类可能用作另一个类的基类,则使用:
Foo.prototype.createNew = function() {
return new (this.prototype.constructor)();
};
Foo.prototype.createCopy = function() {
return new (this.prototype.constructor)(this);
};
如果您关心修改Foo类,那么您总是可以创建自己的实现来发出copy和create函数
(function() {
var Foo = function() {},
MyNamespace = {
Foo: Foo
};
Foo.prototype = {
constructor: window.Foo,
createCopy: function() { /* ... */ },
createNew: function() { /* ... */ }
};
for (var n in window.Foo.prototype) {
Foo.prototype[n] = window.Foo.prototype[n];
}
// Export it.
window.MyNamespace = MyNamespace;
})();
如果您只想将这些方法添加到本机Foo类中,通常是这样做的:
(function() {
if (! Foo.prototype.createCopy) {
Foo.prototype.createCopy = function() { /* ... */ };
}
if (! Foo.prototype.createNew) {
Foo.prototype.createNew = function() { /* ... */ };
}
})();
如果您在最早调用程序/脚本时执行此操作,您将在创建任何实例之前钩住Foo,从而使您能够创建副本。根据您对Craig答案的评论:从任何对象创建新实例都可以在一行中轻松完成:
var newFoo = new (Object.getPrototypeOf(foo).constructor);
但是,对于一些较旧的浏览器,Object.getPrototypeOf
将不起作用。通过将此位添加到脚本中,可以轻松解决此问题:
if (!Object.getPrototypeOf)
{
Object.getPrototypeOf = function(o)
{
if (o instanceof Object)
{
return (o.__proto__ || o.prototype);
}
throw new TypeError('Object.getPrototypeOf called on non-object');
};
}
在大多数情况下,所有考虑事项:
var newFoo = new (foo.constructor);
工作正常,除非某个小丑弄乱了实例,为其分配了构造函数
属性,或者更改了原型
在我看来,您似乎不完全理解JS如何解析上下文,以及如何解析对对象属性的引用
首先,将上一个代码段更改为:
Bar.prototype = Foo.prototype;//__proto__ is non-standard proto reference
Bar.prototype.constructor = Bar;
这在X-browser上应该可以用。
至于其他尝试失败的原因,这很简单:当您编写bar.arr
时,JS只需查找直接连接到实例的arr
属性。如果没有找到,搜索将扩展到Bar.prototye
。如果失败,JS将搜索Bar.prototype
的原型,即foo
(例如,传递给Object.create
)的实例)
在那里,JS确实找到了它要查找的属性,因此bar.arr
引用了foo.arr
。如果更改对象(数组是对象),则使用哪个引用访问对象并不重要。只有一个实例,只要它至少被引用一次,它就会被保存在内存中
全文:
var Foo = function()
{
console.log('the Foo constructor');
};
Foo.prototype.generate = function()
{
this.arr = [1,2,3];
return this;
};
var Bar = function()
{
console.log('the Bar constructor');
};
Bar.prototype = new Foo;//make Bar inherit from Foo
Bar.prototype.constructor = Bar;//but make it use its own constructor
现在,您可以根据需要使用Foo
和Bar
:
var foo = new Foo;
foo.generate();
foo.arr.push(4);
var bar = new Bar;
bar.generate();
console.log(foo.arr);//here [1,2,3,4]
console.log(bar.arr);//[1,2,3]
当然,如果您想Bar
从一个特定的foo实例开始:
var foo = new Foo;
foo.generate();
Bar.prototype = foo;//
Bar.prototype.constructor = Bar;
var bar = new Bar;
console.log(bar.arr);//logs [1,2,3]
//BUT BE CAREFUL:
console.log(foo.arr === bar.arr);//true, bar only references the same property
bar.arr = bar.arr.slice(0);//copies the array
bar.arr.push(4);
console.log(foo.arr);//[1,2,3] still
console.log(bar.arr);//[1,2,3,4] changed
要撤消我在上面所做的更改,我只需取消对prototype属性的屏蔽:
delete bar.arr;
console.log(bar.arr);//back to [1,2,3]
console.log(bar.arr === foo.arr);//true, I've deleted a bar property
就是这样1)proto必须避免,因为显而易见的原因,您可以使用Object.getPrototypeOf以标准方式读取对象的原型。
2) 您没有让构造函数完成所有的工作,而是有一种带有generate的init方法。
因此,在构造函数的末尾调用this.generate更有意义。
这就是原型继承不起作用的原因:构造函数不执行其任务。
同样,您也不能责怪Object.create,因为它既不是构造函数,也不会在示例中生成,所以您基本上构建了一个具有相同原型的对象,仅此而已
如果您确实添加了对this.generate的调用,那么最简单的方法可能是使用foo的constructor属性来构建一个新对象:
var bar = new foo.constructor();
这将给你一个新的项目
所以要回答您的问题:是的,还有另一种方法,使用对象实例的构造函数属性来构建新项
但是没有一个继承方案能够“猜测”,即完全初始化应该用这样或那样的方法来执行。初始化是构造函数的工作。例如,我们可以想象您构建了一个新数组this.arr=[]在构造函数中,如果generate()将在该数组中推送()项,这将是一种有效的方法。但为了优化和清晰起见,您必须避免通过方法添加属性:在构造函数中设置所有实例属性,可能在原型上设置共享属性,并且只在方法中使用这些属性。谢谢您的回答,但您缺少了有趣的部分:在没有类Foo的情况下创建新的isntaces。当sur.prototype=Foo.prototype时使用它代码>。如果我有它,我会直接调用newfoo()