数组

ES6中对于数据做了进一步的增强,以便能够更加方便地创建数组以及操作数组。

创建数组

Array.of

该方法用于将一组值转换为数组。

1
2
Array.of(1); [1]
Array.of(3, 21, 22); [3,21,22]

这个方法的出现主要是为了弥补之前数组构造函数的一些缺陷:

1
2
Arry(); // []
Array(3); // [undefined, undefined, undefined]

你会发现在使用单个数值参数的时候,那个参数被构造器作为数组长度而进行初始化。而两个参数以上的时候,则返回由参数构成的数组。而 Array.of() 方法总会创建一个包含所有传入参数的数组,而不管参数的数量与类型。

Array.from

该方法通常用于将类似数组的对象以及可遍历对象转换成数组。你可以将NodeList,Set,Map等等解构进行数组的转换。

利用该方法的第二个参数,你可以指定一个回调函数,用于对数组进行映射操作。

1
2
3
4
//arguments为函数传入的参数
let args = Array.from(arguments, value => value + 1);
//等同于
let args = Array.from(arguments).map(value => value + 1);

该方法的第三个参数是上下文,在映射函数中如果需要使用this,需要手动传入该参数。

在ES6中, Class 语法允许我们为内置类型(比如 Array)和自定义类新建子类(比如叫 SubArray)。这些子类也会继承父类的静态方法,比如 SubArray.from(),调用该方法后会返回子类 SubArray 的一个实例,而不是 Array 的实例。

实例方法

fill

fill() 方法能使用特定值填充数组中的一个或多个元素。

1
2
3
[1,2,3].fill(1); //用1填充数组所有项 [1,1,1]
[1,2,3].fill(10, 2); //从索引2开始用10填充数组 [1, 2, 10]
[1,2,3,4].fill(0, 2, 3);//从索引2到索引3之间开始用0填充数组 [1,2,0,4]

find/findIndex

该方法的参数是一个回调函数,用于查找数组中满足回调函数测试的第一个值或者第一个索引(index)。

1
2
3
let numbers = [1,342,342,22, 34, 35];
console.log(numbers.find(n => n > 35)); // 342
console.log(numbers.findIndex(n => n > 33)); // 1

copyWithin

该方法用来复制自身元素,然后填充进数组里。

补充

针对可迭代的对象,ES6提供了三个新的方法:entries,keys, values分别用于返回键值对、键名、键值的迭代器,可以使用for…of语法进行访问。而数组的键名其实就是它的索引值。

1
2


字符串

字符串的操作层面,ES6提供了模板对象、多行字符串的支持。

模板字面量

在ES6中,我们可以使用反引号(` )来包裹普通字符串。
在ES5,我们如果要生成一个多行的字符串,通常要使用+连字符,而现在只要使用反引号就可以了。

1
2
3
var str = `Courses are provided in MOOC format with course material available online, mostly as videos complemented with exercise and example files.
All throughout the course, a technical expert will be available online to provide help and answer your questions.
Each course takes approximately 6 to 7 hours to complete, depending on your proficiency, and must be completed within one week.`;

另外,我们可以在字符串模板中制造替换位,从而使用一些变量来填充我们的模板。

1
2
var name = 'scq000';
var str = `Hello, ${name}`; // Hello,scq000

###标签化模板

模板标签(template tag)能对模板字面量进行转换并返回最终的字符串值,标签在模板的起始处被指定。标签实际上就是一个函数,它被调用时接收需要处理的模板字面量数据,然后进行处理后返回一个字符串。这个函数的第一个参数是一个数组,包含被JS解释过的字面量字符串,随后的参数是每个替换位的解释值。

1
2
3
4
5
6
7
8
9
10
function tag(literals, ...substitutions) {
let result = '';
console.log(literals);
console.log(substitutions);
return result;
}
var name = 'scq000';
var age = 12;

let msg = tag`My name is ${name}, and my age is ${age}.`;

在上面这个例子中,literials被赋值为['My name is ', [, and my age is ], [.]],而substutions则是['scq000', 12],即用来替换的值。你可以在这个函数中,利用这两个信息对字符串做进一步的加工。

另外,有个内置的String.raw标签,可以直接获取模板字面量的原始值,即转义之前的值。

1
2
let msg = String.raw`hello$1\nworld`
console.log(msg); //hello$1\nworld

字符串实例方法

对于字符串的实例方法上,新增了一些比较常用的方法。

  1. includes: 在给定文本存在于字符串中的任意位置时会返回 true ,否则返回 false 。
  2. startsWith: 在给定文本出现在字符串起始处时返回 true ,否则返回 false 。
  3. endsWith: 在给定文本出现在字符串结尾处时返回 true ,否则返回 false 。

需要注意的是,与之前indexOf等方法不同,以上这些实例方法的参数不能传入正则表达式,否则会抛出错误。

  1. repeat: 接受一个参数作为字符串的重复次数,返回一个将初始字符串重复指定次数的新字符串。

符号

从ES6开始,引入了第6个基本数据类型:符号(Symbol)。 它是一种特殊的、不可变的数据类型,可以作为对象属性的标识符使用。

创建符号值

1
2
3
const name = Symbol(); //注意这里不能用new,因为Symbol是基本数据类型
const aname = Symbol('aname'); //提供的这个参数主要用来调试
typeof name //"Symbol"

使用符号值

可以在任何能使用“计算属性名”的场合使用符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let firstName = Symbol('first name');
let lastName = Symbol('last name');

let person = {
[firstName]: 'scq'
};
Object.defineProperty(person, firstName, { writable: false });

Object.defineProperties(person, {
[lastName]: {
value: '000',
writable: false
}
});

上面的例子,为person对象定义了两个符号类型属性,并将它们设为只读的。利用这种方式,特别适合创建对象私有成员。

暴露内部方法

利用Symbol的一些属性,可以针对对象的一些内部行为进行操作。

  1. Symbol.iterator: 一个返回对象默认迭代器的方法,使用for…of
  2. Symbol.match: 一个用于对字符串进行匹配的方法,也用于确定一个对象是否可以作为正则表达式使用。使用String.prototype.match
  3. Symbol.replace:一个替换匹配字符串的子串的方法.使用String.prototype.replace
  4. Symbol.search
  5. Symbol.split
  6. Symbol.hasInstance: 一个确定一个构造器对象识别的对象是否为它的实例的方法。使用 instanceof.
  7. Symbol.isConcatSpreadable: 一个布尔值,表明一个对象是否应该flattened为它的数组元素。使用Array.prototype.concat().
  8. Symbol.unscopables: 拥有和继承属性名的一个对象的值被排除在与环境绑定的相关对象外。
  9. Symbol.species: 一个用于创建派生对象的构造器函数。.
  10. Symbol.toPrimitive: 一个将对象转化为基本数据类型的方法。
  11. Symbol.toStringTag: 用于对象的默认描述的字符串值。使用Object.prototype.toString().

功能比较多,就不一一说明了。下面就以iterator来说明一下使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var iterator = {
[Symbol.iterator]() {
return {
start: 0,
next() {
if(this.start < 10) {
return {value: this.start ++, done: false}
}else {
return {value: undefined, done: true}
}
}
}
}
};

for(var value of iterator) {
console.log(vlaue);
}

上面的例子利用了Symbol.iterator这个属性,使得对象iterator获得了迭代器的能力。因此,可以对这个对象使用for...of的语法进行迭代操作。

Set与Map

ES6中增加了两个新的数据结构,Set和Map,补充了原本数组的一些不足之处。Set 是不包含重复值的列表,而Map 则是键与相对应的值的集合。

Set

Set是一种无重复值的有序列表。
创建一个Set很简单,只要使用new关键字就可以了。Set的构造器实际上可以接受任何可迭代对象作为参数。在构造器内部,会使用迭代器来提取参数中的值。

1
2
3
let set1 = new Set(); //创建一个空的集合 Set {}
let set2 = new Set([1,2,3]); //创建一个包含三个元素的集合, Set {1, 2, 3}
let set3 = new Set([1,2,3,4,4]); //创建一个去重后的集合, Set {1, 2, 3}

针对Set,主要提供了以下几个方法:

  1. add: 能向 Set 中添加项目
  2. has: 测试某个值是否存在于 Set 中
  3. delete: 来移除单个值
  4. clear: 来将所有值从 Set 中移除
    还提供了一个size属性用于判断集合长度
1
2
3
4
5
6
let set = new Set([1,2,3,4,5,6]);
set.add(3); //由于3已经在集合里了,并不会添加新项
set.add('nihao'); // Set {1,2,3,4,5,6,'nihao'}
set.has(3); //true
set.delete(3); // Set {1,2,4,5,6,'nihao'}
set.clear(); // Set {}

注意的是,当一个属性被添加到 set 对象时,它的值也被设为true。而在 Set 内部的比较使用了Object.is()方法,来判断两个值是否相等。

Set的遍历

同数组一样,作为可迭代的数据结构,ES6提供了forEach的方法对其进行遍历。你也可以使用,for...of的语法进行。

1
2
3
4
5
let set = new Set(['a','b','c']);
set.forEach((value, key) => console.log('Value: ' + value + ' Key: ' + key));
//Value: a Key: a
//Value: b Key: b
//Value: c Key: c

由于Set的每一项同时被认为键和值,因此,key和value其实是一致的。如果想在回调函数中使用 this ,你可以给 forEach() 传入一个 this 值作为第三个参数。你也可以使用箭头函数来达到同等的效果。

转换成数组

要将Set转换成数组,可以像下面这样:

1
2
3
let set = new Set([1,2,3,4,5]);
Array.from(set); //[1,2,3,4,5]
[...set]; //[1,2,3,4,5]

使用扩展运算符可以更简单地将 Set 转换回数组。

Map

Map与Set相似,数据结构中存储着键值对。实例方法与Set相同的部分是has,delete,clear。你可以调用 set() 方法并给它传递一个键与一个关联的值,来给 Map 添加项;此后使用键名来调用 get() 方法便能提取对应的值。

1
2
3
4
5
6
7
8
let map = new Map();
map.set('key1', 'value1'); //Map {"key1" => "value1"}
map.set('key2', 'value2'); //Map {"key1" => "value1", "key2" => "value2"}
map.get('key1'); //value1
map.has('key2'); //true
map.delete('key1'); //删除成功会返回true
map.has('key1'); //false
map.clear(); //清空所有

而对于map的遍历可以使用forEach语法,其中的回调函数同样是接收三个参数:value,key,context。当然context是可选的。

1
2
3
4
5
let map = new Map();
map.set('key1', 'value1');
map.set('key2', 'value2');
map.forEach((value,key) => console.log(value));
//value1, value2

WeakSet和WeakMap

WeakMap和WeakSet一样,存储了是对象的弱引用方式。而Set和Weak的实例中,则是采用了强引用的方式,只要SetMap的实例存在,其中所存储的对象就无法被垃圾回收机制回收,从而无法释放内存。利用WeakSet和WeakMap的弱引用方式,则可以在内存管理上有着更加容易优化的空间。