Javascript 属性是否应该出现在原型上?

Javascript 属性是否应该出现在原型上?,javascript,oop,prototypal-inheritance,Javascript,Oop,Prototypal Inheritance,下面是一个简单的JavaScript小继承链: var Base =(function(){ function Base(config){ this.name = function(){ return config.name; } this.department = function(){ return config.department;

下面是一个简单的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
它的工作原理与预期一致。不过,我很好奇在对象的原型上放置方法属性有多正确。从规范上讲,我知道实例数据应该与类的实例一起使用,并且对象实际使用的方法应该在原型上,但是如果属性本身是方法(如本例中的name和department),那么情况又如何呢?为了保持不变,我不希望用户在初始化这些对象之后更改它们的属性。在这种情况下,使用原型而不是构造函数向对象添加功能有意义吗

或者,只有在构造函数中放置属性,以便在执行诸如Base.prototype.which之类的操作时可以访问它们,这才是正确的

属性本身是方法的情况如何?为了保持不变,我不希望用户在初始化这些对象之后更改它们的属性

我不再调用这些“属性”,而是调用“访问器”或“getter”方法

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

是的,如果您指的是
计算
之类的功能。如果您指的是像
name
department
number
这样的getter,那么您需要将它们放在构造函数中,因为它们需要对
config
进行特权访问

从规范上讲,我知道实例数据应该与类的实例一起使用,并且对象实际使用的方法应该在原型上

实际上有点不同:

  • 所有特定于实例的内容(数据、具有不同作用域的访问器方法)都需要在实例上进行,并且通常在构造函数中进行设置
  • 所有实例之间共享的所有内容(通常是方法,但在某些罕见的情况下也包括数据)都应该放在原型上

使用
Concrete.prototype=Object.create(Base.prototype)

你问题的标题有点误导

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


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

现在-我想是的,因为ES6还没有被所有浏览器完全支持

但很快,这种支持将在所有浏览器的发布版本中展开,然后您就可以使用这种漂亮的ES6数据类型
WeakMap
。就像一张
地图
,但这里的关键点是对象。因此,您的基本定义可以编写为:

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

可以采用类似的方法,但仍需要外部封闭


这里我们真正需要的是能够真正解决这个问题的类,正如这篇写得非常好的文章所解释的

为什么要让这些对象不可变?我们讨论的是客户端JavaScript吗?然后用户可以更改任何他想要的内容。这种“私有属性”模式被高估了。在编写客户端代码时,只有两件事需要考虑:效率和维护成本。从这个角度来看,我认为将初始值放在实例上,将方法放在原型上更好。也许效率更高(取决于对象的数量),而且根据我的经验更容易维护。@freakish:不变性与安全无关。使用访问器方法(无论是否私有,效率没有那么大的不同)通常具有更好的可维护性。使用私有属性可以在有许多编码人员的项目中或在编写库时提供帮助;不管是客户端还是服务器端。昨晚我在想这个问题,差点在上面贴出一个问题。为什么更喜欢Object.create(Base.prototype)而不是Concrete.prototype=Base.prototype?请参阅和“链接自”中的问题
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;
})();