new Function和with

今天在看一篇将介绍前端的文章时,在讲到js 沙箱隔离的时候提到了 with() + new Function(code) + Proxy 的方式,with 和 new Function 我之前很少(没有)用到,没想到还有这作用,所以这篇来总结和记录下这两者的区别:

with

with语句用于 扩展一个语句的作用域链。语法如下:

with (expression) {
    statement
}

JavaScript查找某个未使用命名空间的变量时,会通过作用域链来查找,作用域链是跟执行代码的context或者包含这个变量的函数有关。'with'语句将某个对象添加到作用域链的顶部,如果在statement中有某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。如果沒有同名的属性,则将拋出ReferenceError异常。

大白话就是:with语句会改变标识符的查找优先级,用来拦截对访问对象的查找,优先从with指定对象的属性中查找。

with指定的对象为obj,with代码块的语句的作用域首先是obj,然后才是window,如果代码块中的属性在obj中存在,则可以正常访问,如果不存在obj中,则会继续向上寻找(window),如果window也没有,则会报 ReferenceError 异常

var a = 1;
var obj = {
    a: 2
};

with (obj) {
    console.log(a); // 2
    console.log(b); // Uncaught ReferenceError: b is not defined
}
var a = 1;
var b = 3;
var obj = {
    a: 2
};

with (obj) {
    console.log(a); // 2
    console.log(b); // 3
}

注意: 不推荐使用with,在 ECMAScript 5 严格模式中该标签已被禁止。推荐的替代方案是声明一个临时变量来承载你所需要的属性。

new Function

Function 构造函数创建一个新的 Function 对象。直接调用此构造函数可用动态创建函数,但会遇到和 eval 类似的的安全问题和(相对较小的)性能问题。然而,与 eval 不同的是,Function 创建的函数只能在全局作用域中运行。

语法:

new Function ([arg1[, arg2[, ...argN]],] functionBody)

顺便提一下eval: 同样不推荐使用eval,可以使用Function来代替

function test() {
  var x = 2, y = 4;
  console.log(eval('x + y'));  // 直接调用,使用本地作用域,结果是 6
  var geval = eval; // 等价于在全局作用域调用
  console.log(geval('x + y')); // 间接调用,使用全局作用域,throws ReferenceError 因为`x`未定义
  (0, eval)('x + y'); // 另一个间接调用的例子
​}

new Function示例:

var a = 1;
function f() {
    var a = 2;
    var g = new Function('console.log(a);'); // 1
    g();
}
f();

new Function + with

with一般用于 一些模板引擎编译后的模板函数中会利用with访问模板数据(ejs 模板引擎就是用的with) 。

我们看一个例子:

var fn=new Function('obj', 'with(obj){return prop.value;}');
var data={
    prop:{
        value:1
    }
};
fn(data);  //1

例子中我们使用 prop.value 来访问data(with的指定对象),但是如果我们的data对象嵌套很深,我们就得写为 prop.value.xx.xxx.xxx.***...

我们利用with可以简写为如下:

function evalPropChain(data, propChainStr){
    return new Function('obj', 'with(obj){return ' + propChainStr + ';}')(data);
}
var data={
    prop:{
      prop1: {
        prop2: {
          value: 1
        }
      }
    }
};
evalPropChain(data, 'prop.prop1.prop2.value'); // 1

参考

posted @ 2021-07-07 23:54  韩帅  阅读(702)  评论(0编辑  收藏  举报