“ JavaScript:好的零件”-实现原型方法的方式?

问题描述 投票:1回答:2

[已阅读本文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的内部函数表示可以解决所有这些问题,实际上没有问题吗?

javascript methods prototype
2个回答
1
投票

我认为这是整个文章的基础,因为getDataFromObject的调用比getDataFromPrototype更快,因为它保存了对原型的间接访问]

没有引擎非常擅长优化原型间接。对于相同类的实例,instance.getDataFromPrototype始终解析为相同的方法,引擎可以利用这一点。有关详细信息,请参见this article

我不是主要想避免每个对象都拥有每个方法的实例吗?在这里可能无关紧要

是。在大多数情况下,它实际上是无关紧要的。因此,使用您喜欢的任何样式的方法编写对象。仅当您实际衡量性能瓶颈时,才可以重新考虑创建多个实例的情况。

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

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

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

应注意,您只是在复制JavaScript VM的内部工作方式:spillTheBeans之类的函数仅在源代码中出现的位置被编译一次,即使它具有secret之类的自由变量也是如此。例如,在SpiderMonkey中,结果称为“原型功能”(不要与原型混淆)。这些是VM的内部功能,无法从JavaScript访问。

在运行时,通过将原型函数的自由变量绑定到当前作用域(的一部分)来创建函数对象,这与您的spillInstanceBeans示例非常相似。

说,确实是使用闭包代替了原型方法,并且this整体上创建了更多的函数对象-真正的隐私和只读属性所带来的健壮性使其值得。提议的样式更多地集中于对象而不是类,因此可能会出现无法与基于类的设计直接比较的不同设计。

正如Bergi所说,衡量并重新考虑性能(在代码的某些部分中是否更为重要。


0
投票
spillInstanceBeans方法仍然是不同的,因为每个实例都需要自己的关闭,但是至少它们只包装了对包含所有昂贵对象的同一函数对象的引用。
© www.soinside.com 2019 - 2024. All rights reserved.