2

我有使用 C# 编程语言的经验,但我现在还必须使用 JS,这对我来说相当新。

我曾尝试在 JS 中开发一个简单的类仿真,如下所示:

http://jsfiddle.net/T74Zm/

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);

我试图将变量封装在valueA.prototype,但似乎所有对象都可用的 JavaScript 规范。

所以我的问题是:

1)。如何进行封装,它非常接近具有 OOP/访问修饰符支持的静态语言?

2)。如何在 JS 中模拟一些访问修饰符,private例如?

4

3 回答 3

7

如果你想使用继承,你不能(至少 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

你可以在这里查看:

http://jsfiddle.net/MLF7v/1/

如果您对 Constructor 函数表示法感觉更舒服,您可以执行以下操作:

(未测试)

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);};
}

这与上面几乎相同,因为没有使用原型并且方法直接复制到对象中。

但是,闭包方法是内存和耗时的,最糟糕的是:它们使用实际上不属于您正在使用的对象的变量......这是一个重要的“语义”问题(这个道具是否属于对我来说,还是不是?)这让继承很头疼。这样做的原因是,扩展对象的方法要么无法访问该私有伪属性(因为它们没有在超级对象闭包中定义),要么可以访问来自“超级对象”的公共私有变量(方法是在超对象中定义的,因此可以访问超对象的闭包)。那是胡说八道。

“文化”方法

以我的拙见,封装并不能阻止任何人如果他/她真的想弄乱你的代码,所以我更喜欢遵循 ​​python 哲学“我们都是成熟的人”,其中包括使用约定说“我想要此属性不得从外部使用”。例如,我在私有属性前面加上“_”,这意味着它们是私有的、受保护的或其他任何东西,但只是远离它。你不要碰你不应该碰的东西。

这种方法除了是最简单和最有效的方法外,还允许您使用继承链,因为属性在对象中,而不是限制在闭包中。

var Person = function (name) {
    this._name = name;
}
Person.prototype.sayHello = function () {...};
Person.prototype.getName = function () {...};
Person.prototype.setName = function () {...};
于 2013-11-13T08:23:33.320 回答
6

封装并不意味着计算机严格强制您不访问某些内容。它可以简单地通过不访问彼此内部的模块和类来实现。如果您的程序具有该属性,那么您正在使用封装。或者至少你得到了封装所带来的每一个好处——马铃薯、potahto。

如果您需要帮助,您可以使用文档和约定,例如下划线前缀,这样您就更容易知道什么是内部的或不是内部的。

想一想 - 如果您将“私有”全局替换为“公共”并重新编译任何 C# 程序,它的功能将完全相同。.SetAccessible而且您以某种方式忽略了即使在 C# 中,如果他们愿意,也可以通过私有访问。既然您似乎对此感到满意,那有什么问题?

通过闭包模拟“private”给你带来的问题比它解决的问题多得多,而且更糟糕,如果你还没有被上述内容说服,我将列出这些问题。

我还将引用 Martin Fowler 的一些权威论点:

访问控制不控制访问

如果您有一个私有字段,则意味着没有其他班级可以使用它。错误的!如果你真的愿意,你可以用几乎任何语言颠覆访问控制机制。通常通过的方式是通过反射。基本原理是调试器和其他系统工具经常需要查看私有数据,因此通常反射接口允许您这样做。

C++ 没有这种反射,但你可以直接使用内存操作,因为 C++ 本质上是开放内存。

访问控制的目的不是阻止访问,而是更多地表明该类更喜欢将某些东西保留给自己。使用访问修饰符,就像编程中的许多事情一样,主要是关于通信。

http://martinfowler.com/bliki/AccessModifier.html#AccessControlDoesNotControlAccess

于 2013-11-13T21:09:11.853 回答
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;  
}

您还可以以通用方式模仿属性 getter 和 setter。

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:请不要直接设置原型,它会破坏原型链。

于 2013-11-13T08:33:23.293 回答