属性应该在原型上吗?

时间:2014-09-08 13:14:00

标签: javascript oop prototypal-inheritance

下面我在JavaScript中有一个简单的小继承链:

var Base  =(function(){
          function Base(config){
            this.name = function(){
                return config.name;
            }
            this.department = function(){
                return config.department;
            }
        }
        Base.prototype.calculate = function(){
            return this.name().length + this.department().length;
        }
        return Base;
    })();
    var base = new Base({
        name: 'me',
        department: 'this one'
    });
    console.log(base.calculate());
    //inheritance
    var Concrete = (function(){
        Concrete.prototype = Base.prototype;
        function Concrete(config){
            Base.apply(this,arguments);
            this.number = function(){
                return config.number;
            }
        }
        return Concrete;
    })();
    var concrete = new Concrete({
        name: 'concrete',
        department: 'the best',
        number: 3
    });
    console.log(concrete.number()); //3
    console.log(concrete.name()); //concrete

它按预期工作。我很好奇,但是在对象的原型上放置方法属性是多么正确。通常我知道实例数据应该与类的实例一起使用,对象实际使用的方法应该在原型上,但是属性本身是方法的情况如何(比如本例中的Base中的名称和部门)?为了保持不变,我宁愿不让用户在初始化之后更改其中一个对象的属性。在这种情况下,使用原型而不是构造函数向对象添加功能是否有意义?

或者只在构造函数中放置属性是正确的,这样当你执行Base.prototype.whatever之类的操作时就可以访问它们了吗?

2 个答案:

答案 0 :(得分:1)

  

属性本身是方法的情况怎么样?为了保持不可变性,我不应该让用户在初始化之后更改其中一个对象的属性。

我不打电话给这些"属性"更多,但"访问者"或者" getter"方法

  

在这种情况下,使用原型而不是构造函数向对象添加功能是否有意义?

是的,如果您的意思是calculate这样的功能。如果您指的是namedepartmentnumber之类的getter,则需要将它们放置在构造函数中,因为它们需要对config的特权访问权限。

  

通常我知道实例数据应该与类的实例一起使用,对象实际使用的方法应该在原型上

实际上有点不同:

  • 所有特定于实例的(具有不同范围的数据,访问器方法)都需要在实例上进行,并且通常在构造函数中设置
  • 在所有实例之间共享的所有内容(通常是方法,但在极少数情况下也是数据)都应该在原型上进行。
Concrete.prototype = Base.prototype;

No!请改用Concrete.prototype = Object.create(Base.prototype);

答案 1 :(得分:1)

你问题的标题有点误导。

直接回答您的标题 - 是的,鼓励原型属性。他们在编写代码和代码的使用方面可能没有太大区别,但在内部,JS如何管理事物,如内存,访问闭包内的变量等,通过原型属性完成后更好。


现在您的实际问题是,使用闭包和私有范围变量隐藏不可变配置是唯一的方法吗?

现在 - 我想是的,因为ES6仍然不是每个浏览器都完全支持。

但很快,支持将在所有发布版本的浏览器中展开,然后您可以使用这个漂亮的ES6数据类型WeakMap。就像Map一样,但这里的键是对象。所以您的Base定义可以写成:

var Base = (function() {
  var properties = new WeakMap(); // You have to keep variable private

  function Base(config) {
    properties.set(this, config);
  }

  Base.prototype = {
    name: function() {
      return properties.get(this).name;
    },

    department: function() {
      return properties.get(this).department;
    },

    calculate: function() {
      return this.name().length + this.department().length;
    }
  };

  return Base;
})();

是的,您仍然需要保持变量属性不可及,因此外部封闭。但在这种情况下,关闭的数量减少了1个。

类似的方法可以用于ES6 Symbol,但外部闭包仍然是必需的。

我们真正需要的是真正解决问题的课程,正如这篇写得非常好的blog

所解释的那样。