Javascript继承封装,高效完成
Javascript inheritance and encapsulation, done efficiently
来自 C++ / Objective-C 背景,我正在尝试学习如何正确有效地重现 Javascript 中的继承和封装模式。我读了很多书(Crockford 等),虽然有很多例子说明如何实现一个或另一个,但我正在努力研究如何将它们结合起来而不引入明显的负面影响。
目前,我有这个代码:
var BaseClass = (function() {
function doThing() {
console.log("[%s] Base-class's 'doThing'", this.name);
}
function reportThing() {
console.log("[%s] Base-class's 'reportThing'", this.name);
}
return function(name) {
var self = Object.create({});
self.name = name;
self.doThing = doThing;
self.reportThing = reportThing;
return self;
}
}());
var SubClass = (function(base) {
function extraThing() {
console.log("[%s] Sub-class's 'extraThing'", this.name);
}
function doThing() {
console.log("[%s] Sub-class's replacement 'doThing'", this.name);
}
return function(name) {
// Create an instance of the base object, passing our 'name' to it.
var self = Object.create(base(name));
// We need to bind the new method to replace the old
self.doThing = doThing;
self.extraThing = extraThing;
return self;
}
}(BaseClass));
它主要做我想做的事:
// Create an instance of the base class and call it's two methods
var base = BaseClass("Bert");
base.doThing(); // "[Bert] Base-class's 'doThing'"
base.reportThing(); // "[Bert] Base-class's 'reportThing'"
var other = BaseClass("Fred");
// Create an instance of the sub-class and call it's three methods (two from the base, one of it's own)
var sub = SubClass("Alfred");
sub.doThing(); // "[Alfred] Sub-class's replacement 'doThing'"
sub.extraThing(); // "[Alfred] Sub-class's 'extraThing'"
sub.reportThing(); // "[Alfred] Base-class's 'reportThing'"
但是,有(至少!)两个问题:
- 我不相信原型链完好无损。如果我通过子 class 的一个实例替换原型中的方法,其他实例看不到它:
- 没有封装 .name 属性
我正在替换原型的函数实现,如下所示:
Object.getPrototypeOf(oneInstance).reportThing = function() { ... }
otherInstance.reportThing() // Original version is still called
这也许不是什么大问题,但它让我怀疑我的理解。
私有变量是我想要有效实现的东西。变量隐藏的模块模式在这里没有帮助,因为它导致每个对象都存在函数定义。我可能缺少一种组合模式的方法,那么有没有一种方法可以在不复制函数的情况下实现私有变量?
如果你想保留原型链,你必须覆盖并使用.prototype:
例子:
主要 Class:
function BaseClass(){
}
BaseClass.prototype.doThing = function(){...}
子Class:
function SubClass(){
}
SubClass.prototype= new BaseClass();
SubClass.prototype.extraThing = function(){};
现在,只要您更改 extraThing 或 doThing,它就会被替换到任何地方。
名称 属性 可以作为 public 变量访问(它不是静态的)。
如果你想要它静态,你必须把它放在原型中。
如果你想让它私有,你必须让它在本地运行:
function BaseClass(nameParam){
var name = nameParam;
}
要创建对象,只需调用函数:
var testObj = new BaseClass("test");
testObj.doThing();
如果你想将私有变量与可重写函数结合起来,你可能会找到你的 answer here。但是如果你能够重写访问私有变量的函数,它就不再是真正的私有变量了。
简单的食谱如下:
function BaseClass(someParams)
{
// Setup the public properties, e.g.
this.name = someParams.name;
}
BaseClass.prototype.someMethod = function(){
// Do something with the public properties
}
现在继承是这样发生的
function SubClass(someParams)
{
// Reuse the base class constructor
BaseClass.call(this, someParams);
// Keep initializing stuff that wasn't initialized by the base class
this.anotherProperty= someParams.anotherProperty;
}
// Copy the prototype from the BaseClass
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;
// Start extending or overriding stuff
SubClass.prototype.someMethod = function(){
// In case you still wanna have the side effects of the original method
// This is opt-in code so it depends on your scenario.
BaseClass.prototype.someMethod.apply(this, arguments);
// Override the method here
}
取自:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
P.S。 Object.create 可能并非所有旧浏览器都支持,但别担心,这个 link 中有一个 polyfill。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
这通常是我在 JavaScript 中处理继承和封装的方式。 defclass
函数用于创建一个不继承任何其他 class 的新 class,extend
函数用于创建一个新的 class它扩展了另一个 class:
var base = new BaseClass("Bert");
base.doThing(); // "Bert BaseClass doThing"
base.reportThing(); // "Bert BaseClass reportThing"
var sub = new SubClass("Alfred");
sub.doThing(); // "Alfred SubClass replacement doThing"
sub.extraThing(); // "Alfred SubClass extraThing"
sub.reportThing(); // "Alfred BaseClass reportThing"
var other = new SubClass("Fred");
SubClass.prototype.reportThing = function () {
console.log(this.name + " SubClass replacement reportThing");
};
other.reportThing(); // Fred SubClass replacement reportThing
<script>
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, keys) {
var prototype = Object.create(constructor.prototype);
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
var BaseClass = defclass({
constructor: function (name) {
this.name = name;
},
doThing: function () {
console.log(this.name + " BaseClass doThing");
},
reportThing: function () {
console.log(this.name + " BaseClass reportThing");
}
});
var SubClass = extend(BaseClass, {
constructor: function (name) {
BaseClass.call(this, name);
},
doThing: function () {
console.log(this.name + " SubClass replacement doThing");
},
extraThing: function () {
console.log(this.name + " SubClass extraThing");
}
});
</script>
阅读以下答案以了解继承在 JavaScript 中的工作原理:
What are the downsides of defining functions on prototype this way?
它解释了原型和构造函数之间的区别。此外,它还展示了原型和 classes 是如何同构的,以及如何在 JavaScript.
中创建“classes”
希望对您有所帮助。
来自 C++ / Objective-C 背景,我正在尝试学习如何正确有效地重现 Javascript 中的继承和封装模式。我读了很多书(Crockford 等),虽然有很多例子说明如何实现一个或另一个,但我正在努力研究如何将它们结合起来而不引入明显的负面影响。
目前,我有这个代码:
var BaseClass = (function() {
function doThing() {
console.log("[%s] Base-class's 'doThing'", this.name);
}
function reportThing() {
console.log("[%s] Base-class's 'reportThing'", this.name);
}
return function(name) {
var self = Object.create({});
self.name = name;
self.doThing = doThing;
self.reportThing = reportThing;
return self;
}
}());
var SubClass = (function(base) {
function extraThing() {
console.log("[%s] Sub-class's 'extraThing'", this.name);
}
function doThing() {
console.log("[%s] Sub-class's replacement 'doThing'", this.name);
}
return function(name) {
// Create an instance of the base object, passing our 'name' to it.
var self = Object.create(base(name));
// We need to bind the new method to replace the old
self.doThing = doThing;
self.extraThing = extraThing;
return self;
}
}(BaseClass));
它主要做我想做的事:
// Create an instance of the base class and call it's two methods
var base = BaseClass("Bert");
base.doThing(); // "[Bert] Base-class's 'doThing'"
base.reportThing(); // "[Bert] Base-class's 'reportThing'"
var other = BaseClass("Fred");
// Create an instance of the sub-class and call it's three methods (two from the base, one of it's own)
var sub = SubClass("Alfred");
sub.doThing(); // "[Alfred] Sub-class's replacement 'doThing'"
sub.extraThing(); // "[Alfred] Sub-class's 'extraThing'"
sub.reportThing(); // "[Alfred] Base-class's 'reportThing'"
但是,有(至少!)两个问题:
- 我不相信原型链完好无损。如果我通过子 class 的一个实例替换原型中的方法,其他实例看不到它:
- 没有封装 .name 属性
我正在替换原型的函数实现,如下所示:
Object.getPrototypeOf(oneInstance).reportThing = function() { ... }
otherInstance.reportThing() // Original version is still called
这也许不是什么大问题,但它让我怀疑我的理解。
私有变量是我想要有效实现的东西。变量隐藏的模块模式在这里没有帮助,因为它导致每个对象都存在函数定义。我可能缺少一种组合模式的方法,那么有没有一种方法可以在不复制函数的情况下实现私有变量?
如果你想保留原型链,你必须覆盖并使用.prototype: 例子: 主要 Class:
function BaseClass(){
}
BaseClass.prototype.doThing = function(){...}
子Class:
function SubClass(){
}
SubClass.prototype= new BaseClass();
SubClass.prototype.extraThing = function(){};
现在,只要您更改 extraThing 或 doThing,它就会被替换到任何地方。 名称 属性 可以作为 public 变量访问(它不是静态的)。
如果你想要它静态,你必须把它放在原型中。
如果你想让它私有,你必须让它在本地运行:
function BaseClass(nameParam){
var name = nameParam;
}
要创建对象,只需调用函数:
var testObj = new BaseClass("test");
testObj.doThing();
如果你想将私有变量与可重写函数结合起来,你可能会找到你的 answer here。但是如果你能够重写访问私有变量的函数,它就不再是真正的私有变量了。
简单的食谱如下:
function BaseClass(someParams)
{
// Setup the public properties, e.g.
this.name = someParams.name;
}
BaseClass.prototype.someMethod = function(){
// Do something with the public properties
}
现在继承是这样发生的
function SubClass(someParams)
{
// Reuse the base class constructor
BaseClass.call(this, someParams);
// Keep initializing stuff that wasn't initialized by the base class
this.anotherProperty= someParams.anotherProperty;
}
// Copy the prototype from the BaseClass
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;
// Start extending or overriding stuff
SubClass.prototype.someMethod = function(){
// In case you still wanna have the side effects of the original method
// This is opt-in code so it depends on your scenario.
BaseClass.prototype.someMethod.apply(this, arguments);
// Override the method here
}
取自: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
P.S。 Object.create 可能并非所有旧浏览器都支持,但别担心,这个 link 中有一个 polyfill。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
这通常是我在 JavaScript 中处理继承和封装的方式。 defclass
函数用于创建一个不继承任何其他 class 的新 class,extend
函数用于创建一个新的 class它扩展了另一个 class:
var base = new BaseClass("Bert");
base.doThing(); // "Bert BaseClass doThing"
base.reportThing(); // "Bert BaseClass reportThing"
var sub = new SubClass("Alfred");
sub.doThing(); // "Alfred SubClass replacement doThing"
sub.extraThing(); // "Alfred SubClass extraThing"
sub.reportThing(); // "Alfred BaseClass reportThing"
var other = new SubClass("Fred");
SubClass.prototype.reportThing = function () {
console.log(this.name + " SubClass replacement reportThing");
};
other.reportThing(); // Fred SubClass replacement reportThing
<script>
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, keys) {
var prototype = Object.create(constructor.prototype);
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
var BaseClass = defclass({
constructor: function (name) {
this.name = name;
},
doThing: function () {
console.log(this.name + " BaseClass doThing");
},
reportThing: function () {
console.log(this.name + " BaseClass reportThing");
}
});
var SubClass = extend(BaseClass, {
constructor: function (name) {
BaseClass.call(this, name);
},
doThing: function () {
console.log(this.name + " SubClass replacement doThing");
},
extraThing: function () {
console.log(this.name + " SubClass extraThing");
}
});
</script>
阅读以下答案以了解继承在 JavaScript 中的工作原理:
What are the downsides of defining functions on prototype this way?
它解释了原型和构造函数之间的区别。此外,它还展示了原型和 classes 是如何同构的,以及如何在 JavaScript.
中创建“classes”希望对您有所帮助。