“泄漏”到全球范围是什么意思



不久前,我提供了一个JavaScript设计模式(模块模式 - 见下文),这是我从John Resig示例中得到的,作为解决某人问题的一部分,我收到了以下评论:

"...这种模式有点过分了 工程设计,不是那么好。还 泄漏到全球范围内。和你的 不向异步加载器开放自己。 但它比临时更好 编码!

所以。。。

如果"泄漏"到全局范围意味着"您的对象被附加到浏览器窗口(对象)"......然后所有内容都已经附加(全局):

这"泄漏"到全球范围:

window.jQuery

。只需调用:window.jQuery,它解析为函数();

这"泄漏"到全球范围:

function HelloWorld() { alert(‘Howdy’); }

。只需拨打:window.HelloWorld(),你就会得到"你好"。

这"泄漏"到全球范围:

var myVariable = 10;

。只需致电:window.myVariable,您将获得 10

如果评论者是正确的,那么上述所有"泄漏"到全局范围内。 因此,就个人而言,我看不到不"泄漏"到全局范围的方法,因为即使您的表单控件也存在在那里(以及)。

因此,这是我的问题...

  • "泄漏"是什么意思全球范围?
  • 为什么这很糟糕?
  • 你如何避免它?
  • 想要创建持久化时自定义对象,为什么模块模式(下图)不好?
  • 设计模式让您封装复杂的逻辑,就是封装突然变坏只是因为我们是用JavaScript编写
  • 或。。。这个评论者是不是错了?

这是我上面提到的模块模式:

<script type="text/javascript">
    var myNamespace = (function($) {
        var publicInstances = {};
        // ***********************
        // myObject
        publicInstances.myObject = myObject;
        function myObject() {
            /// <summary>A pointer to this</summary>
            var self = this;
            this.someProperty = new String();
            this.initialize = function() {
                /// your code here
            }
            this.someMethod = function() {
                /// your code here
            }
            self.initialize();
        }
        return publicInstances;
    })(jQuery);

    jQuery(document).ready(function() {
        // Use would look like
        var myInstance = new myNamespace.myObject();
    });
</script>


已更新
我对下面的答案感到满意,并感谢大家抽出宝贵时间发表评论。

回顾以下答案:
当局部作用域中使用的某些内容无意中可供全局作用域使用时(例如窗口对象),就会发生"泄漏"到全局作用域。 这很糟糕,因为它会打开页面以显示潜在的命名冲突,这可能会导致变量解析为意外的值或类型。

故意使变量全局不被视为"泄漏"。 但是,需要正确命名对象间距,以减少上述命名冲突的可能性。

您无法避免全局范围的变量,但可以通过使用 RequireJSCurl 等插件中提供的异步加载器和定义模块来降低上述风险。

"

泄漏"到全局范围是指本地范围中使用的内容无意中可用于全局范围。 这意味着分配给当前作用域中尚未定义的变量:

function myFunction() {
    a=1;
}
myFunction();
alert(a);
//-> 1

这很糟糕,因为可能存在命名冲突,从而导致变量的值/类型与预期不同。 当您忘记对for语句中使用的变量使用 var 关键字时,它还可能导致旧版 Internet Explorer 中的错误。

我不会故意将变量全局化归类为"泄漏",因为它更像是您将它"倾注"到全局范围内。 但是,这仍然经常被一些人认为是不好的做法(尽管我认为这有点戏剧性),因为仍然存在与window对象的当前属性或其他脚本和库设置的变量的潜在命名冲突。

[[短篇小说]]

永远不要创建全局变量并使用像requirejs或curl这样的异步模块加载器

[

[长话短说]]

该评论的结构很差。

模块系统没有任何问题。我抱怨使用全局变量。(我仍然认为完整的通用模块模式很臃肿)。

你是否应该避免所有全局变量是一个不同的问题,我认为是一个风格问题。您可以使用异步加载程序来传递模块,也可以使用 window 来传递模块。

  • "泄漏"到全球范围是什么意思?

我的意思是创建全局变量。尽量减少全局变量的使用是一种模式。在函数式编程中,全局变量可能为零,但这与使用全局模块的模式不同。

  • 为什么这很糟糕?

全局具有任何状态都可能导致该状态损坏。

  • 你如何避免它?

你不能。不过,您可以最小化全局变量的数量。为了避免完全具有全局状态,您可以使用异步加载器。这些为您定义了一些全局变量,然后您可以使用这些变量。

  • 当想要创建持久自定义对象时,为什么模块模式(如下)不好?

模块模式没有任何问题。问题是全局存储模块。问题是具有全局命名空间。

  • 设计模式可以让你封装复杂的逻辑,封装突然变得糟糕仅仅是因为我们用JavaScript编写吗?

现在我已经清除了评论的意图,这个问题并不真正相关

  • 或。。。这个评论者是不是错了?

该评论充其量措辞不佳。我反对全局命名空间而不是模块,但没有正确说明这一点。

另一种方法是使用异步加载程序和定义模块。这些可以缩小到两个全局变量。 definerequire .

require = function(moduleName, callback)

这将得到一个模块,然后将其返回给您。

define = function(obj)

这将定义一个模块。

这里的概念是你多文件代码,如下所示:

// main.js
require([
  "foo.js",
  "bar.js",
  ...,
], function(foo, bar, ...) {
   // do stuff
}); 
//foo.js
(function() {
    var namespace = modulePatternCode;
    ...
    define(namespace):
})();
//bar.js 
(function() {
    var namespace = modulePatternCode;
    ...
    define(namespace):
})();

您的模块只会"泄漏"它的命名空间持有者,因此这是可以接受的。

使用 RequireJS 的加载器示例:

在 utils.js 中定义实用程序模块:

define(function () {
    return {
        each: function (iterable, callback) {
            // ...
        },
        map: function (iterable, mapper) {
            // ...
        }
    };
});

在另一个模块中使用上面的模块,比如 math.js:

define([ "utils" ], function (utils) {
    return {
        sum: function (numbers) {
            var sum = 0;
            utils.each(numbers, function (n) {
                sum += n;
            });
            return sum;
        },
        average: function (numbers) {
            return this.sum(numbers) / numbers.length;
        }
    };
});

你可以在另一个文件中使用math.js,比如main.js:

console.log("About to add 1-3");
require([ "math" ], function (math) {
    console.log(math.sum([ 1, 2, 3 ]));
});

您仍然可以拥有命名空间,并且仍然可以在模块中保持温暖和舒适:

命名空间.js:

define([ "foo", "bar", "moo" ] function (foo, bar, moo) {
    return {
        foo: foo,
        bar: bar,
        moo: moo
    };
});

然后,其余模块可以在定义期间使用此命名空间:

define([ "namespace" ], function (namespace) {
    namespace.foo(42);
});

或者在运行时,在其他模块中:

define(function () {
    return {
        initialize: function () {
            require([ "namespace" ], function (namespace) {
                namespace.foo(42);
            });
        }
    };
});

在上面的用法中,只有definerequire是全局的。当然,这些只是说明性的例子,因为在 RequireJS 中定义/使用模块有许多不同的风格。

相关内容

  • 没有找到相关文章

最新更新