spillInstanceBeans方法仍然是不同的,因为每个实例都需要自己的关闭,但是至少它们只包装了对包含所有昂贵对象的同一函数对象的引用。
[已阅读本文https://www.toptal.com/javascript/es6-class-chaos-keeps-js-developer-up,随后阅读了“ JavaScript:The Good Parts”,从此我将致力于成为一个更好的JavaScript开发人员。但是,我还有一个问题。我通常实现这样的方法:
function MyClass(){
this.myData = 43;
this.getDataFromObject = function(){
return this.myData;
}
}
MyClass.prototype.getDataFromPrototype = function(){
return this.myData;
}
var myObject = new MyClass();
console.log(myObject.getDataFromObject());
console.log(myObject.getDataFromPrototype());
我认为这是整个文章的基础,因为getDataFromObject更快(在调用过程中,而不是在对象创建过程中),因为它保存了对原型的间接访问,但是由于每个对象都有自己的函数对象实例,因此内存效率也较低。如果那已经错了,请纠正我,您可能可以在这里停止阅读。
其他:文章和书籍都推荐这样的样式:
function secretFactory() {
const secret = "Favor composition over inheritance [...]!"
const spillTheBeans = () => console.log(secret)
return {
spillTheBeans
}
}
const leaker = secretFactory()
leaker.spillTheBeans()
((引自文章,这本书还没有ES6,但思想相似)
我的问题是这个:
const leaker1 = secretFactory()
const leaker2 = secretFactory()
console.log(leaker1.spillTheBeans === leaker2.spillTheBeans) // false
我不是主要想避免每个对象都拥有每个方法的实例吗?在这里这可能无关紧要,但是如果spillTheBeans更加复杂,并且我创建了一个数量庞大的对象,每个对象都包含一万二千种其他方法?
如果是这样,什么是“零件”解决方案?我的假设是:
const spillStaticBeans = () => console.log("Tabs rule!")
const spillInstanceBeans = (beans) => console.log(beans)
function secretFactory() {
const secret = "Favor composition over inheritance [...]!"
return{
spillStaticBeans,
spillInstanceBeans: () => spillInstanceBeans(secret)
}
}
const leaker1 = secretFactory()
const leaker2 = secretFactory()
leaker1.spillStaticBeans()
leaker2.spillInstanceBeans()
console.log(leaker1.spillStaticBeans === leaker2.spillStaticBeans) // true
console.log(leaker1.spillInstanceBeans === leaker2.spillInstanceBeans) // false
spillInstanceBeans方法仍然是不同的,因为每个实例都需要自己的关闭,但是至少它们只包装了对包含所有昂贵对象的同一函数对象的引用。
但是现在我必须将每个方法名称写两到三遍。更糟糕的是,我使用公共的spillStaticBeans和spillInstanceBeans函数使名称空间混乱。为了减轻后者,我可以编写一个meta factory模块:
const secretFactory = (function(){
const spillStaticBeans = () => console.log("Tabs rule!")
const spillInstanceBeans = (beans) => console.log(beans)
return function() {
const secret = "Favor composition over inheritance [...]!"
return{
spillStaticBeans,
spillInstanceBeans: () => spillInstanceBeans(secret)
}
}
}())
可以使用与以前相同的方式,但是现在方法隐藏在闭包中。但是,这有点令人困惑。使用ES6模块,我还可以将它们留在模块范围内,而不必导出它们。但这是要走的路吗?
或者是我一般来说都弄错了,JavaScript的内部函数表示可以解决所有这些问题,实际上没有问题吗?
我认为这是整个文章的基础,因为
getDataFromObject
的调用比getDataFromPrototype
更快,因为它保存了对原型的间接访问]没有引擎非常擅长优化原型间接。对于相同类的实例,
instance.getDataFromPrototype
始终解析为相同的方法,引擎可以利用这一点。有关详细信息,请参见this article。
我不是主要想避免每个对象都拥有每个方法的实例吗?在这里可能无关紧要
是。在大多数情况下,它实际上是无关紧要的。因此,使用您喜欢的任何样式的方法编写对象。仅当您实际衡量性能瓶颈时,才可以重新考虑创建多个实例的情况。
使用ES6模块,我也可以将它们留在模块范围内,而只是不导出它们。但这是要走的路吗?
是的,这是一个明智的解决方案。但是,没有充分的理由将spillInstanceBeans
提取到静态范围,只需将其保留在原位置即可-无论如何,您都必须在secret
上创建一个闭包。
spillInstanceBeans方法仍然是不同的,因为每个实例都需要自己的关闭,但是至少它们只包装了对包含所有昂贵对象的同一函数对象的引用。
应注意,您只是在复制JavaScript VM的内部工作方式:spillTheBeans
之类的函数仅在源代码中出现的位置被编译一次,即使它具有secret
之类的自由变量也是如此。例如,在SpiderMonkey中,结果称为“原型功能”(不要与原型混淆)。这些是VM的内部功能,无法从JavaScript访问。
在运行时,通过将原型函数的自由变量绑定到当前作用域(的一部分)来创建函数对象,这与您的spillInstanceBeans
示例非常相似。
说,确实是使用闭包代替了原型方法,并且this
整体上创建了更多的函数对象-真正的隐私和只读属性所带来的健壮性使其值得。提议的样式更多地集中于对象而不是类,因此可能会出现无法与基于类的设计直接比较的不同设计。
正如Bergi所说,衡量并重新考虑性能(在代码的某些部分中是否更为重要。
spillInstanceBeans方法仍然是不同的,因为每个实例都需要自己的关闭,但是至少它们只包装了对包含所有昂贵对象的同一函数对象的引用。