一. 字符串的一些扩展
-
for...of遍历
-
ES6为字符串添加了遍历器接口,似的字符串可以被for...of循环遍历
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o" -
除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
var text = String.fromCodePoint(0x20BB7);
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "for (let i of text) {
console.log(i);
}
// "𠮷",字符串text只有一个字符,但是for循环会认为它包含两个字符(都不可打印),而for...of循环会正确识别出这一个字符。
-
-
查找字符串的三个新方法
-
JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
2)三个方法都支持第二个参数,表示开始搜索的位置
3)使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
-
repeat()
1) repeat方法返回一个新字符串,表示将原字符串重复n次
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // "
2) 参数如果是小数,会被取整。
'na'.repeat(2.9) // "nana"
-
padStart(). padEnd()
-
ES2017引入了字符串补全长度的功能,如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'
如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串。
-
如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。
'abc'.padStart(10, '0123456789') // '0123456abc'
如果省略第二个参数,默认使用空格补全长度。
-
用途
a. padStart的常见用途是为数值补全指定位数。下面代码生成10位的数值字符串 '1'.padStart(10, '0') // "0000000001" '12'.padStart(10, '0') // "0000000012" '123456'.padStart(10, '0') // "0000123456" b. 另一个用途是提示字符串格式。 '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12" '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
-
-
模板字符串
-
传统的JavaScript语言,输出模板通常是这样写的。
$('#result').append( 'There are <b>' + basket.count + '</b> ' + 'items in your basket, ' + '<em>' + basket.onSale + '</em> are on sale!' );
-
上面这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题
$('#result').append(
There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale!
); 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中,所有模板字符串的空格和换行,都是被保留的
模板字符串中嵌入变量,需要将变量名写在${}之中。
大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性,也可以调用函数
-
如果需要引用模板字符串本身,在需要时执行,可以像下面这样写。
// 写法一
let str = 'return ' + 'Hello ${name}!
';
let func = new Function('name', str);
func('Jack') // "Hello Jack!"// 写法二
let str = '(name) =>Hello ${name}!
';
let func = eval.call(null, str);
func('Jack') // "Hello Jack!"
-
模板编译
-
通过模板字符串,生成正式模板的实例。
var template = ` <ul> <% for(var i=0; i < data.supplies.length; i++) { %> <li><%= data.supplies[i] %></li> <% } %> </ul> `;
上面代码在模板字符串之中,放置了一个常规模板。该模板使用<%...%>放置JavaScript代码,使用<%= ... %>输出JavaScript表达式
2. 编译这个模板字符串
function compile(template){
var evalExpr = /<%=(.+?)%>/g;
var expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
var script =
`(function parse(data){
var output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
var parse = eval(compile(template));
div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });
// <ul>
// <li>broom</li>
// <li>mop</li>
// <li>cleaner</li>
// </ul>
二. 正则的扩展
-
RegExp构造函数
以前创建正则对象有两种方式
var regex = new RegExp('xyz', 'i');
var regex = /xyz/i;现在多了一种
var regex = new RegExp(/xyz/, 'i');
字符串的正则方法
三. 数组的扩展
-
Array.from
-
用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象
ES5的写法
var arr1 = [].slice.call(arrayLike);
ES6的写法
let arr2 = Array.from(arrayLike);
-
实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。
// NodeList对象 let ps = document.querySelectorAll('p'); Array.from(ps).forEach(function (p) { console.log(p); }); // arguments对象 function foo() { var args = Array.from(arguments); // ... }
-
Array.from还可以接受第二个参数,作用类似与数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
Array.from(arrayLike, x => x * x); // 等同于 Array.from(arrayLike).map(x => x * x); Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9]
-
-
Array.of()
-
用于将一组值,转换为数组
Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array.of(3).length // 1
这个方法的主要目的,是弥补数组构造函数Array()的不足,因为参数个数的不同,会导致Array()的行为有差异
Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]
Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载,他的行为非常统一,Array.of总是返回参数值组成的数组,如果没有参数,就返回一个空数组
-
-
数组实例的copyWithin()
-
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员赋值到其他位置(会覆盖原有成员),然后返回当前数组,也就是说,使用这个方法,会修改当前数组
Array.prototype.copyWithin(target, start = 0, end = this.length)
target(必需):从该位置开始替换数据
start(可选): 从该位置开始读取数据,默认为0,如果为负值,表示倒数
end(可选):到该位置前停止读取数据,默认等于数组长度,如果为负值,表示倒数
这三个参数都应该是数值,如果不是,会自动转为数值
[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
上面代码表示将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。
-
-
数组实例的find()
-
数组实例的find方法,用于找出第一个符合条件的数组成员,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。乳沟没有符合条件的成员,则返回undefined
[1, 4, -5, 10].find((n) => n < 0) // -5
-
-
数组实例的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']fill方法从1号位开始,向原数组填充7,到2号位之前结束。
-
-
数组实例的entries(),keys()和values()
1)ES6提供三个新的方法——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"
2)如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历。
数组实例的includes()
四. 函数的扩展
-
函数参数的默认值
-
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello
参数变量是默认声明的,所以不能用let或const再次声明。
3),定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
- 函数的length属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
-
应用
利用参数默认值,可以指定一个参数不得省略,如果省略就抛出一个错误
function throwIfMissing() { throw new Error('Missing parameter'); } function foo(mustBeProvided = throwIfMissing()) { return mustBeProvided; } foo() // Error: Missing parameter
-
-
rest参数
-
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 参数改写数组push方法的例子
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}var a = [];
push(a, 1, 2, 3) rest 参数之后不能再有其他参数(即只能是最后一个参数)
-
-
扩展运算符
-
扩展运算符(spread)是三个点(...)将一个数组转为用逗号分隔的参数序列。
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>]
-
运算符主要用于函数调用。
function push(array, ...items) { array.push(...items); } function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers) // 42
-
替代数组的apply方法
// ES5的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);// ES6的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f(...args); -
扩展运算符的应用
合并数组
// ES5 [1, 2].concat(more) // ES6 [1, 2, ...more] var arr1 = ['a', 'b']; var arr2 = ['c']; var arr3 = ['d', 'e']; // ES5的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
-
扩展运算符还可以将字符串转为真正的数组
[...'hello'] // [ "h", "e", "l", "l", "o" ]
-
何Iterator接口的对象,都可以用扩展运算符转为真正的数组。
var nodeList = document.querySelectorAll('div'); var array = [...nodeList];
-
-
箭头函数
-
ES6允许使用“箭头”(=>)定义函数
var f = v => v; ==> var f = function(v) { return v; };
-
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分
var f = () => 5; // 等同于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };
-
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var sum = (num1, num2) => { return num1 + num2; }
-
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
var getTempItem = id => ({ id: id, name: "Temp" });
-
箭头函数使得表达更加简洁。
上面代码只用了两行,就定义了两个简单的工具函数。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。
-
箭头函数的一个用处是简化回调函数
// 正常函数写法 [1,2,3].map(function (x) { return x * x; }); // 箭头函数写法 [1,2,3].map(x => x * x);
-
使用注意点
a. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
b. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
c. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
-
绑定this
五. 对象的扩展
- 属性的简洁表示法
1) ES6允许直接写入变量和函数,作为对象的属性和方法
var foo = 'bar';
var baz = {foo};
baz // {foo: "bar"}
2) 方法也可以简写
var o = {
method() {
return "Hello!";
}
};
3) 实际例子
var birth = '2000/01/01';
var Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
- Object.assign()
1) 用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
2) Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性)
3) 注意点
a. Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
var obj1 = {a: {b: 1}};
var obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
b. 对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
4) 常见用途
a. 为对象添加属性
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}
b. 为对象添加方法
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
c. 克隆对象
function clone(origin) {
return Object.assign({}, origin);
}
d. 合并多个对象
const merge = (target, ...sources) => Object.assign(target, ...sources);
- 属性的遍历
1) for...in
循环遍历对象自身的和可继承的可枚举属性
2) object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
- Object.keys()/Object.values()/Object.entries()
1) Object.keys()
ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名