二、ECMAScript6
01-let命令
1.特征
1.不能进行变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
2.不能重复声明
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
3.有块级作用域
ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域
{
let a = 10;
var b = 1;
//let声明的变量只在它所在的代码块有效。
}
a // ReferenceError: a is not defined.
b // 1
for
循环的计数器,就很合适使用let
命令
使用var
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
上面代码中,变量i
是var
命令声明的,在全局范围内都有效,所以全局只有一个变量i
。每一次循环,变量i
的值都会发生改变,而循环内被赋给数组a
的函数内部的console.log(i)
,里面的i
指向的就是全局的i
。也就是说,所有数组a
的成员里面的i
,指向的都是同一个i
,导致运行时输出的是最后一轮的i
的值,也就是 10。
使用let
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
变量i
是let
声明的,当前的i
只在本轮循环有效,所以每一次循环的i
其实都是一个新的变量,所以最后输出的是6
for
循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
块级作用域拓展
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 块级作用域内部的函数声明语句,建议不要使用
{
let a = 'secret';
function f() {
return a;
}
}
// 块级作用域内部,优先使用函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
4.可以重新赋值
//var 的情况
var food = '红烧肉';
food = "青椒肉丝";
console.log(food);//"青椒肉丝"
//let 的情况
let food = '红烧肉';
food = "青椒肉丝";
console.log(food);//"青椒肉丝"
5.暂时性死区
ES6 明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
02-const(常量)命令
1.特点
1.不能进行变量提升
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}
2.不能重复声明
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;
3.有块级作用域
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
4.一旦声明,需要立刻赋值
const foo;
// SyntaxError: Missing initializer in const declaration
5.无法重新赋值
const food = "哈哈";
food = "嘻嘻";//报错
console.log(food);
6.暂时性死区
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}
2.本质
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
总结
- 能用const 不用let
- 能用let 不用 var
03-解构
1.对象解构
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
基本用法
//对象解构
const obj = {
name:'玉川',
age:19,
gender:"男"
};
//把对象中对应的值 赋值给对应的变量
// const {name,age,gender } = obj;
// console.log(name,age,gender);
const {name,skin} = obj;
console.log(name,skin); //"玉川" undefined
默认值
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
默认值生效的条件是,对象的属性值严格等于undefined
。
2.数组解构
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [a, b, c] = [1, 2, 3];
不完全解构
//数组的解构.
const arr1 = [1,2,3,4,5];
//声明num1,num2变量, 他们的值是arr1数组里对应的值.
const [num1,num2,num3,num4,num5,num6] = arr1;
console.log(num1,num2,num3,num4,num5,num6); //num6没有值就是undefined
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
默认值
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
ES6 内部使用严格相等运算符(===
),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined
,默认值才会生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
如果一个数组成员是null
,默认值就不会生效,因为null
不严格等于undefined
。
注意
1.如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
因为 JavaScript 引擎会将{x}
理解成一个代码块,从而发生语法错误。
// 正确的写法
let x;
({x} = {x: 1});
只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
2.解构赋值允许等号左边的模式之中,不放置任何变量名。
({} = [true, false]);
({} = 'abc');
({} = []);
3.由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
3.字符串解构
字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
类似数组的对象都有一个length
属性
let {length : len} = 'hello';
len // 5
4.数值和布尔值的解构赋值
只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
5.函数参数的解构赋值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
用途
1.交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
2.从函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
3.函数参数的定义
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
4.提取 JSON 数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
04-箭头函数
简写匿名函数
//1.无参无返回值的函数.
const test = function(){
console.log('呵呵');
}
1.将关键字function省略,在()和{}之间加=>
//funciton不写,改成=>
const test = ()=>{
console.log('呵呵');
}
test();
2.形参只有一个,则可以省略括号
//函数体只有一句话,可以省略大括号
const test = ()=> console.log('呵呵');
test();
3.形参没有或者为多个,则括号不能省略
const test = (name,age) => {
console.log('你好哦'+name+",年龄是"+age);
return 100;
}
console.log(test('玉川',18));
4.函数体只有一句话,可以省略大括号
const test = name => console.log('你好哦,'+name);
test("玉川");
5.函数体只有一句话,并且是返回值那句,那在省略大括号的同时也要省略return.
const test = name => 100;
test();
6.函数体不止一句,则不能省略大括号
const test = (name,age) => {
console.log('你好哦'+name+",年龄是"+age);
return 100;
}
console.log(test('玉川',18));
箭头函数中this的指向
-
箭头函数在创建时,this就会固定,和箭头函数平级的this一样
-
箭头函数在创建时,内部的this会自动确定为,当前 上下文 中的this
-
原理,用变量将this保存下来
let _this = this
-
适用场景
node.js中使用没问题
dom中最好不要使用,此时this会被改变
5.当在一个对象的方法中使用箭头函数时,此时this的指向是对象所在的作用域
05-扩展运算符
1.写法
三个点(…)
2.用途
1.替代函数的apply方法
a.取代apply
方法展开数组,简化求出一个数组最大元素的写法
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
b.push
方法的参数不能是数组,所以只好通过apply
方法变通使用push
方法
// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
2.复制数组
数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
3.合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
3.拓展-对象展开
const obj1 = {
name:'玉川',
age:18,
sayHi:()=>{
console.log('呵呵');
}
}
//对象展开
//如果有同名的属性,后面的会把前面的给覆盖.
const obj2 = {
age:19,
...obj1,
skin:"黄色"
}
console.log(obj2)
06-模板字符串
- 定义的符号 反引号
``
1的左边 - 挖坑:${表达式}
- 注意点
- 坑不变色
- 十有八九写错了,检查两边的符号是否为反引号
//模板字符串.
//1.换行不用拼接,保留我们的格式
//2.可以占位.填坑
const name = '玉川';
const str = `
静夜思
唐.李白
床前明月光
床下鞋两双
举头望明月
低头吻${name}
`;
console.log(str);
07-ES6兼容问题
1.新的浏览器都是兼容es6的.
2.babel网站, 这个网站可以把新语法转成es5