从简单案例中理解ES6新规范——let/const、解构/扩展运算符、Symbol、函数默认参数、Set/Map

113 阅读5分钟

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),值就保存在变量指向的那个内存地址,因此等同于常量。
  • 对于复合类型的数据(主要是ObjectArray),变量指向的内存地址,保存的只是一个指向实际数据的指针,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