如何在JavaScript中动态地向当前执行上下文添加属性?

时间:2013-10-01 22:46:13

标签: javascript

我正在阅读JavaScript语言的教科书。在我研究闭幕主题时,我提出了以下问题。

考虑这个功能:

function foo() {
    extractPropsToCurrentContext({'prop1' : 'hello', 'prop2' : 123});
}

我想要上面代码的结果,等于

function foo() {
    var prop1 = 'hello';
    var prop2 = 123;
}

所以,我的问题是,如何实现函数extractPropsToCurrentContext(/ * Object * /)?

为了澄清,我想将对象的这些属性提取到执行上下文中,而不是在“this”指针下。 (因此,那些提取的道具应该在该函数内私有。)

要澄清的另一件事是,不能假设将使用'new'调用foo。 (比如新的foo())

更新

我的意思是,我们是否有可能使用任何 hacky技巧来绕过浏览器的限制,让更接近到我们想要的结果?就像多年前一样,我们发明了跨域的JSONP,长时间推送消息等等?

2 个答案:

答案 0 :(得分:4)

  

我想将对象的这些属性提取到执行上下文

execution context实际上包含三件事:

  • ThisBinding,您说您不想改变或扩展。

  • VariableEnvironment,它包含声明的变量和函数。这就是你的平等代码会改变的。您可以使用以下hack更改它:

    function getVariableDeclaration(obj) {
        return "var " + Object.keys(obj).map(function(name) {
            return name + " = " + JSON.stringify(obj[name]);
        }).join(",\n    ") + ";";
    }
    function foo() {
        eval(getVariableDeclaration({'prop1' : 'hello', 'prop2' : 123}));
        debugger;
    }
    foo();
    

    但是,这仅适用于非严格模式。有关详细信息,请查看§10.4.2。此外,此hack目前仅限于JSON可序列化值,如果您需要分配任意值,它将变得更加丑陋 - 必须在您想要更改的环境中使用eval

  • LexicalEnvironment,它包含当前标识符绑定(并且可能在执行in contrast to the VariableEnvironment期间更改)。这不是您可能想要的,但可以通过with声明轻松修改:

    function foo() {
        with ({'prop1' : 'hello', 'prop2' : 123}) {
            debugger;
        }
    }
    foo();
    

    同样,它在严格模式下不起作用。请参阅§12.10§10.2.2.3了解其工作原理并隐藏其他绑定。

正如您所看到的,严格模式禁止对(非全局)执行上下文进行任何更改,因为这会使绑定成为非静态和不可优化的。大多数代码也变得难以理解,因此通常认为这是一种不好的做法。

从学术观点(理解语言的运作方式)思考这些黑客是很好的,但你不应该在生产中使用它们。无论你想到什么需要这样的技术,都有更好的解决方案。

答案 1 :(得分:1)

只是想分享我的尴尬解决方案。我们的想法是从父函数的其余部分构造新函数并调用它 在eval

function cc(fnname, ctxname) {
    'use strict';
    var __evalfn = function () {
        'use strict';
        var __fn = __fnname,
            __ctx = __ctxname,
            __fnStr = __fn.toString(),
            __cccall = 'return eval(cc("'+__sfnname+'", "'+__sctxname+'"));',
            __cccallPos = __fnStr.indexOf(__cccall),
            __newFn = '(function () { var ',
            __decl = [], __restBody;
        if (__cccallPos === -1) { throw new Error("Can't find cc call"); }
        __restBody = __fnStr.slice(__cccallPos+__cccall.length);
        for (var __k in __ctx) {
            __decl.push(__k + '=' + __sctxname + '['+JSON.stringify(__k)+']');
        }
        __newFn += __decl.join(',') + ';';
        __newFn += __restBody;
        __newFn += ')()';
        return eval(__newFn);
    },
    __evalStr = __evalfn.toString()
                      .replace(/__fnname/g, fnname)
                      .replace(/__sfnname/g, JSON.stringify(fnname))
                      .replace(/__ctxname/g, ctxname)
                      .replace(/__sctxname/g, JSON.stringify(ctxname));
    return '('+__evalStr+')()';
}

var fn = function () {
    var a=10, ctx = {'b':10, 'c':'hello'};
    return eval(cc("fn", "ctx"));
    console.log(a,b,c);
    return a+b;
};

> fn();
> 10 10 "hello"
> <- 20

要求您将return eval(cc("fn", "ctx"));放入您的函数中。 fn是变量保持函数的名称,您可以在其中使用ccctx是包含新绑定的对象的名称。 cc 的一个优点是它允许ctx中的任何值,从数字和字符串到使用用户定义的构造函数创建的对象。