我有使用C#编程语言的经验,但我现在也必须使用JS,这对我来说很新。
我试图在JS中开发一个简单的类仿真,如下所示:
function A( inputValue ) {
this.Init( inputValue );
this.Print();
}
A.prototype = {
value: null,
Init: function( inputValue ) {
this.value = inputValue;
},
Print: function () {
console.log( this.value );
}
}
var obj = new A(40);
我试图将变量value
封装在A.prototype
中,但似乎是JavaScript规范所有对象都可用。
所以我的问题是:
1)。如何进行封装,这与OOP /访问修饰符支持的静态语言非常接近?
2)。如何在JS中模拟一些访问修饰符,例如private
,例如。?
答案 0 :(得分:6)
如果你想使用继承,你不能(至少AFAIK)。但是对于没有继承链的对象,您可以使用闭包来获得完全相同的效果。问题是,如果你真的需要属性实际上是完全私有的。
您可以执行一个函数,其中哪个闭包包含您想要私有的变量。这些私有变量实际上不属于对象,但只能由对象的方法访问。例如:
var getPersonInstance = function (name) {
// Those are "private" properties
var myName = name;
return {
getName: function () {
return myName
},
setName: function (name) {
myName = name;
},
sayHello = function () {
alert('hello! my name is ' + myName);
}
}
};
var person = getPersonInstance('Juan');
person.getName(); // Returns Juan
person.myName = 'Don Vito' // This sets the person.myName property, which is not the one in the closure
person.setName('John') // Works
person.sayHello(); // alert hello! my name is John
你可以在这里查看:
如果您对构造函数表示法感觉更舒服,可以执行以下操作:
(未经测试)
function Person(name) {
// Anything that is not attached to this will be private
var myName = name;
this.getName = function () { return myName;};
this.setName = function (newName) {myName = newName;};
this.sayHello = function () {alert('hey there, my name is' + myName);};
}
这与上面几乎相同,因为未使用原型并且方法被直接复制到对象中。
但是,闭包方法是内存耗时,最糟糕的是:它们使用的函数实际上并不属于你正在使用的对象......这是一个重要的“语义”问题(< em>这个道具是属于我还是不属于?)让继承成为头痛的问题。原因是扩展对象的方法要么不能访问该私有伪属性(因为它们没有在超级对象闭包中定义),要么可以访问“超级对象”中的公共私有变量(方法在超级项目中定义,因此访问超级项目的关闭。这是胡说八道。
在我看来,如果他/她真的想,那么封装不会阻止任何人搞乱你的代码,所以我更喜欢遵循python哲学“我们都是成熟的人”,其中包括使用惯例说“我希望这个属性不能从外面使用”。例如,我在私有属性前加上'_',这意味着它们是私有的,受保护的,或者其他什么,只是远离它。你不要碰你不应该碰的东西。
这种方法从最简单和最有效的方面开始,允许您使用继承链,因为属性在对象中而不是限制在闭包中。
var Person = function (name) {
this._name = name;
}
Person.prototype.sayHello = function () {...};
Person.prototype.getName = function () {...};
Person.prototype.setName = function () {...};
答案 1 :(得分:6)
封装并不意味着计算机严格执行您不访问的内容。它可以通过不访问彼此内部的模块和类来实现。如果您的程序具有该属性,那么您正在使用封装。或者至少你获得了封装所带来的所有好处 - 马铃薯,potahto。
如果您需要帮助,可以使用文档和约定,例如下划线前缀,这样您就可以更轻松地了解内部与否。
考虑一下 - 如果你将“私有”全局替换为“公共”并重新编译任何C#程序,它的功能将完全相同。并且您在某种程度上忽略了即使在C#中,如果他们愿意,也可以通过.SetAccessible
访问私有。既然你似乎对此感到满意,那么问题是什么?
通过闭包模仿“私人”会给你带来更多和更糟的问题,如果你还没有被上述说服,我会列出哪些问题。
我还会引用Martin Fowler的一些权威论点:
访问控制不控制访问
如果你的字段是私有的,那就意味着没有其他类可以获得 它。错误!如果你真的想要你可以破坏访问控制 几乎任何语言的机制。通常是通过途径 反射。理由是调试器和其他系统工具 经常需要查看私有数据,所以通常是反射接口 允许你这样做。
C ++没有这种反射,但你可以使用它 直接内存操作,因为C ++基本上是开放内存。
访问控制的目的不是阻止访问,而是更多 表示班级更愿意为自己保留一些东西。运用 访问修饰符,就像编程中的许多东西一样,主要是 关于沟通。
http://martinfowler.com/bliki/AccessModifier.html#AccessControlDoesNotControlAccess
答案 2 :(得分:0)
您可以使用闭包来封装变量。
function MyObjectFactory() {
var obj = {},
count = 0;
obj.getCounter = function () {
return count;
};
obj.setCounter = function (val) {
count = val;
};
obj.incrementCounter = function () {
count++;
};
obj.decrementCount = function () {
count--;
};
return obj;
}
您也可以通用方式模仿财产获取者和制定者。
function MyOtherObjectFactory() {
var obj = {}, props = {};
obj.prop = function (name) {
return props[name];
};
obj.setProp = function (name, val) {
props[name] = val;
// call other functions if you'd like
};
// or even better, have a single function that works getter and setter regarding the params
obj.autoProp = function () {
if (arguments[1]) {
// setter
props[arguments[0]] = arguments[1]; // do check first argument is a hashable value
} else if (arguments[0]) {
// make sure we have a key
// getter
return props[arguments[0]];
}
}
}
PS:请不要直接设置原型,它打破了原型链。
答案 3 :(得分:0)
Javascript仍然不支持开箱即用。但是,有一个建议可以做到这一点。
ECMA TC39提出了私有语法。根据建议,为了使javascript类字段为私有,您需要在其前面添加一个井号'#'。这是为了提供某种运行时封装。
示例:
class B {
#hidden = 0;
m() {
return this.#hidden;
}
}
自Chrome v74起,Chrome支持此功能。如果此私有语法成为标准,我们将可以从JavaScript的运行时封装中受益。