有没有办法利用在JavaScript工厂函数中使用原型方法的性能优势? [英] Is there a way to exploit the performance advantages of using prototype methods in JavaScript factory functions?

查看:56
本文介绍了有没有办法利用在JavaScript工厂函数中使用原型方法的性能优势?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm looking for the best way to write object-oriented JavaScript (JS) code in a way that is similar to Java classes.

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

Factory functions (FFs) look like a very promising way of offering class-like functionality in JS and, so far, I've been building them like this:

function FF(constructorArg)
{
   var _privateName = constructorArg;

   var publicMessage = "Hello StackOverflow";

   function publicMethodGetName() {
      return _privateName;
   }

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

但是,我最近发现,与原型方法不同,这种样式的FF重新创建了每个FF实例,因此可能会降低性能.

这个出色的线程的第二个答案中,埃里克·艾略特(Eric Elliot)关于FF说道:

In the second answer in this excellent thread, Eric Elliot says about FFs:

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

If you store prototypes on the parent object, that can be a great way to dynamically swap out functionality, and enable very flexible polymorphism for your object instantiation.

我在网上找不到任何示例.有人可以向我解释一下如何使用上面的FF来做到这一点吗?

I can't find any examples of this online. Could anyone explain to me how I can do this using my FF above?

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

If I know that many objects are going to be created from the same FF does this mean that I could switch that FF to using prototype methods?

推荐答案

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

I'm looking for the best way to write object-oriented JavaScript (JS) code in a way that is similar to Java classes.

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

This is your first mistake. Javascript is a very different language and you should not be trying to emulate some other language in Javascript. I kind of did something similar when I came from C++ and it was a big mistake. What you need to do instead is learn the strengths of Javascript and how best to solve problems the "Javascript way" when writing in Javascript. I know it's a natural tendency to look to do things the way you already know in some other language, but that is a mistake. So, instead of trying to do things the "Java way", ask what is the best way in Javascript to solve some specific problem.

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

For example, the lowest memory way to have methods on a large number of objects is by using Javascript's prototype. You can use the prototype either with manual assignments to the prototype or with the newer ES6 class syntax. Both create methods on a prototype object that is then efficiently shared among all instances.

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

For example, you can use a factory function with a typical prototype like this:

// 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运算符创建对象,如下所示:

Then, you can create an object with the traditional new operator either like this:

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

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

Or, you can use it as a factory function:

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


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


Keep in mind that with ES6 (or a transpiler), you can use ES6 class syntax too:

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

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

Or, you can use it as a factory function:

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

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

Both of these syntaxes create the exact same object definition and prototype.

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

All that said, the memory consumption of not using the prototype is usually not a big deal unless you have both a lot of methods and a lot of objects and there are some significant advantages to not using the prototype. One big one is that you can have truly private instance data in a closure created by your constructor. Here's the same previous example implemented that way where the cntr instance variable is truly private.

// 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的一些观点进行了一些讨论

This does use more memory as there is both a lasting closure created by the constructor function (that contains the cntr variable) and there are new instances of each of the functions that make up the methods. But, it's not a big difference in memory. If you don't have zillions of cntr objects, the memory consumption difference is likely inconsequential. Doug Crawford is one of the champions of such a Javascript coding style. You can see one of his early writeups on the subject here: http://javascript.crockford.com/private.html and there's some discussion of some of Crockford's views here. There's a Crockford video somewhere (I can't see to find it right now) where he defends the non-prototype style.

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

So, it is logical to ask if you can combine the best of both. No, not really. In order to get access to the constructor closure the methods must be defined in the lexical scope of the constructor and to do that, they are not on the prototype. Trying to assign them to the prototype inside the constructor creates a mess that is subject to all sorts of bugs so that's not feasible either.

使用ES6 weakMap对象,可以在使用原型时制作私有实例数据,尽管我会说这样做通常比较麻烦,因为这样做会使编写代码和访问私有数据变得很复杂-但它是可能的.您可以在

Using an ES6 weakMap object, it is possible to make private instance data while using the prototype, though I would say it is generally more trouble that it is worth as it complicates just writing code and accessing private data - but it is possible. You can see an implementation of the private variables using a weakMap in Private instance members with weakmaps in JavaScript and Hiding Implementation Details with ECMAScript 6 WeakMaps.

我提供的观点是,通过某种方式消除对new的需求来隐藏创建新对象的事实并不是真的非常像Javascript或真的非常像OO.当您创建新对象和仅调用函数时,对于阅读代码的人来说应该是显而易见的.在大写的构造函数中使用new会使它在Javascript中非常明显,我认为这是一件好事.我不会故意在代码中避免这种明显的意图.

I'd offer my opinion that hiding the fact that you're creating a new object by somehow removing the need for new is not really very Javascript-like or really very OO-like. It should be obvious to someone reading the code when you are creating a new object and when you are just calling a function. Using new with a capitalized constructor function makes that very obvious in Javascript and I consider that a good thing. I would not purposely look to avoid that obvious intent in my code.

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

If you store prototypes on the parent object, that can be a great way to dynamically swap out functionality, and enable very flexible polymorphism for your object instantiation.

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

It is true that if you use the prototype, it's a nice encapsulated object that contains all the methods for the object and it can make some polymorphism things easier. But, you can also do polymorphism if you aren't using the prototype.

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

For example, suppose you have three objects that don't use the prototype and they assign all of their methods in their constructor and you want to create a mixin combination of all three.

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

You can create an object and then just call the other constructors and they will automatically initialize your object to be a combined object with the behavior of all three (assuming no conflicts in instance data property or method names).

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

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

This will call each of the three constructors and they will each initialize their methods and instance variables. In some ways, this is event simpler than if using the prototype.

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

If you had objects that were using the prototype, then you could do 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切换为使用原型方法?

If I know that many objects are going to be created from the same FF does this mean that I could switch that FF to using prototype methods?

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

It depends upon what tradeoffs work best for your code. If you had 1000 methods and were creating 20,000 objects of that type, then I'd say you probably want to use the prototype so you could share all those methods. If you don't have that many methods or aren't creating a lot of those types of objects or you have plenty of memory, then you may want to optimize for some other characteristic (like private data) and not use the prototype. It's a tradeoff space. There is no single correct answer.

这篇关于有没有办法利用在JavaScript工厂函数中使用原型方法的性能优势?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆