深入理解JavaScript系列(17):面向对象编程之概(5)


class A(object):
 
    def __init__(self):
      self.public = 10
      self.__private = 20
 
    def get_private(self):
        return self.__private
 
# outside:
 
a = A() # A的实例
 
print(a.public) # OK, 30
print(a.get_private()) # OK, 20
print(a.__private) # 失败,因为只能在A里可用
 
# 但在Python里,可以通过特殊规则来访问
 
print(a._A__private) # OK, 20

在Ruby里:一方面有能力来定义private和protected的特性,另一方面,也有特殊的方法( 例如instance_variable_get,instance_variable_set,send等)获取封装的数据。

复制代码 代码如下:


class A
 
  def initialize
    @a = 10
  end
 
  def public_method
    private_method(20)
  end
 
private
 
  def private_method(b)
    return @a + b
  end
 
end
 
a = A.new # 新实例
 
a.public_method # OK, 30
 
a.a # 失败, @a - 是私有的实例变量
 
# "private_method"是私有的,只能在A类里访问
 
a.private_method # 错误
 
# 但是有特殊的元数据方法名,可以获取到数据
 
a.send(:private_method, 20) # OK, 30
a.instance_variable_get(:@a) # OK, 10

最主要的原因是,程序员自己想要获得的封装(请注意,我特别不使用“隐藏”)的数据。 如果这些数据会以某种方式不正确地更改或有任何错误,则全部责任都是程序员,但不是简单的“拼写错误”或“随便改变某些字段”。 但如果这种情况很频繁,那就是很不好的编程习惯和风格 ,因为通常值用公共的API来和对象“交谈”。

重复一下,封装的基本目的是一个从辅助数据的用户中抽象出来,而不是一个防止黑客隐藏数据。 更严重的,封装不是用private修饰数据而达到软件安全的目的。

封装辅助对象(局部),我们用最小的代价、本地化和预测性变化来问为公共接口的行为变化提供可行性,这也正是封装的目的。

另外setter方法​​的重要目的是抽象复杂的计算。 例如,element.innerHTML这个setter——抽象的语句——“现在这个元素内的HTML是如下内容”,而在 innerHTML属性的setter函数将难以计算和检查。 在这种情况下,问题大多涉及到抽象 ,但封装也会发生。

封装的概念不仅仅只与OOP相关。 例如,它可以是一个简单的功能,只封装了各种计算,使得其抽象(没有必要让用户知道,例如函数Math.round(... ...)是如何实现的,用户只是简单地调用它)。 它是一种封装,注意,我没有说他是“private, protected和public”。

ECMAScript规范的当前版本,没有定义private, protected和public修饰符。

然而,在实践中是有可能看到有些东西被命名为“模仿JS封装”。 一般该上下文的目的是(作为一个规则,构造函数本身)使用。 不幸的是,经常实施这种“模仿”,程序员可以产生伪绝对非抽象的实体设置“getter / setter方法”(我再说一遍,它是错误的):

复制代码 代码如下:


function A() {
 
  var _a; // "private" a
 
  this.getA = function _getA() {
    return _a;
  };
 
  this.setA = function _setA(a) {
    _a = a;
  };
 
}
 
var a = new A();
 
a.setA(10);
alert(a._a); // undefined, "private"
alert(a.getA()); // 10

因此,每个人都明白,对于每个创建的对象,对于的getA/setA方法也创建了,这也是导致内存增加的原因(和原型定义相比)。 虽然,理论上第一种情况下可以对对象进行优化。

另外,一些JavaScript的文章经常提到“私有方法”的概念,注意:ECMA-262-3标准里没有定义任何关于“私有方法”的概念。

但是,某些情况下它可以在构造函数中创建,因为JS是意识形态的语言——对象是完全可变的并且有独特的特性(在构造函数里某些条件下,有些对象可以得到额外的方法,而其他则不行)。

此外,在JavaScript里,如果还是把封装曲解成为了不让恶意黑客在某些自动写入某些值的一种理解来代替使用setter方法,那所谓的“隐藏(hidden)”和“私有(private)”其实没有很“隐藏”,,有些实现可以通过调用上下文到eval函数(可以在SpiderMonkey1.7上测试)在相关的作用域链(以及相应的所有变量对象)上获取值)。

复制代码 代码如下:


eval('_a = 100', a.getA); // 或者a.setA,因为"_a"两个方法的[[Scope]]上
a.getA(); // 100

或者,在实现中允许直接进入活动对象(例如Rhino),通过访问该对象的相应属性可以改变内部变量的值:

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgfgws.html