【问题标题】:Should properties ever be on the prototype?属性应该在原型上吗?
【发布时间】:2014-09-08 13:13:59
【问题描述】:

下面我有一个简单的 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 之类的操作时才能访问它们?

【问题讨论】:

  • 你为什么要让这些对象不可变?我们是在谈论客户端 JavaScript 吗?然后用户可以改变他想要的任何东西。这种“私有属性”模式被高估了。在编写客户端代码时,您只应考虑两件事:效率和维护成本。从这个角度来看,我认为将初始值放在实例上,将方法放在原型上会更好。可能更高效(取决于对象的数量)并且根据我的经验更易于维护。
  • @freakish:不变性与安全性无关。使用访问器方法(无论是否私有,效率并没有那么不同)通常具有更好的可维护性。使用私有属性可以帮助有许多程序员的项目,或者在编写库时;无论是客户端还是服务器端。

标签: javascript oop prototypal-inheritance


【解决方案1】:

属性本身就是方法的情况呢?为了使事物保持不变,我宁愿不要让用户在初始化这些对象之一后更改它们的属性。

我不会再将这些“属性”称为“访问器”或“获取器”方法。

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

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

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

其实有点不一样:

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

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

【讨论】:

【解决方案2】:

你的问题的标题有点误导。

直接回答您的标题 - 是的,我们鼓励使用原型属性。在编写代码和代码使用方面,它们可能没有太大区别,但在内部,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中所解释的那样

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-26
    • 2014-10-18
    • 2018-01-14
    • 2012-11-07
    • 2015-04-15
    • 2016-08-09
    • 2010-11-01
    • 2020-06-09
    相关资源
    最近更新 更多