【学习笔记】阮一峰的ES6入门

发布时间:2019-08-10 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了【学习笔记】阮一峰的ES6入门脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

letconst

块作用域

ES6引入块作用域

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

let

  • let声明的变量只在它所在的代码块有效。
  • let不允许在相同作用域内重复声明同一个变量
  • let声明不存在变量提升,会产生暂时性死区

const

  • 声明并赋值一个只读的变量
  • const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动,因此值类型变量不可变,而引用类型变量可以更改实体数据内容。
  • 其他特性和let一致

顶层对象

let、constclass命令声明的全局变量,不属于顶层对象的属性

解构

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构

实质是模式匹配

等号右边的对象需要具备 ITerator 接口

// 基本模式匹配
let [a, b, , c] = [1, 2, 3]; // a = 1; b = 2; c = undefined;
// 复杂模式匹配
[a, b, c] = [1, [2], 3]; // a = 1; b = [2]; c = 3;
[a, [b], c] = [1, [2], 3]; // a = 1; b = 2; c = 3;
[a, [b], c] = [1, [2, 3], 4]; // a = 1; b = 2; c = 4;
[a, [b], c] = [1, 2, 3]; // Uncaught TyPEError: undefined is not a function

// 与rest参数结合使用
let [head, ...tail] = [1, 2, 3, 4]; // head = 1; tail = [2, 3, 4]

// 设置默认值,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效
let [foo = 1] = []; // foo = 1;
let [foo = 1] = [null]; // foo = null;

// 默认值是表达式的情况,表达式惰性求值
function f() {
  console.LOG('aaa');
}
let [x = f()] = [1];

// 默认值为变量的情况,变量必须已声明
let [x = 1, y = x] = []; 
let [x = y, y = 1] = []; // ReferenceError: y is not defined

// 解构字符串
const [a, b, c, d, e] = 'hello'; // a = 'h'; b = 'e'; c = 'l'; d = 'l'; e = 'o'
let { foo, bar } = { foo: "aaa", bar: "bbb" }; // foo = 'aaa'; bar = 'bbb';

// 匹配的模式: 变量
let { foo: f, bar: b } = { foo: "aaa", bar: "bbb" }; // foo = undefined; f = "aaa"; b = "bbb"

// 默认值
let { baz = {} } = { foo: "aaa", bar: "bbb" }; // baz = {};
let {x: y = 3} = {x: 5}; // y = 5;

// 混合解构
let { items: [{name}] } = { id: '1', items: [{name: 'joke'}, {name: 'tony'}] }; // name = 'joke'
let [{name, habbits: [habbit]}] = [{id: '1', name: 'kelly', habbits: ['piano']}, {id: '2', name: 'tony'}]; // name = 'kelly'; habbit = 'piano'

// 嵌套赋值
let obj = {};
let arr = [];
({ foo: obj.PRop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]

// 在直接使用赋值表达式时,需要使用括号包住赋值表达式,大括号写在行首会被解释为代码块
let x;
{x} = {x: 1}; // SyntaxError: syntax error
({x} = {x: 1}); // x = 1;

const arr = [1,2,3,4]
let {0 : First, [arr.length - 1] : last, length} = arr; // first = 1; last = 4; length = 4
// 函数参数的解构赋值
function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

字符串

  • 模板字符串

    ${name}

  • 扩展方法

    • startsWith

      let s = 'Hello world!';
      s.startsWith('world') // true
      s.startsWith('world', 6) // true, 第二个参数针对第n个位置直到结束
    • endsWith

      let s = 'Hello world!';
      s.endsWith('world') // true
      s.endsWith('Hello', 5) // true, 第二参数针对前n个字符
    • includes

      let s = 'Hello world!';
      s.includes('world') // true
      s.includes('Hello', 6) // false, 第二参数针对第n个位置直到结束
    • repeat

      'hello'.repeat(2) // hellohello,repeat重复字符串操作
    • padStart, padEnd

      // 补全字符串方法,接受两个参数,第一个参数指定字符串长度,第二个参数指定补全内容
      'x'.padStart(5, 'ab') // ababx
      'x'.padEnd(3, 'a') // xaa

数值

  • Number原型上新增了isFinite(), isNaN(), parseInt(), parseFloat(), isInteger()方法,用来代替全局方法
  • 扩展方法

    • Math.trunc - 去除小数部分
    • Math.sign - 判断一个数到底是正数、负数、还是零
    • Math.cbrt - 计算立方根

函数

  • 默认值

    • 参数全等于undefined时使用默认值
    • 默认值为函数时惰性执行
    • 默认值为表达式时惰性执行,并不会缓存下计算值
  • rest参数 - 获取函数的多余参数, 代替arguments使用
  • name属性
  • 箭头函数

数组

  • 解构
  • 扩展运算符
  • 扩展方法

    • Array.From - 将类数组对象或可遍历对象转换为数组
    • Array.of - 将一组值转换为数组
    • copyWithin
    • find
    • findIndex
    • fill
    • entries
    • keys
    • values
    • includes - 代替indexOf,更加直观,避免NaN判断的问题
    • flat

      [1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]],单层拉平
      [1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5],两层拉平
      [1, [2, [3]]].flat(Infinity) // [1, 2, 3]
    • flatMap - flat和map的结合

对象

  • 属性简写
  • 属性名表达式

    let userName = 'joe';
    let firstName = 'curly';
    let lastName = 'water';
  [userName]: 23,
  [firstName + lastName]: 25

};


- 方法的name属性

- 属性遍历

- for...in - 遍历原型链,遍历自身和继承的可枚举属性(不含Symbol属性)
- Object.keys - 返回一个数组,包括自身的所有可枚举属性(不含 Symbol 属性)的键名
- Object.getOwnPropertynames - 返回一个数组,包含对象自身的所有属性(不含 Symbol 属性)的键名
- Object.getOwnPropertySymbols - 返回一个数组,包含对象自身的所有Symbol属性的键名
- Reflect.ownKeys - 返回一个数组,包含对象自身的所有键名

- super关键字 - 指向当前对象的原型对象,只能用在简写的对象方法中

- 解构

- 扩展运算符

- 扩展方法

- Object.is() - 严格相等,解决全等运算符`NaN`不等于自身,以及`+0`等于`-0`等问题
- Object.assign() - 用于对象的合并,将对象(source)的所有可枚举属性,复制到目标对象,浅拷贝
- Object.getOwnPropertyDescriptors() - 返回对象属性的描述对象
- Object.setPrototypeOf() - 设置原型对象
- Object.getPrototypeOf() - 读取原型对象
- Object.keys() - 返回一个数组,包括自身的所有可枚举属性(不含 Symbol 属性)的键名
- Object.values() - 返回一个数组,包括自身的所有可枚举属性(不含 Symbol 属性)的键值
- Object.entries() - 方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组
- Object.fromEntries() - Object.entries的逆操作



# Symbol

ES6新加入的类型值,表示独一无二的值

let s = Symbol();


- `Symbol`函数的参数只是表示对当前 Symbol 值的描述

- 不能与其他类型的值进行运算

- 作为属性名,不能使用点运算符

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {

[mySymbol]: 'Hello!'

};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"


- 可以用于定义一组常量,保证这组常量的值都是不相等的

const log = {};

log.levels = {

DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn')

};
console.log(log.levels.DEBUG, 'debug message');
console.log(log.levels.INFO, 'info message');




# SetMap

**Set**

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

`Set`本身是一个构造函数,用来生成 Set 数据结构。

const s = new Set([1, 2, 3]);
s.add(4);
s.delete(4);
s.has(4);
s.clear();
s.size;
s.keys();
s.values();
s.entries();
s.foreach((value, key) => console.log(key + ":" + value));


**WeakSet**

WeakSet 结构与 Set 类似,也是不重复的值的集合。

WeakSet 的成员只能是对象。

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

**Map**

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.set([1], false);

map.size // 2
map.get('foo');
map.has('foo');
map.delete('foo');
map.clear();

for (let key of map.keys()) {
console.log(key);
}

for (let value of map.values()) {
console.log(value);
}

for (let item of map.entries()) {
console.log(item[0], item[1]);
}

// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}

// 等同于使用Map.entries()
for (let [key, value] of map) {
console.log(key, value);
}


**WeakMap**

`WeakMap`结构与`Map`结构类似,也是用于生成键值对的集合。

`WeakMap`只接受对象作为键名(`null`除外)

`WeakMap`的键名所指向的对象,不计入垃圾回收机制。



# Proxy

Proxy 是一个构造函数,可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。



# Reflect

保存Object对象的一些属于语言内部的方法,比如说`defineProperty`/`get`/`apply`

好处在于:让`Object`操作都变成函数行为,在Proxy中可以获取对象的默认行为

> - Reflect.apply(target, thisArg, args)
> - Reflect.construct(target, args)
> - Reflect.get(target, name, receiver)
> - Reflect.set(target, name, value, receiver)
> - Reflect.defineProperty(target, name, desc)
> - Reflect.deleteProperty(target, name)
> - Reflect.has(target, name)
> - Reflect.ownKeys(target)
> - Reflect.isExtensible(target)
> - Reflect.preventExtensions(target)
> - Reflect.getOwnPropertyDescriptor(target, name)
> - Reflect.getPrototypeOf(target)
> - Reflect.setPrototypeOf(target, prototype)



# Promise

所谓`Promise`,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

`Promise`对象是一个构造函数,用来生成`Promise`实例。

状态:`pending`(进行中)、`fulfilled`(已成功)和`rejected`(已失败)

- `Promise` 新建后就会立即执行。
- 调用`resolve``reject`并不会终结 `Promise` 的参数函数的执行。
- `then`方法返回的是一个新的`Promise`实例
- `catch`方法会捕获状态确定前的所有错误,包括在then回调函数中的错误
- `Promise` 会吃掉错误,不会对后续代码执行产生影响
- `finally`方法,不管 `Promise` 对象最后状态如何,都会执行的操作
- `Promise.all`方法用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例。多个`Promise`实例都改变状态,才会调用新`Promise`实例的回调
- 如果作为参数的 `Promise` 实例,自己定义了`catch`方法,那么它一旦被`rejected`,并不会触发`Promise.all()``catch`方法。
- `Promise.race`方法用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例。第一个`Promise`实例都改变状态,进入新`Promise`实例的回调
- `Promise.resolve(reason)`方法也会返回一个新的 `Promise` 实例,该实例的状态为`fulfilled`- `Promise.reject(reason)`方法也会返回一个新的 `Promise` 实例,该实例的状态为`rejected`

const promise = new Promise(function(resolve, reject) {
// ... some code

if (/ 异步操作成功 /){

resolve(value);

} else {

reject(error);

}
});

promise
.then(function(value) { console.log(value) }, function (err) {console.log(err);})
.catch(function(error) { console.log(error) });




# Iterator

Iterator(遍历器)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
var nextIndex = 0;
return {

next: function() {
  return nextIndex < array.length ?
    {value: array[nextIndex++], done: false} :
    {value: undefined, done: true};
}

};
}


ES6 规定,默认的 Iterator 接口部署在数据结构的`Symbol.iterator`属性,或者说,一个数据结构只要具有`Symbol.iterator`属性,就可以认为是“可遍历的”(iterable)

原生具备Iterator接口的数据结构

- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- nodelist 对象

`for...of`循环调用遍历器接口,作为遍历所有数据结构的统一的方法。

`for...in`循环主要是为遍历对象而设计的。

`forEach`无法跳出循环

`for...of`可跳出循环,严格按照顺序遍历



# Generator

Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}

VAR hw = helloWorldGenerator();

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }


- `yield`表达式后面的表达式,只有当调用`next`方法、内部指针指向该语句时才会执行
- 只有调用`next`方法时,Genarator函数才会执行
- `yield`只能被Genarator函数包裹,普通函数不行
- `yield*`表达式,用来在一个 Generator 函数里面执行另一个 Generator 函数



# async

Generator 函数的语法糖

`async`表示函数里有异步操作,`await`表示紧跟在后面的表达式需要等待结果。

`await`命令后面可以是`Promise`对象

`async`函数的返回值是 Promise 对象



# Class

class MyClass {
constructor() {

this.name = name;

}
get prop() {

return 'getter';

}
set prop(value) {

console.log('setter: '+value);

}
}


- 类和模块的内部,默认就是严格模式,所以不需要使用`use strict`指定运行模式

- 类不存在变量提升

new Foo(); // Error
class Foo {

print () { console.log('Hello'); }

}


- name 属性

- 可以使用Generator实现Symbol.iterator遍历器

- 类的方法内部如果含有`this`,它默认指向类的实例。但是如果单独提取方法出来用,容易报错

class Logger {

printName(name = 'there') {
  this.print(`Hello ${name}`);
}

print(text) {
  console.log(text);
}

}

const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined


解决方法

class Logger {

constructor() {
  this.printName = this.printName.bind(this);
}

// ...

}

class Logger {

prinitName () {
    this.print(`Hello ${name}`)    
}
// ...

}


- 如果在一个方法前,加上`static`关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {

static classMethod() {
  return 'hello';
}

}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function


如果静态方法包含`this`关键字,这个`this`指的是类,而不是实例。

父类的静态方法,可以被子类继承。

- 实例属性除了在`constructor()`方法里面定义,也可以直接写在类的最顶层。

class IncreasingCounter {

_count = 0;
get value() {
  console.log('Getting the current value!');
  return this._count;
}
increment() {
  this._count++;
}

}


- 静态属性

class Foo {

static prop = 1;

}


- 私有属性

- 使用_约定

- 结合Symbol使用避免被覆盖

- 使用#代表私有属性
class IncreasingCounter {
  #count = 0;
  get value() {
    console.log('Getting the current value!');
    return this.#count;
  }
  increment() {
    this.#count++;
  }
}
```
  • new.target - 该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。
  • 类继承 - 子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

Decorator

修饰器是一个对类进行处理的函数

@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true
function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactcomponent extends React.Component {}

修饰对象属性

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

function readonly(target, name, descriptor){
  // target要修改的对象
  // name要修饰的属性名
  // descriptor要修饰的属性的描述对象
  descriptor.writable = false;
  return descriptor;
}

修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

Module

CommonJS规范

  • 始于nodejs
  • 特点:同步加载模块,运行时加载
  • 接口:

    // moduleA.js
    module.exports = function( value ){
        return value * 2;
    }
    // moduleB.js
    var multiplyBy2 = require('./moduleA');
    var result = multiplyBy2(4);
  • 原理:通过require读取并执行一个JS文件返回该模块的exports对象的拷贝,一旦输出一个值,模块内部的变化就影响不到这个值。CommonJS 的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象缓存。

AMD规范

  • 为浏览器设计,常用requirejs实现
  • 特点:异步加载模块,运行时加载
  • 接口:

    define('myModule', ['jquery'], function($) {
        // $ 是 jquery 模块的输出
        $('body').text('hello world');
    });
    
    require(['myModule'], function(myModule) {});
    
    // 未使用模块名,类CommonJS使用
    define(function(require, exports, module) {})

ES6 模块

  • 浏览器与服务器通用
  • 特点:ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
  • 原理:通过export命令显式指定输出的代码(并非输出对象),再通过import动态引用。
  • 接口:

    • export命令用于规定模块的对外接口
    • import命令用于输入接口
    • export default规定模块默认接口,本质是输出一个叫default的变量,所以在模块中只能唯一存在,并且不可更改,不能跟声明语句
    • export 其他模块,export和import的复合写法,实际上并没有导入当前模块,只是转发
    • import命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行,importexport命令只能在模块的顶层,不能在代码块之中
    • import()函数完成动态加载,返回一个Promise对象
// Module1
var m = 1;
export {
    m
    n as N
};
// 输出一个default变量,将变量m的值赋给变量default
export default m;

// Module2
import {m, N} from "Module1"; // 导入m和N接口
import { m as M } from "Module1"; // 导入m接口,重命名为M
import module1 from "Module1"; // 导入默认接口
import * as module1 from "Module1"; // 导入所有接口
export * from "Module1"; // 再输出,export *命令会忽略Module1模块的default方法。
  • 加载规则

    浏览器对于带有type="module"<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。

    外部ES6模块脚本特性:

    • 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
    • 模块脚本自动采用严格模式,不管有没有声明use strict
    • 模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。
    • 模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。
    • 同一个模块如果加载多次,将只执行一次。
  • 解决循环加载问题:使用函数声明做提升

编程风格

letconst之间,建议优先使用const

  1. 常量表示,便于理解
  2. 有利于未来多线程编写
  3. JavaScript 编译器会对const进行优化,所以多使用const,有利于提高程序的运行效率

静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

const a = 'apple';
let b = "banana";
b = "batman";

优先使用解构赋值

如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。

对象

  • 单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
  • 对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。
  • 使用属性表达式定义动态属性名
  • 对象的属性和方法尽量使用简写

数组

使用扩展运算符拷贝数组

使用Array.from将类数组对象转为数组

函数

所有配置项都应该集中在一个对象,放在最后一个参数

rest运算符代替arguments变量

使用默认值语法设置函数参数的默认值。

Map

注意区分 Object 和 Map,如果只是需要key: value的数据结构,使用 Map 结构。因为 Map 有内建的遍历机制。

模块

如果模块只有一个输出值,就使用export default,如果模块有多个输出值,就不使用export defaultexport default与普通的export不要同时使用。

如果模块默认输出一个函数,函数名的首字母应该小写。

function makeStyleGuide() {
}

export default makeStyleGuide;

如果模块默认输出一个对象,对象名的首字母应该大写。

const StyleGuide = {
  es6: {
  }
};

export default StyleGuide;

脚本宝典总结

以上是脚本宝典为你收集整理的【学习笔记】阮一峰的ES6入门全部内容,希望文章能够帮你解决【学习笔记】阮一峰的ES6入门所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。