这种定义JS对象的方法有什么用吗?


87

我正在维护一些旧代码,并且注意到使用了以下用于定义对象的模式:

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

有什么目的吗?它是否等同于执行以下操作?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

我并不想按照自己的喜好来进行整个代码库的重构,但我真的很想了解定义对象的这种about回方式背后的原因。

谢谢!


1
在您的确切示例中,没有区别。如果您将其扩展,可能会有所不同,但同时也会出现不同的方法。
特拉维斯J

没关系,可以说对象是作为引用的副本传递的,因此即使在IIFE中定义myFunction时,仍可以在其外部访问它。
adeneo 2014年

1
@adeneo在此示例中,不是myFunction可以使用通过外部定义的一些变量,这些变量不能从外部访问。看看我的回答
Juan Mendes

2
此JavaScript模式称为什么的重复副本,为什么使用它?(不确定我是否应该关闭)。另请参见JavaScript命名空间声明声明
Bergi 2014年

Answers:


116

称为模块模式http://toddmotto.com/mastering-the-module-pattern/

主要原因是您要创建真正的私有方法和变量。对于您而言,这没有意义,因为它没有隐藏任何实现细节。

这是一个使用模块模式的示例。

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

而如果你只是使用了直线对象,addervalue会成为公众

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

您可以通过做类似的事情来打破它

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

但是带有模块版本

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3

2
这并不能真正回答两个选择之间有什么区别的问题?

20
@torazaburo OP的例子不是一个很好的例子,我提供了一个真实的例子来说明何时使用模块模式。
Juan Mendes 2014年

ns.getNext: function () {不会编译。
punund 2014年

如果我确定如何修复它,我将拥有。我以为有一些预防措施delete MyNameSpace.getNext
punund 2014年

2
@punund JS没有编译器,但它具有解释器:)
frogatto

22

目的是限制闭包内函数的可访问性,以帮助防止其他脚本在其上执行代码。周围环绕它关闭你正在重新定义范围内执行的,内部的所有代码封闭,并有效地创建一个私人范围。有关更多信息,请参见此文章:

http://lupomontero.com/using-javascript-closures-to-create-private-scopes/

从文章:

JavaScript中最著名的问题之一是它对全局范围的依赖,这基本上意味着您在函数外部声明的任何变量都位于相同的名称空间中:不祥的窗口对象。由于网页的性质,来自不同来源的许多脚本可以(并且将)在同一页面上运行,并具有相同的全局范围,因此这确实是一件很糟糕的事情,因为它可能导致名称冲突(名称相同的变量被覆盖)和安全性问题。为了使问题最小化,我们可以使用JavaScript强大的闭包来创建私有作用域,在此我们可以确保变量对页面上的其他脚本不可见。



码:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function

1
myFunction在第二个版本中不在全局范围内。目的实际上是提供一个可以定义内部辅助功能的范围。
Barmar 2014年

myFunction之所以在全局范围内是因为它是在全局对象中定义的myObject。在第二个版本中,应用程序中的任何其他代码都可以执行myFunction。在第一个版本中,只有闭包内的代码可以访问myFunction
Jonathan Crowe,

不可以,myFunction只能与一起执行MyObject.myFunction(),与第一个版本相同。
2014年

@JonathanCrowe OP的示例不是一个很好的示例,它公开了模块内部的所有内容,因此是的,它可以在外部访问。请参阅我的答案,以获取有用的模块案例
Juan Mendes 2014年

@JuanMendes的要点是,OP的示例并没有很好地使用模块模式
Jonathan Crowe

6

好处:

  1. 在私有范围内维护变量。

  2. 您可以扩展现有对象的功能。

  3. 性能提高。

我认为以上三个简单点足以遵循这些规则。并且为了简单起见,仅编写内部函数就可以了。


6

在您显示的特定情况下,在功能或可见性方面没有有意义的区别。

原始的编码人员很可能采用这种方法作为一种模板,从而允许他定义可用于定义诸如myFunction以下内容的私有变量:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

这样避免了seconds_per_day每次调用函数时都进行计算,同时还避免了污染全局范围。

但是,没有什么本质上的不同,只是说

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

原始编码器可能更希望能够使用的声明性语法(root.myFunction = function而不是的对象/属性语法)向对象添加功能myFunction: function。但是这种差异主要是偏好问题。

但是,原始编码器采用的结构的优点是可以轻松地在代码的其他位置添加属性/方法:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

最重要的是,如果不需要,则无需采用此方法,但也无需更改它,因为它可以很好地运行并且实际上具有一些优势。


6
  1. 第一个模式可以用作获取对象并通过一些修改返回该对象的模块。换句话说,您可以如下定义此类模块。

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }
    

    并像这样使用它:

    var obj = {};
    module(obj);
    

    因此,一个优势可能是该模块的可重复使用性,供以后使用。


  1. 在第一种模式中,您可以定义一个私有范围来存储您的私有内容,例如私有属性和方法。例如,考虑以下代码段:

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);
    

  1. 此模式可用于向所有类型的对象(例如数组,对象文字,函数)添加方法或属性。

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12
    

我认为主要优势可能是第二个优势(创建私有范围)


5

此模式提供了一个范围,您可以在其中定义在全局范围中不可见的助手功能:

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo 是匿名函数的本地函数,不能从外部引用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.