Es6变量声明的方式
Let声明变量
{1.是块级变量声明的关键字,所声明的变量只在所在的代码块有效,
2.动态赋值。例let a;a=100;
3.let声明的变量名不可以再用。例
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
}
For{for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
例子:
for (let i = 0; i < 3; i++) {
let i = ‘abc’;
console.log(i);
}
// abc
// abc
// abc
}
Var声明变量
{命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined
例子:
console.log(foo); // 输出undefined
var foo = 2;
变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。
例子:
console.log(bar); // 报错ReferenceError
let bar = 2;
}
块级作用域域函数声明:{
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块(即{}包含的部分)级作用域声明。
ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。(浏览器环境可能不遵循这样的规则)
}
Const声明变量
{1.const声明一个只读的常量。一旦声明,常量的值就不能改变。
2.变量的声明和初始化必须同时进行。
3.块级作用域有效
4. const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
例子:
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: “foo” is read-only
}
顶层对象的属性
顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
window.a = 1;
a // 1
a = 2;
window.a // 2
上面代码中,顶层对象的属性赋值与全局变量的赋值,是同一件事。
顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(比如打字出错);最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。
ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
上面代码中,全局变量a由var命令声明,所以它是顶层对象的属性;全局变量b由let命令声明,所以它不是顶层对象的属性,返回undefined。
globalThis 对象
JavaScript 语言存在一个顶层对象,它提供全局环境(即全局作用域),所有代码都是在这个环境中运行。但是,顶层对象在各种实现里面是不统一的。
Es6解构赋值
{
1.赋值运算符“=”左右的模式相同(都是数组,或者都是可遍历的对象)。如果等号的右边不是数组(或者严格地说,不是可遍历的结构,参见《Iterator》一章),那么将会报错。事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
2.数组结构赋值
2.1可以设定默认值,没有对应的值就用默认值代替。例子
let [foo = true] = [];
foo // true
let [x, y = ‘b’] = [‘a’]; // x=‘a’, y=‘b’
let [x, y = ‘b’] = [‘a’, undefined]; // x=‘a’, y=‘b’
2.2默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError: y is not defined
上面最后一个表达式之所以会报错,是因为x用y做默认值时,y还没有声明。
}
3.对象的结构赋值
{
1带被赋值的对象中有属性名,根据属性名相同给变量赋值,如1.3;没有属性名,就根据变量名和属性名相同赋值,如1.1。
例子:
1.1let { bar(变量名), foo } = { foo(属性名): ‘aaa’, bar: ‘bbb’ };
foo // “aaa”
bar // “bbb”
1.2let { baz } = { foo: ‘aaa’, bar: ‘bbb’ };
baz // undefined
解构失败,变量的值等于undefined。
1.3注意:let {foo(匹配模式): baz(实际变量的变量名) } = { foo: ‘aaa’, bar: ‘bbb’ };
baz // “aaa”
2对象的解构也可以指定默认值,默认值生效的条件是,对象的属性值严格等于undefined(即等号左边的值严格是unefined)
例子:var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
}
Es6对字符串的扩展
- 对Unicode编码的支持(更多细节参见教材)
- ES6 为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被for…of循环遍历。
- For …of 的用法
例子:for (let codePoint(存储变量) of ‘foo’(待遍历的字符串)) {
console.log(codePoint)
}
// “f”
// “o”
// “o” - 模板字符串
4.1模板字符串(template string)是增强版的字符串,用反引号()标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在${}之中。大括号内部可以放入任意的 JavaScript 表达式,最终表达式的值都将转化为字符串。 4.2模板字符串可以跟在函数名后作其参数,例子: alert
123// 等同于 alert(123) 在模板字符串转化为函数参数的过程中,非变量部分合并做为函数的第一个参数;每个变量按顺序依次作为后续的参数。 例子: let a = 5; let b = 10; tag
Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ’ world ', ‘’], 15, 50);
Es6 对函数的扩展
1.ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。函数的参数不先声明,直接用合法的标识符。
function log(x, y = ‘World’) {
console.log(x, y);
}
log(‘Hello’) // Hello World
log(‘Hello’, ‘China’) // Hello China
log(‘Hello’, ‘’) // Hello
2.在函数体中不能声明和参数变量同名的变量的
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
3使用参数默认值时,函数不能有同名参数。
例子:
// 不报错
function foo(x, x, y) {
// …
}
// 报错
function foo(x, x, y = 1) {
// …
}
// SyntaxError: Duplicate parameter name not allowed in this context
4 参数尽量不要省略 传入undefined,将触发该参数等于默认值,null则没有这个效果。
有默认值的参数不是尾参数时,不可以只省略他而不省略他后面的参数。例子:
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]
5.函数的length属性只计算第一个给默认值以前的参数个数。
例子:
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
6.有默认值参数的作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。(我的理解:默认值中有赋值表达式,付给参数变量的值(即等号的右边)首先在这个单独的作用域寻找值,没有的话再从上层作用域寻值)
var x = 1
function f(x, y = x) {
console.log(y);
}
f(2) // 2
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
------------------------------------------------------------------------------如果此时,全局变量x不存在,就会报错。
function f(y = x) {
let x = 2;
console.log(y);
}
f() // ReferenceError: x is not defined
Es6
函数的rest参数
Rest参数为一个数组,形式为(…变量名)
rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
// 报错
function f(a, …b, c) {
// …
}
函数的length属性,不包括 rest 参数。
(function(a) {}).length // 1
(function(…a) {}).length // 0
(function(a, …b) {}).length // 1
箭头函数
Iterator
它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。