提高JavaScript中工厂模式的存储效率

时间:2019-03-04 18:04:51

标签: javascript performance memory prototype factory

JavaScript的“对象”(实际上是地图)概念的简单性和灵活性一直吸引着我。因此,我宁愿完全避免使用“对象原型”(和“类”的概念,它们只是对象原型的语法糖)的概念,而是选择一种工厂模式。

我碰到了这篇文章,指出与工厂模式相比,对象原型可以节省内存:https://medium.freecodecamp.org/class-vs-factory-function-exploring-the-way-forward-73258b6a8d15

  

“所有方法将仅在原型对象中创建一次,并由所有实例共享”

假设我有一个网页,该网页多次调用以下函数来“实例化”许多AnglePointer对象:

var AnglePointer = function() {
    var privateMembers = {};
    privateMembers.angle = 0;

    var self = {};

    self.turn = function(degrees) {
        privateMembers.angle += degrees;
        while (privateMembers.angle > 359) {
            privateMembers.angle -= 360;
        }
        while (privateMembers.angle < 0) {
            privateMembers.angle += 360;
        }
    };

    return self;
};

是否可以通过引入如下所示的“共享”对象来提高内存效率?

var AnglePointer = (function() {
    var sharedMembers = {};

    sharedMembers.turn = function(self, privateMembers, degrees) {
        privateMembers.angle += degrees;
        while (privateMembers.angle > 359) {
            privateMembers.angle -= 360;
        }
        while (privateMembers.angle < 0) {
            privateMembers.angle += 360;
        }
    };

    return function() {
        var privateMembers = {};
        privateMembers.angle = 0;

        var self = {};

        self.turn = function(degrees) {
            shared.turn(self, privateMembers, degrees);
        };

        return self;
    };
})();

在第二个版本中,turn函数的实现位于sharedMembers对象内部,从而使AnglePointer的每个“实例”都只有一个小的单行函数来调用共享函数。这样是否可以实现与对象原型类似的内存效率?还是每个实例的turn函数都是单行函数,它们占用的内存还是和以前一样大?如果是后者,对象原型如何避免这个问题?

1 个答案:

答案 0 :(得分:1)

  

只有一个小的单行函数调用共享函数

这是基于一种误解,即会为每个对象创建一个新函数。那是错的。代码中的所有函数仅被解析一次,并且仅存在一次。每次创建时都会创建一个新的闭包(规范称为EnvironmentRecord)。如果函数敌人不访问任何外部变量,则可以优化此闭包,使其更有效地使用内存(并且可能更快)。

因此,您的“新模式”实际上使事情变得更糟(但有一点)。主要问题仍然存在:

self.turn = function(degrees) {
   shared.turn(self, privateMembers, degrees);
};

这无法被优化,因为该函数使用其父级范围内的变量,因此必须创建一个闭包。因此,每个turn方法都必须有一个闭包,并且每个对象都必须保留对其自身闭包的turn的引用。

使用Symbols可以非常准确地实现私有成员:

const angle = Symbol.for("angle");

function AnglePointer() {
  return {
   [angle]: 0,
   turn(degrees) {
     this[angle] = (this[angle] + degrees) % 360;
   },
  };
}

这里turn可以共享,因为它不访问任何外部变量。