在 JavaScript 中提高工厂模式的内存效率
Improving memory efficiency of factory pattern in JavaScript
JavaScript 的 "object"(实际上是地图)概念的简单性和灵活性一直吸引着我。出于这个原因,我更愿意完全避免 "object prototypes"(和 "classes" 的概念,它们只是对象原型的语法糖),而是选择工厂模式。
我看到这篇文章,指出与工厂模式相比,对象原型节省内存:https://medium.freecodecamp.org/class-vs-factory-function-exploring-the-way-forward-73258b6a8d15
"All methods will be created only once in the prototype object and shared by all instances"
假设我有一个网页多次调用以下函数以 "instantiate" 许多 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;
};
我可以通过引入一个像下面这样的 "shared" 对象来提高内存效率吗?
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 的每个 "instance" 只剩下一个小的单行函数调用共享功能。这会达到类似于对象原型的内存效率吗?或者尽管是一个单行函数,每个实例的 turn
函数会占用和以前一样多的内存吗?如果是后者,对象原型如何避免这个问题?
with just a small one-line function calling the shared function
这是基于为每个对象创建一个新函数的错误信念。那是错的。代码中的所有函数只被解析一次并且只存在一次。每次创建时创建的是一个新的闭包(规范称之为 EnvironmentRecord)。如果函数敌人不访问任何外部变量,则可以优化此闭包,使其内存效率更高(并且可能更快)。
因此,您的 "new pattern" 让事情变得更糟(但只是一点点)。主要问题仍然存在:
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
可以共享,因为它不访问任何外部变量。
JavaScript 的 "object"(实际上是地图)概念的简单性和灵活性一直吸引着我。出于这个原因,我更愿意完全避免 "object prototypes"(和 "classes" 的概念,它们只是对象原型的语法糖),而是选择工厂模式。
我看到这篇文章,指出与工厂模式相比,对象原型节省内存:https://medium.freecodecamp.org/class-vs-factory-function-exploring-the-way-forward-73258b6a8d15
"All methods will be created only once in the prototype object and shared by all instances"
假设我有一个网页多次调用以下函数以 "instantiate" 许多 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;
};
我可以通过引入一个像下面这样的 "shared" 对象来提高内存效率吗?
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 的每个 "instance" 只剩下一个小的单行函数调用共享功能。这会达到类似于对象原型的内存效率吗?或者尽管是一个单行函数,每个实例的 turn
函数会占用和以前一样多的内存吗?如果是后者,对象原型如何避免这个问题?
with just a small one-line function calling the shared function
这是基于为每个对象创建一个新函数的错误信念。那是错的。代码中的所有函数只被解析一次并且只存在一次。每次创建时创建的是一个新的闭包(规范称之为 EnvironmentRecord)。如果函数敌人不访问任何外部变量,则可以优化此闭包,使其内存效率更高(并且可能更快)。
因此,您的 "new pattern" 让事情变得更糟(但只是一点点)。主要问题仍然存在:
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
可以共享,因为它不访问任何外部变量。