【问题标题】:Reason for using `prototype` instead of `this`使用 `prototype` 而不是 `this` 的原因
【发布时间】:2012-11-30 20:02:19
【问题描述】:

我正在使用 Lightbox2

https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js

我不明白为什么 Lightbox 的所有内部成员都是原型 (Lightbox.prototype.init) 而不仅仅是成员 (Lightbox.init)?

如果它们特定于每个灯箱实例,使用this.init 会不会更容易?

【问题讨论】:

  • 创建的每个实例都从原型继承方法。这是not the same as using this,当然与将方法分配给构造函数不同。只有实例特定数据被分配给构造函数内的this。应该共享的数据被分配给原型。
  • @Felix Kling 我专门询问灯箱的结构,我知道thisprototype 之间的一般区别。我不明白 prototype 在这种特殊情况下的用途。
  • 与其他任何情况下的优势相同。例如,.init() 应该在所有实例之间共享,因此将其分配给prototype。将其分配给Lightbox 没有意义,因为实例不会继承这些方法。在构造函数中将其分配给this 是可能的,但是每个实例都有自己的方法副本,这会增加内存使用量。当然,喜欢prototype 而不是this 的所有一般理由也适用于特定情况。在特定情况下选择它不必有额外的理由。

标签: javascript


【解决方案1】:

困惑?不要...

这样想:

  1. Lightbox 是您的类定义,但它还不是实例。

  2. 你直接放在类上的东西就像一个静态成员:

    Lightbox.staticFunc = function() {
        // "this" will not point to instance object
    };
    
  3. 无论你放在它的原型上的东西都是共享实例成员:

    Lightbox.prototype.instanceFunc = function() {
        // "this" will point to object instance so members can be accessed
    };
    
  4. 当你创建一个类的实例时,所有的实例成员都可以通过this关键字访问,但是静态的可以通过类定义来访问:

    var someData = Lightbox.staticFunc();
    var l = new Lightbox();
    l.instanceFunc();
    

这是否清楚你对原型成员的理解?

灯箱代码然后

您一直在查看的代码意味着:

// this is a constructor that accesses instance properties (using "this")
// ------
// since properties are accessed via "this.something" means that they are
//    not shared between instances but are part of one particular instance
// ------
function Lightbox(options) {
    this.options = options;
    this.album = [];
    this.currentImageIndex = void 0;
    this.init();
}

// adding an instance method that will be accessible to lightbox object instance
//    that's why it can also access instance members (using "this")
// ------
// all functions that are defined on the prototype are shared between
//    all instances so they consume less resources because not every
//    object instance created them separately.
// ------
Lightbox.prototype.init = function() {
    this.enable();
    return this.build();
};

但是这段代码的某些部分有点令人困惑,即

LightboxOptions = (function() {

    function LightboxOptions() {
        this.fileLoadingImage = 'images/loading.gif';
        this.fileCloseImage = 'images/close.png';
        this.resizeDuration = 700;
        this.fadeDuration = 500;
        this.labelImage = "Image";
        this.labelOf = "of";
    }

    return LightboxOptions;

})();

LightboxOptions 类包含在函数闭包中,即使它没有定义任何私有数据,因此在此示例中可以省略外部立即执行函数,但结果相同:

LightboxOptions = function() {
    this.fileLoadingImage = 'images/loading.gif';
    this.fileCloseImage = 'images/close.png';
    this.resizeDuration = 700;
    this.fadeDuration = 500;
    this.labelImage = "Image";
    this.labelOf = "of";
};

当然可以使用this 在构造函数中定义这些函数,但是它们不会在实例之间共享,因此每个对象实例都会定义相同的函数,从而消耗更多资源。所以这是不一样的,尽管从执行点来看确实是一样的:

CustomClass = function() {
    this.prop = true;
};
CustomClass.prototype.method = function() { alert("I'm shared."); };

与以下略有不同:

CustomClass = function() {
    this.prop = true;
    this.method = function() { alert("I'm duplicated in every instance."); };
};

在为每个对象实例定义函数时,后者会消耗更多资源。

...还有一点要彻底清除这个东西

假设我们有这个类定义:

var C = function() {
    this.prop = true;
    this.method = function() { console.log("Per instance method"); };
}
C.prototype.method = function() { console.log("Shared instance method"); };

如果我们调用这些代码行会发生什么

var a = new C();
var b = new C();
a.method();
b.method();
delete a.method;
a.method();
b.method();

你认为输出会是什么?您至少应该对delete 之后会发生什么感到困惑?哪个方法会被删除?每个实例?共享?两个都?好吧,应该在对象实例a 上删除每个实例方法,这就是为什么之后它报告已调用共享方法的原因。但仅限于ab 仍然有自己的每个实例方法。

所以事不宜迟,输出如下所示:

Per instance method      // a.method
Per instance method      // b.method
Shared instance method   // a.method
Per instance method      // b.method

prototype 属性呢

这些是不同的。当您创建一个对象实例时,所有这些属性都会被复制到每个对象并且不会共享。因此,无论您在特定对象的范围内对它们做什么,都不会反映给其他人。

如果您随后删除特定对象上的此类属性,它仍然可以使用其初始值,就像对象被实例化时一样。

var C = new function() {};
C.prototype.prop = 1;

var a = new C();
var b = new C();

a.prop = 10;   // does not change the value of "b.prop"
delete a.prop; // "a.prop" is now back to 1

【讨论】:

  • 感谢详细解答!
  • @IlyaD:我添加了更多与属性及其行为方式相关的内容。它会清除更多的东西。
  • 最后一件事我不明白:为什么function Lightbox(options) { 使用options 而不是LightboxOptions 以及LightboxOptions 如何变成options
  • @IlyaD:您可以随意命名参数。它们只是函数本地的名称。 LightboxOptions 的实例作为参数传递给构造函数 in this line
  • @IlyaD: 继续 Felix 的回答...LightboxOptionsline number 347 中变为 options 尽管出于易于理解的原因,大括号应在末尾使用为:@987654351 @ 在使用构造函数实例化对象时看不到它们被使用是一件相当奇怪和不寻常的事情。
【解决方案2】:

如果它们特定于灯箱的每个实例,那不是 this.init 更容易使用吗?

他们不应该因此将所有内容都放在prototype 对象中。当您使用prototype 时,所有方法仍然对您可用,只是它们不会成为实例成员。

JavaScript 使用原型链,当它看到一个方法时,它会在原型链中搜索,直到找到指定的方法。如果在中间没有找到,这个过程会一直持续到最终的 Object 对象。

您应该只创建您认为合理或需要的实例成员(通过this),因为如果您使用this 关键字(例如实例成员)放置不必要的方法,则会增加开销(计算浪费)。

【讨论】:

    猜你喜欢
    • 2011-11-06
    • 2017-05-28
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多