Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
当前回答
今天我想到了另一个用法,所以我兴奋地在网上搜索了一下,发现了一个已有的提及:在块作用域内定义变量。
背景
尽管JavaScript表面上与C和c++相似,但它并不将变量作用于定义变量的块:
var name = "Joe";
if ( true )
{
var name = "Jack";
}
// name now contains "Jack"
在循环中声明闭包是一个常见的任务,这可能会导致错误:
for (var i=0; i<3; ++i)
{
var num = i;
setTimeout(function() { alert(num); }, 10);
}
因为for循环没有引入新的作用域,所以相同的num(值为2)将由所有三个函数共享。
一个新的范围:let and with
随着ES6中let语句的引入,在必要时引入一个新的作用域就变得容易了,以避免这些问题:
// variables introduced in this statement
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
setTimeout(function() { alert(i); }, 10);
}
甚至:
for (var i=0; i<3; ++i)
{
// variables introduced in this statement
// are scoped to the block containing it.
let num = i;
setTimeout(function() { alert(num); }, 10);
}
在ES6普及之前,这种使用仍然局限于最新的浏览器和愿意使用编译器的开发人员。然而,我们可以很容易地模拟这种行为使用:
for (var i=0; i<3; ++i)
{
// object members introduced in this statement
// are scoped to the block following it.
with ({num: i})
{
setTimeout(function() { alert(num); }, 10);
}
}
循环现在按预期工作,创建了三个值从0到2的独立变量。注意,在块内声明的变量不局限于它,这与c++中块的行为不同(在C中,变量必须在块的开始声明,所以在某种程度上是类似的)。这种行为实际上非常类似于早期版本的Mozilla浏览器中引入的let块语法,但在其他地方没有被广泛采用。
其他回答
使用with还会使代码在许多实现中变慢,因为现在所有内容都被包装在一个额外的查找范围中。在JavaScript中使用with没有合理的理由。
今天我想到了另一个用法,所以我兴奋地在网上搜索了一下,发现了一个已有的提及:在块作用域内定义变量。
背景
尽管JavaScript表面上与C和c++相似,但它并不将变量作用于定义变量的块:
var name = "Joe";
if ( true )
{
var name = "Jack";
}
// name now contains "Jack"
在循环中声明闭包是一个常见的任务,这可能会导致错误:
for (var i=0; i<3; ++i)
{
var num = i;
setTimeout(function() { alert(num); }, 10);
}
因为for循环没有引入新的作用域,所以相同的num(值为2)将由所有三个函数共享。
一个新的范围:let and with
随着ES6中let语句的引入,在必要时引入一个新的作用域就变得容易了,以避免这些问题:
// variables introduced in this statement
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
setTimeout(function() { alert(i); }, 10);
}
甚至:
for (var i=0; i<3; ++i)
{
// variables introduced in this statement
// are scoped to the block containing it.
let num = i;
setTimeout(function() { alert(num); }, 10);
}
在ES6普及之前,这种使用仍然局限于最新的浏览器和愿意使用编译器的开发人员。然而,我们可以很容易地模拟这种行为使用:
for (var i=0; i<3; ++i)
{
// object members introduced in this statement
// are scoped to the block following it.
with ({num: i})
{
setTimeout(function() { alert(num); }, 10);
}
}
循环现在按预期工作,创建了三个值从0到2的独立变量。注意,在块内声明的变量不局限于它,这与c++中块的行为不同(在C中,变量必须在块的开始声明,所以在某种程度上是类似的)。这种行为实际上非常类似于早期版本的Mozilla浏览器中引入的let块语法,但在其他地方没有被广泛采用。
你可以使用with来避免在使用require.js时显式地管理arity:
var modules = requirejs.declare([{
'App' : 'app/app'
}]);
require(modules.paths(), function() { with (modules.resolve(arguments)) {
App.run();
}});
requirews .declare的实现:
requirejs.declare = function(dependencyPairs) {
var pair;
var dependencyKeys = [];
var dependencyValues = [];
for (var i=0, n=dependencyPairs.length; i<n; i++) {
pair = dependencyPairs[i];
for (var key in dependencyPairs[i]) {
dependencyKeys.push(key);
dependencyValues.push(pair[key]);
break;
}
};
return {
paths : function() {
return dependencyValues;
},
resolve : function(args) {
var modules = {};
for (var i=0, n=args.length; i<n; i++) {
modules[dependencyKeys[i]] = args[i];
}
return modules;
}
}
}
它很适合将在相对复杂的环境中运行的代码放入容器中:我使用它来为“窗口”创建本地绑定,从而运行用于web浏览器的代码。
当您需要将对象结构从平面转换为层次结构时,With与简写对象表示法结合使用非常有用。如果你有:
var a = {id: 123, name: 'abc', attr1: 'efg', attr2: 'zxvc', attr3: '4321'};
所以不要:
var b = {
id: a.id,
name: a.name
metadata: {name: a.name, attr1: a.attr1}
extrastuff: {attr2: a.attr2, attr3: a.attr3}
}
你可以简单地写:
with (a) {
var b = {
id,
name,
metadata: {name, attr1}
extrastuff: {attr2, attr3}
}
}