这种定义JS对象的方式有啥用吗?

Posted

技术标签:

【中文标题】这种定义JS对象的方式有啥用吗?【英文标题】:Does this way of defining JS objects have any purpose?这种定义JS对象的方式有什么用吗? 【发布时间】:2015-01-03 11:40:33 【问题描述】:

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

var MyObject = ;

(function (root) 

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

)(MyObject);

这样做有什么目的吗?是否等同于只做以下事情?

var MyObject = 

    myFunction : function (foo) 
        //do something
    ;

;

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

谢谢!

【问题讨论】:

在您的确切示例中没有区别。如果你扩展它,可能会有所不同,但也会有不同的方法发挥作用。 没什么区别,可以这么说,对象是作为引用的副本传递的,所以即使在 IIFE 内部定义 myFunction,它仍然可以在外部访问。 @adeneo 不是这个例子,myFunction 可以使用一些外部定义的变量,这些变量不能从外部访问。 See my answer What is this javascript pattern called and why is it used? 的可能重复项(不确定是否应该关闭)。另见JavaScript Namespace Declaration 或this one。 @Bergi 也类似于 What is the functional difference between these two different Module pattern syntaxes 【参考方案1】:

目的是限制闭包内函数的可访问性,以帮助防止其他脚本在其上执行代码。通过将其包裹在 closure 周围,您正在重新定义 closure 内所有代码的执行范围,并有效地创建了一个私有范围。有关详细信息,请参阅这篇文章:

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

【讨论】:

myFunction 在第二个版本中不在全局范围内。目的实际上是提供一个可以定义内部辅助功能的范围。 myFunction 在全局范围内,因为它是在全局对象 myObject 中定义的。在第二个版本中,应用程序中的任何其他代码都可以执行myFunction。在第一个版本中,只有闭包中的代码可以访问myFunction 不行,myFunction只能作为MyObject.myFunction()执行,和第一个版本一样。 @JonathanCrowe OP 的示例不是一个很好的示例,它暴露了模块内部的所有内容,所以是的,它可以在外部访问。请参阅我的答案以获取有用的模块案例 @JuanMendes 好点,OP 的例子不是很好地使用了模块模式【参考方案2】:

称为模块模式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

【讨论】:

这并没有真正回答这两种选择之间有什么区别的问题? @torazaburo OP 的例子不是一个很好的例子,我提供了一个真实的例子来说明何时使用模块模式。 这个ns.getNext: function () 不会编译。 如果我确定如何修复它,我会的。我认为有一些结构可以防止delete MyNameSpace.getNext @punund JS 没有编译器它有解释器:)【参考方案3】:

此模式提供了一个作用域,您可以在其中定义在全局作用域中不可见的辅助函数:

(function (root) 

    function doFoo()  ... ;

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

)(MyObject);

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

【讨论】:

【参考方案4】:

在您展示的特定情况下,在功能或可见性方面没有有意义的差异。

很可能最初的编码员采用这种方法作为一种模板,允许他定义私有变量,这些变量可用于定义 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);

归根结底,如果不需要,则无需采用此方法,但也无需更改它,因为它工作得很好,实际上有一些优势。

【讨论】:

【参考方案5】:

    第一个模式可以用作一个模块,它接受一个对象并返回该对象并进行一些修改。换句话说,您可以按如下方式定义此类模块。

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

    并像这样使用它:

    var obj = ;
    module(obj);
    

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


    在第一个模式中,您可以定义一个私有范围来存储您的私有内容,例如私有属性和方法。例如,考虑这个 sn-p:

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

    此模式可用于将方法或属性添加到所有类型的对象,例如数组、对象字面量、函数。

    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
    

在我看来,主要优势可能是第二个(创建私有范围)

【讨论】:

【参考方案6】:

优点:

    在私有范围内维护变量。

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

    性能提高。

我认为以上三个简单的点足以遵循这些规则。为了保持简单,它只需要编写内部函数。

【讨论】:

以上是关于这种定义JS对象的方式有啥用吗?的主要内容,如果未能解决你的问题,请参考以下文章

JS面向对象到底有啥用?面向对象里函数的写法和普通函数写法有啥区别?都有哪些优势?

asp.net中script有啥用?

js中函数的prototype.constructor是指向函数本身,它有啥用

phpcms联动菜单有啥用

setNeedsDisplay:NO 有啥用吗?

thinkphp5中的model模型层,有啥用