我听说全局变量不正确,应该使用哪种替代解决方案?


72

我到处都读到了全局变量不好的地方,应该使用替代方法。特别是在Javascript中,我应该选择哪种解决方案。

我想一个函数,当喂两个参数(function globalVariables(Variable,Value))看起来,如果在本地数组存在变量,如果它的值设置为Value,否则,VariableValue附加。如果在不带参数(function globalVariables())的情况下调用该函数,它将返回数组。也许如果仅使用一个参数(function globalVariables(Variable))触发函数,则它将返回Variable数组中的值。

你怎么看?我想听听您使用全局变量的替代解决方案和参数。

您将如何使用 globalVariables();

function append(){
    globalVariables("variable1","value1"); //globalVariables() would append variable1 to it's local array.
};

function retrieve(){
    var localVariable1 = globalVariables("variable1"); //globalVariables() would return "value1".
};

function retrieveAll(){
    var localVariable1 = globalVariables(); //globalVariables() would return the globalVariable()'s entire, local [persistently stored between calls] array.
};

function set(){
    globalVariables("variable1","value2"); //globalVariables() would set variable1 to "value2".
};

这是单例模式BTW吗?

在这种特定情况下,一个函数可能会在某个时间点设置一个变量,而后来另一个函数(可能是在用户提交表单时)将需要获取该变量。因此,第一个函数不能将变量作为参数传递给后一个函数,因为永远不会从第一个函数调用它。

谢谢,谢谢您的帮助!


11
我认为您不太了解为什么它们不好
Yacoby

1
@Yacoby:我想我[李的答案解释了很多],但是如果您感到不满意,请详细说明。在这种情况下我应该使用什么解决方案?
乔纳森·奥茨

我必须在这里同意@Yacoby。您在示例中有点重新定义了全局变量,因此回到“全局变量不好”的第一步。
Patrick

4
好的,谢谢Yacoby和Patrick,但是我如何在几个不能互相调用的函数之间设置一个持久变量,以致不能发送参数?
乔纳森·奥茨

1
我是否应该将变量值保存在Cookie中,例如作为替代方法?
乔纳森·奥茨

Answers:


139

不鼓励在javascript中使用全局变量的主要原因是,在javascript中,所有代码都共享一个全局命名空间,而javascript也隐含了全局变量,即。在本地范围内未明确声明的变量将自动添加到全局名称空间。过于依赖全局变量可能导致同一页面上的各个脚本之间发生冲突(请参阅Douglas Crockford的文章)。

减少全局变量的一种方法是使用YUI模块模式。基本思想是将所有代码包装在一个函数中,该函数返回一个对象,该对象包含需要在模块外部访问的函数,并将返回值分配给单个全局变量。

var FOO = (function() {
    var my_var = 10; //shared variable available only inside your module

    function bar() { // this function not available outside your module
        alert(my_var); // this function can access my_var
    }

    return {
        a_func: function() {
            alert(my_var); // this function can access my_var
        },
        b_func: function() {
            alert(my_var); // this function can also access my_var
        }
    };

})();

现在要在其他模块中使用功能,请使用FOO.a_func()。解决全局名称空间冲突的这种方法只需要更改的名称FOO


2
这很酷,它将完全重写我的代码,但是我已经遍了,太棒了,已经接受了答案。
Jonathon Oates 2010年

7
呵呵-最后两个括号是一个陷阱!没有它们,您需要参考FOO().a_func()。啊,现在开始变得有意义了!
ErichBSchulz

3
所以命名冲突是唯一的原因?还是还有内存使用方面的缺点?
bhavya_w 2014年

1
这种做法基本上不是针对库或其他可重用代码的吗?如果您正在为Web应用程序编写本机代码,而该代码通常不会在其他地方使用-并假设您的命名不差,或使用填充全局命名空间的库不好-它在您的Web应用程序中有什么用?只是以某种方式更清洁,而不必在每个模块中缩进所有代码?
bryc

1
有趣的是,在普通js中,函数调用位于匿名函数周围的括号之外。但是在我所看到的所有node.js模块模式中,函数调用都在这些括号内。这有什么不同吗?如果是这样,那是什么?
krb686 '16

43

语义学,我的孩子。语义学。

从一个全局变量开始:myApp = {}; 一切都应该在那。唯一的例外是您的AJAX库(有一些极端的例外,例如使用JSONP回调)。

myApp中的属性应该很少。您需要将应用程序属性保存在配置或设置等容器中。

myApp = {
    config:{
        prop:1
    },
    settings:{
        prop:2
    },
    widgets:{
        List: function(props){},
        Item: function(props){}
    }
}

然后,您可能在较低的模块,组件,单例和类构造函数(小部件)中拥有更多属性。

此设置为您带来了额外的好处,就是可以从任何其他位置访问任何属性,因为您可以通过myApp global获取它。但是,应尽可能使用“ this”,因为查找速度更快。只需直接设置该属性,就不用理会伪getter / setter东西。如果您确实需要一个吸气剂/吸气剂,则为该特定用途编写代码。

您的示例不起作用的原因是它太笼统,您似乎正在寻找在全球范围内工作的借口。

而且不要对私有变量聪明。它们也很糟糕:http//clubajax.org/javascript-private-variables-are-evil/


2
伟大的评论mwilcox。我用这篇文章说服了这里的其他人。
克里斯,

这是完成的方式。具有所有必须绝对可全局访问的所有变量的单个全局变量。+1
welbornio 2014年

有人知道有一种工具可以帮助将使用全局范围的大型应用程序转换为此类应用程序吗?
不知道

9

全球国家在几个方面造成问题。一种是代码重用。当您访问某些全局状态时,这意味着组件必须知道其环境(其自身外部)。您应尽可能避免这种情况,因为它会使组件变得不可预测。

假设我有一个对象可以访问您的globalVariables函数,并且想在另一个页面中使用它。我怎么知道定义globalVariables对象,甚至如何定义它?但是,如果您可以将信息传递给构造函数或作为函数的参数,则可以轻松确定该对象需要什么。

同样,当您访问或修改全局范围时,也可能会影响其他对象。这就是为什么像jquery这样的库在全局范围内仅使用单个名称的原因(最小可能)。它减少了与其他库冲突的可能性。换句话说,全局范围超出了您的控制范围,因此很危险。



3

无论选择哪种语言,使用全局变量通常都是一种不好的做法。在严格模式下甚至不(轻松)使用它们我强烈建议您它们。

考虑一下我发现的这段代码:

if (typeof session != 'undefined' && !data.cart.request_status)
  data.input_definitions.passengers =
    inflate_passenger(session, data.input_definitions.passengers);

我需要回头问一个低级的程序员 session变量是从哪里来的,因为没有代码搜索显示在哪里。

我原来从公司的另一个包中设置了全局变量。编写代码就像是在开玩笑:如果您需要解释一下,可能不是那么好。

使用ES6的解决方法:

如果在Node上使用importrequire将所需的内容带入词汇范围,请不要让人们在您不了解您的全局环境的情况下接触它。

import {Sesssion} from 'api-core';
const Session = require('api-core').session;

如果您在为浏览器提供代码的前端,则import除非使用Babel转换ES6代码,否则您将无法使用。

使用Gulp.js进行转码的示例:

// $ npm install --save-dev gulp-babel babel-preset-es2015

// gulpfile.js
const gulp  = require('gulp');
const babel = require('gulp-babel');

gulp.task('transpile', () => {
  return gulp.src('src/app.js')
    .pipe(babel({presets: ['es2015']}))
    .pipe(gulp.dest('dist'));
});

// $ gulp transpile

传统解决方法:

如果不使用ES6功能,则使用一堆全局变量的唯一解决方法是仅使用一个,并希望:

// scripts/app.js
var MyApp = {
  globals: {
    foo: "bar",
    fizz: "buzz"
  }
};

2

解决方案的问题在于,这只会使您的代码难以理解,同时仍然保留了全局变量的所有缺点。您链接到的页面涵盖了问题。您提出的解决方案真正解决的唯一问题是名称空间污染,但代价是无法像声明函数调用一样容易地看到声明了哪些全局变量。

解决方案是编写没有全局变量的代码。如果函数需要一个值,则将其作为参数传递。


2
...,如果对象需要上下文,则将其作为构造函数参数提供。
Stephen C

谢谢,但是在这种情况下,将值作为参数传递是行不通的。我需要各种变量,这些变量可以保持不变,并且可以通过几个函数访问。
乔纳森·奥茨

1

其他答案匿名功能,因为大多数解释文章提到,

匿名功能很难调试,维护,测试或重用。

这是正常功能的例子。它更容易阅读和理解。

/* global variable example */

    var a= 3, b= 6;
    
    function fwithglobal(){
    console.log(a, b); // 3 6 expected
    }
    
    fwithglobal(); // first call
    
    function swithglobal(){
    var a=9;
    console.log(a, b); // not 3 6 but 9 6
    }
    
    swithglobal(); // second call
    

/* global variable alternative(function parameter) */

    function altern(){
    var a= 3, b= 6; // var keyword needed
      f_func(a,b);
      s_func(a,b);
    }
    
    function f_func(n, m){
    console.log(n, m); // 3 6 expected
    }
    
    function s_func(n, m){
    var a=9;
    console.log(n, m); // 3 6 expected
    }
    
    altern(); // only once


1

var ASHIVA_HandsOffNHS = (function() {
    
    // VARIABLES

    var my_var = 10;


    // PRIVATE FUNCTIONS
    
    function bar() {
        window.alert(my_var + 5);
    }


   // PUBLIC OBJECT

    myObject = {};
    
    myObject['a_func'] = function() {
            my_var += 10;
            window.alert(my_var);
        };
        
    myObject['b_func'] = function() {
            my_var = 0;
            window.alert(my_var);
        };

    return myObject;

})();

ASHIVA_HandsOffNHS.a_func();
ASHIVA_HandsOffNHS.b_func();
ASHIVA_HandsOffNHS.a_func();


0

全局变量不好……如果不加管理!

全局变量的潜在风险与经常使用的对象准备就绪时所带来的乐趣和生产率一样高。

我不认为应该寻求一个替代方案。相反,我主张一个负责管理这些全局变量的对象,并且随着代码库/组件的成熟,将它们重构出来

我认为至关重要的是,当前答案中未提及的一件事是对DI和IoC容器的了解。这些解决了人们尝试使用全局变量解决的许多问题,但涵盖了普通全局变量无法解决的相关问题,例如对象生命周期。

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.