掌握前端面試基礎系列一: ES6

javavs.jpg

背景


立刻又到年末了,跳槽的季節。javascript

我又想起來曾經準備面試的情景, 各類蒐集資料, 整理, 面試, 再整理, 十分的辛苦。前端

其實,不管面試哪家公司, 基礎都是免不了的.java

以前就有整理一下這些資料的想法,不過本身比較懶, 一隻沒有動手。面試

最近在作公衆號,就想着乾脆搞一搞,把這些基礎知識整理一下,之後本身也能看。segmentfault

恰好國慶在家看了ES6相關的東西, 這一篇就從ES6開始吧。微信

正文


var, let, const

這三個東西, 常常會被問到。 模塊化

彙總一下,基本上就是:函數

  • var, let, const 有什麼區別?
  • let, const 有沒有變量提高(hosting) ?
  • 什麼是 TDZ ?

首先, 咱們先總體的看下區別:學習

針對這幾點, 咱們一個個看。ui

首先, 咱們仍是先了解一下變量提高.

看個例子:

console.log(a) // undefined
var a = 1

這裏咱們能夠看到, 第一行中的a雖然還沒聲明, 可是咱們用起來卻不會報錯。 這種狀況, 就是聲明的提高。

其實也就是:

var a
console.log(a) // undefined
a = 1

可是, 若是是換成let , 狀況就不同了:

image.png

要理解這個現象, 首先須要搞清楚提高的本質, 理解建立 javascript 變量的三個步驟

  1. 建立
  2. 初始化
  3. 賦值

爲了便於理解, 咱們先看看var建立、初始化和賦值 過程:

function foo(){
  var x = 1
  var y = 2
}

foo()

執行foo 時, 會有一些過程(部分過程):

  1. 進入 foo,爲 foo 建立一個環境。
  2. 找到 foo 中全部用 var 聲明的變量,在這個環境中「建立」這些變量(即 x 和 y)。
  3. 將這些變量「初始化」爲 undefined。
  4. 執行代碼
  5. x = 1 將 x 變量「賦值」爲 1
  6. y = 2 將 y 變量「賦值」爲 2

也就是說 va 聲明, 會在代碼執行以前就將 建立變量,並將其初始化爲 undefined

這就解釋了爲何在 var x = 1 以前 console.log(x) 會獲得 undefined

接下來看 let 聲明的「建立、初始化和賦值」過程

// ...
{
  let x = 1;
  x = 2
}

咱們看一下過程:

  1. 找到全部用 let 聲明的變量,在環境中建立這些變量
  2. 執行代碼(注意如今尚未初始化)
  3. 執行 x = 1,將 x 「初始化」爲 1(這並非一次賦值,若是代碼是 let x,就將 x 初始化爲 undefined)
  4. 執行 x = 2,對 x 進行「賦值」

這就解釋了爲何在 let x 以前使用 x 會報錯:

let x = 'global'
{
  console.log(x) // Uncaught ReferenceError: x is not defined
  let x = 1
}

緣由有兩個

  1. console.log(x) 中的 x 指的是下面的 x,而不是全局的 x.
  2. 執行 log 時 x 還沒「初始化」,因此不能使用(也就是所謂的 TDZ, tempory dead zone, 暫時死區)

說到這裏, 就都清楚了:

  1. let 的「建立」過程被提高了,可是初始化沒有提高。
  2. var 的「建立」和「初始化」都被提高了。

function 也是相似的,並且function 的「建立」「初始化」和「賦值」都被提高了。

這一點, 感興趣的朋友能夠本身實驗一下。

算了, 直接給個例子吧:

<script>
bar()
 
function  bar(){
  console.log(2)
}
</script>

JS 引擎會有一下過程:

  1. 找到全部用 function 聲明的變量,在環境中「建立」這些變量。
  2. 將這些變量「初始化」並「賦值」爲 function(){ console.log(2) }
  3. 開始執行代碼 fn2()`

也就是說 function 聲明會在代碼執行以前就「建立、初始化並賦值」。

這裏作一下簡單的總結:

  1. 函數提高優先於變量提高. 函數提高會把整個函數挪到做用域頂部變量提高只會把聲明挪到做用域頂部
  2. var存在提高,咱們能在聲明以前使用。
  3. let, const 由於存在暫時性死區不能在聲明前使用
  4. var全局做用域下聲明變量, 會致使變量掛載在window上, 而另外二者不會
  5. letconst的做用基本一致,可是後者聲明的變量不能再次賦值。

箭頭函數

這個也是 ES6 裏比較好用的feature, 咱們天天也都會用到。

箭頭函數是 ES6 中新的函數定義形式:

function name(arg1, arg2) {}

// 可使用

(arg1, arg2) => {}

// 來定義。

箭頭函數, 一方面看起來比較簡潔, 另外一方面, 之解決ES5時代,this的問題.

看個例子:

function fn() {
    console.log(this)  // {a: 100} ,該做用域下的 this 的真實的值
    var arr = [1, 2, 3]
    
    // ES5
    arr.map(function (item) {
        console.log(this)  // window
    })
    
    // 箭頭函數
    arr.map(item => {
        console.log(this)  // {a: 100} 這裏打印的就是父做用域的 this
    })
}
fn.call({a: 100})

模塊化

模塊化的好處是十分明顯的:

  1. 解決命名衝突
  2. 提供複用性
  3. 提升代碼可維護性

ES6 以前也有模塊化的方案: AMD, CMD, 就簡單的提一下, 不是本文討論的主要內容。

AMD, CMD

// AMD
define(['./a', './b'], function(a, b) {
  a.do()
  b.do()
})

// CMD
define(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
})

IIFE

也有一種IIFE的 形式:

(function(globalVariable){
   // 造成一個獨立的做用域,不會污染全局
   // ...
})(globalVariable)

CommonJS

CommonJS 最先是 Node.js 在使用,目前也仍然普遍使用。

看個例子:

// a.js

module.exports = {
  a: 1
}

// b.js
var module = require('./a.js')
module.a // 1

ES6 Module

ES Module 是原生實現的模塊化方案, 提供了一種新的, 能夠文件做爲模塊的開發方式。

使用方式就是咱們常見的:

// a.js
export function a() {}
export default function() {}


//b.js

import XXX from './a.js'
import { XXX } from './a.js'

若是隻是輸出一個惟一的對象,使用export default便可:

// util1.js
export default {
    a: 100
}

// index.js 文件
import obj from './util1.js'
console.log(obj) // { a: 100 }

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

// foo.js
export function fn1() {
    alert('fn1')
}

export function fn2() {
    alert('fn2')
}

// index.js
import { fn1, fn2 } from './foo.js'
fn1()
fn2()

Class

class 其實一直是js的保留字,直到 ES6 才正式用到。

(js中並不存在類,class 只是個語法糖。本質上仍是函數, 其實就是要模擬面向對象的語法, 你懂的)

ES6 的 class 就是取代以前構造函數初始化對象的形式,從語法上更加接近面向對象的寫法。

例如:

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

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

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

ES6 class 的寫法:

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

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

注意如下幾點:

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

原型繼承和 Class 繼承

使用 Class 來實現繼承就更加簡單了,至少比構造函數實現繼承簡單不少:

// 動物
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}
// 狗
function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}

Dog.prototype = new Animal()


var husky = new Dog()

husky.bark() // dog bark

ES6 寫法:

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

class Dog extends Animal {
    constructor(name) {
        super(name)
        this.name = name
    }
    
    bark() {
        console.log(`${this.name} say`)
    }
}

const husky = new Dog('哈士奇')
husky.bark()

注意如下兩點:

  • 使用extends便可實現繼承,更加符合經典面嚮對象語言的語法.
  • 子類的constructor必定要執行super(),調用父類的constructor.

以上這幾個方面都是面試中常常問的,ES6的內容遠遠不知這些, 好用的特性還有不少, 好比:

  • ... 」 操做符
  • 解構
  • Set
  • Map
  • 等等

這裏就不一一介紹了。

... 」 操做符 這個能夠參考個人這篇文章:

深刻了解強大的 ES6 「 ... 」 運算符

ES6 面試問的比較多的大概就是以上這幾點, 可能有所紕漏, 後面再作補充吧。

但願對你們有所幫助。

最後

今天是樓主節後第一天上班, 不在狀態。

說個最尷尬的事情吧, 今天要填2019的績效自評, 其中有一項:

image.png

公司在哪些方面能幫助你成長?

原本是一個很正常的問題:

我是這麼寫的:

image.png

寫完笑了笑, 跟同事調侃了一下, 可不得加薪嗎。 說完回頭我就直接點了提交。

提交完感受哪裏不大對?

image.png

就這麼提交了 ???

, 懼怕, 臥, 我幹了什麼

反正也不能改了, 就這樣吧。

文中如有紕漏, 還請各位大佬指正。

最後的最後

若是你以爲內容有幫助能夠關注下個人公衆號 「 前端e進階 」,一塊兒學習成長

clipboard.png

能夠經過公衆號菜單欄的聯繫我, 加入咱們的微信羣, 一塊兒愉快的玩耍。

相關文章
相關標籤/搜索