【问题标题】:Changing JavaScript's global object?改变 JavaScript 的全局对象?
【发布时间】:2011-02-19 18:13:11
【问题描述】:

有没有办法改变 JavaScript 中的 root 对象?

例如,在浏览器中,root 对象是“window”。所以

X = 5;
console.log(Y);

等同于:

window.X = 5;
console.log(window.Y);

我现在想做的是改变这个 root 对象,所以当我执行以下操作时:

X = 6;

我需要这个的原因:

Node.js 应用程序中,程序的每个部分都可以访问全局对象。这是一个大问题,因为 Node.js 网络服务器执行的每个脚本都可以向其中添加新变量。它们将一直存在,直到网络服务器重新启动。我想通过更改全局对象来避免这种情况。

更新

我已经测试了以下代码并得到了一个非常有趣的结果。 您对以下代码有何期待?

var X = {A:"a",B:"b"};

with(X){
    A = 5;
    C = 7;
}
for(a in X){
    console.log(a+" is "+X[a]);
}

/* 
Expected Console Output:
A is 5
B is b
C is 7

Real Console Output:
A is 5;
B is b;

*/

有没有办法按我的预期获得输出?

更新

我现在已经用下面的代码测试了模块系统。

//program.js
var t = require("./module.js");
t.Test();

console.log(A);

//module.js
A = 5;
exports.Test = function(){
    console.log("hello world!");
}

输出是:

hello world!
5

这告诉我,module.js 中定义的变量“A”已添加到 program.js 的全局对象中。该模块也不能解决我的问题。

【问题讨论】:

  • 专有名词不是ROOT而是Global,在浏览器中window实现Global
  • 如果你使用“使用严格”;在您的代码之上,您的示例是不可能的。我来到这里是因为我想做同样的事情,实际上我想用代理对象替换全局对象。这工作正常,但失去了它的全球范围。我认为这与对旧对象的引用有关。

标签: javascript


【解决方案1】:

with语句,但严格模式下不推荐和禁止使用。

最好明确引用保存对象的变量。

针对更新后的问题:

with 将向上搜索作用域链,直到找到具有匹配属性的对象或到达window。在对象上定义新属性是没有好处的。

var X = { A: 5, B: 8, C: 7};
with(X){
    console.log(A, B, C);
}

【讨论】:

  • 谢谢!我完全忘记了“with”,这正是我要寻找的。禁止与否无关紧要。它必须只能在 V8 引擎中运行,并且似乎可以工作。
  • 好吧,我太快了……它不像我预期的那样工作。我更新了我的帖子。
  • @Flash:我确定我不像@David 那样熟悉with 语句,但似乎withX 中寻找属性,如果不是找到后,它会遍历作用域链。如果没有找到,它会创建全局变量。它似乎使用with,您需要先定义这些属性。 var X = {A:null,B:null,C:null,D:"D"};
  • 是的,我发现了同样的事情。但那是一个问题......我不想预先定义这些值。在我的情况下那是没有意义的。
  • @Flash:我刚刚用另一种可能性更新了我的答案。有很多方法可以解决这个问题。最简单的方法是在添加属性时简单地引用对象(正如大卫在他的回答中推荐的那样)
【解决方案2】:

如果你在谈论变量,JavaScript 有函数作用域。

X = 5;  // global variable

console.log( window.X );  // 5

(function() {
   var X = 6;  // declare a local variable by using the "var" keyword

   console.log( X );  // 6
})();

console.log( window.X );  // 5

否则,您可以创建一个对象,并为其添加属性。

X = 5; 

console.log( window.X );  // 5

var obj = {};

obj.X = 6;

console.log( obj.X ); // 6

console.log( window.X );  // 5

编辑:添加另一个可能可以使用的解决方案。

您可以调用匿名函数,但将函数的上下文设置为您的X 对象。那么函数中的this会引用X

var X = {};
(function(){
    this.A = 5;
    this.B = 8;
    this.C = 7;
}).call(X);
for(a in X){
    console.log(a+" is "+X[a]);
}

.call() 方法(以及.apply() 方法)允许您显式设置 thisArgof a calling context. The first argument you pass will be howthis` 在调用的上下文中定义。

或者只是将X 作为参数传入。

var X = {};
(function(X){
    X.A = 5;
    X.B = 8;
    X.C = 7;
})(X);
for(a in X){
    console.log(a+" is "+X[a]);
}

虽然最简单的是简单地引用它(正如我在上面的回答中指出的那样)。

var X = {};
X.A = 5;
X.B = 8;
X.C = 7;
for(a in X){
    console.log(a+" is "+X[a]);
}

或使用 模块 模式:

   /****** I'm guessing at the use of "global" here ********/
global.myNamespace = (function(global,undefined) {

    // define the object to be returned
    var X = {};

    //  define private local variables
    var a_local = 'some value';
    var another_local = 'some other value';

    // define private functions
    function myFunc() {
        // do something with local variables
    }

    // give the return object public members
    X.someProperty = 'some value';
    X.anotherProperty = 'another value';
    X.publicFunc = function() {
        //do something with the local variables
        //    or public properties
    };
    X.anotherFunc = function() {
        //do something with the local variables
        //    or public properties
    };
    // return the object
    return X;

})(global);

console.log(myNamespace);

【讨论】:

  • @Down-voter:请解释一下。我的回答哪一部分不正确?
  • @David:感谢您的回复。你能解释一下那是哪一点吗?我认为 OP 不熟悉 javascript,因此当 OP 执行 X = 5 并看到它向 window 添加属性时,我猜测正在假设这是通常添加属性的方式。根据具体需要,这些解决方案中的任何一个都可能对 IMO 有所帮助。
  • + 1 以获得非常好的答案。但大卫的答案最接近我的预期解决方案。
  • BTW 解释为什么我想像我发布的那样做:在 Node.js 应用程序中,程序的每个部分都可以访问 Global 对象。这是一个大问题,因为 Node.js Web 服务器执行的每个脚本都可以向其中添加新变量。它们将一直存在,直到 Web 服务器重新启动。我想通过更改全局对象来避免这种情况。
  • @FlashFan:我明白你的意思,但如果你不小心,with 可能(如你现在所知)会产生意想不到的结果。我可能是错的,但听起来你正在为你的代码寻找一个新的变量范围。在 javascript 中实现这一点的唯一方法是在函数中。没有块范围(除了非标准的 let 关键字,我不相信 V8 实现)。我会摆脱with(如果你还没有)并使用一个足够独特的全局命名空间,它不会被覆盖,然后在立即调用的函数表达式中进行代码操作。
【解决方案3】:

我在 EcmaScript 6 中找到了调整with(context) { ... } 的方法,这样我们分配的任何新变量都将进入context 对象,而不是全局/window 对象。

感谢这篇文章 Metaprogramming in ES6: Part 3 - Proxies 向我介绍了 ES6 代理功能。

在代理中:

  • 我们重写 has 以返回 true,因此我们的上下文似乎具有所有属性,并且当我们设置任何变量时,它都会转到对象。
  • 我们重写 get 以从我们的上下文中获取属性,或者如果它不存在,我们从 up 对象(默认为全局 window)获取属性。

我知道with 不受欢迎,但这种技术可以创建可变的可扩展模块,我们可以方便地访问成员作为foo 而不是Module.foo,我不认为它不安全或模棱两可。

function scope(x, up=window) {
    return new Proxy(x, {
        has: (o, k) => true,
        get: (o, k) => k in o ? o[k] : up[k]
    });
}

var Test = {};

with (scope(Test)) {
    x = 1;
    y = 2;
    add_x = function(y) {
        console.log(x + y);
    }
}

Test.add_x(10);

with (scope(Test)) {
    x = 3;
    add_y = function(x) {
        console.log(x + y);
    }
}

Test.add_x(20);

Test.y = 5;

Test.add_y(30);

【讨论】:

  • 到目前为止我发现的唯一问题是从模块内部调用setTimeout,这是window 上的一个方法。它用错误的this 调用它。最简单的解决方法就是将其调用为window.setTimeout,或者我们可以修改scope 代理以通过一组方法。我尝试了其他一些无效的解决方法。
猜你喜欢
  • 2014-10-14
  • 2019-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-29
  • 1970-01-01
  • 2013-05-06
  • 2012-10-03
相关资源
最近更新 更多