【JavaScript】 ES6 基础语法

ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准
JavaScript 语言名称是商标( Oracle 公司注册的商标),正式名称是 ECMAScript 。
简洁教程:ES6 教程
详细教程:ES6 教程 阮一峰
简洁归纳:1.5万字概括ES6全部特性(已更新ES2020)

表达式

声明

声明作用域声明次数变量提升
var声明变量值可改变全局范围可以声明多次有,变量可以在声明之前使用undefined
let声明变量值可改变,for循环作计数器所在代码块只能声明一次无,一定要在声明后使用
const声明常量值不可改变,一旦声明必须初始化所在代码块只能声明一次无,一定要在声明后使用
function
class
import

暂时性死区(temporal dead zone,简称 TDZ):
如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

//父作用域有声明,可在子作用域里用
//父子作用域都有声明,子作用域就用子作用域的声明
for (let i = 0; i < 3; i++)  //父作用域
{  							 //子作用域
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc
//例子
var a = [];
for (var i = 0; i < 3; i++) {
    a[i] = function () {
        console.log(i);
    };
    a[i]();
}
console.log('a[1]输出:')
a[1]();

//结果
0
1
2
a[1]输出:
3//var,全局变量只有一个i,数组a的每个函数的i都指向一个i,每循环一次i就+1
1//let,局部变量,每次循环i都是一个新变量,数组a的每个函数的i都指向不同i,
//JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
for (var i = 0; i < 10; i++) {
  setTimeout(function(){
    console.log(i);
  })
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
  setTimeout(function(){
    console.log(j);
  })
}
// 输出 0123456789
//暂时性死区
//console.log(a);  //ReferenceError: a is not defined,声明变量 a 之前,a 不存在
let a = "apple";
 
console.log(b);  //undefined,当脚本开始运行的时候,b 已经存在了,
var b = "banana";

解构赋值

  • 解构遵循匹配模式:只要等号两边的模式相同,左边的变量就会被赋予对应的值
  • 解构赋值规则:只要等号右边的值不是对象或数组,就先将其转为对象
  • 解构不成功时 变量的值等于undefined
  • 不完全解构:左边只匹配一部分右边
  • 默认值:
    • 解构默认值生效条件:属性值严格等于undefined
    • undefined和null无法转为对象,因此无法进行解构
    • 如果默认值是一个表达式,只有在需要该表达式的时候,才会求值
      let [x = f()] = [1]; 因为x能取到值,所以函数f根本不会执行
    • let [x = y, y = 1] = []; // ReferenceError: y is not defined
      x用y做默认值时,y还没有声明。

表示

  • 数组解构:const [x, y] = [1, 2]
    • 规则:等号的右边是数组(或严格地说,具有Iterator接口)
    • 默认:const [x, y = 2] = [1]
  • 对象解构:const { x, y } = { x: 1, y: 2 }
    • 变量必须与属性同名,才能取到正确的值,顺序不对没事
    • 真正被赋值的是属性后面的变量,而不是属性。
    • 默认:let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
    • 改名:const { x, y: z } = { x: 1, y: 2 }
  • 字符串解构:const [a, b, c, d, e] = "hello"
  • 数值解构:const { toString: s } = 123
  • 布尔解构:const { toString: b } = true
  • 函数参数解构:
    • 数组解构:function Func([x = 0, y = 1]) {}
    • 对象解构:function Func({ x = 0, y = 1 } = {}) {}

应用场景

  • 交换变量值:[x, y] = [y, x]
  • 返回函数多个值:const [x, y, z] = Func()
  • 定义函数参数:Func([1, 2])
  • 提取JSON数据:const { name, version } = packageJson
  • 定义函数参数默认值:function Func({ x = 1, y = 2 } = {}) {}
  • 遍历Map结构:for (let [k, v] of Map) {}
  • 输入模块指定属性和方法:const { readFile, writeFile } = require(“fs”)

内置对象

扩展

字符串扩展
//JavaScript 共有 6 种方法可以表示一个字符
'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
//JavaScript 字符串允许直接输入字符,以及输入字符的转义形式
'中' === '\u4e2d' // true
  • Unicode表示法:大括号包含表示Unicode字符(\u{0xXX}或\u{0XXX})
    • 下面不能在字符串里面直接使用,只能使用转义形式。
      • U+005C:反斜杠(reverse solidus)
      • U+000D:回车(carriage return)
      • U+2028:行分隔符(line separator) 正则表达式依然不允许直接输入该字符
      • U+2029:段分隔符(paragraph separator) 正则表达式依然不允许直接输入该字符
      • U+000A:换行符(line feed)
  • 字符串遍历:可通过for-of遍历字符串
  • 字符串模板:可单行可多行可插入变量的增强版字符串 反引号 `
    • 模板字符串中嵌入变量,需要将变量名写在${ }之中
    • 模板字符串还能嵌套
  • 标签模板:模板字符串紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。
  • String.raw():返回把字符串所有变量替换且对斜杠进行转义(即斜杠前面再加一个斜杠)的结果
  • String.fromCodePoint():返回码点对应字符,但不能识别码点大于0xFFFF的字符。
  • codePointAt():返回字符对应码点(String.fromCodePoint()的逆操作),能够正确处理4个字节储存的字符(Unicode 码点大于0xFFFF的字符)
  • normalize():把字符的不同表示方法统一为同样形式,返回新字符串(Unicode正规化),比如两个字符合成一个字符,像拼音有音调
  • repeat():把字符串重复n次,返回新字符串 'x'.repeat(3); // "xxx"
  • matchAll():返回正则表达式在字符串的所有匹配
  • at():接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)

  • replace():只能替换第一个匹配
  • replaceAll():可以一次性替换所有匹配。

  • padStart():如果某个字符串不够指定长度,在头部补全'x'.padStart(4, 'ab') // 'abax'
  • padEnd():如果某个字符串不够指定长度,在尾部补全'x'.padEnd(5, 'ab') // 'xabab'

  • trim():消除字符串空格
  • trimStart():消除字符串头部的空格,
  • trimEnd():消除尾部的空格

  • indexOf():确定一个字符串是否包含在另一个字符串中
  • includes():是否存在指定字符串
  • startsWith():是否存在字符串头部指定字符串
  • endsWith():是否存在字符串尾部指定字符串
//如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`);

//上面代码中,所有模板字符串的空格和换行,都是被保留的,
//比如<ul>标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。
$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`.trim());
数值扩展
  • 二进制表示法:0b或0B开头表示二进制(0bXX或0BXX)
  • 八进制表示法:0o或0O开头表示二进制(0oXX或0OXX)
  • 数值分隔符:下划线(_)作为分隔符
    • 不能放在数值的最前面(leading)或最后面(trailing)。
    • 不能两个或两个以上的分隔符连在一起。
    • 小数点的前后不能有分隔符。
    • 科学计数法里面,表示指数的e或E前后不能有分隔符。
    • 分隔符不能紧跟着进制的前缀0b、0B、0o、0O、0x、0X
  • Number.EPSILON:数值最小精度
  • Number.MIN_SAFE_INTEGER:最小安全数值(-2^53)
  • Number.MAX_SAFE_INTEGER:最大安全数值(2^53)
  • Number.parseInt():返回转换值的整数部分
  • Number.parseFloat():返回转换值的浮点数部分
  • Number.isFinite():是否为有限数值
  • Number.isNaN():是否为NaN
  • Number.isInteger():是否为整数
  • Number.isSafeInteger():是否在数值安全范围内
  • Math.trunc():返回数值整数部分
  • Math.sign():返回数值类型(正数1、负数-1、零0)
  • Math.cbrt():返回数值立方根
  • Math.clz32():返回数值的32位无符号整数形式
  • Math.imul():返回两个数值相乘
  • Math.fround():返回数值的32位单精度浮点数形式
  • Math.hypot():返回所有数值平方和的平方根
  • Math.expm1():返回e^n - 1
  • Math.log1p():返回1 + n的自然对数(Math.log(1 + n))
  • Math.log10():返回以10为底的n的对数
  • Math.log2():返回以2为底的n的对数
  • Math.sinh():返回n的双曲正弦
  • Math.cosh():返回n的双曲余弦
  • Math.tanh():返回n的双曲正切
  • Math.asinh():返回n的反双曲正弦
  • Math.acosh():返回n的反双曲余弦
  • Math.atanh():返回n的反双曲正切
对象扩展
  • 简洁表示法:直接写入变量和函数作为对象的属性和方法({ prop, method() {} })
  • 属性名表达式:字面量定义对象时使用[]定义键([prop],不能与上同时使用)
  • 方法的name属性:返回方法函数名
    • 取值函数(getter)和存值函数(setter):get/set 函数名(属性的描述对象在get和set上)
    • bind返回的函数:bound 函数名
    • Function构造函数返回的函数实例:anonymous
  • 属性的可枚举性和遍历:描述对象的enumerable
  • super关键字:指向当前对象的原型对象(只能用在对象的简写方法中method() {})
  • Object.is():对比两值是否相等
  • Object.assign():合并对象(浅拷贝),返回原对象
  • Object.getPrototypeOf():返回对象的原型对象
  • Object.setPrototypeOf():设置对象的原型对象
  • __proto__:返回或设置对象的原型对象

属性遍历

  • 描述:自身、可继承、可枚举、非枚举、Symbol
  • 遍历
    • for-in:遍历对象自身可继承可枚举属性
    • Object.keys():返回对象自身可枚举属性键组成的数组
    • Object.getOwnPropertyNames():返回对象自身非Symbol属性键组成的数组
    • Object.getOwnPropertySymbols():返回对象自身Symbol属性键组成的数组
    • Reflect.ownKeys():返回对象自身全部属性键组成的数组
  • 规则
    • 首先遍历所有数值键,按照数值升序排列
    • 其次遍历所有字符串键,按照加入时间升序排列
    • 最后遍历所有Symbol键,按照加入时间升序排列
数组扩展

ES6中的三点运算符

  • 扩展运算符...:转换数组为用逗号分隔的参数序列([…arr],相当于rest/spread参数的逆运算)
    内部使用for…of循环,所以也可以用于 Set 结构
  • Array.from():转换具有Iterator接口的数据结构为真正数组,返回新数组
    • 类数组对象:包含length的对象、Arguments对象、NodeList对象
    • 可遍历对象:String、Set结构、Map结构、Generator函数
  • Array.of():转换一组值为真正数组,返回新数组
  • copyWithin():把指定位置的成员复制到其他位置,返回原数组
  • find():返回第一个符合条件的成员
  • findIndex():返回第一个符合条件的成员索引值
  • fill():根据指定值填充整个数组,返回原数组
  • keys():返回以索引值为遍历器的对象
  • values():返回以属性值为遍历器的对象
  • entries():返回以索引值和属性值为遍历器的对象
  • 数组空位:ES6明确将数组空位转为undefined(空位处理规不一,建议避免出现)

扩展应用

  • 克隆数组:const arr = […arr1]
  • 合并数组:const arr = […arr1, …arr2]
  • 拼接数组:arr.push(…arr1)
  • 代替apply:Math.max.apply(null, [x, y]) => Math.max(…[x, y])
  • 转换字符串为数组:[…“hello”]
  • 转换类数组对象为数组:[…Arguments, …NodeList]
  • 转换可遍历对象为数组:[…String, …Set, …Map, …Generator]
  • 与数组解构赋值结合:const [x, …rest/spread] = [1, 2, 3]
  • 计算Unicode字符长度:Array.from(“hello”).length => […“hello”].length

重点难点
使用keys()、values()、entries()返回的遍历器对象,可用for-of自动遍历或next()手动遍历

函数扩展

允许函数的最后一个参数有尾逗号
有时函数也可以写成:methods() {fun1(){}, fun2(){},}methods : fun1(){}, fun2(){},

  • 参数默认值:为函数参数指定默认值
  • 形式:function Func(x = 1, y = 2) {}
  • 参数赋值:惰性求值(函数调用后才求值)
  • 参数位置:尾参数
  • 参数作用域:函数作用域
  • 声明方式:默认声明,不能用const或let再次声明
  • length:返回没有指定默认值的参数个数
  • 与解构赋值默认值结合:function Func({ x = 1, y = 2 } = {}) {}
  • 应用
    • 指定某个参数不得省略,省略即抛出错误:function Func(x = throwMissing()) {}
    • 将参数默认值设为undefined,表明此参数可省略:Func(undefined, 1)
  • rest/spread参数(…):返回函数多余参数
    • 形式:以数组的形式存在,之后不能再有其他参数
    • 作用:代替Arguments对象
    • length:返回没有指定默认值的参数个数但不包括rest/spread参数
  • 严格模式:在严格条件下运行JS
    • 应用:只要函数参数使用默认值、解构赋值、扩展运算符,那么函数内部就不能显式设定为严格模式
  • name属性:返回函数的函数名
    • 将匿名函数赋值给变量:空字符串(ES5)、变量名(ES6)
    • 将具名函数赋值给变量:函数名(ES5和ES6)
    • bind返回的函数:bound 函数名(ES5和ES6)
    • Function构造函数返回的函数实例:anonymous(ES5和ES6)
  • 箭头函数(=>):函数简写
    • 无参数:() => {}
    • 单个参数:x => {}
    • 多个参数:(x, y) => {}
    • 解构参数:({x, y}) => {}
    • 嵌套使用:部署管道机制
    • this指向固定化
      • 并非因为内部有绑定this的机制,而是根本没有自己的this,导致内部的this就是外层代码块的this
      • 因为没有this,因此不能用作构造函数
  • 尾调用优化:只保留内层函数的调用帧
    • 尾调用
      • 定义:某个函数的最后一步是调用另一个函数
      • 形式:function f(x) { return g(x); }
    • 尾递归
      • 定义:函数尾调用自身
      • 作用:只要使用尾递归就不会发生栈溢出,相对节省内存
      • 实现:把所有用到的内部变量改写成函数的参数并使用参数默认值

箭头函数误区

  • 函数体内的this是定义时所在的对象而不是使用时所在的对象
  • 可让this指向固定化,这种特性很有利于封装回调函数
  • 不可当作构造函数,因此箭头函数不可使用new命令
  • 不可使用yield命令,因此箭头函数不能用作Generator函数
  • 不可使用Arguments对象,此对象在函数体内不存在(可用rest/spread参数代替)
  • 返回对象时必须在对象外面加上括号
正则扩展
  • 变更RegExp构造函数入参:允许首参数为正则对象,尾参数为正则修饰符(返回的正则表达式会忽略原正则表达式的修饰符)
  • 正则方法调用变更:字符串对象的match()、replace()、search()、split()内部调用转为调用RegExp实例对应的RegExp.prototype[Symbol.方法]
  • u修饰符:Unicode模式修饰符,正确处理大于\uFFFF的Unicode字符
    • 点字符(.)
    • Unicode表示法
    • 量词
    • 预定义模式
    • i修饰符
    • 转义
  • y修饰符:粘连修饰符,确保匹配必须从剩余的第一个位置开始全局匹配(与g修饰符作用类似)
  • unicode:是否设置u修饰符
  • sticky:是否设置y修饰符
  • flags:返回正则表达式的修饰符

y修饰符隐含头部匹配标志^
单单一个y修饰符对match()只能返回第一个匹配,必须与g修饰符联用才能返回所有匹配

运算符扩展

Symbol 数据类型

ES6 Symbol
ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol 。

  • Symbol,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
  • Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。
  • 使用场景:由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。
  • Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
  • Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
let sy1 = Symbol("kk"); 
let sy;
sy === sy1;       
console.log("sy1 = ",sy1)
console.log("sy = ",sy)

在这里插入图片描述

let sy = Symbol("key1");
 
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法2
let syObject = {
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject);   // {Symbol(key1): "kk"}

//
let syObject = {};
syObject[sy] = "kk";
 
syObject[sy];  // "kk"
syObject.sy;   // undefined

Symbol.for() ,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。
Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。

Set 数据结构

Set 和 Map 数据结构

  • ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。(就像数学中的集合)
  • Set本身是一个构造函数,用来生成 Set 数据结构。
  • 可以去除数组重复成员
  • 可以去除字符串里面的重复字符
  • 向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。
  • 两个空对象不相等

Set 结构的实例的属性:

  • constructor:构造函数,返回Set
  • size:返回实例成员总数

方法:

  • add():添加值,返回实例
  • delete():删除值,返回布尔
  • has():检查值,返回布尔
  • clear():清除所有成员

遍历方法:Set的遍历顺序就是插入顺序

  • keys():返回以属性值为遍历器的对象
  • values():返回以属性值为遍历器的对象
  • entries():返回以属性值和属性值为遍历器的对象
  • forEach():使用回调函数遍历每个成员
  • 扩展运算符也能遍历 let arr = [... set]

应用场景

  • 去重字符串:[...new Set(str)].join("")
  • 去重数组:[...new Set(arr)]Array.from(new Set(arr))
  • 集合数组:
    • 声明:const a = new Set(arr1)、const b = new Set(arr2)
    • 并集:new Set([…a, …b])
    • 交集:new Set([…a].filter(v => b.has(v)))
    • 差集:new Set([…a].filter(v => !b.has(v)))
  • 映射集合:
    • 声明:let set = new Set(arr)
    • 映射:set = new Set([…set].map(v => v * 2))或set = new Set(Array.from(set, v => v * 2))

Map 数据结构

ES6 Map 与 Set
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

  • Map 和 Object 的区别
    • 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
    • Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
    • Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
    • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

属性:

  • constructor:构造函数,返回Map
  • size:返回实例成员总数
    方法:
  • get():返回键值对
  • set():添加键值对,返回实例
  • delete():删除键值对,返回布尔
  • has():检查键值对,返回布尔
  • clear():清除所有成员
  • keys():返回以键为遍历器的对象
  • values():返回以值为遍历器的对象
  • entries():返回以键和值为遍历器的对象
  • forEach():使用回调函数遍历每个成员

注意:

  • 对同一个键多次赋值,后面的值将覆盖前面的值
  • 对同一个对象的引用,被视为一个键
  • 对同样值的两个实例,被视为两个键
  • 键跟内存地址绑定,只要内存地址不一样就视为两个键

Proxy 类

Proxy
ES6的代理模式 | Proxy
var proxy = new Proxy(target, handler);
在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
target :所要拦截的目标对象
handler:也是一个对象,用来定制拦截行为方法

Proxy.revocable():返回可取消的Proxy实例(返回{ proxy, revoke },通过revoke()取消代理)

  • 拦截方式
    get():拦截对象属性读取
    set():拦截对象属性设置,返回布尔
    has():拦截对象属性检查k in obj,返回布尔
    deleteProperty():拦截对象属性删除delete obj[k],返回布尔
    defineProperty():拦截对象属性定义Object.defineProperty()Object.defineProperties(),返回布尔
    ownKeys():拦截对象属性遍历for-inObject.keys()Object.getOwnPropertyNames()Object.getOwnPropertySymbols(),返回数组
    getOwnPropertyDescriptor():拦截对象属性描述读取Object.getOwnPropertyDescriptor(),返回对象
    getPrototypeOf():拦截对象原型读取instanceofObject.getPrototypeOf()Object.prototype.__proto__Object.prototype.isPrototypeOf()Reflect.getPrototypeOf(),返回对象
    setPrototypeOf():拦截对象原型设置Object.setPrototypeOf(),返回布尔
    isExtensible():拦截对象是否可扩展读取Object.isExtensible(),返回布尔
    preventExtensions():拦截对象不可扩展设置Object.preventExtensions(),返回布尔
    apply():拦截Proxy实例作为函数调用proxy()proxy.apply()proxy.call()
    construct():拦截Proxy实例作为构造函数调用new proxy()

  • 应用场景
    Proxy.revocable():不允许直接访问对象,必须通过代理访问,一旦访问结束就收回代理权不允许再次访问
    get():读取未知属性报错、读取数组负数索引的值、封装链式操作、生成DOM嵌套节点
    set():数据绑定(Vue数据绑定实现原理)、确保属性值设置符合要求、防止内部属性被外部读写
    has():隐藏内部属性不被发现、排除不符合属性条件的对象
    deleteProperty():保护内部属性不被删除
    defineProperty():阻止属性被外部定义
    ownKeys():保护内部属性不被遍历

Reflect 类

Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
Reflect 对象使用函数的方式实现了 Object 的命令式操作。
定义:保持Object方法的默认行为

方法:
get():返回对象属性
set():设置对象属性,返回布尔
has():检查对象属性,返回布尔
deleteProperty():删除对象属性,返回布尔
defineProperty():定义对象属性,返回布尔
ownKeys():遍历对象属性,返回数组(Object.getOwnPropertyNames()+Object.getOwnPropertySymbols())
getOwnPropertyDescriptor():返回对象属性描述,返回对象
getPrototypeOf():返回对象原型,返回对象
setPrototypeOf():设置对象原型,返回布尔
isExtensible():返回对象是否可扩展,返回布尔
preventExtensions():设置对象不可扩展,返回布尔
apply():绑定this后执行指定函数
construct():调用构造函数创建实例

设计目的:
将Object属于语言内部的方法放到Reflect上
将某些Object方法报错情况改成返回false
让Object操作变成函数行为
Proxy与Reflect相辅相成

废弃方法:
Object.defineProperty() => Reflect.defineProperty()
Object.getOwnPropertyDescriptor() => Reflect.getOwnPropertyDescriptor()

重点难点:
Proxy方法和Reflect方法一一对应
Proxy和Reflect联合使用,前者负责拦截赋值操作,后者负责完成赋值操作

语句与运算

Class 类

  • 类:

    • 可以为匿名或命名
    • 不可重复声明
    • 类定义不会被提升,必须在访问前对类进行定义
    • 类也可以使用表达式的形式定义 const MyClass = class Me { }
    • 类必须使用new调用,函数那样调用Class,将会报错
      • new.target属性,一般用在构造函数之中,返回new命令作用于的那个构造函数。
      • Class 内部调用new.target,返回当前 Class
      • 子类继承父类时,new.target会返回子类
  • 属性:

    • prototype:类的所有方法都定义在类的prototype属性上面 类名.prototype

    • name:返回紧跟在class关键字后面的类名(存在时)。类名.name

    • 原型对象的属性:定义在类上

    • 实例对象的属性:定义在this对象上,实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层

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

  • 方法:

    • 类中方法不需要 function 关键字。
    • 类中方法与方法之间不需要逗号分隔,加了会报错
    • ES6中,类的内部所有定义的方法,都是不可枚举的(non-enumerable),ES5的写法可以
    • 向类添加方法:Object.assign(Point.prototype, { toString(){}, toValue(){} });
    • 默认方法/构造函数:constructor()
    • 在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。存值函数和取值函数是设置在属性的 Descriptor 对象上的
    • Generator函数:如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数
  • 静态属性:加上static关键字

  • 静态方法:类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”

  • 静态块:允许在类的内部设置一个代码块,在类生成时运行一次,主要作用是对静态属性进行初始化。

  • 私有方法:

  • 私有属性:

    • 在属性前加#
    • V8 引擎改进了in运算符,使它也可以用来判断私有属性。
  • 类的继承:class Child extends Father { … }

    • 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
    • 调用父类构造函数,只能出现在子类的构造函数。
class Child extends Father {
    constructor(){
        super();
    }
}
//声明类
class Point {
	//构造函数
    constructor(a) {
        this.a = a;
    }
    toString() {...}
    toValue() {...}
    static classMethod() {}
}

Point.classMethod() 

// 等同于
Point.prototype = {
  constructor(a) {...},
  toString() {},
  toValue() {},
  ...
};

//使用
let p = new Point(6)

//覆盖方法 / 初始化时添加方法
Point.prototype={
    //methods
}

//添加方法
Object.assign(Point.prototype,{
    //methods
})

Iterator 遍历器

定义:为各种不同的数据结构提供统一的访问机制
原理:创建一个指针指向首个成员,按照次序使用next()指向下一个成员,直接到结束位置(数据结构只要部署Iterator接口就可完成遍历操作)
作用:

  • 为各种数据结构提供一个统一的简便的访问接口
  • 使得数据结构成员能够按某种次序排列
  • ES6创造了新的遍历命令for-of,Iterator接口主要供for-of消费

形式:for-of(自动去寻找Iterator接口)
数据结构:

  • 集合:Array、Object、Set、Map
  • 原生具备接口的数据结构:String、Array、Set、Map、TypedArray、Arguments、NodeList

部署:默认部署在Symbol.iterator(具备此属性被认为可遍历的iterable)
遍历器对象:

  • next():下一步操作,返回{ done, value }(必须部署)
  • return():for-of提前退出调用,返回{ done: true }
  • throw():不使用,配合Generator函数使用

ForOf循环:
定义:调用Iterator接口产生遍历器对象(for-of内部调用数据结构的Symbol.iterator())
遍历字符串:for-in获取索引,for-of获取值(可识别32位UTF-16字符)
遍历数组:for-in获取索引,for-of获取值
遍历对象:for-in获取键,for-of需自行部署
遍历Set:for-of获取值 => for (const v of set)
遍历Map:for-of获取键值对 => for (const [k, v] of map)
遍历类数组:包含length的对象、Arguments对象、NodeList对象(无Iterator接口的类数组可用Array.from()转换)
计算生成数据结构:Array、Set、Map

keys():返回遍历器对象,遍历所有的键
values():返回遍历器对象,遍历所有的值
entries():返回遍历器对象,遍历所有的键值对

与for-in区别:

  • 有着同for-in一样的简洁语法,但没有for-in那些缺点、
  • 不同于forEach(),它可与break、continue和return配合使用
  • 提供遍历所有数据结构的统一操作接口

应用场景:

  • 写具有Iterator接口的数据结构的Symbol.iterator
  • 解构赋值:对Set进行结构
  • 扩展运算符:将部署Iterator接口的数据结构转为数组
  • yield*:yield*后跟一个可遍历的数据结构,会调用其遍历器接口
  • 接受数组作为参数的函数:for-of、Array.from()、new Set()、new WeakSet()、new Map()、new WeakMap()、Promise.all()、Promise.race()

Module 模块

  • ES6 的模块化分为导出(export) 与导入(import)两个模块。
  • 模块导入导出各种类型的变量,如字符串,数值,函数,类。
  • 导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。
  • 不仅能导出声明还能导出引用(例如函数)。
  • export 命令可以出现在模块的任何位置,但必需处于模块顶层。
  • import 命令会提升到整个模块的头部,首先执行。
  • as 取别名
  • import 命令的特点
    • 只读属性:不允许在加载模块的脚本里面,改写接口的引用指向,即可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
    • 单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import 。
    • 静态执行特性:import 是静态执行,所以不能使用表达式和变量
  • export default 命令
    • 在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
    • export default 中的 default 是对应的导出接口变量。
    • 通过 export 方式导出,在导入时要加{ },export default 则不需要。
    • export default 向外暴露的成员,可以使用任意变量来接收。
  • export 与 import 可以在同一模块使用,使用特点:
    • 可以将导出接口改名,包括 default。
    • 复合使用 export 与 import ,也可以导出全部,当前模块导出的接口会覆盖继承导出的。
/*-----export [test.js]-----*/
let myName = "Tom";
let myAge = 20;
let myfn = function(){
    return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass =  class myClass {
    static a = "yeah!";
}
export { myName, myAge, myfn, myClass }
 
/*-----import [xxx.js]-----*/
import { myName, myAge, myfn, myClass } from "./test.js";
console.log(myfn());// My name is Tom! I'm 20 years old.
console.log(myAge);// 20
console.log(myName);// Tom
console.log(myClass.a );// yeah!
//导入的变量名,须和导出的接口名称相同,即顺序可以不一致。
/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }
 
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
//使用 as 重新定义导出的接口名称,隐藏模块内部的变量
/*-----export [test1.js]-----*/
let myName = "Tom";
export { myName }
/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }
/*-----import [xxx.js]-----*/
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";
console.log(name1);// Tom
console.log(name2);// Jerry
//可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
import {a} from "./xxx.js"
a = {}; // error
 
import {a} from "./xxx.js"
a.foo = "hello"; // a = { foo : 'hello' }

异步编程

Promise 类

参考资料:
JavaScript Promise
异步函数Async(es2017)

① 概念
Promise 是一个 ECMAScript 6 提供的,目的是更加优雅地书写复杂的异步任务。

② 状态
Promise 对象代表一个异步操作,有三种状态:

  • pending(进行中)
  • resolved(已完成,又称 Fulfilled)
  • rejected(已失败)

执行回调里的 resolve(); 这个 promise 就被标记为 resolverd

③ 构造函数/起始函数
new Promise(function(resolve, reject){...})
Promise 构造函数只有一个参数,是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject 都是函数:

  • resolve() 代表一切正常。
  • reject() 是出现异常时所调用的。

throw "Some error"; 或者 reject("Some error")
resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列
resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

//例子
new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("First");
        resolve();
    }, 1000);
})

④ 方法
Promise 类有三个方法,这三个方法的参数都是函数,

  • .then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列
  • .catch() 则是设定 Promise 的异常处理序列,
  • .finally() 是在 Promise 执行的最后一定会执行的序列。

resolve() 中可以放置一个参数向下一个 then 传递一个值
then 中的函数返回一个值,该值传递给下一个 then。
then 中的函数返回一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作。

1.异步函数执行的结果总是一个promise,这个promise在异步函数开始执行的时候被创建。
2.然后函数体被执行,执行可能会通过return或throw永久完成,或者通过await临时完成(这种情况,之后会继续执行)。
3.promise被返回。

当执行异步函数体的时候,return x将会resolve(x),throw err将会reject(err),promise的完成是异步的。换句话说,then(),catch()中的回调函数总是在当前代码完成之后执行。

异步的方法函数最后一个参数为回调函数
回调函数的第一个参数包含了错误信息(error)

//例子
new Promise(function (resolve, reject) {
    console.log(1111);
    resolve(2222);
}).then(function (value) {
    console.log(value);
    return 3333;
}).then(function (value) {
    console.log(value);
    throw "An error";
}).catch(function (err) {
    console.log(err);
});

//结果
1111
2222
3333
An error
//例子
//分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒

//1
setTimeout(function () {
    console.log("First");
    setTimeout(function () {
        console.log("Second");
        setTimeout(function () {
            console.log("Third");
        }, 3000);
    }, 4000);
}, 1000);

//2
new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("First");
        resolve();
    }, 1000);
}).then(function () {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("Second");
            resolve();
        }, 4000);
    });
}).then(function () {
    setTimeout(function () {
        console.log("Third");
    }, 3000);
});

//3
function print(delay, message) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(message);
            resolve();
        }, delay);
    });
}

print(1000, "First").then(function () {
    return print(4000, "Second");
}).then(function () {
    print(3000, "Third");
});

//4
function print(delay, message) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(message);
            resolve();
        }, delay);
    });
}

async function asyncFunc() {
    await print(1000, "First");
    await print(4000, "Second");
    await print(3000, "Third");
}
asyncFunc();

Generator 函数

  • Generator 有两个区分于普通函数的部分:
    • 在 function 后面,函数名之前有个 * ;
    • 函数内部有 yield 表达式。
  • Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。
  • 通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
  • next 方法:
    • 当 next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。
    • 当 next 传入参数的时候,该参数会作为上一步yield的返回值。
  • return 方法
    • return 方法返回给定值,并结束遍历 Generator 函数。
    • return 方法提供参数时,返回该参数;不提供参数时,返回 undefined 。
  • throw 方法
    • throw 方法可以再 Generator 函数体外面抛出异常,再函数体内部捕获。
  • yield* 表达式
    • yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数
//Generator 函数
function* sendParameter(){
    console.log("start");
    var x = yield '2';
    console.log("one:" + x);
    var y = yield '3';
    console.log("two:" + y);
    console.log("total:" + (x + y));
}

//next不传参
var sendp1 = sendParameter();
sendp1.next();
// start
// {value: "2", done: false}
sendp1.next();
// one:undefined
// {value: "3", done: false}
sendp1.next();
// two:undefined
// total:NaN
// {value: undefined, done: true}

//next传参
var sendp2 = sendParameter();
sendp2.next(10);
// start
// {value: "2", done: false}
sendp2.next(20);
// one:20
// {value: "3", done: false}
sendp2.next(30);
// two:30
// total:50
// {value: undefined, done: true}
function* foo(){
    yield 1;
    yield 2;
    yield 3;
}
var f = foo();
f.next();
// {value: 1, done: false}
f.return("foo");
// {value: "foo", done: true}
f.next();
// {value: undefined, done: true}
var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('catch inner', e);
  }
};
 
var i = g();
i.next();
 
try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('catch outside', e);
}
// catch inner a
// catch outside b
function* callee() {
    console.log('callee: ' + (yield));
}
function* caller() {
    while (true) {
        yield* callee();
    }
}
const callerObj = caller();
callerObj.next();
// {value: undefined, done: false}
callerObj.next("a");
// callee: a
// {value: undefined, done: false}
callerObj.next("b");
// callee: b
// {value: undefined, done: false}
 
// 等同于
function* caller() {
    while (true) {
        for (var value of callee) {
          yield value;
        }
    }
}

Async 函数

ES6 async 函数
async/await 异步函数
异步函数Async(es2017)

async function name([param[, param[, ... param]]]) { statements }

  • 函数声明:async function
  • 参数:
    • name: 函数名称。
    • param: 要传递给函数的参数的名称。
    • statements: 函数体语句。
  • 返回值:async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。

[return_value] = await expression;

  • await 操作符只能在异步函数 async function 内部使用
  • 参数:expression:一个 Promise 对象或者任何要等待的值。
  • 返回值:返回 Promise 对象的处理结果。
    • 如果等待的不是 Promise 对象,则返回该值本身。
    • 如果等待的是 Promise 对象,await 将等待 Promise 正常处理完成并返回其处理结果
//定义函数,该函数返回一个Promise对象
function testAwait (x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}
//定义async异步函数 //
async function helloAsync() {
  var x = await testAwait ("hello world");
  console.log(x); 
}
//调用函数
helloAsync ();
//
//结果
//hello world
//定义普通函数
function testAwait(){
   console.log("testAwait");
}
//定义async异步函数
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
}
//调用函数
helloAsync();
//
//结果
// testAwait
// helloAsync
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值