是否可以以外部作用域不可见的方式声明/调用函数
Is it possible to declare/call a function in such a way that outer scopes are not visible?
假设您有以下代码:
var something = "here";
function test(){
console.log(something)
}
显然,当您调用test()
时,控制台日志将显示here
,因为它将通过作用域链向上移动,直到找到something
或到达链的末端。但是有没有一种方法可以清除作用域链,这样它就不会在本地作用域之外寻找变量。
我知道在不使用严格模式的情况下,可以使用with
来添加到作用域链中,所以有没有相反的方法,从链中删除作用域,或者用其他方法使外部作用域不可访问。
我能想到的最接近实现这一点的方法是将函数体和任何参数一起传递给网络工作者,并在工作者中执行它,并将结果发回。当然,这是额外的开销,实现这样的目标有点过于复杂。
实际上并不需要这个,也不知道它的使用情况,但认为看看它是否可能会很有趣。
正如问题中所说,我想做这件事的唯一方法是与网络工作者合作。Jeremy J Starcher在评论中提到了使用iframe,直到提到这一点我才想到。因此,也提供了一个例子。
Web Worker示例
- 所有需要的数据都需要作为参数传递
- 浏览器需要支持
- 网络工作者(当然)
- 结构化克隆,在本例中使用
- 参数必须是可克隆的
- 可传输对象,本例中未使用
- 如果以上两者都不受支持,则需要对字符串的参数进行(反)序列化
- 需要使用回调,因为工作进程是异步的(是否使用promise?)
- 可以修改以允许将函数绑定到某个上下文,但这需要上下文能够作为参数传递,还没有尝试过
主javascript
(function(window){
'use strict';
function respond(oEvent){
this.cb&&this.cb(oEvent.data);
this.worker.terminate();
}
window.sandbox = function(fn){
return function(){
var worker = new Worker("sandboxWorker.js");
worker.onmessage = respond.bind({
worker:worker,
cb:[].pop.call(arguments)
});
worker.postMessage({
"fn":fn.toString(),
"args":[].slice.call(arguments,0)
});
};
};
})(window);
sandboxWorker.js
onmessage = function (oEvent) {
var fn = eval("("+oEvent.data.fn+")");
var res = fn.apply(null,oEvent.data.args);
postMessage(res);
};
测试
//Assume jQuery has been included
function maliciousAdd(a,b){
jQuery = function(){ alert("No it doesn't fix everything"); }
return a+b;
}
var sandBoxed = window.sandbox(maliciousAdd);
sandBoxed(1,2,function(result){
jQuery(document.body).css("background","#3F3");
console.log(result);
});
JSFiddle演示
IFrame示例
- 若来源相同,函数仍然可以访问父窗口等
- 如果使用不同的来源,则必须使用
postMessage
- 在同源iframe的情况下,不需要(反)序列化/克隆参数
- 同步,但也可以使其异步
Javascript:根据Felix Kling 的建议,修改为使用Function
生成内部沙箱函数
window.onload = function(){
var frame = document.createElement("iframe");
frame.src = "";
frame.style.display = "none";
window.document.body.appendChild(frame);
var win = (frame.contentWindow) ? frame.contentWindow : (frame.contentDocument.document) ? frame.contentDocument.document : frame.contentDocument;
window.sandbox = function(fn){
return function(){
var args = [].slice.call(arguments);
win.location.reload();
win.sandbox = win.Function(
'fn','args',
'fn = eval("("+fn+")"); return fn.apply(this,args);'
);
win.document.head.appendChild(script);
return win.sandbox(fn.toString(),args);
};
};
};
JSFiddle演示
文档
- postMessage(兼容性)
- 使用Web Worker(兼容性)
- 结构化克隆算法
- 可传输对象
相关文章:
- Eval未声明函数
- JavaScript + mocha:可能是在 for 循环中声明函数的闭包问题
- 使用 .on 函数运行 jQuery 声明函数
- 返回并重新声明函数中的变量
- SyntaxError:严格模式不允许在词法嵌套语句中声明函数
- JavaScript函数的类型是在声明函数之前定义的
- 在 initComponent Ext JS4 中声明函数
- 在对象中声明函数名称,为什么
- 在变量中声明函数的不同方法
- 声明函数是否会创建具有函数名称和分配给它的函数对象的变量
- 在 angularjs 控制器范围内声明函数和属性,但不附加到$scope
- 在声明函数中使用“this”时严格违规,函数表达式不违规
- 只有从html set onclick事件调用代码声明函数时,该函数才不存在
- 在jQuery中动态声明函数
- 声明函数并在jquery事件绑定上调用它
- 在Javascript中声明函数的最有效方法是什么
- javascript声明函数超时
- Javascript:动态声明函数的正确方法
- 为闭包声明函数两次
- 习语Javascript编码风格-何时在原型上声明函数,何时在函数构造函数内声明函数