美文网首页
Javascript 的全局变量和namespace

Javascript 的全局变量和namespace

作者: Miss_Ella | 来源:发表于2020-11-23 10:05 被阅读0次

全局变量的使用应该引起注意,只有系统范围相关的对象才声明为全局变量, 并且避免命名冲突。 比较好的减少全局变量的策略是创建少量的全局对象,这些对象将用作基础模块和子系统的namespace(命名空间)。下面将介绍几种namespace的创建方法。

静态namespace

静态namespace是hard coded, 可以将一个namespace赋值给另一个namespace, 两个namespace将引用相同的对象。

1.直接赋值

直接赋值是最基本的方法,这种方法比较费时费力,如果想重新命名,需要花费一番功夫。 但是这种方法却很安全和明确。方法如下:

var myApp = {}
 
myApp.id = 0;
 
myApp.next = function() {
    return myApp.id++;  
}
 
myApp.reset = function() {
    myApp.id = 0;   
}
 
window.console && console.log(
    myApp.next(),
    myApp.next(),
    myApp.reset(),
    myApp.next()
); //0, 1, undefined, 0 

通过使用它来引用同级属性,可以使以后的维护更容易,但是这有点冒险,因为没有什么可以阻止重新分配name space的功能:

var myApp = {}
 
myApp.id = 0;
 
myApp.next = function() {
    return this.id++;   
}
 
myApp.reset = function() {
    this.id = 0;    
}
 
myApp.next(); //0
myApp.next(); //1
var getNextId = myApp.next;
getNextId(); //NaN whoops!

2.使用object literal表示法

如果我们只使用一次命名空间,切换命名空间就比较容易,但有时候命名空间的值还是会出乎意料。我们可以假设定义在命名空间构造函数里面的对象的值不会被重新赋值。

var myApp = {
 
    id: 0,
 
    next: function() {
        return this.id++;   
    },
 
    reset: function() {
        this.id = 0;    
    }
}
window.console && console.log(
    myApp.next(),
    myApp.next(),
    myApp.reset(),
    myApp.next()
) //0, 1, undefined, 0

3.模块模式

通过函数包装器(通常是自调用)将逻辑与全局范围隔离开,该函数包装器返回表示模块公共接口的对象。 通过立即调用该函数并将结果分配给名称空间变量,我们将模块的API锁定在名称空间中。 此外,未包含在返回值中的所有变量将永远保持私有状态,仅对引用它们的公共函数可见。

var myApp = (function() {
 
    var id= 0;
 
    return {
        next: function() {
            return id++;    
        },
 
        reset: function() {
            id = 0;     
        }
    };  
})();   
 
window.console && console.log(
    myApp.next(),
    myApp.next(),
    myApp.reset(),
    myApp.next()
) //0, 1, undefined, 0  

像上面的object literal 示例一样,可以很容易地切换接收名称空间,还有其他优点:object literal 是固定的–它全部与属性分配有关,没有空间支持逻辑, 此外,object literal 必须初始化所有属性,并且属性值不能轻易相互引用(因此,内部闭包是不可能的)。 模块模式不受这些限制,并为我们带来了私有性的额外好处。

动态namespace

我们也可以称此部分为namespace注入。 namespace由在函数包装器内直接引用的代理表示–这意味着我们不再需要捆绑返回值来分配给名称空间。 这使名称空间定义更加灵活,并使在独立的名称空间(甚至在全局上下文中)存在模块的多个独立实例变得非常容易。 动态命名空间支持模块模式的所有功能,并具有直观易读的附加优势。

4.提供name space参数

在这里,我们只是将namespace作为参数传递给自调用函数。 id变量是私有的,因为它没有分配给context。

var myApp = {};
(function(context) { 
    var id = 0;
 
    context.next = function() {
        return id++;    
    };
 
    context.reset = function() {
        id = 0;     
    }
})(myApp);  
 
window.console && console.log(
    myApp.next(),
    myApp.next(),
    myApp.reset(),
    myApp.next()
) //0, 1, undefined, 0  

我们甚至可以将context设置为全局对象(只需更改一个单词!)。 对于图书馆供应商来说,这是一笔巨大的财富–他们可以将其功能包装在一个自调用功能中,然后由用户决定是否应该是全局的(John Resig在编写JQuery时就是这个概念的早期采用者)

var myApp = {};
(function(context) { 
    var id = 0;
 
    context.next = function() {
        return id++;    
    };
 
    context.reset = function() {
        id = 0;     
    }
})(this);   
 
window.console && console.log(
    next(),
    next(),
    reset(),
    next()
) //0, 1, undefined, 0  

5.将this用作namespace代理

詹姆斯·爱德华兹(James Edwards)发表的一篇文章。 “我最喜欢的JavaScript设计模式”显然被许多人误解了,他们认为他最好还是采用模块模式。
模式的优点在于它仅使用设计的语言,仅此而已。 而且,由于名称空间是通过this关键字注入的(在给定的执行上下文中是静态的),因此不能修改它。

var myApp = {};
(function() {
    var id = 0;
 
    this.next = function() {
        return id++;    
    };
 
    this.reset = function() {
        id = 0;     
    }
}).apply(myApp);    
 
window.console && console.log(
    myApp.next(),
    myApp.next(),
    myApp.reset(),
    myApp.next()
); //0, 1, undefined, 0

更好的是,apply(和call)API提供了上下文和参数的自然分隔–因此,将附加参数传递给模块创建者是非常干净的。 下面的示例演示了这一点,还显示了如何在多个名称空间上独立运行模块:

var subsys1 = {}, subsys2 = {};
var nextIdMod = function(startId) {
    var id = startId || 0;
 
    this.next = function() {
        return id++;    
    };
 
    this.reset = function() {
        id = 0;     
    }
};
 
nextIdMod.call(subsys1);    
nextIdMod.call(subsys2,1000);   
 
window.console && console.log(
    subsys1.next(),
    subsys1.next(),
    subsys2.next(),
    subsys1.reset(),
    subsys2.next(),
    subsys1.next()
) //0, 1, 1000, undefined, 1001, 0

当然,如果我们想要一个全局id生成器,那么轻而易举……

nextIdMod();    
 
window.console && console.log(
    next(),
    next(),
    reset(),
    next()
) //0, 1, undefined, 0

我们一直以id生成器工具为例,无法充分发挥这种模式的潜力。 通过包装整个库并使用this关键字作为namespace的代表,我们使用户可以轻松地在他们选择的任何context(包括全局context)中运行该库

//library code
var protoQueryMooJo = function() {  
    //everything
}
 
//user code
var thirdParty = {};
protoQueryMooJo.apply(thirdParty);

其他注意事项

尽量避免嵌套名称空间。 它们更难维护(对于人和计算机而言),并且会使代码可读性变差。 正如Peter Michaux指出的那样,深层嵌套的名称空间可能是怀旧Java开发人员试图重新创建他们熟悉和喜爱的冗长包链的遗产。

可以跨.js文件跨越单个namespace(尽管只能通过名称空间注入或每个变量的直接分配),但是应谨慎使用依赖项。 此外,将命名空间绑定到文件可以帮助读者更轻松地浏览代码行。

由于JavaScript没有正式的namespace构造,因此存在大量的本地解决方案。 这篇文章仅详细介绍了其中一些。

Further reading

James Edwards: My Favorite JavaScript Design Pattern
Peter Michaux: JavaScript Namespacing

相关文章

  • Javascript 的全局变量和namespace

    全局变量的使用应该引起注意,只有系统范围相关的对象才声明为全局变量, 并且避免命名冲突。 比较好的减少全局变量的策...

  • JavaScript命名空间

    JavaScript由于不像其他OO语言那样有namespace,所有全局变量(不管是有意还是无意)都会被添加到w...

  • JavaScript Namespace

    https://stackoverflow.com/questions/881515/how-do-i-decla...

  • 作用域_全局变量

    JavaScript 全局变量 变量在函数外定义,即为全局变量。 全局变量有 全局作用域: 网页中所有脚本和函数都...

  • JavaScript函数闭包

    JavaScript闭包 JavaScript 变量可以是局部变量或全局变量。私有变量可以用到闭包。 全局变量1....

  • 什么是js闭包?

    熟悉javascript的人应该都知道,在javascript中,有两种变量存在,即局部变量和全局变量。局部变量和...

  • Javascript 变量、函数的声明

    javascript变量 全局变量和局部变量按照变量的作用域来区分,和大多数编程语言类似,javascript变量...

  • JS作用域链

    1、JavaScript的全局变量和局部变量 (1) 全局变量 js的全局变量也可以看做window对象的属性。如...

  • TypeScript16(namespace命名空间)

    我们在工作中无法避免全局变量造成的污染,TypeScript提供了namespace 避免这个问题出现 内部模块,...

  • JavaScript模块化基础

    三种常见的JavaScript组织代码的方式: IIFE 保留全局变量 由于JavaScript采用的是函数级作用...

网友评论

      本文标题:Javascript 的全局变量和namespace

      本文链接:https://www.haomeiwen.com/subject/idjmiktx.html