ECMAScript6(3)
四、ES6对于对象、函数、数组的扩展
1.对象扩展
属性简写,例如:
let name = “terry”; let age = 12; function sayName(){ ... } let obj = { name,age,sayName }; //等同于 let obj = { name:”terry”,age:12,sayName:function(){ ... } }
方法简写,例如:
let o = { method(){ return "Hello!"; } };
//等同于
let o = { method: function(){ return "Hello!"; } };
属性名表达式,ES6允许字面量定义对象时,可以把表达式放在方括号内,例如:
let propKey = 'foo';
let obj = { [propKey]: true, ['a' + 'bc']: 123 };
访问方法、函数的name属性,会返回函数名
const person = { sayName(){ console.log('hello!'); } };
person.sayName.name //"sayName"
数值的比较,同值相等,与===类似,不同之处在于:+0不等于-0,以及NaN等于自身
Object.is(value1,value2)
Object.is('foo', 'foo') //true
Object.is({}, {}) //false
对象的合并
Object.assign(target,o1,o2…)可以将源对象o1、o2...所有的可枚举属性,复制到目标对象target中。
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。它除了合并还有以下应用:
为对象添加属性和方法
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2){ ... },
anotherMethod(){ ... }
});
克隆对象
function clone(origin){ return Object.assign({}, origin); }
为属性提供默认值
function processContent(options) {
//DEFAULTS对象是默认值,options对象是用户提供的参数
options = Object.assign({}, DEFAULTS, options);
//...
}
__proto__属性本质上属于内部属性,指向当前对象的prototype对象,一般不直接使用。
Object.getPrototypeOf(obj)用于读取一个对象的原型对象。
Object.setPrototypeOf(obj,prototype)则用来设置一个对象的prototype对象,返回参数对象本身。它是ES6正式推荐的设置原型对象的方法。该方法等同如下写法:
function (obj, proto){
obj.__proto__ = proto;
return obj;
}
Object.keys(obj)返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键名。
Object.values(obj)返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。
Object. entries(obj)返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。
2.函数的扩展
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面,例如:
function log(x, y = 'World'){ console.log(x, y); }
通常情况下,定义了默认值的参数应是函数的尾参数,访问函数的length属性将返回没有指定默认值的参数个数。
参数默认值可以与解构赋值的默认值结合起来使用,例如:
function foo({x, y = 5}){ console.log(x, y); } foo({}) //undefined 5 foo({x: 1}) //1 5 foo({x: 1, y: 2}) //1 2
ES6引入了rest参数(形式为...变量名),用于获取函数的多余参数(只有rest参数时则获取函数的所有参数),这样就不需要使用arguments对象了,需要注意的是,rest参数只能为最后一个参数。rest参数搭配的变量是一个数组,该变量将多余参数放入数组中,例如:
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10
扩展运算符是三个点(...),它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列,常用于剥离操作((...new Set(“hello”)) ==> [‘h’, ‘e’, ‘l’, ‘o’]),例如:
console.log(...[1,2,3]); //1 2 3(直接打印无逗号,但作为函数参数时相当于加逗号传参) console.log(1,...[2,3,4],5);//1 2 3 4 5 function foo(a,b){ console.log(arguments); //Arguments {0:1,1:1},类数组 console.log([...arguments]); //[1,2] console.log(Array.from(arguments)); //[1,2] console.log(Array.prototype.slice.call(arguments));//[1,2] console.log(a+b); //3 } foo(1,2);
ES6允许使用“箭头”(=>)定义函数,如:
v => v;等价于function(v){ return v; };
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分;
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回(只有一条语句且为返回语句时才能省略大括号)。
箭头函数中this的取值为包含该箭头函数的外部函数的this,如果箭头函数没有外部函数,那么它的this就指向全局对象。
3.数组的扩展
Array.from用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象(包括ES6新增的数据结构Set和Map)。
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 };
//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']
Array.of()用于将一组值转换为数组:Array.of(3, 11, 8); //[3,11,8]
这个方法的主要目的是为了弥补数组构造函数Array()的不足,因为参数个数的不同,会导致Array()的行为有差异。
小结:将类数组对象转化为数组的几种方法
let array_like = {"0":"terry","1":"larry",length:2}; console.log(array_like); //从数组对象中解构出slice方法 let {slice} = []; //1. 使用原始的Array.prototype.slice转换 console.log(slice.call(array_like,0)); //2. 使用Array.from转换 console.log(array_like); console.log(Array.from(array_like)); //3. 使用Array.from转换可以遍历的对象 let set = new Set([1,2,3,1,2,4,5,6]); console.log(set); console.log(Array.from(set));
数组实例的find()方法用于找出第一个符合条件的数组成员。它的参数是一个回调函数,这个回调函数可以接收三个参数,依次为当前的值、当前的位置和原数组,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined:
[1, 4, -5, 10].find((n) => n < 0); //-5 let arr = [ {name:"terry",age:12}, {name:"tom",age:14}, {name:"larry",age:13}, {name:"jacky",age:18}, {name:"vicky",age:11} ] let result = arr.find(function(item){ return item.age === 13; }) //let result = arr.find(item=>item.age === 13);
数组实例的findIndex()方法的用法与find()方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1:
[1, 5, 10, 15].findIndex(function(value, index, arr){ return value > 9; }); //2
数组实例的fill()方法可以使用给定值来填充一个数组。
['a', 'b', 'c'].fill(7); //[7, 7, 7]
new Array(3).fill(7); //[7, 7, 7]
数组实例的includes()方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes()方法类似。ES2016引入了该方法。
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true
数组实例的entries()、keys()和values()方法用于遍历数组,它们都返回一个遍历器对象,可以用for...of...循环进行遍历,区别是keys()是对键名的遍历,values()是键值的遍历(但数组中的键值与键名相同),entries()是对键值对的遍历。
(for...in循环获取键名,通过“对象[键名/索引]”获得键值;for...of循环获取键值,辅助keys()方法获取键名,entries()方法获取键值对,且for...of是利用迭代器机制来遍历的)
五、ES6中的集合API
1.Set集合
Set类似于数组,但所有成员的值都是无重复的唯一值(可利用此特性为数组去重)。
Set本身是一个构造函数,用来生成Set数据结构,Set对象的实例化与数组类似,且除了数组外,还可以接收具有iterable 接口的其他数据结构:
let set = new Set();
let set = new Set([1,2,3,1,2]); //重复的1和2不会出现在这个实例中
Set结构的实例有以下属性:
Set.prototype.constructor:构造函数,默认就是Set函数
Set.prototype.size:返回Set实例的成员总数
Set结构的实例有以下方法:
add(value):添加某个值,返回Set结构本身
delete(value):删除某个值,返回一个布尔值,表示删除是否成功
has(value):返回一个布尔值,表示该值是否为Set的成员
clear():清除所有成员,没有返回值
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
2.Map集合
Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。
Map可以接收一个数组作为参数,该数组的成员是一个个表示键值对的数组:
let map = new Map();
let map = new Map(entry);
let map = new Map([ ['name', '张三'], ['title', 'Author'] ]);
Map结构的实例有以下属性:
Map.prototype.size:返回Map结构的成员总数
Map结构的实例有以下方法:
set(key, value):set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键
get(key):get方法读取key对应的键值,如果找不到key,返回undefined
has(key):has方法返回一个布尔值,表示某个键是否在当前Map对象之中
delete(key):delete方法删除某个键,返回true,如果删除失败,返回false
clear():清除所有成员,没有返回值
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
3.Symbol
可以通过Symbol()产生一个不会与其它变量产生冲突的变量,这个变量用于向对象中插入一个可用属性而不对对象中的其它属性造成影响。
Symbol的典型应用就是向对象中添加迭代器,但对象仍可自定义iterator变量不被影响。
//配置迭代器的原理,Symbol(“iterator”)产生可通过“iterator”访问的非iterator变量名的唯一变量,它具有迭代的能力
let Symbol.iterator = Symbol(“iterator”);
let s = new Set(); //s具有迭代器s[Symbol.iterator]
4.Iterator
遍历器是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator的遍历过程是这样的:创建一个指针对象,指向当前数据结构的起始位置(遍历器对象本质上就是一个指针对象),然后依次调用指针对象的next()方法,可以不断将指针指向数据结构的下一个成员,直到它指向数据结构的结束位置。
Iterator的作用有三个:一是为各种数据结构提供一个统一的简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是方便ES6中新的遍历命令for...of使用,当使用for...of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。
一种数据结构只要部署了Iterator接口,我们就称这种数据结构是“可遍历的”,可以通过let iterator = iterObj[Symbol.iterator]();访问其Iterator对象,如:
let iterator = [1,2,3][Symbol.iterator]();
let iterator = “hello”[Symbol.iterator]();
原生具备 Iterator 接口的数据结构有:Array、Map、Set、String、TypedArray、函数的arguments对象、NodeList对象。