5

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

考虑到这个功能:

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

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

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

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

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

需要澄清的另一件事是,您不能假设 foo 将被“new”调用。(如新的 foo())

更新:

我的意思是,我们有没有可能使用任何hacky技巧来绕过浏览器的限制,以接近我们想要的结果?比如,几年前,我们发明了 JSONP 用于跨域、长拉用于消息推送等?

4

2 回答 2

4

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

一个执行上下文实际上由三件事组成:

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

  • the 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它保存当前的标识符绑定(与相比,在执行过程中VariableEnvironment可能会发生变化)。这可能不是您想要的,但可以通过以下with语句轻松修改:

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

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

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

从学术的角度思考这些技巧(理解语言的工作原理)很好,但你永远不应该在生产中使用它们。无论您想到什么需要这种技术,都有更好的解决方案。

于 2013-10-02T00:05:57.177 回答
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,从数字和字符串到使用用户定义的构造函数创建的对象。

于 2013-10-02T01:14:23.287 回答