属性是否应该出现在原型上
Should properties ever be on the prototype?
下面我在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
这样的功能。如果您指的是像name
、department
或number
这样的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,但仍然需要外部封闭。
我们真正需要的是真正解决问题的类,正如这篇写得很好的博客
- 检查是否已加载原型
- JavaScript中的原型继承是否演示了类型多态性
- 是否可以在Java中模拟Javascript风格的原型
- 在定义了构造函数之后,是否可以将实例属性添加到JavaScript原型中
- 是否可以将方法添加到空值的原型中
- 对象构造:原型是否真的有必要
- 是否可以从 javascript 中的原型方法访问私有类属性
- 是否存在替换Javascript构造函数的问题'原型,而不是添加到原型中
- JS原型是否可以访问初始化期间传递给对象的参数
- 使用原型时是否可以直接将类方法作为回调传递
- 如果修改原型,是否有任何不良副作用
- objective-c的参数[self]是否与JavaScript的原型相关
- 在javascript中使用原型设计是否会对ajax调用和异步代码产生负面影响
- 是否需要在经典继承中更改子类的“原型.构造函数”
- 对于 JavaScript 的继承,引用或复制父级的原型是否更好
- 检查自定义元素原型是否源自特定原型
- 属性是否应该出现在原型上
- Angular$watch在对象上是否包括其原型成员
- 修改子项的原型是否会影响父项的原型
- JavaScript的数组映射原型是否有点偏差