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

Should properties ever be on the prototype?

本文关键字:原型 是否 属性      更新时间:2023-09-26

下面我在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之类的操作时可以访问它们才是正确的?

在属性本身就是方法的情况下呢?为了保持事物不变,我宁愿不让用户在初始化后更改其中一个对象的属性。

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

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

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

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

实际上有点不同:

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

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

问题的标题有点误导。

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


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

现在-我认为是的,因为ES6仍然不是所有浏览器都完全支持的。

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

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,但仍然需要外部封闭。

我们真正需要的是真正解决问题的类,正如这篇写得很好的博客

中所解释的那样