让我们实际比较这两种方式,看看哪一种更快:http: //jsperf.com/traditional-oop-vs-derek-s-oop-variant
如您所见,您的方法比传统方法慢得多。原因是:
- 您的构造函数做的事情超出了要求。因此,如果您创建多个实例,那么创建实例的额外成本就会增加。
- 正如@Alxandr 提到的,
method
每次您无缘无故地创建一个新实例时,您都会创建一个新的匿名函数。它只会有用一次,之后就会浪费处理能力。
- 您正在调用一个函数来检查
prototype
构造函数是否具有给定方法,prototype
如果没有,则将该方法添加到。这似乎是不必要的。您无需创建函数即可为您执行此操作。恕我直言,函数调用只是额外的开销。
既然你要求批评:
我知道增强Object.prototype
不是一种好的做法。但除此之外,这种方法还有什么缺点吗?
除了非常缓慢之外,您的方法还受到以下影响:
- 难以理解。您可能会发现这种方法很直观。但是,阅读您的代码的人肯定会想知道该函数的
this.method
作用。他们需要阅读 的定义Object.prototype.method
才能完全理解您的代码。
- 不直观。正如我之前提到的,
prototype
在构造函数中定义属性是没有意义的。它只需要一次,之后它就会变成额外的行李。最好将构造函数逻辑和prototype
属性分开。
- 它可能会导致意外行为。正如@basilikum 指出的那样,如果您从不调用构造函数,那么
prototype
将永远不会设置属性。当您尝试访问prototype
. 例如,从prototype
没有属性继承属性时,将继承直到调用基构造函数。
我相信您的目标是将构造函数和prototype
属性封装到一个实体中:
但是,我试图想出一种方法来在原型上定义函数,而不用将函数定义与构造函数分开。
是否有捷径可寻?让我们看看,JavaScript 是一种原型面向对象的语言。因此,我们应该更多地关注prototype
而不是构造函数。
上图取自以下答案:https ://stackoverflow.com/a/8096017/783743
这张图向我们展示了:
- 每个构造函数都有一个名为的属性
prototype
,它指向构造函数的原型对象。
- 每个原型都有一个名为的属性
constructor
,它指向原型对象的构造函数。
- 我们从构造函数创建一个实例。然而,实例实际上继承自
prototype
,而不是构造函数。
这是非常有用的信息。传统上,我们总是先创建一个构造函数,然后再设置它的prototype
属性。然而,这些信息告诉我们,我们可以先创建一个原型对象,然后constructor
在其上定义属性。
例如,传统上我们可以这样写:
function Shape() {
this.x = 0;
this.y = 0;
}
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};
然而,使用我们新发现的知识,我们可能会写出相同的东西:
var shape = {
constructor: function () {
this.x = 0;
this.y = 0;
},
move: function (x, y) {
this.x += x;
this.y += y;
}
};
这两个示例中包含的信息是相同的。然而,我们需要一些额外的脚手架来使第二个示例工作。特别是我们需要做:
var Shape = shape.constructor;
Shape.prototype = shape;
这不是一个大问题。我们只需创建一个函数来为我们执行此操作:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
现在我们可以定义Shape
如下:
var Shape = defclass({
constructor: function () {
this.x = 0;
this.y = 0;
},
move: function (x, y) {
this.x += x;
this.y += y;
}
});
如您所见,封装在 JavaScript 中很容易实现。您需要做的就是侧身思考。然而,继承是一个不同的问题。你需要做更多的工作来实现继承。
希望这有帮助。