了解 javascript 全局命名空间和闭包

Posted

技术标签:

【中文标题】了解 javascript 全局命名空间和闭包【英文标题】:understanding the javascript global namespace and closures 【发布时间】:2012-04-04 03:53:44 【问题描述】:

我正在努力提高我对 javascript 中全局命名空间的理解,我对以下几点感到好奇:

    是否有一个“GOD”(即父级)对象,所有对象(因为除了基元之外的所有事物都是对象)都可以回答,如果有,该对象是否会是“窗口”?

    李>

    为什么在全局级别拥有变量/函数是个坏主意?

    如果在全局范围内拥有变量/函数真的是个坏主意,那么闭包是避免这种情况的最佳方法吗?示例:

    function parent()
        var x = 'some value';//this var would be considered global to all children functions but not in the true global namespace
        function child1()
            x.someMethod()
         
        function child2()
            x*something;
        
        function child3()
            x+=something;
            child2()
            child1()
        
        child3()
    
    parent()
    

【问题讨论】:

【参考方案1】:

    是否有一个(即父母)对象?

    Yes. 从技术上讲,它是所有这些原语的成员的全局对象;只是碰巧在浏览器中,window 对象全局对象。

    > window.String === String;
    true
    

    为什么在全局级别拥有变量/函数是个坏主意?

    因为如果您要添加大量第 3 方库/脚本,它们都共享同一个全局对象,因此存在名称冲突的可能性。这是所有使用$ 作为别名的库(jQuery、Prototype 等)的现实问题。

    如果在全局范围内拥有变量/函数真的是个坏主意,那么闭包是避免这种情况的最佳方法吗?

    x 不应被视为全球性的。它是通过在parent() 函数内声明子函数而形成的闭包的一部分。您的 sn-p 的 问题 部分是 parent() 是全局的;如果其他代码重新声明parent(),会发生什么?这样会更好:

    (function () 
    
    function parent()
        var x = 'some value';
        function child1()
            x.someMethod()
         
        function child2()
            x*something;
        
        function child3()
            x+=something;
            child2()
            child1()
        
        child3()
    
    parent()
    
    ());
    

    x 可以在子函数中访问这一事实还不错;您应该自己编写这些函数,所以您应该知道x 的存在。请记住,如果您在这些子函数中使用var 重新声明x,则不会影响parent() 中的x

【讨论】:

因为它对子函数是全局的,但对全局对象不是,这意味着所有子函数都可以访问它而不会作为参数过去 @codewombat:但您通常只会在另一个函数中声明一个函数因为您需要它来访问该范围内的变量。 既然你有一个自执行的父函数,如果任何代码进行 dom 操作,应该在 body 节点的底部调用它吗? 那么我如何从例如按钮点击中调用 child3?【参考方案2】:

    是的,在浏览器环境中,“上帝对象”是窗口。它通常被称为 global object,而不是 god object ;) 在非浏览器环境(例如 nodejs)中,全局对象可能使用除 window 之外的其他名称。

    如果将所有内容都作为全局变量,则可能会遇到名称冲突。还有encapsulation 的问题 - 换句话说,通过将变量只放入需要它的范围内,您的代码通常会更好。

    是的,这几乎是首选方法。你也可以使用IIFE的

【讨论】:

如果 IIFE 中存在修改 dom 的代码或者在 body 节点的底部调用你的 js 更好(以确保所有 html加载)? 全局对象这个名字很好,但是“上帝对象”听起来很酷:D 是的,如果 IIFE 中的代码在准备好之前尝试访问 DOM,则 IIFE 可能会导致错误。通过将 IIFE 放在页面底部或使用 jQuery 的 $( document ).ready(function() ... );【参考方案3】:

    据我所知,我会说是的,window 是父对象。但是,在 Iframe 中,您有自己的窗口对象,与可以通过 window.parent 访问的周围窗口不同

    拥有大量全局变量是个坏主意,因为可能存在名称冲突,因此很难检测到错误。一般来说,设计一些命名空间更安全(参见 $ 来自 jQuery 等)并模块化代码。

    小心,parent 是一个潜在存在的窗口字段。这被认为是对象,因此与 2) 中的观察相同。

【讨论】:

那么有没有办法“防错”父函数,因为它是唯一的全局对象? 模块模式例如:adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth【参考方案4】:

如果您需要将变量放入全局命名空间,并且您可能会在某个时候创建​​单个对象变量并将您的其他变量作为属性或方法添加到其中。给对象起一个其他人不太可能使用的名称(诚然,这是出现冲突问题的地方,但可以通过谨慎的标准化命名来缓解)。

例如而不是:

var thing1 = 'table';
var anotherthing = 'chair';
var mypet = 'dog';
var count = 4;
var show_something: function( _txt )  return _txt.trim(); ;

这样做:

var cmjaimet_obj = 
  thing1: 'table',
  anotherthing: 'chair',
  mypet: 'dog',
  count: 4,
  show_something: function( _txt )  return _txt.trim(); 
;

然后将它们称为属性:

例如而不是:

count += 2;
anotherthing = 'sofa';
console.log( show_something( 'Thing: ' + anotherthing ) );

这样做:

cmjaimet_obj.count += 2;
cmjaimet_obj.anotherthing = 'sofa';
console.log( cmjaimet_obj.show_something( 'Thing: ' + cmjaimet_obj.anotherthing ) );

【讨论】:

以上是关于了解 javascript 全局命名空间和闭包的主要内容,如果未能解决你的问题,请参考以下文章

你真的理解JavaScript的作用域吗

python globals()和locals(),global和nonlocal,以及闭包

python函数之进阶 函数嵌套,命名空间,闭包

Python之命名空间闭包装饰器

python开发函数进阶:命名空间,作用域,函数的本质,闭包,内置方法(globales)

命名空间和模块