《JavaScript:The Good Parts》——实现原型方法的方法?

Mir*_*ode 4 javascript methods prototype

阅读完这篇文章https://www.toptal.com/javascript/es6-class-chaos-keeps-js-developer-up以及随后的“JavaScript:优秀部分”后,我将致力于成为一名更好的 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());
Run Code Online (Sandbox Code Playgroud)

我的假设是,整篇文章的基础是 getDataFromObject 更快(在调用期间,而不是在对象创建期间),因为它保存了对原型的间接访问,但它的内存效率也较低,因为每个对象都有一个自己的函数对象实例。如果这已经是错误的,请纠正我,你可能可以停止阅读这里。

否则:文章和书都推荐这样的风格:

function secretFactory() {
    const secret = "Favor composition over inheritance [...]!"
    const spillTheBeans = () => console.log(secret)

    return {
        spillTheBeans
    }
}

const leaker = secretFactory()
leaker.spillTheBeans()
Run Code Online (Sandbox Code Playgroud)

(引自文章,书中还没有ES6,但思路是相似的)

我的问题是这样的:

const leaker1 = secretFactory()
const leaker2 = secretFactory()

console.log(leaker1.spillTheBeans === leaker2.spillTheBeans) // false
Run Code Online (Sandbox Code Playgroud)

我不是最想避免每个对象都有每个方法自己的实例吗?它在这里可能微不足道,但如果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
Run Code Online (Sandbox Code Playgroud)

OverflowInstanceBeans 方法仍然不同,因为每个实例都需要自己的闭包,但至少它们只是包装对包含所有昂贵内容的同一函数对象的引用。

但现在我必须将每个方法名称写两到三遍。更糟糕的是,我用公共的spillStaticBeans 和spillInstanceBeans 函数弄乱了命名空间。为了缓解后者,我可以编写一个元工厂模块:

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)
        }
    }

}())
Run Code Online (Sandbox Code Playgroud)

这可以像以前一样使用,但现在这些方法隐藏在闭包中。然而,这有点令人困惑。使用 ES6 模块,我还可以将它们保留在模块范围内而不导出它们。但这是要走的路吗?

或者我总体上是错误的,JavaScript 的内部函数表示处理了这一切,并且实际上没有问题?

Ber*_*rgi 5

我的假设是整篇文章的基础是getDataFromObject调用速度更快,getDataFromPrototype因为它保存了对原型的间接访问

不会。引擎非常擅长优化原型间接。对于同一类的实例,总是instance.getDataFromPrototype解析为相同的方法,引擎可以利用这一点。有关详细信息,请参阅这篇文章

我不是最想避免每个对象都有每个方法自己的实例吗?在这里可能微不足道

是的。在大多数情况下,它实际上微不足道的。因此,使用您喜欢的任何风格来编写您的对象和方法。仅当您实际测量了性能瓶颈时,才需要重新考虑创建许多实例的情况。

使用 ES6 模块,我还可以将它们保留在模块范围内而不导出它们。但这是要走的路吗?

是的,这是一个明智的解决方案。但是,没有充分的理由提取spillInstanceBeans到静态范围,只需将其保留在原来的位置即可 - 无论如何,您都必须创建一个闭包secret