【前端学习】ES6(二)

九、数组的扩展

扩展运算符(…)
1、常规用法
…后跟空数组则没有任何作用

[...[],1]
// [1] 注意不是[undefined,1]

仅当在函数参数列表中,扩展运算符才可以放在小括号内。

(...[1,2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1,2]))
// Uncaught SyntaxError: Unexpected number
console.log(...[1,2])
// 1 2

2、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);

3、应用
3.1克隆数组

// ES5
const a1 =[1,2];
const a2 = a1.concat();
a2[0]=2;
a1 // [1, 2]
//ES6
// 写法一
const a2 =[...a1];
// 写法二
const[...a2]= a1;

3.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.3和解构相结合

// ES5
a = list[0], rest = list.slice(1)
// ES6
[a,...rest]= list

3.4直接展开字符串

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

3.5任何使用iterator接口的对象都可以用扩展运算符转成数组
Map 和 Set 结构,Generator 函数

//map对象
let map =newMap([
[1,'one'],
[2,'two'],
[3,'three'],
]);
let arr =[...map.keys()];// [1, 2, 3]
//generator函数
const go =function*(){
  yield 1;
  yield 2;
  yield 3;
};
[...go()]// [1, 2, 3]

数组的空位
1、空位
数组的空位指,数组的某一个位置没有任何值。比如,Array构造函数返回的数组都是空位。

Array(3) // [, , ,]

注意,空位不是undefined,空位是null,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false

上面代码说明,第一个数组的 0 号位置是有值的,第二个数组的 0 号位置没有值。
2、ES5处理空位
ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。
a、forEach(), filter(), reduce(), every() 和some()都会跳过空位。
b、map()会跳过空位,但会保留这个值
c、join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。

// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true) // ['a','b']
// every方法
[,'a'].every(x => x==='a') // true
// reduce方法
[1,,2].reduce((x,y) => x+y) // 3
// some方法
[,'a'].some(x => x !== 'a') // false
// map方法
[,'a'].map(x => 1) // [,1]
// join方法
[,'a',undefined,null].join('#') // "#a##"
// toString方法
[,'a',undefined,null].toString() // ",a,,"

3、ES6处理空位
ES6 则是明确将空位转为undefined。
Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。

Array.from(['a',,'b'])
// [ "a", undefined, "b" ]

扩展运算符(…)也会将空位转为undefined。
fill()会将空位视为正常的数组位置。
for…of循环也会遍历空位。如果改成map方法遍历,空位是会跳过的。
entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0

Array.prototype.sort()的稳定性
ES2019 明确规定,Array.prototype.sort()的默认排序算法必须稳定。这个规定已经做到了,现在 JavaScript 各个主要实现的默认排序算法都是稳定的。

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

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

值得提醒的是,扩展运算符(…)也可以将某些数据结构转为数组。
扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。

Array.from({ length: 3 });
// [ undefined, undefined, undefined ]
// 这是from和扩展运算符的区别

Array.of()
Array.of方法用于将一组值,转换为数组。这个方法的主要目的,是弥补数组构造函数Array()的不足。

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

数组实例的 copyWithin()
Array.prototype.copyWithin(target, start = 0, end = this.length)
数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

// 将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]
// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 将2号位到数组结束,复制到0号位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 对于没有部署 TypedArray 的 copyWithin 方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]

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

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

数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
indexOf方法无法识别数组的NaN成员,但是findIndex方法可以借助Object.is方法做到。弥补了数组的indexOf方法的不足。

数组实例的 fill()
fill方法使用给定值,填充一个数组。

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

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

数组实例的 entries(),keys() 和 values()
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"

next遍历器

let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

数组实例的 includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

数组实例的 flat(),flatMap()
数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2) //拉平两层
// [1, 2, 3, 4, 5]

flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。也是只能展开一层。

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

十、对象的扩展

属性的简洁表示法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

let birth ='2000/01/01';
constPerson={
  name:'张三',
//等同于birth: birth
  birth,
// 等同于hello: function ()...
  hello(){ console.log('我的名字是',this.name);}
};

属性名表达式

// ES5 对象字面量声明时只能使用"."
var obj = {
  foo: true,
  abc: 123
};
// ES6
let propKey = 'foo';
let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};

注意,属性名表达式与简洁表示法,不能同时使用,会报错。
注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object],这一点要特别小心。

方法的 name 属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。

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

如果对象的方法使用了取值函数(getter)和存值函数(setter),则name属性不是在该方法上面,而是该方法的属性的描述对象的get和set属性上面,返回值是方法名前加上get和set。

const obj = {
  get foo() {},
  set foo(x) {}
};
obj.foo.name
// TypeError: Cannot read property 'name' of undefined
const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
descriptor.get.name // "get foo"
descriptor.set.name // "set foo"

bind方法创造的函数,name属性返回bound加上原函数的名字;
Function构造函数创造的函数,name属性返回anonymous

(new Function()).name // "anonymous"
var doSomething = function() {
  // ...
};
doSomething.bind().name // "bound doSomething"

属性的可枚举性和遍历
Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。

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

目前,有四个操作会忽略enumerable为false的属性。
for…in循环:只遍历对象自身的和继承的可枚举的属性。
Object.keys():返回对象自身的所有可枚举的属性的键名。
JSON.stringify():只串行化对象自身的可枚举的属性。
Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
前三个是 ES5 就有的,最后一个Object.assign()是 ES6 新增的。其中,只有for…in会返回继承的属性,其他三个方法都会忽略继承的属性,只处理对象自身的属性。
实际上,引入“可枚举”(enumerable)这个概念的最初目的,就是让某些属性可以规避掉for…in操作,不然所有内部属性和方法都会被遍历到。比如,对象原型的toString方法,以及数组的length属性,就通过“可枚举性”,从而避免被for…in遍历到。
ES6 规定,所有 Class 的原型的方法都是不可枚举的。
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 或字符串,也不管是否可枚举。

super关键字
ES6 新增了一个关键字super,指向当前对象的原型对象。
super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。

对象的扩展运算符
ES2018将扩展运算符应用到了对象上
1、解构赋值

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

解构赋值要求等号右边是一个对象,如果等号右边是undefined或null,就会报错,因为它们无法转为对象。
解构赋值的拷贝是浅拷贝。
解构赋值必须是最后一个参数,否则会报错。
扩展运算符的解构赋值,不能复制继承自原型对象的属性。

let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3 // { b: 2 }
o3.a // undefined

上面代码中,对象o3复制了o2,但是只复制了o2自身的属性,没有复制它的原型对象o1的属性。

const o = Object.create({ x: 1, y: 2 });
o.z = 3;
let { x, ...newObj } = o;
let { y, z } = newObj;
x // 1
y // undefined
z // 3

上面代码中,变量x是单纯的解构赋值,所以可以读取对象o继承的属性;变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值。
扩展运算符后面必须是一个变量名。

let { x, ...{ y, z } } = o;
// SyntaxError: ... must be followed by an identifier in declaration contexts
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}

// 等同于 {...Object(true)}
{...true} // {}
// 等同于 {...Object(undefined)}
{...undefined} // {}
// 等同于 {...Object(null)}
{...null} // {}
{...'hello'}// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

克隆带有继承属性的对象的三种方法

// 写法一
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)
)

链判断运算符
链判断运算符有三种用法。
obj?.prop // 对象属性
obj?.[expr] // 同上
func?.(…args) // 函数或对象方法的调用

iterator.return?.()

上面代码中,iterator.return如果有定义,就会调用该方法,否则直接返回undefined。

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()

注意以下几点:
(1)短路机制

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

上面代码中,如果a是undefined或null,那么x不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值
(2)delete 运算符

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

上面代码中,如果a是undefined或null,会直接返回undefined,而不会进行delete运算。
(3)括号的影响
如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。

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

上面代码中,?.对圆括号外部没有影响,不管a对象是否存在,圆括号后面的.c总是会执行。
一般来说,使用?.运算符的场合,不应该使用圆括号。
(4)报错场合
以下写法是禁止的,会报错。

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

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

Null判断运算符

// ES5
const headerText = response.settings.headerText ||'Hello, world!';
const animationDuration = response.settings.animationDuration ||300;
const showSplashScreen = response.settings.showSplashScreen ||true;
// ES6(2020)
const headerText = response.settings.headerText ??'Hello, world!';
const animationDuration = response.settings.animationDuration ??300;
const showSplashScreen = response.settings.showSplashScreen ??true;

上面代码中,默认值只有在属性值为null或undefined时,才会生效。
??有一个运算优先级问题,它与&&和||的优先级孰高孰低。现在的规则是,如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。

// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs
// 一定要用括号表明优先级 应当写成
(lhs && middle)?? rhs;
lhs &&(middle ?? rhs);
(lhs ?? middle)&& rhs;
lhs ??(middle && rhs);
(lhs || middle)?? rhs;
lhs ||(middle ?? rhs);
(lhs ?? middle)|| rhs;
lhs ??(middle || rhs);

十一、对象的新增方法

Object.is()
ES5 比较两个值是否相等,只有两个运算符:相等运算符(= =)和严格相等运算符(= = =)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo','foo')
// true
Object.is({},{})
// false

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
实现

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
});

Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

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

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
如果只有一个参数,Object.assign会直接返回该参数。

const obj ={a:1};
Object.assign(obj)=== obj // true

如果只有一个参数,Object.assign会直接返回该参数。

typeof Object.assign(2)// "object"

由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。
如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果undefined和null不在首参数,就不会报错。

Object.assign(undefined)// 报错
Object.assign(null)// 报错

let obj ={a:1};
Object.assign(obj,undefined)=== obj // true
Object.assign(obj,null)=== obj // true

其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。

const v1 ='abc';
const v2 =true;
const v3 =10;
const obj =Object.assign({}, v1, v2, v3);
console.log(obj);// { "0": "a", "1": "b", "2": "c" }

注意:
(1)浅拷贝(如果第一层都是基本数据类型就是深拷贝)
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
(2)同名属性的替换
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。(覆盖)
(3)带继承的克隆

function clone(origin){
  let originProto =Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}

(4)合并多个对象
将多个对象合并到某个对象。

const merge =
(target,...sources)=>Object.assign(target,...sources);

如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。

const merge =
(...sources)=>Object.assign({},...sources);

(5)为属性指定默认值

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

由于存在浅拷贝的问题,DEFAULTS对象和options对象的所有属性的值,最好都是简单类型,不要指向另一个对象。否则,DEFAULTS对象的该属性很可能不起作用。还要注意覆盖问题。

Object.getOwnPropertyDescriptors()
ES5 的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了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 } }

实现:

function getOwnPropertyDescriptors(obj) {
  const result = {};
  for (let key of Reflect.ownKeys(obj)) {
    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  }
  return result;
}

该方法的引入目的,主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。
Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法,就可以实现正确拷贝

const source = {
  set foo(value) {
    console.log(value);
  }
};
const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
//   set: [Function: set foo],
//   enumerable: true,
//   configurable: true }

上面代码中,两个对象合并的逻辑可以写成一个函数。

const shallowMerge = (target, source) => Object.defineProperties(
  target,
  Object.getOwnPropertyDescriptors(source)
);

继承

// ES5
const obj = {
  __proto__: prot,
  foo: 123,
};
// ES6
const obj = Object.create(prot);
obj.foo = 123;
// 或者
const obj = Object.assign(
  Object.create(prot),
  {
    foo: 123,
  }
);

** _ proto _ 属性,Object.setPrototypeOf(),Object.getPrototypeOf() **
1、_ _ proto _ _ 属性(前后各两个下划线),用来读取或设置当前对象的原型对象(prototype)。目前,所有浏览器(包括 IE11)都部署了这个属性。
2、Object.getPrototypeOf() 该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。
如果参数不是对象,会被自动转为对象。

Object.keys(),Object.values(),Object.entries()
ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历(enumerable)**属性的键名。
ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for…of循环使用。


Object.values({ [Symbol()]: 123, foo: 'abc' });
// ['abc'] 过滤掉了Symbol值

const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

entries的实现

// Generator函数的版本
function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield [key, obj[key]];
  }
}
// 非Generator函数的版本
function entries(obj) {
  let arr = [];
  for (let key of Object.keys(obj)) {
    arr.push([key, obj[key]]);
  }
  return arr;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值