《理解 ES6》阅读整理:块绑定(Block Binding)
变量声明一直是JavaScript中一个需要技巧的部分。在大多数基于C的编程语言中,变量(更正式地说是绑定)在声明的时候创建,然而在JavaScript中并不是这样。在JavaScript中,变量在何处创建取决于你用什么方式声明它。ES6提供了新的声明方式,让你可以更容易控制变量的作用域。接下来将说明为什么用var声明变量容易造成困扰以及ES6的块级绑定。
var声明和提升(var Declarations and Hoisting)
在JavaScript中,用var做变量声明相当于在函数顶部声明变量(如果在函数外面声明,那么变量拥有全局作用域),而不管这个变量实际上在何处声明,这称为提升(hoisting)。下面的例子可以说明什么是提升:
function getValue(condition) {
if (condition) {
var value = "blue";
return value;
} else {
//在这里value是可以被访问,值为undefined
return null;
}
//在这里value是可以被访问,值为undefined
}
如果你不了解JavaScript,你可能认为变量value只有在condition为true的时候才被创建。但实际上变量value无论如何也会被创建。上面这段代码实际上与下面这段代码效果一样:
function getValue(condition) {
var value;
if (condition) {
value = "blue";
return value;
} else {
return null;
}
}
可以看到,value的声明被提升到函数顶部,但是仍然在初始的位置完成初始化操作。这意味着变量value在else分支也可以被访问到,但在else中访问value其值为undefined因为在else块中并没有初始化value。变量提升通常给JavaScript新手带来困惑,并且在实际项目带来bug。因为这个原因,ES6提供了块级作用域选项让开发者更好的控制变量的生命周期。
块级声明(Block-Level Declarations)
块级声明的含义是:在变量声明的块作用域外,不能访问此变量。块级声明以下面两种方式声明:
(1)在函数内声明
(2)在块内声明(以{}包含)
很多基于C的语言都支持块级声明,ES6提供块级声明也是想让开发者拥有同样的开发灵活性。
let声明(let Declarations)
let的声明语言和var的声明语言是一样的。你可以将代码中的var用let替换,从而将变量的作用范围限制在当前块内。用let声明的变量不会被提升到块的顶部。最好是在块的最开始部分用let声明变量,这样在整个块内就都能访问到变量。来看下面的示例代码:
function getValue(condition) {
if (condition) {
let value = "blue";
return value;
} else {
//在这里value不能被访问
return null;
}
//在这里value不能被访问
}
上面的getValue函数实际上很像我们在其他基于C的语言中声明的函数。value变量是用let声明的,不会被提升,所以现在只能在if块内访问到value变量。如果condition为false,那么value变量永远也不会被声明或初始化。
禁止重复声明(No Redeclaration)
如果某个变量已经在作用域内声明,那么用let再次声明这个变量,就会报错。请看下面的代码:
var count = 30; let count = 40; //报错
在上面的代码中,count被声明两次:var和let各声明一次。因为let不能重复定义一个已经存在的变量,所以上面的代码会抛出错误。但是如果let是在自己块内声明一个同名变量,将不会报错:
var count = 30;
if (condition) {
let count = 40; //没有报错
}
这里let定义count变量不会报错,是因为它在if块内创建的,而不是在外部的块。在if块内访问count的值为40,而不是外面的30。这与C/C++等语言的作用方式是一样的。
常量声明(const Declarations)
ES6支持用关键字const作常量声明。用const声明的变量在初始化后不能被修改。每个const变量在声明时必须被初始化,看下面的代码:
const maxItems = 30; const name; //语法错误,name没被初始化
maxItens在声明时被初始化,而name在声明时未被初始化,因而会报错。
常量声明 vs let声明(Constants vs. let Declarations)
const声明和let声明一样,都是块级声明,也就是说在块内用const声明的变量无法在块外被访问。const声明的变量也不会被提升。看下面的代码:
if (condition) {
const maxItems = 5;
}
//这里不能访问到maxItems变量
const另外一个与let相似的性质是,在作用域内也不能重复声明变量。不管这个变量是用var还是let声明的。看下面的代码:
var message = "Hello";
let age = 25; //下面两句都会报错
const message = "Goodbye!";
const age = 30;
在这些相似的性质之外,const和let之间还有一个重要的不同之处。对一个已经用const声明的变量赋值会报错,不管是strict还是non-strict模式:
const maxItems = 5; maxItems = 5; //报错
const声明对象(Object Declarations with const)
const声明的对象可以修改对象的属性,看下面的代码:
const person = {
name: "Nicholas"
};
person.name = "Greg"; //没问题
//报错
person = {
name: "Greg"
};
简单的说就是用const声明的变量,本身绑定的值不能变,但是指向的对象却是可以修改的。
循环内块绑定(Block Bindings in Loop)
开发者最想要支持块级绑定的地方也许是在for循环内,在循环内声明的变量不能在外部块访问到。下面的代码在JavaScript中是很常见的:
for (var i = 0; i < 10; i++) {
process(items[i]);
}
console.log(i); //i可以被访问,输出10
在其他支持块级作用域的语言中,i是不可在循环体外被访问的。但是在JavaScript中,i被提升,在循环体外也可以被访问到。这容易导致问题,因为开发者的意图也是想让i只在循环体内被访问到。用let代替var,就可以达到预期的效果:
for (let i = 0; i < 10; i++) {
process(items[i]);
}
console.log(i); //报错,i不可被访问
在这个例子中,i只能在循环内被访问。当循环结束后,i不可再被访问。
循环内的函数(Functions in Loops)
在循环体内创建函数一直是一个问题来源,因为循环变量可以在循环外被访问。下面是一段很典型的代码:
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
//输出10次10
funcs.forEach(function(func) {
func();
});
你可能希望这段代码输出0到9,但实际上这段代码输出10次10。原因在于,每次循环i都被共享,意味着在循环内每次创建函数时都引用了同一个变量。当循环结束时,i的值为10。所以最终上面的代码打印出10次10。通常的解决方法是立即执行函数表达式(IIFE),用闭包在每次循环时复制一份变量解决:
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
//输出0到9
funcs.forEach(function(func) {
func();
});
在ES6中,你可以用let和const达到IIFE的效果。
循环内的let声明(let Declarations in Loops)
在循环体内用let声明变量可以达到上面代码中使用IIFE的效果。在每次迭代时,循环会创建一个新的同名变量,并且初始化其值:
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
//输出0到9
functions.forEach(function(func) {
func();
});
很明显,上面这段代码在完成了开发者的需求的同时,更简洁。let声明每次迭代时都创建一个新的变量i。在for-in和for-of循环中也类似:
var funcs = [],
var object = {
a: true,
b:true,
c: true
}; for (let key in object) {
funcs.push(function() {
console.log(key);
});
} //输出a,b,c
funcs.forEach(function(func) {
func();
});
在这个例子中,每次循环时,一个新的key绑定被创建,每个函数都有自己的key变量副本。如果我们用var声明变量key,那么会输出三个c。
循环体内的const声明(const Declarations in Loops)
ES6并没有明确禁止在循环中使用const声明变量,const根据loop的类型也会有不同的行为。在通常的for循环中,你可以用const声明一个变量然后初始化它,但是如果你试图修改变量值,解释器会给出警告:
var funcs = []; //第一次迭代后报错
for(const i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
在上面这段代码中,i被声明为一个const变量。开始迭代时i为0,顺利执行。当执行i++时报错,因为你试图修改一个常量。因此只有当你不想在循环中修改某个变量时,才能用const定义。在for-in和for-of循环中,const变量和let变量类似。看下面的代码:
var funcs = [],
var object = {
a: true,
b:true,
c: true
}; //没有报错
for (const key in object) {
funcs.push(function() {
console.log(key);
});
} //输出a,b,c
funcs.forEach(function(func) {
func();
});
上面这段代码不会报错,因为每次循环都会创建一个新的const绑定,并没有对之前的变量进行修改操作。但是在循环内,循环变量key的值是不能被修改的(因为是const变量)。
全局块绑定(Global Block Bindings)
let和const另外一个与var不同的地方在于全局作用域行为。当你在全局作用域用var创建一个变量,这个变量会成为全局对象的一个属性(比如在浏览器中全局对象是window)。那意味着你有可能用var覆盖掉一个全局变量。看下面的代码:
//在浏览器中
var RegExp = "Hello!";
console.log(window.RegExp); //"Hello!" var ncz = "Hi";
console.log(window.ncz); //"Hi!"
在上面的代码中,尽管RegExp在window对象中定义,但仍然可以被var声明覆盖。下面的ncz变量被定义成一个全局变量,并且成为window对象的一个属性。如果你使用let或者const声明全局变量,那么不同的是,变量不会成为全局对象的属性,这也意味着你不能用let或者覆盖一个全局变量:
//在浏览器中
let RegExp = "Hello!";
console.log(RegExp); //"Hello"
console.log(window.RegExp === RegExp); //false const ncz = "Hi!";
console.log(ncz); //"Hi"
console.log("ncz" in window); //false
当用let声明一个全局变量RegExp后,你不能用RegExp继续访问原有的值,但是可以通过window.RegExp继续访问,RegExp和window.RegExp不相同。相似地,ncz创建后,并不会成为全局window对象的一个属性。当你不想创建全局对象属性的时候,let和const比var更加安全,因为它们不会修改全局对象属性。
《理解 ES6》阅读整理:块绑定(Block Binding)的更多相关文章
- 深入理解ES6之《块级作用域绑定》
众所周知,js中的var声明存在变量提升机制,因此ESMAScript 6引用了块级作用域来强化对变量生命周期的控制let const 声明不会被提升,有几个需要注意的点1.不能被重复声明 假设作用域 ...
- 《深入理解ES6》笔记——块级作用域绑定(1)
本章涉及3个知识点,var.let.const,现在让我们了解3个关键字的特性和使用方法. var JavaScript中,我们通常说的作用域是函数作用域,使用var声明的变量,无论是在代码的哪个地方 ...
- 分布式文件系统 之 数据块(Block)
众所周知,HDFS中以数据块(block)为单位进行存储管理.本文简单介绍一下HDFS中数据块(block)的概念,以及众多分布式存储系统(不止是HDFS)使用block作为存储管理基本单位的意义. ...
- html中的块元素(Block)和内联元素(Inline)(转)
我们首先要了解,所有的html元素,都要么是块元素(block).要么是内联元素(inline).下面了解一下块元素.内联元素各自的特点: 块元素(block)的特点: 1.总是在新行上开始:2.高度 ...
- CSS包含块containing block详解
“包含块(containing block)”,W3c中一个很重要的概念,今天带大家一起来好好研究下. 初步理解 在 CSS2.1 中,很多框的定位和尺寸的计算,都取决于一个矩形的边界,这个矩形,被称 ...
- css中margin重叠和一些相关概念(包含块containing block、块级格式化上下文BFC、不可替换元素 non-replaced element、匿名盒Anonymous boxes )
平时在工作中,总是有一些元素之间的边距与设定的边距好像不一致的情况,一直没明白为什么,最近仔细研究了一下,发现里面有学问:垂直元素之间的margin有有互相重叠的情况:新建一个BFC后,会阻止元素与外 ...
- 整理:WPF中Binding的几种写法
原文:整理:WPF中Binding的几种写法 目的:整理WPF中Bind的写法 <!--绑定到DataContext--> <Button Content="{Bindin ...
- 深入理解ES6(二)(解构赋值)
变量的解构赋值 (1) 数组的解构赋值 1.基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring ). 只要等号两边的模式相同,左边的变量 ...
- 包含块( Containing block ) 转自W3CHelp
包含块简介 在 CSS2.1 中,很多框的定位和尺寸的计算,都取决于一个矩形的边界,这个矩形,被称作是包含块( containing block ). 一般来说,(元素)生成的框会扮演它子孙元素包含块 ...
随机推荐
- ssh通道技术
所有机器均为Linux操作系统. 机器是A,中间服务器为B,目标服务器是C. 从A可以ssh到B,从B可以ssh到C,但是A不能直接ssh到C. 现在展示利用ssh通道技术从A直接传输文件到C. ...
- Inside The C++ Object Model - 03
object Lessons 1.C++中布局以及存取时间上的的额外负担是由virtual引起的:virtual function.virtual base class.或是由于多继承引起的. 2.C ...
- PLSQL在64位系统连接不上32位的服务器
1 主要是因为PLSQL只能接纳32位的客户端 2 下载oracle32位客户端 http://www.oracle.com/technetwork/topics/winsoft-085727.htm ...
- ASP.NET 操作Cookie详解 增加,修改,删除
ASP.NET 操作Cookie详解 增加,修改,删除 Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密).定义于RFC2109.它 ...
- 种类并查集(POJ 1703)
1703 -- Find them, Catch them http://poj.org/problem?id=1703 题目大意:有2个敌对帮派,输入D a b表示a,b在不同帮派,输入A a b表 ...
- div一直浮动在页面的底部
永远在底部是那种无论滚动条怎么拉,都可以看见悬浮在底部的那种,如果是那种,是用固定定位做的.另外注意页面中最后的元素或者body要空出固定条的高度,不然最后的元素会被遮挡. html: <div ...
- Python字典方法总结
1.清空字典中元素清空,dict变为{} L.clear()-> None. Remove all items from L 1 2 3 4 >>> L ={'shaw':2 ...
- 《C和指针(Pointer on c)》 学习笔记
转载:http://dsqiu.iteye.com/blog/1687944 首先本文是对参考中三个连接的博客进行的整理,非常感谢三位博主的努力,每次都感叹网友的力量实在太强大了…… 第一章 快速上手 ...
- APK动态加载框架(DL)解析
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/39937639 (来自singwhatiwanna的csdn博客) 前言 好久 ...
- Tlist
Tlist (Classes.pas) 在我刚开始接触TList的时候,TList搞得我迷雾重重,都是Capacity属性惹的祸.我查了Delphi的帮助,它说Capacity是TList的最大容量, ...