我听说全局变量不好,我应该使用啥替代解决方案?

Posted

技术标签:

【中文标题】我听说全局变量不好,我应该使用啥替代解决方案?【英文标题】:I've Heard Global Variables Are Bad, What Alternative Solution Should I Use?我听说全局变量不好,我应该使用什么替代解决方案? 【发布时间】:2011-02-06 11:38:09 【问题描述】:

我已经阅读了所有应该使用global variables are bad 和替代品的地方。特别是在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".
;

这是Singleton Pattern BTW?

在这个特定的场景中,一个函数可能会在某个时间点设置一个变量,而很久以后另一个函数,也许当用户提交一个表单时,将需要获取该变量。因此,第一个函数不能将变量作为参数传递给后面的函数,因为它永远不会被第一个函数调用。

谢谢你,感谢你的帮助!

【问题讨论】:

【参考方案1】:

你真的不想这样做。 至于为什么看到例如置顶帖:What is the most EVIL code you have ever seen in a production enterprise environment?

附带说明一下,您始终可以执行“全局”代码,而不会在全局代码中乱扔垃圾:

(function() 
  var notaglobal = 1;
  alert(notaglobal);
)();
//notaglobal is not defined in this scope

【讨论】:

【参考方案2】:

在 javascript 中不鼓励使用全局变量的主要原因是,在 javascript 中,所有代码共享一个全局命名空间,而且 javascript 也隐含了全局变量,即。未在本地范围内显式声明的变量会自动添加到全局命名空间中。过多依赖全局变量可能会导致同一页面上的不同脚本之间发生冲突(阅读Douglas Crockford's articles)。

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

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的名称即可。

【讨论】:

crockford 的文章描述了 z33m 的解决方案yuiblog.com/blog/2006/06/01/global-domination【参考方案3】:

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();

【讨论】:

【参考方案4】:

全局变量不好......如果不管理!

全局变量的潜在风险与准备好使用常用对象的乐趣和生产力收益一样高。

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

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

【讨论】:

【参考方案5】:

其他答案大多用匿名函数解释为this文章提及,

匿名函数难以调试、维护、测试或重用。

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

/* 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

【讨论】:

【参考方案6】:

无论选择何种语言,使用全局变量通常都是一种不好的做法。在strict mode 时,它们甚至(很容易)都不允许使用,我强烈推荐。

考虑一下我找到的这段代码:

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"
  
;

【讨论】:

【参考方案7】:

语义我的孩子。语义。

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

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

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

那么你可能在较低的模块、组件、单例和类构造函数(小部件)中有更多的属性。

此设置为您提供了额外的好处,即您可以从任何其他位置访问任何属性,因为您可以使用 myApp 全局获取它。但是,您应该尽可能使用“this”,因为查找速度更快。只需直接设置属性,不要打扰伪 getter/setter 的东西。如果您确实需要 getter/setter,请针对特定用途对其进行编码。

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

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

【讨论】:

【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:

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

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

【讨论】:

...如果对象需要上下文,则将其作为构造函数参数提供。 谢谢,但在这种情况下,将值作为参数传递是行不通的。我需要各种保持持久性并且可供多个函数访问的变量。

以上是关于我听说全局变量不好,我应该使用啥替代解决方案?的主要内容,如果未能解决你的问题,请参考以下文章

ArrayList 有啥问题?

为啥使用全局变量类(单例)是不好的做法? [复制]

Kitty+MTPuTTY设置全局字体(xshell的替代方案)

老是听说鼠标用的多容易出现双击,有没有啥好的解决办法?

正确的全局变量定义/声明

现有 Model.Document 上 .save() 的有效替代方案