我了解的一些ES6的新特性

---
theme: orange
---
## * let var const
let、var、const都是JS中用来声明变量的关键字。

### var

`var`声明的变量的作用域是它当前的执行上下文,有**声明前置**、**变量提升**。(作用域)

`var`声明的变量只在其声明的**块**或者**子块**中可以使用。

```
var a = 1;
function func() {
  var b = 2;
  console.log('function', a, b); // 1 2
  {
    var b = 3;
    console.log('{} change b', a, b);// 1 3
  }
  console.log('after {} change b', a, b);// 1 3
  if(a == 1) {
    console.log('if for', a, b);// 1 3
  }
}
console.log('window/global', a, b); // 1 undefined
func();
```
### let

`let`声明的变量只在`let`命令所在的代码块内有效,不存在变量提升。

```
let a = 'a';
function func() {
  let b = 'b';
  console.log('function let', a, b); // a b
  {
    let b = 'c';
    console.log('{} change b', a, b);// a c
  }
  console.log('after {} change b', a, b); // a b
  if(a == 'a') {
    console.log('if for let', a, b); // a b
  }
}
console.log('window/global let', a, b);// a undefined
func();
```

### const

`const`和`let`很类似,都具有`let`的特性,但是`const`声明的是只读变量,声明之后不可以改变其值。在`const`声明之后,必须要为其赋值。
### let 和 var 的区别

`var`有变量提升,而`let`和`const`有块级作用域,不可以变量提升。

`var`可以挂载到`windows`上

#### 作用域

`var`声明的变量的作用域是它当前的执行上下文(全局-函数外、函数内)

`let`声明的变量的作用域是它当前所处的代码块(全局、函数内、代码块`{}`内)

#### 重复说明

`var`允许在同一作用域中重复声明,而`let`在同一作用域生命的话会抛出异常。

#### 绑定全局变量

`var`在全局环境下声明变量时,会在全局对象里新建一个属性,而`let`并不会。

```
var varValue = 'var';
let letValue = 'let';

console.log(this.varValue); // var
console.log(this.letValue); // undefined
```

然而,`let`在全局环境下声明变量时,是保存在何处的呢?

```
var varValue = 'var';
let letValue = 'let';

function test() {}
console.dir(test);
```

上述代码输出结果为:

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5e933a1ceb86477b84c632589d5caaf7~tplv-k3u1fbpfcp-watermark.image?)
从结果中可以看出,`let`在全局环境下声明的变量保存在`[[Scopes]][0]:Script`的变量对象的属性中,后者`[[Scopes]][1]`为全局对象。

#### 变量提升与暂存死区

##### 何为变量提升?

通俗来讲,变量提升是指在JS代码执行过程中,JS引擎把变量的声明部分和函数的声明部分提升到代码开头的行为。变量被提升之后,会给变量设置默认值为undefined。

而对于`var`来说,不管变量声明是写在何处,最后都会被提到作用域的顶端。

```
console.log(num);
var num = 1;
```

由于变量的声明被提升了,输出的结果是`undefined`。(num作为全局变量会被提升到全局作用域的位置)

等价于:

```
var num;
console.log(num);
num = 1;
```

除了变量提升,函数实际上也存在提升,JS中具名的函数声明的形式有两种:
```
// 函数声明式
function foo() {};
// 变量形式声明式
var fn = function() {};
```

等价于:

```
/* 
 * 变量形式声明的函数 会像普通的变量一样提升
 * 输出结果:Uncaught TypeError: fn is not a function
 */
fn();
var fn = function() {}

/*
 * 函数声明式的函数 会将声明的内容一起提升到顶端
 * 输出结果: 1
 */
foo();
function foo() {
  console.log(1);
}
```
而`let`在执行上下文的创建阶段,只会创建变量,并不会初始化变量(undefined),`let`定义的变量在它们的定义被执行时才会初始化。

如果`let`定义的变量在未定义之前使用的话,会报错,这是**暂时性死区**导致的。

> 顺便一提:var可以挂载到windows上

### let 和 const 的区别

`const`与`let`同为ES6的新特性,都存在块级作用域且不能进行变量提升。唯一的区别在于`const`声明的变量是只读变量,即声明之后不能改变其值。

但是其实,`const`只读属性并不是指变量的值不变,而是指变量指向的内存地址中所保存的数据不能够被改动。

> 注意:如果const定义的是对象类型的数据,其指针指向的是内存中保存的数据,其数据中指针指向的数据是可以进行修改的。
## 解析构值

通过解析构值,可以将属性/值从对象/数组中取出并赋值给其他变量。

```
let a, b;
[a, b] = [10, 20];
console.log(a, b); // 10 20

var x = [1, 2, 3, 4, 5];
var [y, z] = x;
console.log(y, z); // 1 2

var o = {a: 1, b: '1'};
var {a, b} = o;
console.log(a, b); // 1 '1'

var a, b;
({a, b} = {a: 1, b: 2}); // (...)是必须的 且 (...)前必须有分号,不然会被当成上一行的函数执行
console.log(a, b); // 1 2

// 对象的解析构值分配新的变量名
var o = {a: 1, b: '1'};
var {a: c, b: d} = o;
console.log(c, d); // 1 '1'
```

为了防止从数组/对象中取出一个值为`undefined`的对象,可以给数组的参数预设默认值。

```
var a, b;
[a = 2, b = 3] = [1];
console.log(a, b); // 1 3

var {a = 1, b = 2} = {a: 3};
console.log(a, b); // 3 2

var {a:aa = 1, b:bb = 2} = {a: 3};
console.log(aa, bb); // 3 2
```
解析构值还可以做到方便的交换两个变量的值。

```
var a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
```
解析构值同样能够解析函数返回的数据。

```
function foo() {
  return [1, 2];
}
var a, b;
[a, b] = foo();
console.log(a, b); // 1 2
```
如果解析构值中数据为空`[, ,]`则可以忽略其值。
#### 函数参数默认值

--- 取自MDN实例

##### ES5版本

```
function drawES5Chart(options) {
  options = options === undefined ? {} : options;
  var size = options.size === undefined ? 'big' : options.size;
  var cords = options.cords === undefined ? { x: 0, y: 0 } : options.cords;
  var radius = options.radius === undefined ? 25 : options.radius;
  console.log(size, cords, radius);
  // now finally do some chart drawing
}

drawES5Chart({
  cords: { x: 18, y: 30 },
  radius: 30
});
```
##### ES2015

```
function drawES2015Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {})
{
  console.log(size, cords, radius);
  // do some chart drawing
}

drawES2015Chart({
  cords: { x: 18, y: 30 },
  radius: 30
});
```
#### 解析嵌套对象和数组

--- 取自MDN实例

```
const metadata = {
  title: 'Scratchpad',
  translations: [
    {
      locale: 'de',
      localization_tags: [],
      last_edit: '2014-04-14T08:43:37',
      url: '/de/docs/Tools/Scratchpad',
      title: 'JavaScript-Umgebung'
    }
  ],
  url: '/en-US/docs/Tools/Scratchpad'
};

let {
  title: englishTitle, // rename
  translations: [
    {
       title: localeTitle, // rename
    },
  ],
} = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle);  // "JavaScript-Umgebung"
```
#### For of 迭代和解构

--- 取自MDN实例

```
var people = [
  {
    name: 'Mike Smith',
    family: {
      mother: 'Jane Smith',
      father: 'Harry Smith',
      sister: 'Samantha Smith'
    },
    age: 35
  },
  {
    name: 'Tom Jones',
    family: {
      mother: 'Norah Jones',
      father: 'Richard Jones',
      brother: 'Howard Jones'
    },
    age: 25
  }
];

for (var {name: n, family: {father: f}} of people) {
  console.log('Name: ' + n + ', Father: ' + f);
}

// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
```
#### 从作为函数实参的对象中提取数据

--- 取自MDN实例

```
function userId({id}) {
  return id;
}

function whois({displayName: displayName, fullName: {firstName: name}}){
  console.log(displayName + " is " + name);
}

var user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
      firstName: "John",
      lastName: "Doe"
  }
};

console.log("userId: " + userId(user)); // "userId: 42"
whois(user); // "jdoe is John"
```
#### 对象属性计算名和解构

--- 取自MDN实例

```
let key = "z";
let { [key]: foo } = { z: "bar" };

console.log(foo); // "bar"
```
#### 数组/对象中的Rest

```
let [a, ...rest] = [1, 2, 3, 4];
console.log(a, rest); // 1 [2, 3, 4]

let {a, b, ...rest} = {a: 1, b: 2, c: 3, d: 4};
console.log(a, b, rest); // 1 2 {c: 3, d: 4}
```

#### 解析对象时会查找原型链

如果属性不在对象自身,将从**原型链**中查找。(---取之MDN)

```
// 声明对象 和 自身 self 属性
var obj = {self: '123'};
// 在原型链中定义一个属性 prot
obj.__proto__.prot = '456';
// test
const {self, prot} = obj;
// self "123"
// prot "456"(访问到了原型链)
```
## 展开运算符

`...`相当于遍历对象的简写。

```
let arr = [1, 2, 3];
/**
 * 结果都是1
 * 但是展开结果不同
 */
console.log(Math.min(...arr)); // 1 2 3
console.log(Math.min(arr)); // [1, 2, 3]

// 字符串也可以通过展开运算符转为数组
console.log([...'str']); // ['s', 't', 'r'] = 'str'.split('')

// 将类数组(arguments / DOM)转化成数组
funtion func() {
  console.log(arguments); // Arguments(2)[1, 2, callee: f, Symbol(Symbol.iterator): f]
  console.log([...arguments]); // [1, 2]
}
func(1, 2);
```
> 对象的展开是构成新的对象且不能直接展开,必须要在`{}`中展开。
>
> 如果展开的不是对象,会自动将其转化成对象。
>
> 如果展开运算符后面是字符串`{...'str'}`或者数组`{...[0, 1]}`,会自动转换成一个类似数组的对象`{0: 's', 1: 't', 2: 'r'}` `{0: 0, 1: 1}`,其他为空对象。
>
> 对象中的对象属性不会展开。

Vuex中经常会使用到展开运算符。
## * Set Map

#### Map(字典)

`Se`t 和 `Map` 主要的应用场景在于 **数据重组** 和 **数据储存**。

Map是一组键值对的结构,可以作为`key`和`value`放到对象中(原先的对象不能,都会转换成字符串)。

`Map`称为一种**字典**的数据结构。

```
// 初始化Map需要一个二维数组 或者 空数组  --- [key, value]
let m1 = new Map([['aa': 1], ['bb': 2], ['cc': 3]]);
m1.get('aa'); // 1

let m2 = new Map();
m2.set('dd', 4); // 添加
m2.set('ee', 5);
m2.set('ff', 6);
m2.has('dd'); // 是否存在key - 'dd': true
m2.get('ee'); // 5
m2.delete('ff'); // 删除key - 'ff'
m2.get('ff'); // undefined

// 遍历方法
m2.keys(); // 返回一个包含集合中所有键的迭代器
m2.values(); // 返回一个包含集合中所有值得迭代器
m2.entries(); // 返回一个包含Set对象中所有元素的键值对迭代器
m2.forEach(callback, thisArg); // 用于对集合成员执行callback函数操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值
```
平时需要对对象进行增删改查的话,用`Map`性能会更比用对象更有优势。

`Map`解决了对象在使用过程中的一些不足。

`Map`不存储重复的值。

##### WeakMap

`WeakMap`在防止内存溢出方面,比`Map`更好,但是`WeakMap`的功能没有`Map`齐全。

如果`WeakMap`中引用的键值被清空的话,`WeakMap`中对应的值也会清空。(垃圾回收机制(没有被引用的值将会被清空))

`WeakMap`存储的是**弱引用对象**,引用的只是键名。

```
wm.has(key);
wm.get(key);
wm.set(key);
wm.delete(key);
```
#### Set(集合)

`Set`会自动给数组去重,`Set`也有对应的`WeakSet`。

`Set`称为一种**集合**的数据结构。

```
// [value, value]
let s1 = new Set([1, 2, 3]);
let s2 = new Set();
```

`set`可以将重复的元素自动过滤,即不存储重复的值。

```
let s = new Set([1, 2, 3, 3, '3']);
console.log(s); // {1, 2, 3, '3'}

s.add(4); // 添加
s.delete(1); // 删除
s.has(1); // 判断是否存在
s.clear(); // 清空集合

// 遍历方法
s.keys(); // 返回一个包含集合中所有键的迭代器
s.values(); // 返回一个包含集合中所有值得迭代器
s.entries(); // 返回一个包含Set对象中所有元素的键值对迭代器
s.forEach(callback, thisArg); // 用于对集合成员执行callback函数操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值
```
##### WeakSet

`WeakSet` 存储的是**弱引用对象**。

`WeakSet`对象是无法被遍历的,由于垃圾回收机制对于该对象的影响使其不能拿到它包含的所有元素。(如果对象没有被引用的话,,垃圾回收机制不会管该对象是否在`WeakSet`中都会对它进行处理)。

```
ws.add(value);
ws.delete(value);
wa.has(value);
```
## * Symbol:唯一标识符

`Symbol`的出现可以保证对象中每个属性的名称独一无二,防止命名属性的冲突,所以`Symbol`表示独一无二的值,它是JS语言的第七种数据类型。

```
let s = Symbol();
typeof s; // Symbol

// 字符串
var s1 = Symbol('foo');
var s2 = Symbol('bar');
var s3 = Symbol('foo');
s1 // Symbol(foo)
s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
console.log(s1 === s2); // false

// 对象
const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)
```
`Symbol`不能和其他类型的值进行运算,但是可以转换成字符串/布尔值。

```
let ms = Symbol();
let s = Symbol();
let obj = {};
// 1
obj[ms] = 'hi';
// 2
let obj = {
  [ms]: 'hi',
  [s]: function(arg) {}
};
// 3
Object.defineProperty(obj, ms);    

console.log(obj['ms']);
obj[s]("hh");
```
## * 箭头语法

`=>`

箭头语法内部的`this`是词法作用域,由上下文确定。

**数据优先 数据驱动**

先考虑数据,再贯穿视图。

## 空合并运算符

空合并运算符(`??`)是一个逻辑操作符,当左侧的操作数为`null`或者`undefined`时,返回其右侧操作数,否则返回左侧操作数。

与`||`不同的地方在于,`||`左侧的操作数为`''`或者是`0`时,可能会返回右侧的操作数。

```
function fn(name, age) {
  // name = name || "11";
  // age = age || 18;
  name = name ?? "11";
  age = age ?? 18;
  return {name, age};
}
console.log(fn("22", "")); // {name: "22", age: ""} --- ??  {name: "22", age: 18} --- ||
```
## 可选链操作符

`?`作为可选链操作符。

```
let user = {};
// let age = user.name && user.name.age;
// console.log(age);

let age = user?.name?.age;
console.log(age);
```
## bigInt

JS中所有的数字都保存成64位浮点数 `=` 数值精度只能到53个二进制位(相当于16个十进制位) `=` 大于或等于2的1024次方的数值,JS无法表示,会返回`Infinity`。

```
const max = Number.MAX_SAFE_INTEGER;
console.log(max);
// console.log(max + 4);
// console.log(max + 6);
// console.log(4n); // bigInt
console.log(BigInt(max) + 4n);
console.log(BigInt(max) + 6n);
```

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值