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