ES6知识点笔记

什么是ES6

1.ECMAScript与JavaScript

ECMAScript是JavaScript的规范、标准,ECMAScript由ECMA制定,JavaScript由Netscape 公司创造。

2.ES6与ECMAScript 2015

ES6 是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等。

3.Babel转码器

Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,让使用ES6时不用担心现有环境是否支持。

// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {
  return item + 1;
});

let与const

1.let
`1.let与var类似,但变量只在声明代码块内有效`
//var
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10  

//let
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

`**for循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域`
for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc
`2.let不存在变量提升(即变量不可以在声明之前使用)而var存在`
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
`3.暂时性死区: 在代码块内,使用let命令声明变量之前,该变量都是不可用的。`
function bar(x = y, y = 2) {
  return [x, y];
}
bar();// 报错    y没有声明,交换两个参数位置。

//**typeof的不安全性
typeof x; // ReferenceError
let x;

//如果变量未声明 typeof反而不报错
typeof undeclared_variable // "undefined"
`4.不允许重复声明`
// 报错
function func() {
  let a = 10;
  let a = 1;
}
2.块级作用域
  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 同时,函数声明还会提升到所在的块级作用域的头部。
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function
上面例子实际运行的代码:
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

//**考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
// 块级作用域内部的函数声明语句,建议不要使用
{
  let a = 'secret';
  function f() {
    return a;
  }
}
// 块级作用域内部,优先使用函数表达式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

//**ES6 的块级作用域必须有大括号
// 第一种写法,报错
if (true) let x = 1;
// 第二种写法,不报错
if (true) {
  let x = 1;
}
3.const命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。并且必须立即初始化。
其他与let一致:在块级作用域内有效、变量不提升、存在暂时性死区、不可重复声明。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

//立即初始化
const foo;
// SyntaxError: Missing initializer in const declaration

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

4.顶层对象属性

顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1

let b = 1;
window.b // undefined
5.globleThis对象

ES2020 在语言标准的层面,引入globalThis作为顶层对象,在任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this。

变量的解构赋值

//set结构也可以使用数组的解构赋值
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"

//事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
1.默认值
let [foo = true] = [];
foo // true

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

let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined
2.对象的解构赋值
`1.对象的属性没有次序,变量必须与属性同名,才能取到正确的值。`
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
//相当于 let { foo:foo, bar:bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let {baz}={foo:"aaa",bar:"bbb"}
baz//undefined

`2.对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。
foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。`
var {foo:baz}={foo:"aaa",bar:"bbb"}
baz//"aaa"
foo // error: foo is not defined

let baz;
let {bar:baz}={bar:1}//SyntaxError:Duplicate declaration "baz"
`3.上面代码中,解构赋值的变量都会重新声明,所以报错了。
不过,因为var命令允许重新声明,所以这个错误只会在使用let和const命令时出现。
如果没有第二个let命令,上面的代码就不会报错。`
let foo;
({foo}={foo:1});//成功
let baz;
({bar:baz}={bar:1})//成功

`4.下面只有line是变量,loc和start都是模式,不是变量`
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};
let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc  // Object {start: Object}
start // Object {line: 1, column: 5}

`5.Object.setPrototypeOf()  赋原型`
const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);
const { foo } = obj1;
foo // "bar"

`6.对象解构的默认值`
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
//默认值生效的条件是,对象的属性值严格等于undefined。
3.字符串的解构赋值
`1.[]`
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

`2.数组对象有length属性  {}`
let {length : len} = 'hello';
len // 5
4.数值和布尔值的解构赋值
`1.解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。`
let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
5.函数参数的解构赋值
function move({x = 0, y = 0} = {}) {
  return [x, y];
}

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



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]


[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]
6.圆括号问题
`变量声明语句,模式不能使用圆括号。`
// 全部报错
let [(a)] = [1];

let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let { o: ({ p: p }) } = { o: { p: 2 } };


`赋值语句的非模式部分,可以使用圆括号。`
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
7.用途
`1.交换变量的值`
let x = 1;
let y = 2;

[x, y] = [y, x];

`2.从函数返回多个值`
// 返回一个数组
function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象
function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

`3.函数参数的定义`
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});

`4.提取 JSON 数据`
let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);  // 42, "OK", [867, 5309]

`5.函数参数的默认值`
jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
} = {}) {
  // ... do stuff
};
`6.遍历 Map 结构`
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
  console.log(key + " is " + value);		// first is hello       second is world
}
//只获取键值
// 获取键名
for (let [key] of map) {
  // ...
}
// 获取键值
for (let [,value] of map) {
  // ...
}

`7.输入模块的指定方法`
const { SourceMapConsumer, SourceNode } = require("source-map");

字符串的扩展

       ES6 为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历。

for (let codePoint of 'foo') {
  console.log(codePoint)		// "f" "o" "o"
}

1.模板字符串

       模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
2.fromCodePoint

       与 ES5 提供的String.fromCharCode()方法类似,但能识别码点大于 0xFFFF 的字符。

String.fromCharCode(0x20BB7)
// "ஷ"

String.fromCodePoint(0x20BB7)
// "𠮷"
3.codePointAt

       charAt() 方法无法读取整个字符,charCodeAt() 方法只能分别返回前两个字节和后两个字节的值。
       ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

let s = '𠮷a';

s.length // 3
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

s.codePointAt(0) // 134071
s.codePointAt(1) // 57271

s.codePointAt(2) // 97
4.String.raw

该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串.

String.raw`Hi\n${2+3}!`
// 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"

String.raw`Hi\u000A!`;
// 实际返回 "Hi\\u000A!",显示的是转义后的结果 "Hi\u000A!"

String.raw`Hi\\n`
// 返回 "Hi\\\\n"

String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"
5.normalize
6.includes(), startsWith(), endsWith()

       传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

`这三个方法都支持第二个参数,表示开始搜索的位置`
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
7.repeat()

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

'x'.repeat(3) // "xxx"
'na'.repeat(0) // ""

`参数如果是小数,会被取整。`
'na'.repeat(2.9) // "nana"

`如果repeat的参数是负数或者Infinity,会报错。`
'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError

`如果参数是 0 到-1 之间的小数,则等同于 0, 因为会先进行取整运算
 参数NaN等同于 0。
 如果参数是字符串,则会先转换成数字。`
'na'.repeat(-0.9) // ""
'na'.repeat(NaN) // ""
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"
8.padStart(),padEnd()

       如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

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

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

`原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串`
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'

`如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。`
'abc'.padStart(10, '0123456789')

`如果省略第二个参数,默认使用空格补全长度。`
'x'.padStart(4) // '   x'
'x'.padEnd(4) // 'x   '
9.trimStart(),trimEnd()

ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

const s = '  abc  ';

s.trim() // "abc"
s.trimStart() // "abc  "
s.trimEnd() // "  abc"
`浏览器还部署了额外的两个方法,trimLeft()是trimStart()的别名,trimRight()是trimEnd()的别名。`
10.matchAll()

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

数值的扩展

1.二进制和八进制表示法

ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。

0b111110111 === 503 // true
0o767 === 503 // true

如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。

Number('0b111')  // 7
Number('0o10')  // 8
2.Number.isFinite(), Number.isNaN()

Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。
Number.isNaN()用来检查一个值是否为NaN。
这两个新方法只对数值有效

isFinite(25) // true
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false

isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
3.Number.parseInt(), Number.parseFloat()

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

4.Number.isInteger()

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

`JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。`
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false

`由于 JavaScript 采用 IEEE 754 标准,数值存储为64位双精度格式,数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位)。`
Number.isInteger(3.0000000000000002) // true
`如果一个数值的绝对值小于Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0。`
Number.isInteger(5E-324) // false
Number.isInteger(5E-325) // true  太小直接转换成0
5.Number.EPSILON

Number.EPSILON的实质是一个可以接受的最小误差范围,2.220446049250313e-16

function withinErrorMargin (left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}

0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true

1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true
6.安全整数和 Number.isSafeInteger()

ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来安全整数的上下限。

Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true

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

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

7.Math 对象的扩展

Math.trunc() 用于去除一个数的小数部分,返回整数部分。
Math.sign() 用来判断一个数到底是正数、负数、还是零。
Math.cbrt() 用于计算一个数的立方根。
Math.clz32() 将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。
Math.imul() 返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。(等同于(a * b)|0的效果)
Math.fround() 返回一个数的32位单精度浮点数形式。
Math.hypot() 返回所有参数的平方和的平方根。

对数方法:
Math.expm1() 返回 ex - 1,即Math.exp(x) - 1。
Math.log1p() 返回1 + x的自然对数,即Math.log(1 + x)。
Math.log10() 返回以 10 为底的x的对数。
Math.log2() 返回以 2 为底的x的对数。

双曲函数方法:

  • 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)
8.指数运算符

ES2016 新增了一个指数运算符 (**) 。

2 ** 2 // 4
2 ** 3 // 8

指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。

let a = 1.5;
a **= 2;
// 等同于 a = a * a;

let b = 4;
b **= 3;
// 等同于 b = b * b * b;
9.BigInt 数据类型、BigInt 对象

函数扩展

1.定义了默认值的参数,应该是函数的尾参数。
function f(x, y = 5, z) {
  return [x, y, z];
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]
2.函数的 length 属性

返回没有指定默认值的参数个数。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
3. rest参数

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。

// arguments变量的写法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

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

rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

// 报错
function f(a, ...b, c) {
  // ...
}

函数的length属性,不包括 rest 参数。

4.严格模式

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

5.name属性

返回该函数的函数名。

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

如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

Function构造函数返回的函数实例,name属性的值为anonymous。

(new Function).name // "anonymous"

bind返回的函数,name属性值会加上bound前缀。

function foo() {};
foo.bind({}).name // "bound foo"

(function(){}).bind({}).name // "bound "
6. 箭头函数

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

数组扩展

1.扩展运算符

任何定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。

`自定义Iterator`
Number.prototype[Symbol.iterator] = function*() {
  let i = 0;
  let num = this.valueOf();
  while (i < num) {
    yield i++;
  }
}

console.log([...5]) // [0, 1, 2, 3, 4]
2.Array.from()

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

// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
  return p.textContent.length > 100;
});

// arguments对象
function foo() {
  var args = Array.from(arguments);
  // ...
}
3.Array.of()

Array.of方法用于将一组值,转换为数组。

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

Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
4.copyWithon()
Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数。

target(必需):从该位置开始替换数据。如果为负值,表示倒数。
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
5.find()和findIndex()

数组实例的find方法,用于找出第一个符合条件的数组成员。如果没有符合条件的成员,则返回undefined。
数组实例的findIndex方法,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);    // 26
6.fill()

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

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

new Array(3).fill(7)
// [7, 7, 7]

`fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。`
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]

let arr = new Array(3).fill([]);
arr[0].push(5);
arr
// [[5], [5], [5]]
7.entries(),keys() 和 values()
8.includes()
9.flat(),flatMap()
10.数组的空位
11.sort()

对象的扩展

1.属性名表达式

把表达式放在方括号内

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123,
  ['h' + 'ello']() {
    return 'hi';
  }
};
2. 属性遍历

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 键,按照加入时间升序排列。

Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
3.super关键字

指向当前对象的原型对象。

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

JavaScript 引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

const proto = {
  x: 'hello',
  foo() {
    console.log(this.x);
  },
};

const obj = {
  x: 'world',
  foo() {
    super.foo();
  }
}

Object.setPrototypeOf(obj, proto);

obj.foo() // "world"
4. 链判断运算符

ES2020引入“链判断运算符” ?.

const firstName = message?.body?.user?.firstName || 'default';

//相当于
const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';

a?.b
// 等同于
a == null ? undefined : a.b

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

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

a?.()
// 等同于
a == null ? undefined : a()

规定如果?.后面紧跟一个十进制数字,那么?.不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。

5.Null 判断运算符

ES2020 引入了一个新的 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,默认值也会生效。

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

对象新增方法

1. Object.is()

== 和 === 的改进版。
ES5 可以通过下面的代码,部署Object.is

Object.defineProperty(Object, 'is', {
  value: function(x, y) {
    if (x === y) {
      // 针对+0 不等于 -0的情况
      return x !== 0 || 1 / x === 1 / y;
    }
    // 针对NaN的情况
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true
});
2.Object.assign()

对象合并
与扩展运算符一样 都为浅拷贝 第一层深拷贝第二层开始便是引用(数组合并也类似 concat 与 拓展运算符)
(1)浅拷贝
(2)同名属性替换
(3)数组处理
(4)取值函数处理

3.Object.getOwnPropertyDescriptors()

作用:
(1)主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。
(2)配合Object.create()方法,将对象属性克隆到一个新对象。这属于浅拷贝。

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

proto 本质上是一个内部属性,而不是一个正式的对外的 API。无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。

5.Object.keys(),Object.values(),Object.entries()
6. Object.fromEntries()

Symbol

js的第七种数据类型

1.Symbol.prototype.description
const sym = Symbol('foo');

sym.description // "foo"
2.消除魔术字符串
function getArea(shape, options) {
  let area = 0;

  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

字符串Triangle就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。

const shapeType = {
  triangle: Symbol()
};

function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });
3.属性名遍历

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

Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。

Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

4.Symbol.for(),Symbol.keyFor()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值