es6学习(上)

一. 字符串的新增方法

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

et s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

这三个方法都支持第二个参数,表示开始搜索的位置。

et s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

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

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

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

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

//但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于-0,repeat视同为 0。
'na'.repeat(-0.9) // ""  
'na'.repeat(NaN) // ""

//如果repeat的参数是字符串,则会先转换成数字。
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"

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') // '0123456abc'

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

//padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

//另一个用途是提示字符串格式。
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串,除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效;

const s = '  abc  ';
s.trim() // "abc"
s.trimStart() // "abc  "
s.trimEnd() // "  abc"

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

二. 正则的扩展

new RegExp(/abc/ig, 'i')
  1. sticky属性,表示是否设置了y修饰符。
var r = /hello\d/y;
r.sticky // true
  1. 正则表达式使用圆括号进行组匹配。
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
  1. 如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false
  1. 如果一个正则表达式在字符串里面有多个匹配,现在一般使用g修饰符或y修饰符,在循环里面逐一取出;

三. 数值的扩展

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

Number('0b111')  // 7     Number('0o10')  // 8
Number.isFinite(), Number.isNaN()
//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.isFinite一律返回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

如果参数类型不是NaN,Number.isNaN一律返回false。
它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。

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
  1. Number.parseInt(), Number.parseFloat()
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
//这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
  1. Number.isInteger()用来判断一个数值是否为整数。
Number.isInteger(25) // true		Number.isInteger(25.1) // false
//JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。
Number.isInteger(25) // true	Number.isInteger(25.0) // true
//如果参数不是数值,Number.isInteger返回false。
Number.isInteger() // false		Number.isInteger(null) // false
Number.isInteger('15') // false		Number.isInteger(true) // false
Number.isInteger(3.0000000000000002) // true

上面代码中,Number.isInteger的参数明明不是整数,但是会返回true。原因就是这个小数的精度达到了小数点后16个十进制位,转成二进制位超过了53个二进制位,导致最后的那个2被丢弃了。
类似的情况还有,如果一个数值的绝对值小于Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0。这时,Number.isInteger也会误判。
Number.isInteger(5E-324) // false Number.isInteger(5E-325) // true
上面代码中,5E-325由于值太小,会被自动转为0,因此返回true。
总之,如果对数据精度的要求较高,不建议使用Number.isInteger()判断一个数值是否为整数。

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

Number.isSafeInteger('a') // false
Number.isSafeInteger(null) // false
Number.isSafeInteger(NaN) // false
Number.isSafeInteger(Infinity) // false
Number.isSafeInteger(-Infinity) // false
Number.isSafeInteger(3) // true
Number.isSafeInteger(1.2) // false
Number.isSafeInteger(9007199254740990) // true
Number.isSafeInteger(9007199254740992) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false

这个函数的实现很简单,就是跟安全整数的两个边界值比较一下。
12. Math 对象的扩展
Math.trunc()用于去除一个数的小数部分,返回整数部分

Math.trunc(4.1)  //4		
Math.trunc(-4.9)  //-4
Math.trunc(-0.1234)  //-0		
Math.trunc(true) //1
Math.trunc('123.456') //123	
Math.trunc(null)  //0
Math.trunc(NaN)  //NaN		
Math.trunc()   //NaN
Math.trunc(undefined)  //NaN

Math.sign()方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。它会返回五种值。

Math.sign(-5) // -1			
Math.sign(5) // +1
Math.sign(0) // +0			
Math.sign(-0) // -0
Math.sign(NaN) // NaN		
Math.sign('')  // 0
Math.sign(true)  // +1 		
Math.sign(false)  // 0
Math.sign(null)  // 0		
Math.sign('9')  // +1
Math.sign('foo')  // NaN		
Math.sign()  // NaN
Math.sign(undefined)  // NaN

Math.cbrt()方法用于计算一个数的立方根。

Math.cbrt(-1) // -1		
Math.cbrt(0)  // 0
Math.cbrt(1)  // 1		
Math.cbrt(2)  // 1.2599210498948732
Math.cbrt('8') // 2		
Math.cbrt('hello') // NaN

Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。

Math.clz32(0) // 32		Math.clz32(1) // 31
Math.clz32(1000) // 22
Math.clz32(0b01000000000000000000000000000000) // 1
Math.clz32(0b00100000000000000000000000000000) // 2
Math.clz32(1 << 1) // 30		Math.clz32(1 << 2) // 29
Math.clz32(1 << 29) // 2
Math.clz32(3.2) // 30    Math.clz32(3.9) // 30

对于空值或其他类型的值,Math.clz32方法会将它们先转为数值,然后再计算。

Math.clz32() // 32			
Math.clz32(NaN) // 32
Math.clz32(Infinity) // 32		
Math.clz32(null) // 32
Math.clz32('foo') // 32		
Math.clz32([]) // 32
Math.clz32({}) // 32		
Math.clz32(true) // 31

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

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

Math.fround方法返回一个数的32位单精度浮点数形式。

Math.fround(0)   // 0		Math.fround(1)   // 1
Math.fround(2 ** 24 - 1)   // 16777215
//如果参数的绝对值大于 224,返回的结果便开始丢失精度。
Math.fround(2 ** 24)       // 16777216
Math.fround(2 ** 24 + 1)   // 16777216
//Math.fround方法的主要作用,是将64位双精度浮点数转为32位单精度浮点数。如果小数的精度超过24个二进制位,返回值就会不同于原值,否则返回值不变(即与64位双精度值一致)。
// 未丢失有效精度
Math.fround(1.125) // 1.125	Math.fround(7.25)  // 7.25
// 丢失精度
Math.fround(0.3)   // 0.30000001192092896
Math.fround(0.7)   // 0.699999988079071
Math.fround(1.0000000123) // 1
//对于 NaN 和 Infinity,此方法返回原值。对于其它类型的非数值,Math.fround 方法会先将其转为数值,再返回单精度浮点数。
Math.fround(NaN)      // NaN		Math.fround(Infinity) // Infinity
Math.fround('5')      // 5			Math.fround(true)     // 1
Math.fround(null)     // 0			Math.fround([])       // 0
Math.fround({})       // NaN

Math.hypot方法返回所有参数的平方和的平方根。

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755
Math.hypot();            // 0
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5');   // 7.0710678118654755
Math.hypot(-3);          // 3

如果参数不是数值,Math.hypot方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。
13. 指数运算符(**)。

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

这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。

2 ** 3 ** 2    // 512   相当于 2 ** (3 ** 2)
  1. BigInt 数据类型
    es2020引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n。
    BigInt 与普通整数是两种值,它们之间并不相等。
42n === 42 // false
//BigInt 可以使用负号(-),但是不能使用正号(+),因为会与 asm.js 冲突。
-42n // 正确			+42n // 报错
//JavaScript 原生提供BigInt对象,可以用作构造函数生成 BigInt 类型的数值。转换规则基本与Number()一致,将其他类型的值转为 BigInt。
BigInt(123) // 123n		BigInt('123') // 123n
BigInt(false) // 0n		BigInt(true) // 1n
//BigInt()构造函数必须有参数,而且参数必须可以正常转为数值,下面的用法都会报错。
new BigInt() // TypeError		BigInt(undefined) //TypeError
BigInt(null) // TypeError		BigInt('123n') // SyntaxError
BigInt('abc') // SyntaxError

四. 函数的扩展

  1. ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

使用参数默认值时,函数不能有同名参数。
函数的length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
16. 剩余参数,ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(2, 5, 3) // 10
//注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
// 报错
function f(a, ...b, c) {
  // ...
}
//函数的length属性,不包括 rest 参数。
(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1
  1. 严格模式
    ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
  2. name属性,返回该函数的函数名
function foo() {}
foo.name // "foo"
var f = function () {};
ES5:   f.name // ""
ES6:   f.name // "f"
const bar = function baz() {};
ES5:	bar.name // "baz"
ES6:	bar.name // "baz"

Function构造函数返回的函数实例,name属性的值为anonymous。
bind返回的函数,name属性值会加上bound前缀。
19. 箭头函数

var f = v => v;
// 等同于
var f = function (v) {
  return v;
};

//由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
//箭头函数可以与变量解构结合使用。
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}
  1. 尾部调用,就是指某个函数的最后一步是调用另一个函数。
function f(x){
  return g(x);
}
以下三种情况,都不属于尾调用。
// 情况一
function f(x){
  let y = g(x);
  return y;
}
// 情况二
function f(x){
  return g(x) + 1;
}
// 情况三
function f(x){
  g(x);
}
  1. 函数参数的尾逗号
function clownsEverywhere(
  param1,
  param2,
) { /* ... */ }
clownsEverywhere(
  'foo',
  'bar',
);
  1. Function.prototype.toString()
    修改后的toString()方法,明确要求返回一模一样的原始代码,包括注释。
function /* foo comment */ foo () {}
foo.toString()
// "function /* foo comment */ foo () {}"

五. 数组的扩展

  1. 扩展运算符
console.log(...[1, 2, 3])  // 1 2 3
console.log(1, ...[2, 3, 4], 5)    // 1 2 3 4 5
[...document.querySelectorAll('div')]    // [<div>, <div>, <div>]

由于扩展运算符可以展开数组,所以不再需要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);

另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。

// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);

扩展运算符的应用
(1)复制数组

const a1 = [1, 2];
// 写法一   const a2 = [...a1];
// 写法二    const [...a2] = a1;

(2)合并数组

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);    // [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]    // [ 'a', 'b', 'c', 'd', 'e' ]

(3)与解构赋值结合,用于生成数组。

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest  // []
const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
(4)字符串,扩展运算符还可以将字符串转为真正的数组。

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

(6)Map 和 Set 结构,Generator 函数
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。

let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
const go = function*(){
  yield 1;
  yield 2;
  yield 3;
};
[...go()] // [1, 2, 3]
  1. 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']

只要是部署了 Iterator 接口的数据结构,Array.from都能将其转为数组。

Array.from('hello')      // ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
Array.from(namesSet)     // ['a', 'b']

扩展运算符(…)也可以将某些数据结构转为数组。

// NodeList对象
[...document.querySelectorAll('div')]
  1. Array.of()用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
26. 数组实例的 find() 和 findIndex()
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

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

数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2
  1. 数组实例的 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']
  1. 数组实例的 entries()keys()values(),用于遍历数组。它们都返回一个遍历器对象,可以用for…of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"
  1. 数组实例的 includes()
    Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
    方法类似
[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

Array.prototype.sort() 的排序稳定性
30. 排序稳定性(stable sorting)是排序算法的重要属性,指的是排序关键字相同的项目,排序前后的顺序不变。

const arr = [ 'peach', 'straw', 'apple', 'spork'];
const stableSorting = (s1, s2) => {
  if (s1[0] < s2[0]) return -1;
  return 1;
};
arr.sort(stableSorting)
// ["apple", "peach", "straw", "spork"]

上面代码对数组arr按照首字母进行排序。排序结果中,straw在spork的前面,跟原始顺序一致,所以排序算法stableSorting是稳定排序。

const unstableSorting = (s1, s2) => {
  if (s1[0] <= s2[0]) return -1;
  return 1;
};
arr.sort(unstableSorting)
// ["apple", "peach", "spork", "straw"]

六. 对象的扩展

  1. 属性的简洁表示法,变量直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
//除了属性简写,方法也可以简写。
const o = {
  method() {
    return "Hello!";
  }
};
// 等同于
const o = {
  method: function() {
    return "Hello!";
  }
};
  1. 属性名表达式
    JavaScript 定义对象的属性,有两种方法。
// 方法一   obj.foo = true;
// 方法二    obj['a' + 'bc'] = 123;

ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。

let propKey = 'foo';
let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};
  1. 方法的name属性
    函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
const person = {
  sayName() {
    console.log('hello!');
  },
};
person.sayName.name   // "sayName"

有两种特殊情况:bind方法创造的函数,name属性返回bound加上原函数的名字;Function构造函数创造的函数,name属性返回anonymous。
34. 属性的可枚举性和遍历
Object.getOwnPropertyDescriptor()方法可以获取该属性的描述对象。

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

七. 对象的新增方法

  1. Object.is()用来比较两个值是否严格相等,与严格比较运算符===的行为基本一致
Object.is('foo','foo')  //true
Object.is({},{})  //false
//不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true			NaN === NaN // false
Object.is(+0, -0) // false		Object.is(NaN, NaN) // true
  1. **Object.assign()**用于对象的合并,并将元对象的所有可枚举属性,复制到目标对象
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}

Object.assign()方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
39. 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 } }
  1. **Object.setPrototypeOf()**方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
let proto = {};		let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;		proto.z = 40;
obj.x // 10		obj.y // 20
obj.z // 40
//该方法等同于下面的函数。
function setPrototypeOf(obj, proto) {
  obj.__proto__ = proto;
  return obj;
}
  1. Object.getPrototypeOf(),该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。
    Object.getPrototypeOf(obj);
    下面是一个例子。
function Rectangle() {
  // ...
}
const rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype   // true
Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype    // false
42. Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }

八. Symbol数据结构

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型

let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1 // Symbol(foo)				s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"		s2.toString() // "Symbol(bar)"

注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false

Symbol 值不能与其他类型的值进行运算,会报错。
但是,Symbol 值可以显式转为字符串。
另外,Symbol 值也可以转为布尔值,但是不能转为数值。
有时,我们希望重新使用同一个 Symbol 值,Symbol.for()方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
Symbol.for("bar") === Symbol.for("bar")  // true
Symbol("bar") === Symbol("bar")  // false


//Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

上面代码中,变量s2属于未登记的 Symbol 值,所以返回undefined。
注意,Symbol.for()为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。

九. Set和Map数据结构

  1. Set
    它类似于数组,但是成员的值都是唯一的,没有重复的值。
    Set本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
  console.log(i);
}
// 2 3 5 4

上面代码通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]    // [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 例三
const set = new Set(document.querySelectorAll('div'));
set.size // 56

去除数组的重复成员 […new Set(array)]
上面的方法也可以用于,去除字符串里面的重复字符。

[...new Set('ababbc')].join('')   // "abc"

在 Set 内部,两个NaN是相等的。另外,两个对象总是不相等的。

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():清除所有成员,没有返回值。

上面这些属性和方法的实例如下。

s.add(1).add(2).add(2);  // 注意2被加入了两次
s.size // 2    		s.has(1) // true
s.has(2) // true		s.has(3) // false
s.delete(2);			s.has(2) // false

去除数组重复成员的另一种方法。

function dedupe(array) {
  return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
Set 结构的实例有四个遍历方法,可以用于遍历成员。
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员

可以省略values方法,直接用for…of循环遍历 Set。

let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
  console.log(x);
}
// red
// green
// blue

扩展运算符(…)内部使用for…of循环,所以也可以用于 Set 结构。

let set = new Set(['red', 'green', 'blue']);
let arr = [...set];      // ['red', 'green', 'blue']

44. Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

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

Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

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"

如果对同一个键多次赋值,后面的值将覆盖前面的值。

const map = new Map();
map.set(1, 'aaa').set(1, 'bbb');
map.get(1) // "bbb"

如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0和-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。

let map = new Map();
map.set(-0, 123);
map.get(+0) // 123
map.set(true, 1);
map.set('true', 2);
map.get(true) // 1
map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3
map.set(NaN, 123);
map.get(NaN) // 123

Map实例的属性和操作方法:
size属性返回Map结构的成员总数;
Map.prototype.set(key,value):set方法设置键名key对应的键值为value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就新生成该键;

Map.prototype.get(key)get方法读取key对应的键值,返回undefined;
Map.prototype.has(key): has方法返回一个布尔值,表示某个键是否在当前Map对象之中;
Map.prototype.delete(key): delete方法删除某个键,返回true,如果删除失败,返回false;
Map.prototype.clear():clear方法清除所有成员,没有返回值
Map 遍历方法。
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 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"




//Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)。
const map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);
[...map.keys()]   // [1, 2, 3]
[...map.values()]   // ['one', 'two', 'three']
[...map.entries()]   // [[1,'one'], [2, 'two'], [3, 'three']]
[...map]     // [[1,'one'], [2, 'two'], [3, 'three']]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

the_lower

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值