es6常用语法简记

声明方式

let声明:

声明一个变量,有暂时性死区、无变量提升有块级作用域

  // 声明的是一个变量,变量在声明且赋值后,能更改其赋的值
  let a = 1;
  a = 2;
  console.log(a); // 2
  // 暂时性死区:只要代码块内有let它所声明的变量就“绑定”这个区域,不再受外部的影响。
  var a = 3;
  {
    a = 123; // Identifier 'a' has already been declared
    let a;
  }
  // 无变量提升
  console.log(a); // a is not defined
  let a = '张三';
  // 块级作用域
  {
    let n = '张三'
  }
  console.log(n); // n is not defined

 

const声明:

声明一个常量,有暂时性死区、无变量提升有块级作用域

  // 声明一个常量,一旦声明必须立即初始化,一旦初始化之后,它的值不能再进行任何修改!
  const a = 2;
  a = 4;// Assignment to constant variable.(赋值给常数变量。)
  // 初始值为对象时为特殊情况
  const obj = {};
  obj.name = "张三";
  obj.age = 24;
  console.log(obj); // {name: "张三", age: 24}
  // 初始值为数组时也是特殊情况
  const arr = [];
  arr[0] = 1;
  arr[1] = 2;
  console.log(arr); // [1, 2]
  // 其他的同let是一样的

 

var声明:

var声明一个变量,无暂时性死区,有变量提升无块级作用域,只有函数作用域和全局作用域

  // 声明一个变量
  var a = 1;
  a = 2;
  console.log(a); // 2
  // 无暂时性死区
  var a = 3;
  {
    a = 123;
    var a;
  }
  console.log(a); // 123
  // 变量提升
  console.log(a); // undefined
  var a = 3;
  // 全局作用域:
  var a = 3;
  function fun () {
    console.log(a);
  }
  fun(); // 3
  // 函数作用域
  function fun () {
    var a = 3;
    console.log(a);
  }
  fun(); // 3
  console.log(a); // a is not defined

 

补充:对象window中声明的变量,具有全局作用域,在函数中声明的变量具有函数作用域,在代码块中用let、const声明的变量具有块级作用域。

 

变量的解构赋值:

这个东西只说用法,没有太多概念性的东西

交换变量的值

  let x = 1;
  let y = 2;
  [x, y] = [y, x];
  console.log(x,y); // 2 1

 

从函数返回多个值

  // 返回一个数组
  function fun () {
    return [1, 2, 3];
  }
  let [x, y, z] = fun();
  console.log(x, y, z); // 1, 2, 3
  // 返回一个对象
  function fun () {
    return {
      name: '张三',
      age: 24,
      sex: '女'
    }
  }
  let {name, age, sex} = fun();
  console.log(name, age, sex); // 张三 24 女

 

函数参数的定义

   // 参数是一组有次序的值
   function f([x, y, z]) { ... }
   f([1, 2, 3]);
   // 参数是一组无次序的值
   function f({x, y, z}) { ... }
   f({z: 3, y: 2, x: 1});

 

提取 JSON 数据

 let data = {
   name: '张三',
   age: 24
 };
 let {name, age} = data;
 console.log(name, age); // 张三 24

 

函数参数的解构赋值

   function add([x, y]){
     return x + y;
   }
   add([1, 2]); // 3

 

遍历 Map 结构

   for (let [key, value] of map) {
     console.log(key + " is " + value);
   }

 

输入模块的指定方法

   const { SourceMapConsumer, SourceNode } = require("source-map");

 

 

字符串扩展

字符串的遍历器接口

   for (let codePoint of 'foo') {
     console.log(codePoint)
   }

 

模板字符串

  let name = '张三';
  let str = `${name}的女朋友`;
  console.log(str); // 张三的女朋友

 

includes() :

返回布尔值,表示是否找到了参数字符串。

  let str = "张三的女朋友";
  console.log(str.includes("张三")); // true

补充: indexOf如果查找到了就返回索引,未找到则返回-1

 

startsWith() :

返回布尔值,表示参数字符串是否在原字符串的头部。

   let str = "张三的女朋友";
   console.log(str.startsWith("张")); || console.log(str.indexOf("张") == 0); // true

 

endsWith():

返回布尔值,表示参数字符串是否在原字符串的尾部。

   let str = "张三的女朋友";
   console.log(str.endsWith("友")); || console.log(str.indexOf("友") == str.length - 1); // true

 

repeat():方法返回一个新字符串,表示将原字符串重复n次。

  let str = '6';
  console.log(str.repeat(3)); // 666

 

padStart()用于头部补全

    'x'.padStart(5, 'ab') // 'ababx'
    'x'.padStart(4, 'ab') // 'abax'

 

 

padEnd():用于尾部补全

    'x'.padEnd(5, 'ab') // 'xabab'
    'x'.padEnd(4, 'ab') // 'xaba'

 

关于去除空格

(1). trim():

方法会从一个字符串的两端删除空白字符。

  let str = ' 10 10 ';
  console.log(str); // ' 10 10 '
  console.log(str.trim()); // '10 10'
  // 如果要去除所有空格
  let str = ' 10 10 ';
  console.log(str.replace(/ /g, '')); // 1010

 

(2). trimStart():

消除字符串头部的空格

  let str = ' 10 10 ';
  console.log(str); // ' 10 10 '
  console.log(str.trimStart()); // '10 10 '

 

(3). trimEnd()

消除尾部的空格

  let str = ' 10 10 ';
  console.log(str); // ' 10 10 '
  console.log(str.trimEnd()); // ' 10 10'

 

replaceAll():

可以一次性替换所有匹配。

  'aabbcc'.replaceAll('b', '_') == 'aabbcc'.replace(/b/g, '_') // true

 

matchAll():返回一个正则表达式在当前字符串的所有匹配

  let str = 'aabb';
  console.log([...str.matchAll(/a/g)]);//[["a", index: 0, input: "aabb", groups: undefined] ["a",
  index: 1, input: "aabb", groups: undefined]]

 

正则表达式

y 修饰符

“粘连”(sticky)修饰符。后一次匹配都从上一次匹配成功的下一个位置开始,y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"]
r2.exec(s) // null

 

先行断言

x只有在y前面才匹配

let s = 'xyzx';
let RE = /x(?=y)/;
RE.exec(s); // ["x", index: 0, input: "xyzx", groups: undefined]

注:以上可以看出匹配到的结果索引在第一个的位置

 

先行否定断言

x只有不在y前面才匹配

let s = 'xyzx';
let RE = /x(?!y)/;
RE.exec(s); // ["x", index: 3, input: "xyzx", groups: undefined]

注:以上可以看出匹配到的结果索引在第四个的位置

 

后行断言

z只有在y后面才匹配

let s = 'xyzx';
let RE = /(?<=y)z/;
RE.exec(s); // ["z", index: 2, input: "xyzx", groups: undefined]

注:以上可以看出匹配到的结果索引在第3个的位置

 

后行否定断言

z只有不在y后面才匹配

let s = 'xyyiizx';
let RE = /(?<!y)z/;
RE.exec(s); // ["z", index: 5, input: "xyyiizx", groups: undefined]

注:以上可以看出匹配到的结果在第6个位置

 

JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。ES2018 引入后行断言

 

flags返回正则表达式的修饰符。

/xyz/i.flags // "i"

 

sticky表示是否设置了y修饰符。

/xyz/y.sticky // true

 

数值的扩展

数值分隔符

ES2021,允许 JavaScript 的数值使用下划线(_)作为分隔符。

1_000_000_000_000 // 1000000000000

 

数值分隔符有几个使用注意点

-   不能放在数值的最前面(leading)或最后面(trailing)。

-   不能两个或两个以上的分隔符连在一起。

-   小数点的前后不能有分隔符。

-   科学计数法里面,表示指数的`e`或`E`前后不能有分隔符。

 

 

Number.isFinite()

用来检查一个数值是否为有限的(finite),即不是Infinity。

Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false

 

Number.isNaN()

用来检查一个值是否为NaN。

Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(9/NaN) // true
Number.isNaN('true' / 0) // true
Number.isNaN('true' / 'true') // true

 

Number.parseInt()

ES6 将全局方法parseInt(),移植到Number对象上面,行为完全保持不变。为了逐步减少全局性方法,使得语言逐步模块化。

Number.parseInt('12.34') // 12

注:用来获取一个浮点数的整数部分

 

Number.parseFloat()

ES6 将全局方法parseFloat(),移植到Number对象上面,行为完全保持不变。为了逐步减少全局性方法,使得语言逐步模块化。

Number.parseFloat('123a') // 123

注:用来截取数值部分

 

Number.isInteger()

用来判断一个数值是否为整数。

Number.isInteger(25) // true
Number.isInteger(25.1) // false

 

整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。

Number.isInteger(25) // true
Number.isInteger(25.0) // true

 

由于 JavaScript 采用 IEEE 754 标准,数值存储为64位双精度格式,数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位)。如果数值的精度超过这个限度,第54位及后面的位就会被丢弃,这种情况下,Number.isInteger可能会误判。

Number.isInteger(3.0000000000000002) // true

 

安全整数## 和 Number.isSafeInteger()

JavaScript 能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。

Number.MAX_SAFE_INTEGER === 9007199254740991// true
Number.MIN_SAFE_INTEGER === -9007199254740991// true

 

Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内。

Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false

 

Math 对象的扩展

Math.trunc()

用于去除一个数的小数部分,返回整数部分。

Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc('123.456') // 123 // 内部使用`Number`方法将其先转为数值。

 

Math.sign()

用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

-   参数为正数,返回`+1`;

-   参数为负数,返回`-1`;

-   参数为 0,返回`0`;

-   参数为-0,返回`-0`;

-   其他值,返回`NaN`。

Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN

 

Math.cbrt()

用于计算一个数的立方根。

Math.cbrt(-1) // -1
Math.cbrt(0)  // 0
Math.cbrt(1)  // 1

 

Math.imul()

返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。

Math.imul(2, 4)   // 8
Math.imul(-1, 8)  // -8
Math.imul(-2, -2) // 4

 

Math.hypot

返回所有参数的平方和的平方根。

Math.hypot(3, 4);        // 5

注:勾三股四玄五

 

对数方法

1.`Math.expm1(x)`返回 ex - 1,即`Math.exp(x) - 1`。

2. `Math.log1p(x)`方法返回`1 + x`的自然对数,即`Math.log(1 + x)`。如果`x`小于-1,返回`NaN`。

3. `Math.log10(x)`返回以 10 为底的`x`的对数。如果`x`小于 0,则返回 NaN。

4. `Math.log2(x)`返回以 2 为底的`x`的对数。如果`x`小于 0,则返回 NaN。

 

双曲函数方法

-   `Math.sinh(x)` 返回`x`的双曲正弦(hyperbolic sine)

-   `Math.cosh(x)` 返回`x`的双曲余弦(hyperbolic cosine)

-   `Math.tanh(x)` 返回`x`的双曲正切(hyperbolic tangent)

-   `Math.asinh(x)` 返回`x`的反双曲正弦(inverse hyperbolic sine)

-   `Math.acosh(x)` 返回`x`的反双曲余弦(inverse hyperbolic cosine)

-   `Math.atanh(x)` 返回`x`的反双曲正切(inverse hyperbolic tangent)

 

BigInt 数据类型

JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。ES2020 引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

 

为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n。

1234 // 普通整数
1234n // BigInt
// BigInt 的运算
1n + 2n // 3n

 

BigInt 同样可以使用各种进制表示,都要加上后缀n。

0b1101n // 二进制
0o777n // 八进制
0xFFn // 十六进制

 

BigInt 与普通整数是两种值,它们之间并不相等。

42n === 42 // false

 

typeof运算符对于 BigInt 类型的数据返回bigint。

typeof 123n // 'bigint'

 

BigInt 可以使用负号(-),但是不能使用正号(+),因为会与 asm.js 冲突。

-42n // 正确
+42n // 报错

 

JavaScript 以前不能计算70的阶乘(即70!),因为超出了可以表示的精度。

let p = 1;
for (let i = 1; i <= 70; i++) {
  p *= i;
}
console.log(p); // 1.197857166996989e+100

 

现在支持大整数了,就可以算了,浏览器的开发者工具运行下面代码,就OK。

let p = 1n;
for (let i = 1n; i <= 70n; i++) {
  p *= i;
}
console.log(p); // 11978571...00000000n

 

数学运算方面,BigInt 类型的+、-、*和**这四个二元运算符,与 Number 类型的行为一致。除法运算/会舍去小数部分,返回一个整数。

9n / 5n

// 1n

// 例外:

-   不带符号的右移位运算符`>>>`

-   一元的求正运算符`+`

 

BigInt 不能与普通数值进行混合运算。

1n + 1.3 // 报错

 

BigInt 与字符串混合运算时,会先转为字符串,再进行运算。

'' + 123n // "123"

 

函数扩展

函数参数的默认值

function fn(x, y = 1) {
  console.log(x + y);
}
fn(10); // 11
// 注:通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

 

rest 参数

// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

 

3.严格模式

ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

// 报错
function doSomething(a, b = a) {
  'use strict';
  // code
}
// 报错
const doSomething = function ({a, b}) {
  'use strict';
  // code
};
// 报错
const doSomething = (...a) => {
  'use strict';
  // code
};
const obj = {
  // 报错
  doSomething({a, b}) {
    'use strict';
    // code
  }
};

 

name 属性

function foo() {}
foo.name // "foo"

 

箭头函数

const fn = x => x+1; // 最简写法
// 等价于:
const fn = (x) => {
    return x+1;
}

注:如果只有一个函数形参,()可省略;如果箭头函数只有一行语句{}和return可省略;

 

特殊情况

//由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

 

 

特别注意!!!(此处面试考了几百遍,敲黑板!!!)

(1)箭头函数没有自己的`this`对象,内部的`this`就是定义时上层作用域中的`this`。。

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

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

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

 

数组的扩展

扩展运算符

复制数组

const a1 = [1, 2];
const a2 = [...a1]; // [1, 2];

注:返回的是一个新数组

 

合并数组

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const newArr = [...arr1, ...arr2]; //['a', 'b', 'c'];
// `a3``a4`是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了引用指向的值,会同步反映到新数组。
const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];
const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];
a3[0] === a1[0] // true
a4[0] === a1[0] // true

 

 

将字符串转为真正的数组。

[...'hello'] 
// [ "h", "e", "l", "l", "o" ]

 

数组去重

[...new Set(arr)];

注:如果是数组对象,不能使用该方法去重

 

Array.from()

用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

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']

 

Array.of()

用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]

 

find()

用于找出第一个符合条件的数组成员。

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

 

findIndex()

返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

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

 

fill()

使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

注:第一个参数是用什么填充,第二个是填充起始位置的索引,第三个参数是结束位置的索引

 

includes()

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

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

 

keys()

是对键名的遍历,数组的键名为索引。

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

 

values()

是对键值的遍历

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

 

entries()

是对键值对的遍历。

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

 

flat()

用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]

 

flatMap()

flatMap()方法对原数组的每个成员执行一个函数

[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]

 

超级重点(最常用的数组方法)

forEach

方法对数组的每个元素执行一次给定的函数。(类似于for循环)

[1,2,3].forEach((item, index, arr) => console.log(item,index,arr))
// 1 0 (3) [1, 2, 3]
// 2 1 (3) [1, 2, 3]
// 3 2 (3) [1, 2, 3]

注:个人喜欢结合箭头函数使用,该方法可以传入三个参数,item数组中正在处理的当前元素,index数组中正在处理的当前元素的索引,arr源数组。

 

 

filter

创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。(也就是过滤出满足当前条件的所有元素)

[{name: '张三',id: 1},{name: '李四',id: 2},{name: '张三',id: 3}].filter(item => item.name == "张三");
// [{name: '张三',id: 1},{name: '张三',id: 3}] 筛选出name == "张三"的数据.

也可以设置同forEach一样的参数。该方法可以传入三个参数,item数组中正在处理的当前元素,index数组中正在处理的当前元素的索引,arr源数组。

 

map

创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

// 第一种用法
[{name: '张三',id: 1},{name: '李四',id: 2},{name: '张三',id: 3}].map(item => item.name);
// ["张三", "李四", "张三"]
// 第二种用法
[1, 2, 3].map(item => item*10);
// [10, 20, 30]
// 第三种用法
[{name: '张三',id: 1},{name: '李四',id: 2},{name: '张三',id: 3}].map(item => {
    return {
        value: item.id,
        label: item.name
    }
});
// [{label: '张三',value: 1},{label: '李四',value: 2},{label: '张三',value: 3}]

该方法可以传入三个参数,item数组中正在处理的当前元素,index数组中正在处理的当前元素的索引,arr源数组。

 

some

方法测试数组中是不是至少有1个元素通过了被提供的函数测试。(只要有一个元素满足条件就返回true)

const roleList = [{roleId: 1, roleName: '超级管理员'},{roleId: 2, roleName: '审核员'},{roleId: 3, roleName: '学员'}]; // 当前用户的角色
// 判断他是不是超级管理员
const isAdmin = roleList.some(item => item.roleId == 1); // true
if (isAdmin) {
}

该方法可以传入三个参数,item数组中正在处理的当前元素,index数组中正在处理的当前元素的索引,arr源数组。

 

every

测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

const roleList = [{roleId: 1, roleName: '超级管理员'},{roleId: 2, roleName: '审核员'},{roleId: 3, roleName: '学员'}]; // 当前用户的角色
// 判断他不是超级管理员
const isAdmin = roleList.every(item => item.roleId != 1); // false
if (isAdmin) {
}

该方法可以传入三个参数,item数组中正在处理的当前元素,index数组中正在处理的当前元素的索引,arr源数组。

 

reduce()

累加

let reduce=(...arr)=>{ 
    return arr.reduce((num,sum)=>{ 
        return sum + num; 
    },0); 
} 
console.log(reduce(...arr));//15

 

 

将二维数组转化为一维

const flattened = [[0, 1], [2, 3], [4, 5]].reduce((a, b) => a.concat(b),[]);
// [0, 1, 2, 3, 4, 5]

 

计算数组中每个元素出现的次数

const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
const countedNames = names.reduce((allNames, name) => {
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

 

数组去重

let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
let result = arr.sort().reduce((init, current) => {
    if(init.length === 0 || init[init.length-1] !== current) {
        init.push(current);
    }
    return init;
}, []);
console.log(result); //[1,2,3,4,5]

 

按顺序运行Promise

function runPromiseInSequence(arr, input) {
  return arr.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input)
  );
}
function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5);
  });
}
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2);
  });
}
function f3(a) {
 return a * 3;
}
function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4);
  });
}
const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10)
  .then(console.log);   // 1200

 

使用 reduce实现map

if (!Array.prototype.mapUsingReduce) {
  Array.prototype.mapUsingReduce = function(callback, thisArg) {
    return this.reduce(function(mappedArray, currentValue, index, array) {
      mappedArray[index] = callback.call(thisArg, currentValue, index, array)
      return mappedArray
    }, [])
  }
}
[1, 2, , 3].mapUsingReduce(
  (currentValue, index, array) => currentValue + index + array.length
) // [5, 7, , 10]

该方法可以传入四个参数,acc累加器,cur当前值,idx当前索引,arr为源数组。以上操作还有其他方式,就不一一列举了。

 

对象的扩展

属性的简洁表示法

const name = "张三";
const id = 1;
const obj = {
  name: name,
  id: id
};
// 如果对象的属性名和变量名一致,可简写成:
const obj = {name,id};
obj // {name: "张三", id: 1}

 

 

属性名表达式

// 第一种
obj['a' + 'bc'] = 123;
// 第二种
let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};
obj.hello() // hi

 

方法的 name 属性

const person = {
  sayName() {
    console.log('hello!');
  },
};
person.sayName.name   // "sayName"

 

属性的可枚举性和遍历

Object.getOwnPropertyDescriptor 方法可以获取该属性的描述对象。

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

 

描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。

目前,有四个操作会忽略`enumerable`为`false`的属性。

-   `for...in`循环:只遍历对象自身的和继承的可枚举的属性。

-   `Object.keys()`:返回对象自身的所有可枚举的属性的键名。

-   `JSON.stringify()`:只串行化对象自身的可枚举的属性。

-   `Object.assign()`: 忽略`enumerable`为`false`的属性,只拷贝对象自身的可枚举的属性。

 

 

属性的遍历

ES6 一共有 5 种方法可以遍历对象的属性。

(1)for...in

 

`for...in`循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

 

`Object.keys`返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

(3)Object.getOwnPropertyNames(obj)

 

`Object.getOwnPropertyNames`返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4)Object.getOwnPropertySymbols(obj)

 

`Object.getOwnPropertySymbols`返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5)Reflect.ownKeys(obj)

 

`Reflect.ownKeys`返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

-   首先遍历所有数值键,按照数值升序排列。

-   其次遍历所有字符串键,按照加入时间升序排列。

-   最后遍历所有 Symbol 键,按照加入时间升序排列。

// 比较简单就不写了

 

对象的扩展运算符

解构赋值

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

 

 

扩展运算符

(1). 取出参数对象的所有可遍历属性,拷贝到当前对象之中

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

 

(2). 如果扩展运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象。

{...'hello'}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

 

(3). 合并两个对象(此处用的较多)

let params = {
    ...this.pageObj, // 分页对象
    ...this.searchForm  // 搜索框表单对象
}

 

(4). 如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉

let obj = {name: '张三', id: 1}; // 可能里面还有几十条数据,但是我们只想改变用户名
let userObj = {...obj,name:'李四'}; // {name: "李四", id: 1}

 

扩展: 如果想完整克隆一个对象,还拷贝对象原型的属性,可采用以下方法。

// 写法一
const clone1 = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};
// 写法二
const clone2 = Object.assign(
  Object.create(Object.getPrototypeOf(obj)),
  obj
);
// 写法三
const clone3 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)

 

Object.is()

它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
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}

 

如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

 

浅拷贝

const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
// `Object.assign()`拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

 

数组的处理

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

 

取值函数的处理

const source = {
  get foo() { return 1 }
};
const target = {};
Object.assign(target, source)
// `Object.assign()`只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。

 

为对象添加属性

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}

 

为对象添加方法

Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});
// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) {
  ···
};
SomeClass.prototype.anotherMethod = function () {
  ···
};

 

为属性指定默认值

const DEFAULTS = {
  logLevel: 0,
  outputFormat: 'html'
};
function processContent(options) {
  options = Object.assign({}, DEFAULTS, options);
  console.log(options);
  // ...
}

 

Object.getOwnPropertyDescriptors()

返回指定对象所有自身属性(非继承属性)的描述对象。

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

 

__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

以上方法都是用来操作原型链的,比较简单省略不写

 

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

Object.keys()返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。Object.values()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
  console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

 

Object.fromEntries()

方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }

 

运算符扩展

指数运算符(**)

2 ** 2 // 4
2 ** 3 // 8
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;

 

链判断运算符

如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。

// 错误的写法
const  firstName = message.body.user.firstName || 'default';
// 正确的写法
const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';
// 链判断运算符简写
const firstName = message?.body?.user?.firstName || 'default';

 

(1)短路机制

本质上,?.运算符相当于一种短路机制,只要不满足条件,就不再往下执行。

a?.[++x]
// 等同于
a == null ? undefined : a[++x]

 

(2)括号的影响

如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。

(a?.b).c
// 等价于
(a == null ? undefined : a.b).c

 

(3)报错场合

以下写法是禁止的,会报错。

// 构造函数
new a?.()
new a?.b()
// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 链判断运算符的左侧是 super
super?.()
super?.foo
// 链运算符用于赋值运算符左侧
a?.b = c

 

4)右侧不得为十进制数值

为了保证兼容以前的代码,允许foo?.3:0被解析成foo ? .3 : 0,因此规定如果?.后面紧跟一个十进制数字,那么?.不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。

 

Null 判断运算符

读取对象属性的时候,如果某个属性的值是null或undefined,有时候需要为它们指定默认值。

const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;

 

开发者的原意是,只要属性的值为null或undefined,默认值就会生效,但是属性的值如果为空字符串或false或0,默认值也会生效。

为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

 

逻辑赋值运算符

将逻辑运算符与赋值运算符进行结合。

// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)

 

// 老的写法
user.id = user.id || 1;

 

// 新的写法
user.id ||= 1;

 

Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。

const sym = Symbol('foo');
sym.description // "foo"

你只需要暂时知道有这么个数据类型,表示独一无二的值即可。因为项目上真的很少很少用到。如果想看详细介绍,请移步阮一峰的es6文档

 

Set 和 Map 数据结构

Set

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

  set

  1. add方法为其添加成员;

  2. has查看是否有该值;

  3. delete删除某个值,返回布尔值

  4. clear清除所有成员

  5. size返回Set实例的成员总数

  // 比较简单,省略代码

 

2.Map

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

 map

  1. has查看是否有该值

  2. get获取属性

  3. set设置属性

  4. delete删除属性

  5. size属性返回 Map 结构的成员总数。

  6. clear方法清除所有成员,没有返回值。

  // 比较简单,省略代码

 

Set和Map的遍历

//set
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);
}
//-------------------------------------------------------------------------
// map
const map = new Map([
  ['F', 'no'],
  ['T''yes'],
]);
for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

 

4.WeakSet

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

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
const ws = new WeakSet();
const obj = {};
const foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo);    // false
ws.delete(window);
ws.has(window);    // false

 

WeakMap

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

const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"

 

WeakMap 的用途

let myWeakmap = new WeakMap();
myWeakmap.set(
  document.getElementById('logo'),
  {timesClicked: 0})
;
document.getElementById('logo').addEventListener('click', function() {
  let logoData = myWeakmap.get(document.getElementById('logo'));
  logoData.timesClicked++;
}, false);

上面代码中,document.getElementById('logo')是一个 DOM 节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,对应的键名就是这个节点对象。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。

 

WeakRef

用于直接创建对象的弱引用

let target = {};
let wr = new WeakRef(target);

target是原始对象,构造函数WeakRef()创建了一个基于target的新对象wr。这里,wr就是一个 WeakRef 的实例,属于对target的弱引用,垃圾回收机制不会计入这个引用,也就是说,wr的引用不会妨碍原始对象target被垃圾回收机制清除。

 

Proxy

用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

var proxy = new Proxy({}, {
  get: function(target, propKey) {
    return 35;
  }
});
let obj = Object.create(proxy);
obj.time // 35

-   **get(target, propKey, receiver)** :拦截对象属性的读取,比如`proxy.foo`和`proxy['foo']`。

-   **set(target, propKey, value, receiver)** :拦截对象属性的设置,比如`proxy.foo = v`或`proxy['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)`。

 

Reflect

也是 ES6 为了操作对象而提供的新 API,该方法用的比较少,所以暂不讨论,只需要暂时知道有这些东西即可。

(1) 将`Object`对象的一些明显属于语言内部的方法(比如`Object.defineProperty`),放到`Reflect`对象上。现阶段,某些方法同时在`Object`和`Reflect`对象上部署,未来的新方法将只部署在`Reflect`对象上。也就是说,从`Reflect`对象上可以拿到语言内部的方法。

(2) 修改某些`Object`方法的返回结果,让其变得更合理。比如,`Object.defineProperty(obj, name, desc)`在无法定义属性时,会抛出一个错误,而`Reflect.defineProperty(obj, name, desc)`则会返回`false`。

(3) 让`Object`操作都变成函数行为。某些`Object`操作是命令式,比如`name in obj`和`delete obj[name]`,而`Reflect.has(obj, name)`和`Reflect.deleteProperty(obj, name)`让它们变成了函数行为。

(4)`Reflect`对象的方法与`Proxy`对象的方法一一对应,只要是`Proxy`对象的方法,就能在`Reflect`对象上找到对应的方法。这就让`Proxy`对象可以方便地调用对应的`Reflect`方法,完成默认行为,作为修改行为的基础。也就是说,不管`Proxy`怎么修改默认行为,你总可以在`Reflect`上获取默认行为。

 

Promise

含义

是异步编程的一种解决方案

 

特点。

(1)对象的状态不受外界影响。`Promise`对象代表一个异步操作,有三种状态:`pending`(进行中)、`fulfilled`(已成功)和`rejected`(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

 

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。`Promise`对象的状态改变,只有两种可能:从`pending`变为`fulfilled`和从`pending`变为`rejected`。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对`Promise`对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

 

2.基本用法

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

 

.then()

操作成功时的回调

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

 

.catch()

操作失败时的回调

getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

 

.finally()

指定不管 Promise 对象最后状态如何,都会执行的操作。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

 

.all()

用于将多个 Promise 实例,包装成一个新的 Promise 实例。(可用于同时进行多个请求)

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

 

.race()

将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);
p
.then(console.log)
.catch(console.error);

 

 

.allSettled()

等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。

const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ]

 

.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

Promise.any([
  fetch('https://v8.dev/').then(() => 'home'),
  fetch('https://v8.dev/blog').then(() => 'blog'),
  fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) =>// 只要有一个 fetch() 请求成功
  console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
  console.log(error);
});

 

10..resolve()操作成功时传入

(1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么`Promise.resolve`将不做任何修改、原封不动地返回这个实例。

 

(2) 参数是一个thenable对象

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

 

(3).参数不是具有then()方法的对象,或根本就不是对象

const p = Promise.resolve('Hello');
p.then(function (s) {
  console.log(s)
});
// Hello

 

(4)不带有任何参数

const p = Promise.resolve();
p.then(function () {
  // ...
});

 

事件循环(考点)

setTimeout(function () {
  console.log('three');
}, 0);
Promise.resolve().then(function () {
  console.log('two');
});
console.log('one');
// one
// two
// three

上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。

 

reject()操作失败时传入

Promise.reject('出错了')
.catch(e => {
  console.log(e === '出错了')
})
// true

 

Iterator 和 for...of 循环

Iterator

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

 

Iterator 的遍历过程:

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

 

(2)第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。

 

(3)第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。

 

(4)不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。

 

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

 

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

-   Array

-   Map

-   Set

-   String(字符串是一个类似数组的对象,也原生具有 Iterator 接口。)

-   TypedArray

-   函数的 arguments 对象

-   NodeList 对象(类似数组的对象)

 

 

只要数据结构具备Iterator(遍历器)接口便可以使用for...of遍历,对象(Object)没有默认部署 Iterator 接口,但是能通过特殊的方式进行for...of遍历

for...of 循环

数组

var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
  console.log(a); // 0 1 2 3
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.duanlonglong.com/qdjy/1236.html