读<<ECMAScript 6 入门>>总结

329 阅读16分钟

说在前面

本文是自己在阅读阮一峰老师的<<ECMAScript 6 入门>>后,自己的一些总结和归纳。 不是十分的全面,但基本包括的常用的。还有一些我没涉及到的,麻烦大家自行去下方连接了解学习

如果有错误或者哪里有些不对的地方,或者觉得解释不是很清楚的,还请大家指出来

附上连接ECMAScript 6 入门

变量

  1. var 全局变量 变量提升

  2. let 块级变量 不存在变量提升 具有暂时性死区(let声明的变量跟这个块绑定,如果在这个块内,没有定义先使用就会报错(即使外部定义的有,也不能使用),如图),具有块级作用域

  3. TDZ 暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的

    var num = 10
    if(1) {
        console.log(num); // 结果报错,原因 TDZ
        let num = 20;
    }
    
  4. const 声明常量 ,声明必须赋值 ,具有块级作用域

    if(true) {
        const a = 10; // 声明必须赋值,否则报错
        if(true) {
            // console.log(a); 结果报错,原因 TDZ
            const a = 20;
            console.log(a);
        }
        console.log(a);
    }
    console.log(a);   //结果20 10 报错
    

const对于复杂数据类型

复杂数据类型里面的值是可以更改的, 但是不能重新赋值

const arr = [1,2,3]
arr[0] = 4;   
console.log(arr) // [ 4, 2, 3 ]
arr = [7,8,9];
console.log(arr) // 报错

let const var 三者区别

var let const
函数级作用域 块级作用域 块级作用域
变量提升 不存在变量提升 不存在变量提升
值可更改 值可更改 值不可更改
暂时性死区 暂时性死区

const 要比 let 关键字效率高

解构赋值

数组解构

let arr = [1,2,3]
let [a,b,c,d=5,e] = arr; // d这样写是设置默认值
console.log(a,b,c,d,e)  //1 2 3 5 undefined

解构成功了就是对应的值 不成功就是undefined

对象解构

// 顺序不重要 重要的是变量名字
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa" 
bar // "bbb"
// 若和属性名不同则为undefined 同样可以设置默认值
let { baz,bbb='ccc' } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
bbb // ccc 

起别名

//  匹配属性:别名名称
let { bar: barname, foo } = { foo: 'aaa', bar: 'bbb' };
console.log(barname) // aaa

Symbol

ES6新引入的原始数据类型 第七种

原来6种undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

生成

let s = Symbol(); // 不用new

// 传递参数(字符串) 是它的描述信息
let s1 = Symbol('foo');
s1 // Symbol(foo)

//如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。
const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)

Symbol.prototype.description(ES9)

const sym = Symbol('foo');

sym.description // "foo"

作为属性名的 Symbol

每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符或者是key

注意,Symbol 值作为对象属性名时,不能用点运算符。

也可以作为常量,利用值不相等

const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。

在对象中

​ 在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。

let s = Symbol();

let obj = {
  [s]: function (arg) { ... }
};

obj[s](123);
// 增强写法
let obj = {
  [s](arg) { ... }
};

​ 如果s不放在方括号中,该属性的键名就是字符串s,而不是s所代表的那个 Symbol 值。

属性名的编译

​ Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

// 可以通过Object.getOwnPropertySymbols()方法获取 返回数组
const obj = {};
let a = Symbol('a');let b = Symbol('b');

obj[a] = 'Hello';obj[b] = 'World';

const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols// [Symbol(a), Symbol(b)]

//Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};
Reflect.ownKeys(obj)//  ["enum", "nonEnum", Symbol(my_key)]

Symbol.for(),Symbol.keyFor()

Symbol('')  // 全局注册
Symbol.for() // 对全局注册的重新利用 
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');

s1 === s2 // true
// 你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。

Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
// 变量s2属于未登记的 Symbol 值,所以返回undefined。

函数的扩展

  1. 可以设置默认参数(可以结合解构使用)

  2. 增加了 length 属性(返回没有指定默认值的参数个数

    (function (a) {}).length // 1
    (function (a = 5) {}).length // 0
    (function (a, b, c = 5) {}).length // 2
    // 注意位置
    (function (a = 0, b, c) {}).length // 0
    (function (a, b = 1, c) {}).length // 1
    
  3. rest参数

    // 示例代码
    function add(...values) {
      let sum = 0;
    
      for (var val of values) {
        sum += val;
      }
    
      return sum;
    }
    
    add(2, 5, 3) // 10
    //  函数的length属性,不包括 rest 参数。
    
  4. name 属性 返回 函数名称(ES6。ES5返回空)

  5. 尾调用:指某个函数的最后一步是调用另一个函数

  6. 尾递归:尾调用自身,就称为尾递归

  7. 柯里化(currying),意思是将多参数的函数转换成单参数的形式。

箭头函数(重点)

var f = () => 5;  // 没有或者只有一个参数可以不加()
// 等同于
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};
// 如果多余一条语句需要用{}包裹且加上return 
// 如果要返回一个对象 需要加上()
let getTempItem = id => ({ id: id, name: "Temp" });

使用注意点

(1)函数体内的this对象,是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

一道关于箭头函数的面试题

var a = 100;
var obj = {
	age:20,
	say:()=>{
		alter(this.age)
	}
}
obj.say()  // 100 对象没有作用域 this指向全局。若不定义则为undefined

扩展运算符

console.log(...[1, 2, 3])
// 1 2 3
//扩展运算符后面还可以放置表达式。
const arr = [
  ...(x > 0 ? ['a'] : []),
  'b',
];
// 只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

扩展运算符应用

//  代替apply方法
// ES5 的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);
// 复制数组
const a2 = [...a1];
// 合并数组
const a3 = [...a2,...a1]

还可以解构字符串

可以把伪数组转换成真正的数组

补充,什么是伪数组

1、伪数组是一个对象 2、这个对象必须要有length属性 3、如果这个对象的length不为0,那么必须要有按照下标存储的数据

如何判断数据是不是伪数组:

1、首先他是个对象 2、是对象,有length属性 3、有length,值必须是number类型 4、length值是number类型,并且值不为0,这个对象还得按照下标存储数据

Array扩展方法

构造函数的方法:Array.from()

// 可以把伪/类数组变成数组
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
// 第二个参数可以对元素进行处理,再放回
let arr2 = Array.from(arrayLike, item=> item*2); 

Array.of

Array.of(3, 11, 8) // [3,11,8]
// 把一组值转换成数组

实例方法:find()

[1, 4, -5, 10].find((n) => n < 0)
// -5

查找第一个满足条件的对象

接收函数作为参数, 如果存在返回对应的对象,不存在返回undefined

实例方法:findlndex()

用法与上面相同,返回的是第一个满足条件的成员位置,如果没有找到,返回 -1

实例方法:includes()

表示某个数组是否包含给定的值,返回布尔值

[1,2,3].includes(2) // true

对象的扩展

属性方法的简洁表示

// 当键值一样的时候可以简写
return {
	a: a,
	// 等同于
	a
}
// 方法
const o = {
  method() {
    return "Hello!";
  }
};
// 等同于
const o = {
  method: function() {
    return "Hello!";
  }
};

方法的name属性

同函数

对象的扩展运算符(ES8)

可以看解构

链判断运算符 (ES10)

读取对象内部的某个属性,判断该对象是否存在

三种用法

  • obj?.prop // 对象属性
  • obj?.[expr] // 同上
  • func?.(...args) // 函数或对象方法的调用

例子

// 下面是判断对象方法是否存在,如果存在就立即执行的例子。
iterator.return?.()

Null 判断运算符(es10)

只有运算符左侧的值为nullundefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';

需要更多自行百度,这个目前暂不支持

对象的新增方法

Object.assign() 对象的合并

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
// 第一个参数是目标对象,后面的参数都是源对象。
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

注意,如果有同名,则后者覆盖前者。 是浅拷贝!!!

若只有一个则直接返回本身

Object.keys(),Object.values(),Object.entries()

返回 键,值,键值数组(把每个键值作为一个数组返回)

Object.fromEntries()

object.entries()的逆操作

模板字符串

模板字符串不用拼接来显示变量名字

let name = '张三'
let sayHello = `Hello ,my name is ${name}`  // 没声明报错
console.log(sayHello) //Hello ,my name is 张三

可以换行

let ul = 
`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`
console.log(ul)
/*
<ul>
  <li>first</li>
  <li>second</li>
</ul>
*/

可以调用函数

function fn() {
  return "Hello World";
}
`foo ${fn()} bar`   // 没声明报错
// foo Hello World bar

标签模板

alter`hello`   等同于 alter("hello")

String的扩展方法

startwith 和 endwith

判断是否以。。开头 或者结尾 返回布尔值

实例方法 repeat()

参数为整数, 将字符串重复几次输出

Set数据结构

利用set 可以做数据去重

Set 实例的属性和方法

//Set 结构的实例有以下属性。

Set.prototype.constructor://构造函数,默认就是Set函数。
Set.prototype.size://返回Set实例的成员总数。
//Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

Set.prototype.add(value)://添加某个值,返回 Set 结构本身。
Set.prototype.delete(value)://删除某个值,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value)://返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear()://清除所有成员,没有返回值。

遍历Set

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
set.forEach((value, key) => console.log(key + ' : ' + value))
// "red", "red"
// "green", "green"
// "blue", "blue"

WeakSet

WeakSet 结构与 Set 类似,但是有区别

  1. WeakSet 的成员只能是对象,而不能是其他类型的值。
  2. WeakSet 中的对象都是弱引用
  3. 需要自行百度

Map

键值对,原本键只可以用 字符串,现在不局限于 字符串

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')  // 设置  参数 对象,内容
m.get(o) // "content"  获取

m.has(o) // true  判断是否含有
m.delete(o) // true  删除
m.has(o) // false

// 同样也可以接收数组
const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);
map.size // 2  返回成员数
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author“
map.clear() //  清空
map.size // 0

// 若一键多值则覆盖

遍历方法

  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员的遍历器。
  • Map.prototype.forEach():遍历 Map 的所有成员。

跟set 基本相同 同样支持扩展运算符

WeakMap

类似weakSet和set关系 特点

  1. WeakMap只接受对象作为键名(null`除外),不接受其他类型的值作为键名。
  2. WeakMap的键名所指向的对象,不计入垃圾回收机制。

Proxy 代理

ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

var proxy = new Proxy(target, handler);
// target参数表示所要拦截的目标对象,
// handler参数也是一个对象,用来定制拦截行为。

Proxy 支持的拦截操作

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

目前只看几个个人觉得常用的

推荐直接点击这个点击这个

Get 属性读取

三个参数,依次为目标对象、属性名和 proxy 实例本身:可选(严格地说,是操作行为所针对的对象),示例代码如下

var person = {
  name: "张三"
};
var proxy = new Proxy(person, {
  get: function(target, propKey) {
    if (propKey in target) {
      return target[propKey];
    } else {
      throw new ReferenceError("Prop name \"" + propKey + "\" does not exist."); // 没有这层返回undefined
    }
  }
});
proxy.name // "张三"
proxy.age // 抛出一个错误

get方法可以继承

如果想更多了解,可以点击这个

Set 属性设置

四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

示例代码

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }
    // 对于满足条件的 age 属性以及其他属性,直接保存
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错

如果想更多了解,可以点击这个

Class 的基本语法

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }  // 方法之间不需要加,

  toString() { // 不用加function
    return '(' + this.x + ', ' + this.y + ')';
  }
  doStuff() {
    	console.log('stuff');
  }
}
// 可以直接new
var b = new Point();
b.doStuff() // "stuff"

类的数据类型就是函数,类本身就指向构造函数。

class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true 构造函数指向类本身

类的所有方法都定义在类的prototype属性上面

在类的实例上面调用方法,其实就是调用原型上的方法。

class B {}
let b = new B();

b.constructor === B.prototype.constructor // true

通过Object.assign方法可以很方便地一次向类添加多个方法。

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});
// 原因 类的方法都定义在prototype对象上面

constructor 方法

默认方法,new时自动调用,类里必须含有,没有定义生成空的方法

class Point {
}
// 等同于
class Point {
  constructor() {}
}

​ 实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

//定义类
class Point {

  constructor(x, y) {
    this.x = x; // 如果是无参, 例如 x= 0 则可以携程下面的
    this.y = y; //
  } 
    //  x = 0;  不用再写constructor 调用不变

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}
var point = new Point(2, 3);
point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true

取值函数(getter)和存值函数(setter)

学过java之类的应该好理解 就是个重载

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

##属性表达式

即类的属性名,可以采用表达式。

let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}

Class 表达式

const MyClass = class Me {
  getClassName() {
    return Me.name; // 只存在内部,相当于this 且和类名相同
  }
};
// 如果不用 相当于
const MyClass = class { /* ... */ };

注意点

  1. 默认严格模式

    严格模式主要有以下限制。

    • 变量必须声明后再使用
    • 函数的参数不能有同名属性,否则报错
    • 不能使用with语句
    • 不能对只读属性赋值,否则报错
    • 不能使用前缀 0 表示八进制数,否则报错
    • 不能删除不可删除的属性,否则报错
    • 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
    • eval不会在它的外层作用域引入变量
    • evalarguments不能被重新赋值
    • arguments不会自动反映函数参数的变化
    • 不能使用arguments.callee
    • 不能使用arguments.caller
    • 禁止this指向全局对象
    • 不能使用fn.callerfn.arguments获取函数调用的堆栈
    • 增加了保留字(比如protectedstaticinterface
  2. 类不存在变量提升

  3. name属性,返回类名

  4. Generator 方法 ,方法前加* 就是一个 Generator 函数。

  5. this 默认指向实例

静态属性等

不做过多介绍,需要自行看书 这里

Model

导入/加载 import

// 从fs模块加载 3 个方法,其他方法不加载
// 其他模块导出后才可以导入
import { stat, exists, readFile } from 'fs';
// 可以用as 作为重命名
import { lastName as name } from './profile.js';
// 接口 不允许改写
// 整体加载 *号
import * as circle from './circle';

这种加载称为“编译时加载”或者静态加载

导出 export

var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export { firstName, lastName, year };// 写法不唯一
// 也可以导出类和函数  不做列举

默认导出 export default

即导入这个模块就是导入这个,只可以存在一个

// export-default.js

export default function () {
  console.log('foo');
}

// import-default.js

import customName from './export-default';
customName(); // 'foo'

特别注意

export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。

// 报错  原因,因为没有提供对外的接口
export 1;

// 报错 同上
var m = 1;
export m;

// 报错 同上
function f() {}
export f;

// 正确写法
export var m = 1;

var m = 1;
export {m};

export function f() {};

function f() {}
export {f};

export 与 import 的复合写法

export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

模块继承

需要自行 点击这里

Promise对象

作用:解决异步编程

基本用法

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

  if (/* 异步操作成功条件 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolverejectresolve改为成功状态,把结果作为参数传出。reject改为失败状态,把错误作为参数传出

常用方法

then,catch,finally

promise.then(function(posts) {
  // 操作执行成功代码
}).catch(function(error) {
  // 操作执行失败代码
  console.log('发生错误!', error);
}).finally((fina)=>{
    // 无论成功失败都要执行的代码  ES8引入
});

想要深入了解或者了解其他方法请点击这里