我刚刚进入JavaScript,我正试图围绕原型继承。似乎有多种方法可以达到同样的效果,所以我想看看是否有任何最佳实践或理由以一种方式做事。这就是我所说的:
// Method 1
function Rabbit() {
this.name = "Hoppy";
this.hop = function() {
console.log("I am hopping!");
}
}
// Method 2
function Rabbit() {}
Rabbit.prototype = {
name: "Hoppy",
hop: function() {
console.log("I am hopping!");
}
}
// Method 3
function Rabbit() {
this.name = "Hoppy";
}
Rabbit.prototype.hop = function() {
console.log("I am hopping!");
}
// Testing code (each method tested with others commented out)
var rabbit = new Rabbit();
console.log("rabbit.name = " + rabbit.name);
rabbit.hop();
所有这些似乎都具有相同的效果(除非我遗漏了什么)。那么一种方法优于另一种方法吗?你是怎么做到的?
答案 0 :(得分:12)
当您在原型上放置方法时,每个实例对象都共享与方法相同的引用。如果您有10个实例,则该方法有1个副本。
当您执行示例1中所做的操作时,每个实例对象都有自己相同方法的版本,因此如果您创建了10个对象,则会运行10个代码副本。
使用原型是有效的,因为javascript具有用于将函数与实例相关联的机制,即它为执行函数设置this
属性。
所以使用原型是首选,因为它占用的空间更少(当然,除非你想要的是这样)。
在方法2中,您通过将原型设置为对象文字来设置原型。请注意,在这里您要设置一个属性,我认为您不打算这样做,因为所有实例都将获得相同的属性。
在方法3中,您一次构建一个分配原型。
我更喜欢方法3。即在我的构造函数中,我设置了属性值
myObj = function(p1){
this.p1; // every instance will probably have its own value anyway.
}
myObj.prototype.method1 = function(){..} // all instances share the same method, but when invoked **this** has the right scope.
答案 1 :(得分:3)
让我们一次看一个例子。第一:
function Rabbit() {
this.name = "Hoppy";
this.hop = function() { //Every instance gets a copy of this method...
console.log("I am hopping!");
}
}
var rabbit = new Rabbit();
正如您在问题中所述,上述代码将起作用。它将创建Rabbit
类的新实例。每次创建实例时,hop
方法的副本都将存储在该实例的内存中。
第二个例子看起来像这样:
function Rabbit() {}
Rabbit.prototype = {
name: "Hoppy",
hop: function() { //Now every instance shares this method :)
console.log("I am hopping!");
}
}
var rabbit = new Rabbit();
这次,Rabbit
的每个实例都会共享hop
方法的副本。这会更好,因为它使用更少的内存。但是,每个Rabbit
都将具有相同的名称(假设您没有隐藏构造函数中的name
属性)。这是因为该方法继承自prototype
。在JavaScript中,当您尝试访问对象的属性时,将首先在对象本身上搜索该属性。如果在那里找不到它,我们会查看prototype
(依此类推,直到我们到达prototype
属性为null
的对象为止的原型链上。)
你的第三个例子就像我做的那样。应在prototype
上声明实例之间共享的方法。您可能希望在构造函数中设置的name
等属性可以基于每个实例声明:
function Rabbit(rabbitName) {
this.name = rabbitName;
}
Rabbit.prototype.hop = function() {
console.log("Hopping!");
}
答案 2 :(得分:1)
这是一个经常被误解的重要问题。这取决于你想要做什么。一般来说,hvgotcode的答案是正确的。任何经常被实例化的对象都应该将方法和属性附加到原型中。
但在非常具体的情况下,其他人也有优势。阅读本文,包括评论:http://net.tutsplus.com/tutorials/javascript-ajax/stop-nesting-functions-but-not-all-of-them/
有时上面的方法1有帮助,使您具有“私有”可读/可写属性和方法。虽然这通常不值得在重度实例化的对象中牺牲,对于仅实例化一次或几次的对象,或者没有很多内部任务,或者如果您处于具有许多不同技能水平和敏感度的开发团队环境中,可以提供帮助。
一些开发者采用了另一种良好的策略,试图弥合其他人的一些缺点。那就是:
var Obj = function() {
var private_read_only = 'value';
return {
method1: function() {},
method2: function() {}
};
};
答案 3 :(得分:1)
// option 4
var Rabbit {
constructor: function () {
this.name = "Hoppy";
return this;
},
hop: function() {
console.log("I am hopping!");
}
};
var rabbit = Object.create(Rabbit).constructor();
console.log("rabbit.name = " + rabbit.name);
rabbit.hop();
使用new
进行原型OO时,构造函数是完全可选的。
正如已经指出的那样,如果你可以通过原型分享一些东西。原型在内存方面更有效,而且在实例化时间方面更便宜。
然而,一个完全有效的替代方案是
function Rabbit() {
// for some value of extend https://gist.github.com/1441105
var r = extend({}, Rabbit);
r.name = "Hoppy";
return r;
}
这里扩展的“实例”具有“原型”的属性。真正的原型OO的唯一优势是它是一个实时链接,这意味着对原型的更改反映了所有实例。
答案 4 :(得分:0)
进行一些性能测试(声明大约1百万个兔子变量)。第一种方法将耗费大部分时间和内存。