我试图在JavaScript中模拟一个简单的Holder“class”,其中包含一个“私有”属性,该属性包含一些东西以及“公共”getter和setter“方法”来访问该值。
下面提到HolderA
所展示的方法,例如here。另一种方法我或多或少通过变异来达到,但我想它也必须被认为是一种成语。我喜欢它,因为它不包含this
或prototype
内容,看起来非常基本和实用。两者之间有区别吗?
测试代码(我在nodejs
下运行)似乎表明这两种方法是相同的,只是在第一种情况下我得到的对象有typeof
object
而在第二个function
。
var test = function(o) {
var initialValueCorrect = (!(typeof o.getX()==='undefined'))&&(o.getX()===0);
var VALUE_TO_SET = 10;
o.setX(VALUE_TO_SET);
var getSetWorks = o.getX()===VALUE_TO_SET;
var xIsPrivate = (typeof o.x === 'undefined');
var xHasCorrectValue;
if (!xIsPrivate)
xHasCorrectValue = o.x === VALUE_TO_SET;
return {initialValueCorrect: initialValueCorrect,
getSetWorks : getSetWorks,
xIsPrivate: xIsPrivate,
xHasCorrectValue: xHasCorrectValue};
};
var HolderA = (function() {
function foo(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
};
return foo;
})();
var createHolderB = (function() {
var x;
function foo(_x) {
x = _x;
return foo;
}
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
var objects = [{object: new HolderA(0), name: "approach with constructor-invocation and 'this'"},
{object: createHolderB(0), name: "approach with normal function invocation and closed variable"}];
for (var i = 0; i<objects.length ; i++) {
var testResult = test(objects[i].object);
console.log('['+objects[i].name+']: the object is a: '+(typeof objects[i].object)
+'\n\n\t\t\t'+JSON.stringify(testResult)+'\n\n\n\n\n');
}
正如Bergi
指出我上面的代码中的函数createHolderB
是完全错误的,只创建一个单例对象。所以,并不是真正的“构造函数”功能。为此,我现在创建了createHolderC
,可用于真正创建具有隐藏私有属性的多个对象,如下所示:
var objectC1 = createHolderC()(0);
现在,HolderA
和createHolderC
功能之间是否存在任何实质性差异,还是纯粹风格上的区别?
var createHolderC = function () {
return (function() {
var x;
function foo(_x) {
x = _x;
return foo;
};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
};
答案 0 :(得分:1)
createHolderB
不会像HolderA
那样创建新的持有者。它本质上是一种单身模式。您可能还想将其称为模块。请注意createHolderB() === createHolderB
。
createHolderC
仍然与HolderA
不同,因为它返回函数对象,而不是实例。去除不必要的IEFE时,您可能会更好地看到差异:
function HolderA(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
// implicit `return this;` when called via `new`
}
function createHolderC() {
var x;
function foo(_x) {
x = _x;
return foo;
};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
}
典型的工厂看起来像这样:
function createHolderD(x) {
var foo = {};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
}
(或者甚至是return {getX: …, setX: …};
),与HolderA
的唯一区别在于原型继承。
答案 1 :(得分:0)
基本上两者都展示了对private
变量的x
访问行为。但这两者之间的区别是
构造函数
var HolderA = (function() {
function foo(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
};
return foo;
})();
这是一个自动执行的函数,它返回构造函数 foo。
这就是您在创建此类持有人时使用new
的原因{object: new HolderA(0)
功能
var createHolderB = (function() {
var x;
function foo(_x) {
x = _x;
return foo;
}
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
即使这也是一个自动执行的函数,但在执行时它会创建x变量并返回函数foo,而foo通过闭包来访问x
。
您只是通过普通函数调用object: createHolderB(0)
创建这种类型的持有者。
答案 2 :(得分:0)
这两种情况在实践中都非常糟糕。
如果您正在制作包含“私有变量”范围的closures(匿名函数),则表示您正在为类的每个实例创建这些函数即可。这会耗费内存和性能。我曾经创建过像这样的类,但是一旦我制作了大量的实例,发现它很难达到性能。
就像在Python中一样,通过某种约定使私有变量变为私有,如_name
中的下划线。然后你可以出于调试原因打破它(因为闭包范围中的变量不可访问)。
我就是这样做的:
function MyClass(arg) {
this._privateVar = arg;
}
MyClass.prototype.getPrivateVar = function() {
return this._privateVar;
}
你也可以制作getter和setter:
Object.defineProperty(MyClass.prototype, "privateVar", {
get: function() {
return this._privateVar;
}
}
但是不要试图将javascript推入其不适合的事物中,否则你将会付出性能,代码可读性和调试复杂性。也适用于其他语言。