ECMAScript6(ES6+,ES2015)有哪些特性?
1 let和const
let定义变量,const定义常量,不能修改。
1.1 let
ES6之前 var(弃用) | let | |
---|---|---|
块级作用域 | 定义的变量没有块级作用域的概念 | 定义变量有块级作用域,所声明的变量只在let命令所在的代码块内有效。for循环就很适合用let。 |
变量提升 | 存在,变量可以在声明前使用,值为undefined | 不存在,如果先使用变量,再声明变量会报错ReferenceError |
循环 | var i 作为计数器,要用闭包实现循环。 | 实现循环很简单for (const i = 0; i < 10; i++) { console.log(i); // 0-9 } |
{
var a = "hello";
const b = "hello";
let c = "hello";
}
console.log(a); // hello
console.log(b); // b is not defined
console.log(c); // c is not defined
另外,let还有两个知识点:
1.1.1 暂时性死区
在let命令声明变量tmp之前,都属于变量tmp的死区,只要用到该变量就会报错。如果一个变量根本没有被声明,那么使用typeof反而不会报错
:
if (true) {
typeof tmp;//ReferenceError
let tmp;
}
有些死区比较隐蔽,不太容易发现
:
function bar(x=y,y=2) {//如果换成x=2,y=x则不会报错
return [x,y];
}
let x=x;//报错
暂时性死区的本质:只要进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
1.1.2 不允许重复声明
let不允许在相同作用域内重复声明同一个变量。(在同一个作用域) 以下都是会报错的情况: 😢
function () {
let a = 10;
var a = 1;
}
function () {
let a = 10;
let a = 1;
}
function func(arg) {
let arg;
}
不会报错: 😁
function func(arg) {
{
let arg;
}
}//两个作用域
1.2 const
1.2.1 const 特点
(1)const 声明一个只读的常量,一旦声明,常量的值就不能改。
(2)const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
(3)与let相同,const
命令声明的常量同样不会提升,同样存在暂时性死区。
(4)const
保证的并不是变量的值不变,而是变量指向的那个内存地址所保存的数据不得改动。
- 对于简单类型的数据(
数值Number
、字符串String
、布尔值Boolean
),值就保存在变量指向的那个内存地址,因此等同于常量。- 对于复合类型的数据(主要是
Object
和Array
),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
- (1)(2)(4)案例
const name = "hello"; name = "hello world"; // 报错 let name = "hello"; name = "hello world"; // 正常运行 const people = { love: "吃饭", }; people.love = "学习"; // 复杂类型的变量可以修改 正常运行
1.2.2 什么时候用const?
定义不变的量
const PT=3.1415926
定义对象
const obj = {
name:"小明",
age:2
}
//obj = {name:"小红"} //❌TypeError
obj.name = "小红" //✅属性可以修改
定义函数
const fun = function(){
}
2 解构运算符 扩展运算符
2.1 解构运算符{}
const people = {
name: "张三",
love: "吃饭",
};
// 把name 和love 解构出来
const { name, love } = people;
console.log(name, love); // 张三 吃饭
在实际项目中,解构通常用于接口返回数据的处理,如下所示:
const data = await getInfo(); // 接口数据
const { name = "xxx", sex = 0 } = data; // 把自己想要的数据解构出来,并设置默认值
2.2 扩展运算符...
把对象里面的全部内容解构出来。
const obj1 = {
name: "张三",
};
const obj2 = {
age: 18,
};
const user = { ...obj1, ...obj2 }; // { name:'张三',age:18 }
3 函数默认参数
在 ES6 中,函数是可以设置默认参数的,如下所示:
function demo(name = "张三") {
console.log("我是" + name);
}
- 注:如果函数设置了默认值,那么函数的 length 属性将会失效,如下所示:
通过
length
可以获取函数的参数个数function demo(name = "张三") { console.log("我是" + name); } console.log(demo.length); // 输出0 实际是有一个参数
4 Symbol
Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。Symbol 值通过 Symbol
函数生成,如下所示:
const fnName = Symbol();
typeof fnName; // "symbol"
4.1 Symbol 应用 手动实现一个 myCall 函数
使用
Symbol
创建出了一个唯一的函数名称,这样就确保不会跟 obj 原有的名称冲突。 call 函数可以用来改变函数的 this 指向,下面我们自己实现一个原生的call
方法myCall
来具体说明。如下所示:
const obj = {
name: "张三",
};
const userInfo = {
name: "李四",
say: function (age) {
console.log("我叫" + this.name, "年龄" + age);
},
};
userInfo.say.call(obj, 18); // 输出我叫张三 年龄18
- 原本 say 函数里面的 this 指向的是 userInfo 本身,经过 call 调用后改变了 this 指向,从而指向了 obj。
下面我们自己实现一个 myCall 方法,如下所示:
Function.prototype.myCall = function (obj, ...arg) {
// ...arg 表示后面的所以参数
const fnName = Symbol();
obj[fnName] = this;
const res = obj[fnName](...arg);
delete obj.fnName;
return res;
};
- myCall 函数的原理就是把要改变 this 指向的函数作为对象的一个属性,这样的话在实现这个函数的时候,this 自然就会指向这个对象了。
- (看不懂上面的函数那也没关系,因为这不是我们的重点。我们需要关心的是
Symbol
的使用)
5 Set 和 Map 数据结构
ES6 提供了新的数据结构 Set
。它类似于数组,但是成员的值都是唯一的,没有重复的值
5.1 Set
API:add()
delete()
size
has()
clear()
const arr = new Set([1, 2, 3]);
arr.add(4); // 向arr中添加元素
arr.delete(1); // 删除数据为1的元素
arr.size; // 返回arr长度
arr.has(2); // 判断arr中是否有2这个元素
arr.clear(); // 清除所有元素
使用 Set
来实现数组去重:
const arr = [1, 2, 3, 4, 1, 2, 3];
const arr2 = [...new Set(arr)];
console.log(arr2); // [1,2,3,4]
5.2 Map
API:set()
get()
size
has()
const m = new Map();
m.set("name", "张三"); // 设置元素
m.get("name"); // 张三
m.has("name"); // 判断有没有这个元素
m.size; // 获取map的长度
Map
的使用很像对象 Object
,也是通过健值的方式储存数据,不同的是:
Object
中的键只能是字符串Map
中的健可以是任意数据类型:const m = new Map(); const k = { name: "张三", }; m.set(k, 18); m.get(k); // 18