最近在閱讀《深入立即ECMAScript6》這本書,打算把自己學到的、用到的和理解的來說一說。 我會基本按照書上的順序來說,大概會分為三到四篇的樣子,不會全部都寫,我只會挑一些常用的,或者我覺得比較有意思的來寫。
塊級作用域繫結(let、const)
為何要引入塊級作用域
在ES6之前,JavaScript中是沒有塊級作用域的概念的,只有全域性作用域和塊級作用域。也就是說在塊級作用域宣告與外部同名的變數,會覆蓋掉外部的變數。而沒有塊級作用域的話,主要會有以下兩個問題。
- 塊級作用域內部變數洩露
var i = 1;
console.log(i) // 1
for(var i =0;i<10;i++){
console.log(i); // 0...9
}
console.log(i) // 10
複製程式碼
- 迴圈中建立函式保留了相同的變數引用
var funcs = [];
for(var i = 0;i < 10;i++){
funcs.push(function (){
console.log(i);
});
}
funcs.forEach(function(func){
func(); // 輸出10次數字10
});
複製程式碼
上面兩個問題都是沒有塊級作用域導致的,但是ES6發生了改變,為我們引入了塊級作用域。
let宣告
let宣告的用法
基本與var相同,但是它的生命週期在塊級作用域內部,一旦出去塊級作用域便會消失。同時不能重複宣告同名變數
示例
基本用法
var i = 0;
if(true){
let i = 100000;
}
console.log(i); // 0
複製程式碼
迴圈中的let宣告
var i = 1;
console.log(i) // 1
for(let i =0;i<10;i++){
console.log(i); // 0...9
}
console.log(i) // 1
複製程式碼
執行機制
其實let在每次建立的時候都會建立一個新的變數,並以之前迭代中同名變數的值將其初始化。簡而言之就是let每次都會建立值的副本,而不是像var一樣修改引用的值。
當第一次執行for迴圈的時候,會新建一個變數i(在記憶體中開闢一塊空間),然後將0賦值給這個變數,繼續執行下面的語句;當第二次執行的時候,又會新建一個變數i(在記憶體中重新開闢一個區域),之後再將原來i的值賦值給新的i,繼續在執行下面的語句。迴圈往復,直到迴圈的條件不滿足為止。
const宣告
const宣告的用法
與let的用法基本也是相同的,但是它一旦被定義就不能被修改。當然,說的是原始值,如果你使用的是引用,你可以更改引用內部的值,但是你無法更改對引用的繫結。而且const宣告必須被初始化,不然就會報錯。
示例
基本語法
const name; //錯誤,未初始化
const maxItems = 30;
maxItems = 100; //報錯,不能更改繫結
複製程式碼
迴圈中的const宣告
var funcs = [];
//完成一次迭代之後會報錯
for(const i =0;i<10;i++){
funcs.push(function(){
console.log(i);
});
}
複製程式碼
這裡會報錯的原因是變數i被const宣告為常量,當迭代到i++是,這條語句想要修改常量i的值,所以會報錯。還有神奇的地方,讓我們來看看下面的例子
var funcs = [];
person = {
name : 'Godfrey',
age : 20,
sex : 'male'
};
for(const key in person){
funcs.push(function(){
console.log(key);
});
}
funcs.forEach(function(func){
func(); // 輸出name、age、sex
});
複製程式碼
你會發現這裡和我們上面說的有些不一樣了,不是說const是常量嗎,不能被修改嗎?為什麼在這個for-in迴圈中可以被正確執行。
彆著急,是這樣的,在for-in和for-of迴圈中,每次迭代不會修改已有的繫結,而是會建立一個新的繫結,就像我上面講的let在迴圈中的表現是一樣的。也就是說在某些情況下,const會和let表現是完全相同的。
臨時死區(TDZ)
在引入let宣告和const宣告的同時,也帶來了臨時死區的概念。
簡單的說,臨時死去就是在變數未初始化之前不能使用。下面我們來看例子
if(true){
console.log(typeof a); // 引用錯誤
let a = 1;
}
複製程式碼
JavaScript引擎在掃描到變數的時候,如果遇到的是var宣告的變數,就發生變數提升,如果是let、const宣告的變數,就加入“臨時死區”。
tips:在塊級作用域之外宣告的訪問let、const宣告的同名變數不會報錯,也就是說“臨時死區”只作用在當前塊級作用域。
字串和正規表示式
ES6引入了一些有關字串方面的新方法和新特性。主要有以下幾個方面,字串、正規表示式、模板字面量
字串新特性
- 更好的Unicode支援
- codePointAt()方法——charCodeAt()的Unicode-16版本
接受一個編碼單元的位置而非字串的位置作為引數,返回與字串給定位置的碼位。
let test = "?a"
console.log(test.charCodeAt(0)); // 55362
console.log(test.charCodeAt(1)) // 57271
console.log(test.charCodeAt(2)) // 97
console.log(test.codePointAt(0)); // 134071
console.log(test.codePointAt(1)) // 57271
console.log(test.codePointAt(2)) // 97
複製程式碼
charCodeAt(0)返回的是位置0處的第一個編碼單元,但是這個往往不是我們想要的結果,而codePointAt(0)返回的是第一個字的完整編碼單元。
複製程式碼
- String.fromCodePoint()方法
通過這個方法我們可以根據指定碼位來生成一個字元
console.log(String.fromCodePoint(134071)); //?
複製程式碼
- normalize()方法 這個方法是暫且不用管他,主要是涉及到國際化方面
字串新方法
- includes()
如果在字串檢測到指定文字則返回true,否則返回false。 - startsWith()
如果在字元川的起始部分檢測到指定文字則返回true,否則返回false。 - endsWith()
如果在字串的結尾部分檢測到指定文字則返回true,否則返回false。
let test = 'Hello world';
console.log(test.includes("Hello")); // true
console.log(test.includes("a")); // false
console.log(test.startsWith("Hello")); // true
console.log(test.startsWith("Hello",4)); // false
console.log(test.endsWith("d")); // true
console.log(test.startsWith("Hello")); // false
console.log(test.endsWith("o",8)); // true
複製程式碼
以上三個方法都會接受兩個引數。第一個是要檢測的文字內容,第二個是一開始搜尋的位置的索引。includes和startsWith都會從索引的位置開始搜尋,而endsWith會從字串長度減去這個索引值的位置開始匹配。
上面就是就是ES6字串主要的新增方法。
正規表示式新特性
ES6為了更好的滿足開發中對字串的操作,同時也增強了正規表示式。
- u修飾符
在正規表示式新增了u修飾符時,正規表示式的操作就從編碼單元變為字元模式。
let test = "?";
console.log(text.length); // 2
console.log(/^.$/.test(test)); // false
console.log(/^.$/u.test(test)); // true
複製程式碼
- y修飾符
y修飾符會影響正規表示式搜尋過程中的lastIndex屬性,如果使用了y修飾符,當開始匹配的時候,便會從latIndex開始進行。如果在指定位置沒能匹配成功,則繼續停止匹配。
var text = "kidd1 kidd2 kidd3";
var pattern = /kidd\d\s?/;
var result = pattern.exec(text);
var globalPattern = /kidd\d\s?/g;
var globalResult = globalPattern.exec(text);
var stickyPattern = /kidd\d\s?/y;
var stickReslut = stickyPattern.exec(text);
console.log(result[0]); // "kidd1 "
console.log(globalResult[0]); // "kidd1 "
console.log(stickReslut[0]); // "kidd1 "
pattern.lastIndex = 1;
globalPattern.lastIndex = 1;
stickyPattern.lastIndex = 1;
result = pattern.exec(text);
globalResult = globalPattern.exec(text);
stickReslut = stickyPattern.exec(text);
console.log(result[0]); // "kidd1 "
console.log(globalResult[0]); // "kidd2 "
console.log(stickReslut[0]); // null
複製程式碼
tips:
- y修飾符只在字串物件中起作用,對字串的方法無效。
- 還有就是使用^字元的時候,只會在字串的起始位置和多行模式的首行進行匹配。當lastIndex為0時,完全沒問題,會向普通正規表示式一般無二;但是當lastIndex不為0時,就會永遠也匹配不到正確結果
- 修正了正規表示式建構函式中複製的錯誤
在ES6之前,使用建構函式建立正規表示式有一個小bug,就是使用正規表示式宣告正規表示式,第一個引數為正規表示式,第二個引數可以為正規表示式新增一些修飾符。但是當第一個引數是另一個正規表示式的引用時,再傳入第二個引數,ES5就會報錯。現在ES6修正了這個問題
var regex1 = /ab/g;
//在ES5中時會丟擲錯誤的。但在ES6中就是正常的。
var regex2 = new Regex2(regex1,"i");
console.log(regex1.toString()); // "/ab/g"
console.log(regex2.toString()); // "/ab/i"
複製程式碼
- flags屬性
ES6還為正規表示式物件新增了一個屬性——flags,是幫助我們更快捷的訪問正規表示式中的修飾符。通過正規表示式物件呼叫它,她會返回該正規表示式的所有修飾符。
var regex = /ab/g;
console.log(regex.flags); // "g"
複製程式碼
模板字面量
這是ES6引入的全新的語法。主要是為了解決字串多行模式、佔位符和提供更強大的字串操作。
基礎語法
模板字面量基礎的使用方法看起來與字串字面量沒有大的區別,無非是將字串字面量的引號(單、雙都可以)換成了反撇號(`),最直觀的特點是能支援多行字串。
var message = `123
456
789`;
console.log(message); //"123
// 456
// 789"
複製程式碼
上面是模板字面量最基本的用法,看起來好像沒什麼特別強大的地方,ES5也完全能做到,就是麻煩一點,需要手動拼接。好!接下來才是模板字面量真正強大的地方(其實,是否強大與否主要還是看應用,畢竟程式設計還是偏向應用科學的)。
字串佔位符
字串佔位符的基礎語法是${},中間可以包含任意的JavaScript表示式。字串佔位符為你提供了將“表示式”嵌入到字串中的功能,並且她會計算計算出結果。話不多說,來看個簡單的例子。
let a = 123,
b = 456;
message = `Sum is ${a + b}`;
console.log(message); // "Sum is 579";
複製程式碼
字串佔位符也是支援巢狀的。
標籤模板
首先我們要說明什麼是標籤,標籤就是在模板字面量第一個反撇號(`)之前標註的字串。當然這個字串不需要新增引號。當然,如果這個標籤僅僅是個字串是沒什麼作用的,如果它可以是一個函式物件的引用,那就很強大了,這樣的話,我們在處理同型別字串,直接在它們前面新增一個tag,就很方便了。
function tag(literals,...substitutions){
let result = "";
for(let i = 0;i< substitutions.length; i++){
result += literals[i];
result += substitutions[i];
};
result += literals[literals.length-1];
return result;
}
let a = 123,
b = 456,
message = tag `Sum is ${a+b}`;
console.log(message); // "Sum is 579";
複製程式碼
上面我們有tag標籤模擬了模板字面量預設的行。我們可以看到函式中有兩個引數,其實兩個都是陣列。
- literals
literals引數實際上是一個陣列,你可以將他看成是傳入了佔位符以外的所有字串,只不過將那些字串根據佔位符分割開來這樣就形成了一個陣列,存放在literals中。還有一點就是,如果模板字面量的第一位是一個佔位符,那literals陣列的第一個值就是空字串。保證了literals總是比substitutions多一個。 - substitutions
既然有存放字串的陣列了,那就必須有存放字串佔位符的地方,那就是substitutions陣列了。它其中存放了經過計算後的表示式的值。
上面是有關字串、正規表示式的ES6新增或修改的內容。
本篇文章的內容都是我借鑑的書本上面的知識,再新增了一些自己的理解。可能會有錯誤的地方,如果您看到了,那請你指出來。如果對文章的內容哪裡有不明白的地方,可以在下面留言,我會進行更加細緻的說明。