一面 1:ES 基礎知識點與高頻考題解析

一面 1:ES 基礎知識點與高頻考題解析

JavaScript 是 ECMAScript 規範的一種實現,本小節重點梳理下 ECMAScript 中的常考知識點,而後就一些容易出現的題目進行解析。前端

知識點梳理

  • 變量類型程序員

    • JS 的數據類型分類和判斷
    • 值類型和引用類型
  • 原型與原型鏈(繼承)es6

    • 原型和原型鏈定義
    • 繼承寫法
  • 做用域和閉包面試

    • 執行上下文
    • this
    • 閉包是什麼
  • 異步json

    • 同步 vs 異步
    • 異步和單線程
    • 前端異步的場景
  • ES6/7 新標準的考查數組

    • 箭頭函數
    • Module
    • Class
    • Set 和 Map
    • Promise
    • *

變量類型

JavaScript 是一種弱類型腳本語言,所謂弱類型指的是定義變量時,不須要什麼類型,在程序運行過程當中會自動判斷類型。瀏覽器

ECMAScript 中定義了 6 種原始類型:網絡

  • Boolean
  • String
  • Number
  • Null
  • Undefined
  • Symbol(ES6 新定義)

注意:原始類型不包含 Object。數據結構

題目:類型判斷用到哪些方法?

typeof

typeof xxx獲得的值有如下幾種類型:undefined boolean number string object functionsymbol ,比較簡單,再也不一一演示了。這裏須要注意的有三點:閉包

  • typeof null結果是object ,實際這是typeof的一個bug,null是原始值,非引用類型
  • typeof [1, 2]結果是object,結果中沒有array這一項,引用類型除了function其餘的所有都是object
  • typeof Symbol()typeof獲取symbol類型的值獲得的是symbol,這是 ES6 新增的知識點

instanceof

用於實例和構造函數的對應。例如判斷一個變量是不是數組,使用typeof沒法判斷,但可使用[1, 2] instanceof Array來判斷。由於,[1, 2]是數組,它的構造函數就是Array。同理:

function Foo(name) {
    this.name = name
}
var foo = new Foo('bar')
console.log(foo instanceof Foo) // true
題目:值類型和引用類型的區別

值類型 vs 引用類型

除了原始類型,ES 還有引用類型,上文提到的typeof識別出來的類型中,只有objectfunction是引用類型,其餘都是值類型。

根據 JavaScript 中的變量類型傳遞方式,又分爲值類型引用類型,值類型變量包括 Boolean、String、Number、Undefined、Null,引用類型包括了 Object 類的全部,如 Date、Array、Function 等。在參數傳遞方式上,值類型是按值傳遞,引用類型是按共享傳遞。

下面經過一個小題目,來看下二者的主要區別,以及實際開發中須要注意的地方。

// 值類型
var a = 10
var b = a
b = 20
console.log(a)  // 10
console.log(b)  // 20

上述代碼中,a b都是值類型,二者分別修改賦值,相互之間沒有任何影響。再看引用類型的例子:

// 引用類型
var a = {x: 10, y: 20}
var b = a
b.x = 100
b.y = 200
console.log(a)  // {x: 100, y: 200}
console.log(b)  // {x: 100, y: 200}

上述代碼中,a b都是引用類型。在執行了b = a以後,修改b的屬性值,a的也跟着變化。由於ab都是引用類型,指向了同一個內存地址,即二者引用的是同一個值,所以b修改屬性時,a的值隨之改動。

再借助題目進一步講解一下。

說出下面代碼的執行結果,並分析其緣由。
function foo(a){
    a = a * 10;
}
function bar(b){
    b.value = 'new';
}
var a = 1;
var b = {value: 'old'};
foo(a);
bar(b);
console.log(a); // 1
console.log(b); // value: new

經過代碼執行,會發現:

  • a的值沒有發生改變
  • b的值發生了改變

這就是由於Number類型的a是按值傳遞的,而Object類型的b是按共享傳遞的。

JS 中這種設計的緣由是:按值傳遞的類型,複製一份存入棧內存,這類類型通常不佔用太多內存,並且按值傳遞保證了其訪問速度。按共享傳遞的類型,是複製其引用,而不是整個複製其值(C 語言中的指針),保證過大的對象等不會由於不停複製內容而形成內存的浪費。

引用類型常常會在代碼中按照下面的寫法使用,或者說容易不知不覺中形成錯誤

var obj = {
    a: 1,
    b: [1,2,3]
}
var a = obj.a
var b = obj.b
a = 2
b.push(4)
console.log(obj, a, b)

雖然obj自己是個引用類型的變量(對象),可是內部的ab一個是值類型一個是引用類型,a的賦值不會改變obj.a,可是b的操做卻會反映到obj對象上。

    • *

原型和原型鏈

JavaScript 是基於原型的語言,原型理解起來很是簡單,但卻特別重要,下面仍是經過題目來理解下JavaScript 的原型概念。

題目:如何理解 JavaScript 的原型

對於這個問題,能夠從下面這幾個要點來理解和回答,下面幾條必須記住而且理解

  • 全部的引用類型(數組、對象、函數),都具備對象特性,便可自由擴展屬性(null除外)
  • 全部的引用類型(數組、對象、函數),都有一個__proto__屬性,屬性值是一個普通的對象
  • 全部的函數,都有一個prototype屬性,屬性值也是一個普通的對象
  • 全部的引用類型(數組、對象、函數),__proto__屬性值指向它的構造函數的prototype屬性值

經過代碼解釋一下,你們可自行運行如下代碼,看結果。

// 要點一:自由擴展屬性
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;

// 要點二:__proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);

// 要點三:函數有 prototype
console.log(fn.prototype)

// 要點四:引用類型的 __proto__ 屬性值指向它的構造函數的 prototype 屬性值
console.log(obj.__proto__ === Object.prototype)

原型

先寫一個簡單的代碼示例。

// 構造函數
function Foo(name, age) {
    this.name = name
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
// 建立示例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
// 測試
f.printName()
f.alertName()

執行printName時很好理解,可是執行alertName時發生了什麼?這裏再記住一個重點 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的__proto__(即它的構造函數的prototype)中尋找,所以f.alertName就會找到Foo.prototype.alertName

那麼如何判斷這個屬性是否是對象自己的屬性呢?使用hasOwnProperty,經常使用的地方是遍歷一個對象的時候。

var item
for (item in f) {
    // 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性,可是這裏建議你們仍是加上這個判斷,保證程序的健壯性
    if (f.hasOwnProperty(item)) {
        console.log(item)
    }
}
題目:如何理解 JS 的原型鏈

原型鏈

仍是接着上面的示例,若是執行f.toString()時,又發生了什麼?

// 省略 N 行

// 測試
f.printName()
f.alertName()
f.toString()

由於f自己沒有toString(),而且f.__proto__(即Foo.prototype)中也沒有toString。這個問題仍是得拿出剛纔那句話——當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的__proto__(即它的構造函數的prototype)中尋找

若是在f.__proto__中沒有找到toString,那麼就繼續去f.__proto__.__proto__中尋找,由於f.__proto__就是一個普通的對象而已嘛!

  • f.__proto__Foo.prototype,沒有找到toString,繼續往上找
  • f.__proto__.__proto__Foo.prototype.__proto__Foo.prototype就是一個普通的對象,所以Foo.prototype.__proto__就是Object.prototype,在這裏能夠找到toString
  • 所以f.toString最終對應到了Object.prototype.toString

這樣一直往上找,你會發現是一個鏈式的結構,因此叫作「原型鏈」。若是一直找到最上層都沒有找到,那麼就宣告失敗,返回undefined。最上層是什麼 —— Object.prototype.__proto__ === null

原型鏈中的this

全部從原型或更高級原型中獲得、執行的方法,其中的this在執行時,就指向了當前這個觸發事件執行的對象。所以printNamealertName中的this都是f

    • *

做用域和閉包

做用域和閉包是前端面試中,最可能考查的知識點。例以下面的題目:

題目:如今有個 HTML 片斷,要求編寫代碼,點擊編號爲幾的連接就 alert彈出其編號
<ul>
    <li>編號1,點擊我請彈出1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

通常不知道這個題目用閉包的話,會寫出下面的代碼:

var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++) {
    list[i].addEventListener('click', function(){
        alert(i + 1)
    }, true)
}

實際上執行纔會發現始終彈出的是6,這時候就應該經過閉包來解決:

var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++) {
    list[i].addEventListener('click', function(i){
        return function(){
            alert(i + 1)
        }
    }(i), true)
}

要理解閉包,就須要咱們從「執行上下文」開始講起。

執行上下文

先講一個關於 變量提高 的知識點,面試中可能會碰見下面的問題,不少候選人都回答錯誤:

題目:說出下面執行的結果(這裏筆者直接註釋輸出了)
console.log(a)  // undefined
var a = 100

fn('zhangsan')  // 'zhangsan' 20
function fn(name) {
    age = 20
    console.log(name, age)
    var age
}

console.log(b); // 這裏報錯
// Uncaught ReferenceError: b is not defined
b = 100;

在一段 JS 腳本(即一個<script>標籤中)執行以前,要先解析代碼(因此說 JS 是解釋執行的腳本語言),解析的時候會先建立一個 全局執行上下文 環境,先把代碼中即將執行的(內部函數的不算,由於你不知道函數什麼時候執行)變量、函數聲明都拿出來。變量先暫時賦值爲undefined,函數則先聲明好可以使用。這一步作完了,而後再開始正式執行程序。再次強調,這是在代碼執行以前纔開始的工做。

咱們來看下上面的面試小題目,爲何aundefined,而b卻報錯了,實際 JS 在代碼執行以前,要「全文解析」,發現var a,知道有個a的變量,存入了執行上下文,而b沒有找到var關鍵字,這時候沒有在執行上下文提早「佔位」,因此代碼執行的時候,提早報到的a是有記錄的,只不過值暫時尚未賦值,即爲undefined,而b在執行上下文沒有找到,天然會報錯(沒有找到b的引用)。

另外,一個函數在執行以前,也會建立一個 函數執行上下文 環境,跟 全局上下文 差很少,不過 函數執行上下文 中會多出this arguments和函數的參數。參數和arguments好理解,這裏的this我們須要專門講解。

總結一下:

  • 範圍:一段<script>、js 文件或者一個函數
  • 全局上下文:變量定義,函數聲明
  • 函數上下文:變量定義,函數聲明,thisarguments

this

先搞明白一個很重要的概念 —— this的值是在執行的時候才能確認,定義的時候不能確認! 爲何呢 —— 由於this是執行上下文環境的一部分,而執行上下文須要在代碼執行以前肯定,而不是定義的時候。看以下例子

var a = {
    name: 'A',
    fn: function () {
        console.log(this.name)
    }
}
a.fn()  // this === a
a.fn.call({name: 'B'})  // this === {name: 'B'}
var fn1 = a.fn
fn1()  // this === window

this執行會有不一樣,主要集中在這幾個場景中

  • 做爲構造函數執行,構造函數中
  • 做爲對象屬性執行,上述代碼中a.fn()
  • 做爲普通函數執行,上述代碼中fn1()
  • 用於call apply bind,上述代碼中a.fn.call({name: 'B'})

下面再來說解下什麼是做用域和做用域鏈,做用域鏈和做用域也是常考的題目。

題目:如何理解 JS 的做用域和做用域鏈

做用域

ES6 以前 JS 沒有塊級做用域。例如

if (true) {
    var name = 'zhangsan'
}
console.log(name)

從上面的例子能夠體會到做用域的概念,做用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。上面的name就被暴露出去了,所以,JS 沒有塊級做用域,只有全局做用域和函數做用域

var a = 100
function fn() {
    var a = 200
    console.log('fn', a)
}
console.log('global', a)
fn()

全局做用域就是最外層的做用域,若是咱們寫了不少行 JS 代碼,變量定義都沒有用函數包括,那麼它們就所有都在全局做用域中。這樣的壞處就是很容易撞車、衝突。

// 張三寫的代碼中
var data = {a: 100}

// 李四寫的代碼中
var data = {x: true}

這就是爲什麼 jQuery、Zepto 等庫的源碼,全部的代碼都會放在(function(){....})()中。由於放在裏面的全部變量,都不會被外泄和暴露,不會污染到外面,不會對其餘的庫或者 JS 腳本形成影響。這是函數做用域的一個體現。

附:ES6 中開始加入了塊級做用域,使用let定義變量便可,以下:

if (true) {
    let name = 'zhangsan'
}
console.log(name)  // 報錯,由於let定義的name是在if這個塊級做用域

做用域鏈

首先認識一下什麼叫作 自由變量 。以下代碼中,console.log(a)要獲得a變量,可是在當前的做用域中沒有定義a(可對比一下b)。當前做用域沒有定義的變量,這成爲 自由變量 。自由變量如何獲得 —— 向父級做用域尋找。

var a = 100
function fn() {
    var b = 200
    console.log(a)
    console.log(b)
}
fn()

若是父級也沒呢?再一層一層向上尋找,直到找到全局做用域仍是沒找到,就宣佈放棄。這種一層一層的關係,就是 做用域鏈

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a) // 自由變量,順做用域鏈向父做用域找
        console.log(b) // 自由變量,順做用域鏈向父做用域找
        console.log(c) // 本做用域的變量
    }
    F2()
}
F1()

閉包

講完這些內容,咱們再來看一個例子,經過例子來理解閉包。

function F1() {
    var a = 100
    return function () {
        console.log(a)
    }
}
var f1 = F1()
var a = 200
f1()

自由變量將從做用域鏈中去尋找,可是 依據的是函數定義時的做用域鏈,而不是函數執行時,以上這個例子就是閉包。閉包主要有兩個應用場景:

  • 函數做爲返回值,上面的例子就是
  • 函數做爲參數傳遞,看如下例子
function F1() {
    var a = 100
    return function () {
        console.log(a)
    }
}
function F2(f1) {
    var a = 200
    console.log(f1())
}
var f1 = F1()
F2(f1)

至此,對應着「做用域和閉包」這部分一開始的點擊彈出alert的代碼再看閉包,就很好理解了。

    • *

異步

異步和同步也是面試中常考的內容,下面筆者來說解下同步和異步的區別。

同步 vs 異步

先看下面的 demo,根據程序閱讀起來表達的意思,應該是先打印100,1秒鐘以後打印200,最後打印300。可是實際運行根本不是那麼回事。

console.log(100)
setTimeout(function () {
    console.log(200)
}, 1000)
console.log(300)

再對比如下程序。先打印100,再彈出200(等待用戶確認),最後打印300。這個運行效果就符合預期要求。

console.log(100)
alert(200)  // 1秒鐘以後點擊確認
console.log(300)

這倆到底有何區別?—— 第一個示例中間的步驟根本沒有阻塞接下來程序的運行,而第二個示例卻阻塞了後面程序的運行。前面這種表現就叫作 異步(後面這個叫作 同步 ),即不會阻塞後面程序的運行

異步和單線程

JS 須要異步的根本緣由是 JS 是單線程運行的,即在同一時間只能作一件事,不能「一心二用」。

一個 Ajax 請求因爲網絡比較慢,請求須要 5 秒鐘。若是是同步,這 5 秒鐘頁面就卡死在這裏啥也幹不了了。異步的話,就好不少了,5 秒等待就等待了,其餘事情不耽誤作,至於那 5 秒鐘等待是網速太慢,不是由於 JS 的緣由。

講到單線程,咱們再來看個真題:

題目:講解下面代碼的執行過程和結果
var a = true;
setTimeout(function(){
    a = false;
}, 100)
while(a){
    console.log('while執行了')
}

這是一個頗有迷惑性的題目,很多候選人認爲100ms以後,因爲a變成了false,因此while就停止了,實際不是這樣,由於JS是單線程的,因此進入while循環以後,沒有「時間」(線程)去跑定時器了,因此這個代碼跑起來是個死循環!

前端異步的場景

  • 定時 setTimeout setInterval
  • 網絡請求,如 Ajax <img>加載

Ajax 代碼示例

console.log('start')
$.get('./data1.json', function (data1) {
    console.log(data1)
})
console.log('end')

img 代碼示例(經常使用於打點統計)

console.log('start')
var img = document.createElement('img')
// 或者 img = new Image()
img.onload = function () {
    console.log('loaded')
    img.onload = null
}
img.src = '/xxx.png'
console.log('end')
    • *

ES6/7 新標準的考查

題目:ES6 箭頭函數中的 this和普通函數中的有什麼不一樣

箭頭函數

箭頭函數是 ES6 中新的函數定義形式,function name(arg1, arg2) {...}可使用(arg1, arg2) => {...}來定義。示例以下:

// JS 普通函數
var arr = [1, 2, 3]
arr.map(function (item) {
    console.log(index)
    return item + 1
})

// ES6 箭頭函數
const arr = [1, 2, 3]
arr.map((item, index) => {
    console.log(index)
    return item + 1
})

箭頭函數存在的意義,第一寫起來更加簡潔,第二能夠解決 ES6 以前函數執行中this是全局變量的問題,看以下代碼

function fn() {
    console.log('real', this)  // {a: 100} ,該做用域下的 this 的真實的值
    var arr = [1, 2, 3]
    // 普通 JS
    arr.map(function (item) {
        console.log('js', this)  // window 。普通函數,這裏打印出來的是全局變量,使人費解
        return item + 1
    })
    // 箭頭函數
    arr.map(item => {
        console.log('es6', this)  // {a: 100} 。箭頭函數,這裏打印的就是父做用域的 this
        return item + 1
    })
}
fn.call({a: 100})
題目:ES6 模塊化如何使用?

Module

ES6 中模塊化語法更加簡潔,直接看示例。

若是隻是輸出一個惟一的對象,使用export default便可,代碼以下

// 建立 util1.js 文件,內容如
export default {
    a: 100
}

// 建立 index.js 文件,內容如
import obj from './util1.js'
console.log(obj)

若是想要輸出許多個對象,就不能用default了,且import時候要加{...},代碼以下

// 建立 util2.js 文件,內容如
export function fn1() {
    alert('fn1')
}
export function fn2() {
    alert('fn2')
}

// 建立 index.js 文件,內容如
import { fn1, fn2 } from './util2.js'
fn1()
fn2()
題目:ES6 class 和普通構造函數的區別

class

class 其實一直是 JS 的關鍵字(保留字),可是一直沒有正式使用,直到 ES6 。 ES6 的 class 就是取代以前構造函數初始化對象的形式,從語法上更加符合面向對象的寫法。例如:

JS 構造函數的寫法

function MathHandle(x, y) {
  this.x = x;
  this.y = y;
}

MathHandle.prototype.add = function () {
  return this.x + this.y;
};

var m = new MathHandle(1, 2);
console.log(m.add())

用 ES6 class 的寫法

class MathHandle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  add() {
    return this.x + this.y;
  }
}
const m = new MathHandle(1, 2);
console.log(m.add())

注意如下幾點,全都是關於 class 語法的:

  • class 是一種新的語法形式,是class Name {...}這種形式,和函數的寫法徹底不同
  • 二者對比,構造函數函數體的內容要放在 class 中的constructor函數中,constructor即構造器,初始化實例時默認執行
  • class 中函數的寫法是add() {...}這種形式,並無function關鍵字

使用 class 來實現繼承就更加簡單了,至少比構造函數實現繼承簡單不少。看下面例子

JS 構造函數實現繼承

// 動物
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}
// 狗
function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}
Dog.prototype = new Animal()
// 哈士奇
var hashiqi = new Dog()

ES6 class 實現繼承

class Animal {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat`)
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name)
        this.name = name
    }
    say() {
        console.log(`${this.name} say`)
    }
}
const dog = new Dog('哈士奇')
dog.say()
dog.eat()

注意如下兩點:

  • 使用extends便可實現繼承,更加符合經典面嚮對象語言的寫法,如 Java
  • 子類的constructor必定要執行super(),以調用父類的constructor
題目:ES6 中新增的數據類型有哪些?

Set 和 Map

Set 和 Map 都是 ES6 中新增的數據結構,是對當前 JS 數組和對象這兩種重要數據結構的擴展。因爲是新增的數據結構,目前還沒有被大規模使用,可是做爲前端程序員,提早了解是必須作到的。先總結一下二者最關鍵的地方:

  • Set 相似於數組,但數組能夠容許元素重複,Set 不容許元素重複
  • Map 相似於對象,但普通對象的 key 必須是字符串或者數字,而 Map 的 key 能夠是任何數據類型

Set

Set 實例不容許元素有重複,能夠經過如下示例證實。能夠經過一個數組初始化一個 Set 實例,或者經過add添加元素,元素不能重複,重複的會被忽略。

// 例1
const set = new Set([1, 2, 3, 4, 4]);
console.log(set) // Set(4) {1, 2, 3, 4}

// 例2
const set = new Set();
[2, 3, 5, 4, 5, 8, 8].forEach(item => set.add(item));
for (let item of set) {
  console.log(item);
}
// 2 3 5 4 8

Set 實例的屬性和方法有

  • size:獲取元素數量。
  • add(value):添加元素,返回 Set 實例自己。
  • delete(value):刪除元素,返回一個布爾值,表示刪除是否成功。
  • has(value):返回一個布爾值,表示該值是不是 Set 實例的元素。
  • clear():清除全部元素,沒有返回值。
const s = new Set();
s.add(1).add(2).add(2); // 添加元素

s.size // 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2);
s.has(2) // false

s.clear();
console.log(s);  // Set(0) {}

Set 實例的遍歷,可以使用以下方法

  • keys():返回鍵名的遍歷器。
  • values():返回鍵值的遍歷器。不過因爲 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),因此keys()values()返回結果一致。
  • entries():返回鍵值對的遍歷器。
  • forEach():使用回調函數遍歷每一個成員。
let set = new Set(['aaa', 'bbb', 'ccc']);

for (let item of set.keys()) {
  console.log(item);
}
// aaa
// bbb
// ccc

for (let item of set.values()) {
  console.log(item);
}
// aaa
// bbb
// ccc

for (let item of set.entries()) {
  console.log(item);
}
// ["aaa", "aaa"]
// ["bbb", "bbb"]
// ["ccc", "ccc"]

set.forEach((value, key) => console.log(key + ' : ' + value))
// aaa : aaa
// bbb : bbb
// ccc : ccc

Map

Map 的用法和普通對象基本一致,先看一下它能用非字符串或者數字做爲 key 的特性。

const map = new Map();
const obj = {p: 'Hello World'};

map.set(obj, 'OK')
map.get(obj) // "OK"

map.has(obj) // true
map.delete(obj) // true
map.has(obj) // false

須要使用new Map()初始化一個實例,下面代碼中set get has delete顧名便可思義(下文也會演示)。其中,map.set(obj, 'OK')就是用對象做爲的 key (不光能夠是對象,任何數據類型均可以),而且後面經過map.get(obj)正確獲取了。

Map 實例的屬性和方法以下:

  • size:獲取成員的數量
  • set:設置成員 key 和 value
  • get:獲取成員屬性值
  • has:判斷成員是否存在
  • delete:刪除成員
  • clear:清空全部
const map = new Map();
map.set('aaa', 100);
map.set('bbb', 200);

map.size // 2

map.get('aaa') // 100

map.has('aaa') // true

map.delete('aaa')
map.has('aaa') // false

map.clear()

Map 實例的遍歷方法有:

  • keys():返回鍵名的遍歷器。
  • values():返回鍵值的遍歷器。
  • entries():返回全部成員的遍歷器。
  • forEach():遍歷 Map 的全部成員。
const map = new Map();
map.set('aaa', 100);
map.set('bbb', 200);

for (let key of map.keys()) {
  console.log(key);
}
// "aaa"
// "bbb"

for (let value of map.values()) {
  console.log(value);
}
// 100
// 200

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// aaa 100
// bbb 200

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// aaa 100
// bbb 200

Promise

Promise是 CommonJS 提出來的這一種規範,有多個版本,在 ES6 當中已經歸入規範,原生支持 Promise 對象,非 ES6 環境能夠用相似 Bluebird、Q 這類庫來支持。

Promise 能夠將回調變成鏈式調用寫法,流程更加清晰,代碼更加優雅。

簡單概括下 Promise:三個狀態、兩個過程、一個方法,快速記憶方法:3-2-1

三個狀態:pendingfulfilledrejected

兩個過程:

  • pending→fulfilled(resolve)
  • pending→rejected(reject)

一個方法:then

固然還有其餘概念,如catchPromise.all/race,這裏就不展開了。

關於 ES6/7 的考查內容還有不少,本小節就不逐一介紹了,若是想繼續深刻學習,能夠在線看《ES6入門》。

小結

本小節主要總結了 ES 基礎語法中面試常常考查的知識點,包括以前就考查較多的原型、異步、做用域,以及 ES6 的一些新內容,這些知識點但願你們都要掌握。

相關文章
相關標籤/搜索