gpt4 book ai didi

javascript - 有没有一种方法可以利用在JavaScript工厂函数中使用原型(prototype)方法的性能优势?

转载 作者:行者123 更新时间:2023-11-30 07:56:32 24 4
gpt4 key购买 nike

我正在寻找以类似于Java类的方式编写面向对象的JavaScript(JS)代码的最佳方法。

工厂函数(FF)看起来是在JS中提供类功能的一种非常有前途的方式,到目前为止,我一直在像这样构建它们:

function FF(constructorArg)
{
var _privateName = constructorArg;

var publicMessage = "Hello StackOverflow";

function publicMethodGetName() {
return _privateName;
}

return {
publicMethodGetName: publicMethodGetName,
publicMessage: publicMessage
};
}


但是, I've recently discovered that, unlike prototype methods, this style of FF recreates each method for every FF instance and can thus disadvantage performance

this excellent thread中的第二个答案中,Eric Elliot谈到了FF:


  如果将原型存储在父对象上,那可能是一个很好的方法
  动态交换功能,并实现非常灵活的
  对象实例化的多态性。


我在网上找不到任何示例。谁能向我解释我如何使用上面的FF做到这一点?

如果我知道将从同一个FF创建许多对象,这是否意味着我可以将该FF切换为使用原型方法?

最佳答案

我正在寻找编写面向对象的JavaScript(JS)的最佳方法
  类似于Java类的代码。


这是您的第一个错误。 Javascript是一种非常不同的语言,因此您不应该尝试在Javascript中模仿其他语言。当我来自C ++时,我做了类似的事情,这是一个很大的错误。相反,您需要做的是学习Java脚本的优势以及如何在用Java脚本编写时以“ Java脚本方式”最好地解决问题。我知道,以其他语言已经知道的方式去做事情是一种自然的趋势,但这是一个错误。因此,与其尝试用“ Java方式”做事,不如问Java脚本中解决某些特定问题的最佳方法是什么。

例如,对大量对象具有方法的最低内存方式是使用Javascript的原型。您可以通过手动分配原型或使用较新的ES6 class语法来使用原型。两者都在原型对象上创建方法,然后在所有实例之间有效地共享它们。

例如,您可以将工厂函数与典型的原型一起使用,如下所示:

// constructor and factory function definition
function MyCntr(initialCnt) {
if (!(this instanceof MyCntr)) {
return new MyCntr(initialCnt);
} else {
// initialize properties
this.cntr = initialCnt || 0;
}
}

MyObject.prototype = {
getCntr: function() {
return this.cntr++;
},
resetCntr: function() {
this.cntr = 0;
}
};


然后,您可以使用传统的 new运算符创建一个对象,如下所示:

var m = new MyCntr(10);
console.log(m.getCntr()); // 10


或者,您可以将其用作工厂功能:

var m = MyCntr(10);
console.log(m.getCntr()); // 10




请记住,通过ES6(或编译器),您也可以使用ES6类语法:

class MyCntr {
constructor(initialCnt) {
if (!(this instanceof MyCntr)) {
return new MyCntr(initialCnt);
} else {
// initialize properties
this.cntr = initialCnt || 0;
}
}

getCntr() {
return this.cntr++;
}

resetCntr() {
this.cntr = 0;
}
}

var m = new MyCntr(10);
console.log(m.getCntr()); // 10


或者,您可以将其用作工厂功能:

var m = MyCntr(10);
console.log(m.getCntr()); // 10


这两种语法都会创建完全相同的对象定义和原型。



综上所述,不使用原型的内存消耗通常不是什么大问题,除非您同时拥有许多方法和大量对象,并且不使用原型具有一些明显的优势。一个很大的问题是,在构造函数创建的闭包中可以真正拥有私有实例数据。这是与以前的示例相同的实现方式,其中 cntr实例变量是真正的私有变量。

// constructor and factory function definition
function MyCntr(initialCnt) {
// truly private instance variable
var cntr;

if (!(this instanceof MyCntr)) {
return new MyCntr(initialCnt);
} else {
// initialize properties
cntr = initialCnt || 0;
}

this.getCntr = function() {
return cntr++;
}

this.resetCntr = function() {
cntr = 0;
}
}


这确实会占用更多的内存,因为构造函数(包含 cntr变量)创建了持久的闭包,并且每个函数都有新的实例来构成方法。但是,这在内存上没有太大的区别。如果您没有成千上万的cntr对象,则内存消耗差异可能是无关紧要的。道格·克劳福德(Doug Crawford)是这种Java编码风格的拥护者之一。您可以在这里看到他关于该主题的早期文章之一: http://javascript.crockford.com/private.html,并且对Crockford的一些观点 here进行了一些讨论。某处有一个Crockford视频(我现在看不到),在那里他捍卫了非原型风格。



因此,逻辑上要问一下您是否可以结合两者的优点。不,不是。为了能够访问构造函数闭包,必须在构造函数的词法范围内定义方法,而要做到这一点,它们就不在原型上。尝试将它们分配给构造函数内部的原型会造成混乱,该混乱会受到各种错误的影响,因此也不可行。

使用ES6 weakMap对象,可以在使用原型时制作私有实例数据,尽管我会说,这样做通常更麻烦,因为这样做仅使编写代码和访问私有数据变得很复杂-但它是可能的。您可以在 Private instance members with weakmaps in JavaScriptHiding Implementation Details with ECMAScript 6 WeakMaps中使用 weakMap看到私有变量的实现。



我想说一下,隐藏通过某种方式消除对 new的需要来创建新对象的事实,并不是真的很像Javascript或真的非常像OO。当您创建新对象以及仅调用函数时,对于阅读代码的人来说应该是显而易见的。将 new与大写的构造函数一起使用会使Javascript变得很明显,我认为这是一件好事。我不会故意避免在我的代码中避免明显的意图。




  如果将原型存储在父对象上,那可能是一个很好的方法
  动态交换功能,并实现非常灵活的
  对象实例化的多态性。


的确,如果使用原型,那么它是一个很好的封装对象,其中包含该对象的所有方法,并且可以使某些多态性变得更容易。但是,如果您不使用原型,也可以进行多态。

例如,假设您有三个不使用原型的对象,并且它们在其构造函数中分配了所有方法,并且您想创建这三个对象的混合组合。

您可以创建一个对象,然后调用其他构造函数,它们将自动将您的对象初始化为具有这三种行为的组合对象(假设实例数据属性或方法名称没有冲突)。

function MyCombo() {
ObjectA.call(this);
ObjectB.call(this);
ObjectC.call(this);
}


这将调用三个构造函数中的每一个,并且它们将分别初始化其方法和实例变量。在某些方面,这比使用原型要简单得多。

如果您有使用原型的对象,则可以执行以下操作:

function MyCombo() {
ObjectA.call(this);
ObjectB.call(this);
ObjectC.call(this);
}

Object.assign(MyCombo.prototype, ObjectA.prototype, ObjectB.prototype,
ObjectC.prototype, {myComboMethod: function() {...}});

var x = new MyCombo();
x.methodA();
x.methodB();





  如果我知道要从同一FF创建许多对象
  这是否意味着我可以将FF切换为使用原型方法?


这取决于哪种权衡最适合您的代码。如果您有1000个方法并正在创建20,000个该类型的对象,那么我想您可能想使用原型,以便可以共享所有这些方法。如果您没有那么多方法,或者没有创建大量此类对象,或者您有足够的内存,那么您可能想要针对其他一些特性(例如私有数据)进行优化,而不使用原型。这是一个权衡的空间。没有一个正确的答案。

关于javascript - 有没有一种方法可以利用在JavaScript工厂函数中使用原型(prototype)方法的性能优势?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38426494/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com