优先选择原型方法而不是实例方法的建议是一般性的建议,还是仅适用于所讨论的方法确实独立于其他实例属性的情况?
或者,换句话说,如果我希望使用实例方法,因为我希望这样的方法由调用它的单个对象参数化,那么我是否错误设计了我的 JavaScript 应用程序,因为有更好的方法来实现这一点(即依赖于对象状态的方法,例如其实例变量)?
在 JavaScript Ninja 的秘密 - 第二版的第 7.2.1 节中,解释了定义实例方法之间的区别
function Ninja() {
this.swung = false;
this.swingSword = function() {
return 'swing';
};
}
const n1 = new Ninja();
const n2 = new Ninja();
console.log(n1.swingSword())
console.log(n2.swingSword())
与定义原型方法
function Ninja() {
this.swung = false;
}
Ninja.prototype.swingSword = function() {
return 'swing';
}
const n1 = new Ninja();
const n2 = new Ninja();
console.log(n1.swingSword())
console.log(n2.swingSword())
除了如果两者都被定义,则前者优先的事实之外,还声称前者也会带来性能损失(至少在创建的对象数量足够大的情况下),因为该方法的新副本将为每个
new Ninja()
调用创建。
我明白这一点,但这种说法并不意味着适用于这个简单的玩具示例,而是提供一般指导(至少这是我解释本书文本的方式!),而且我不明白为什么会这样应该是这样的。
确实,我忍不住想到一个稍微复杂一点的例子,其中构造函数有一些参数,这些参数是由实例方法的闭包捕获的,这意味着实际上每个对象的函数都是不同的:
function Ninja(foo) {
this.swung = foo;
this.swingSword = function() {
return foo;
};
}
const n1 = new Ninja('one');
const n2 = new Ninja('two');
console.log(n1.swingSword()) // this calls function() { return 'one'; };
console.log(n2.swingSword()) // this calls function() { return 'two'; };
在这种情况下,我认为原型方法不是一个选择,因为它在整个对象中是通用的,所以它不能由每个对象自定义,不是吗?
这也适用于成员变量,例如最后一个示例中的
swung
必须是实例属性,但在原始示例中很可能是原型属性,因为它只是 false
,它不会改变。
function Ninja() {}
Ninja.prototype.swung = false;
Ninja.prototype.swingSword = function() {
return 'swing';
}
const n1 = new Ninja();
const n2 = new Ninja();
console.log(n1.swung)
console.log(n2.swung)
从 ES 6 开始,您可以使用类。 JavaScript 中的类基于原型构建,但也具有一些类特有的语法和语义。
class Person {
#name;
constructor(name) {
this.#name = name;
}
sayName() {
console.log(this.#name);
}
}
const bob = new Person("Bob");
const bill = new Person("Bill");
bob.sayName();
bill.sayName();