1

诚然,我对 javascript 的内部工作原理知之甚少,但需要制作一个库并想学习(因此在这里询问)。我理解使用闭包并导出到窗口不会污染全局命名空间,但除此之外它让我有点困惑。

(function() {
    var Drop = window.Drop = function() { 

        var files = [];

        var add = function(word) {
              files.push(word);    
              return files;
        }

        return {
            files: files,
            add: add
        }
    }
})()


// All of these seem to be the same?
var a = Drop();
var b = new Drop();
var c = new Drop;

// Each has their own state which is what I want.
a.add("file1");
b.add("file2");
c.add("file3");
  • 为什么三种“初始化”Drop的方式都一样?
  • 究竟是什么让他们有能力拥有自己的状态?
  • 是否有替代返回语法的方法来在 Drop 上导出这些函数?
  • 是否有一种更好的最佳实践方式来创建这样的自包含库?

我在网上搜索过,但在这个主题上几乎没有一致性。

4

2 回答 2

2
  1. 第一种方式 ( Drop()) 只是正常调用函数,this全局对象也是如此(window在浏览器环境中)。正如您所期望的那样,它完成它的工作,然后返回一个对象。

    第二种方式 ( new Drop()) 创建一个新Drop对象并执行this设置为该对象的构造函数。但是,您不会this在任何地方使用并返回从对象字面量创建的对象,因此该Drop对象将被丢弃并返回对象字面量。

    第三种方式(new Drop)在语义上与第二种方式相同;这只是句法上的差异。

  2. 它们都有自己的状态,因为每次调用时Drop,它都有自己的一组局部变量,与任何其他调用的局部变量不同Drop

  3. 您可以转换您的代码以使用正常的new语法和原型。这有几个优点:即,您只需创建add一次函数,而不是为每次Drop调用创建一个函数。您修改后的代码可能如下所示:

    function Drop() {
        this.files = [];
    }
    Drop.prototype.add = function(word) {
        this.files.push(word);
        return this.files;
    };
    

    但是,通过这样做,您将无法在没有new. 但是,有一种解决方法:您可以将其添加为内部的第一行function Drop

    if(!(this instanceof Drop)) {
        return new Drop();
    }
    

    因为当你用 调用它时newthis将是 a Drop,当你没有调用它时new,它将是 athis以外的东西Drop,你可以看看是否this是 a Drop,如果是,继续初始化;否则,使用 重新调用它new

    还有另一个语义差异。考虑以下代码:

    var drop = new Drop();
    var adder = drop.add;
    adder(someFile);
    

    您的代码将在这里工作。基于原型的代码不会,因为this它将是全局对象,而不是drop. 这也有一个解决方法:在构造函数的某个地方,您可以这样做:

    this.add = this.add.bind(this);
    

    当然,如果你的库的消费者不打算从对象中提取函数,你就不需要这样做。此外,您可能需要对Function.prototype.bind没有它的浏览器进行填充。

  4. 不,这完全是口味问题。

于 2013-02-09T23:09:08.100 回答
1
  • 为什么三种“初始化”Drop的方式都一样?

// All of these seem to be the same?
var a = Drop();
var b = new Drop();
var c = new Drop;

当你new在 JavaScript 中使用调用一个函数时,函数this内部的值变成了新的对象。

但是在您的情况下它们相同的原因是您根本没有使用this。您正在使用对象文字语法创建一个单独的对象,然后返回它,因此new没有影响。


  • 究竟是什么让他们有能力拥有自己的状态?

因为每次函数调用都会创建一个新对象,所以每次调用的每个对象都是完全不同的。

分配给对象的函数在每次Drop调用中都会重新创建,因此会在封闭变量范围内创建一个闭包。因此,每个调用的files数组对于每个相应调用中所做的函数都是可连续访问的。


  • 是否有替代返回语法的方法来在 Drop 上导出这些函数?

是的。将函数和数组分配给this,然后删除该return语句。但这将需要使用new. 或者,将函数放在 的.prototype对象上Drop,它们将在所有使用 的实例之间共享new,但将分配给的数组保留this在构造函数中,使其不被共享。

对于引用数组的原型函数,他们将使用this.files.


  • 是否有一种更好的最佳实践方式来创建这样的自包含库?

JavaScript 非常灵活。有很多方法可以解决一个问题,每种方法都有自己的优点/缺点。一般来说,它会归结为利用闭包原型继承或两者的某种组合。


这是一个完整的原型继承版本。另外,外部(function() {})()没有被使用,所以我要添加一个变量来利用它。

(function() {
    var totalObjects = 0; // visible only to functions created in this scope

    var Drop = window.Drop = function() { 
        this.files = [];
        this.serialNumber = totalObjects++;
    }

    Drop.prototype.add = function(word) {
        this.files.push(word);    
        return this.files;
    };
})();
于 2013-02-09T23:11:10.073 回答