一. 字符串的一些擴展
-
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)屬性的鍵名