譯: 函數提高與提高面試的相關問題

做者關於提高的話題,總共有兩篇。(後來又有一個討論篇),再次搬過來。水平有限,若是翻譯的不許確請包涵,並去看原文。下面開始:javascript

這是我以前的關於「提高」的文章,標題爲《用let,const來指導你的JavaScript變量提高》(/)的第二部分。所以在深刻研究以前,請確保你已經閱讀過第一部分。java

以前我只討論過變量提高,是由於函數提高在JavaScript中與變量提高不一樣。它有本身的獨特方式。我將在這對函數進行擴展,以及在面試中面試官總會提問的「提高」(變量和函數)的棘手問題。es6

但願經過完成這兩部分,你能真的在你的JavaScript檢查清單中劃掉它。 面試

讓咱們開始吧。segmentfault

函數提高

在JavaScript裏有兩種方式建立函數,經過函數聲明函數表達式。那就看看這兩種方式是如何影響「提高」的。函數

函數聲明

用指定的參數來定義函數。
語法:學習

function name(param1, param2, ...) {
  [statements]
}

在JavaScript裏,函數聲明提高函數定義。
所以,這些函數在被聲明以前便可使用
例子:翻譯

hoisted() // output: "Hoisted"

function hoisted() {
  console.log('Hoisted')
}

下面的例子,展現JavaScript編譯器如何看待上面的代碼:3d

// Hoisted code
function hoisted() {
  console.log('Hoisted')
}// Rest of the code
hoisted() // output: "Hoisted"

若是你在全局做用域或者功能區做用域(在JavaScript裏基本叫局部做用域)進行函數聲明,這個行爲是真實的。
這很是有用,由於你能在代碼開頭使用高級邏輯,使其可讀與可理解。
提示:不要在「if/else」中進行函數聲明。code

函數表達式

「function」關鍵字也能在一個表達式中定義一個函數。
語法:

const myFunction = function [name](param1, param2, ...) {
  [statements]
}

函數名是可選的,所以能夠是匿名函數。咱們能夠用箭頭函數,以下所示:

const myFunction = (param1, param2, ...) => {
  [statements]
}

在JavaScript裏,函數表達式沒有被提高。
所以,你不能使用函數表達式在定義它們以前。
例子:

notHoisted() // TypeError: notHoisted is not a function

const notHoisted = function() {
  console.log('foo')
}

關於函數建立中「提高」的部分,以上全部這些都要記住。
如今來看看面試問題!

「提高」面試問題

「提高」的不穩定行爲一直是面試過程當中的熱門問題。用以前和如今這兩篇文章的知識,能夠解答這個話題中的任何問題。

問題 1 :

var a = 1;

function b() {
  a = 10;
  return;

  function a() {}
}

b();
console.log(a);

輸出: 1, 這是爲何?! ?
這是由於「function a() {}」聲明被建立在「函數/局部」做用域中。這個新「a」函數在聲明和定義的時候被提高到它的封閉函數b的頂端。下面演示發生了什麼:

var a = 1;

function b() {
  // Hoisted
  function a() {}

  a = 10;
  return;
}

b();

console.log(a)

所以,賦值a=10;不能改變全局做用域a的值,任然是1,而是將局部a從函數改成整數10。
若是聲明函數a不在這,那將輸出10。

問題 2:

function foo(){
    function bar() {
        return 3;
    }
    return bar();
    function bar() {
        return 8;
    }
}
alert(foo());

輸出:8
兩個bar都是用函數聲明的函數,將被提高到foo的局部做用域頂端。

然而,返回8的bar()將晚於第一個返回3的提高。所以,這個返回8的函數將被執行。

以後的場景:

function foo(){
    //Hoisted before
    function bar() {
        return 3;
    }
    // Hoisted after
    function bar() {
        return 8;
    }    return bar();
    
}
alert(foo());

問題 3:

function parent() {
    var hoisted = "I'm a variable";
    function hoisted() {
        return "I'm a function";
    }
    return hoisted(); 
}
console.log(parent());

輸出:「類型錯誤:hoisted不是一個函數」
這個很詭異。函數 vs 變量!咱們來分析下。
咱們都知道,說到變量提高,只有聲明被提高(值是「undefined」),而不是定義!

若是是函數聲明的方式聲明函數,聲明和定義一併提高!
如今,若是是多個相同標識符聲明(變量和函數在同一做用域裏)這樣的狀況,這個變量的提高會直接忽略

解釋器只聲明函數並提高它。

最終,這個聲明變量變量賦值被執行(沒有被提高),且值分配給了被提高的函數,值「I‘m a variable」 ,

這是一個簡單的字符串而不是函數。因而報錯了!

這後面的場景重現了問題:

function parent() {    // Function declaration hoisted with the definition
    function hoisted() {
        return "I'm a function";
    }    // Declaration ignored, assignment of a string
    hoisted = "I'm a variable";
    return hoisted(); 
}
console.log(parent());

問題 4:

alert(foo());
function foo() {
  var bar = function() {
    return 3;
  };
  return bar();
  var bar = function() {
    return 8;
  };
}

輸出:3
這個簡單。函數foo()自己將做爲函數聲明在全局範圍內提高。在函數foo裏面,是兩個明確的函數表達式的例子。

編譯器不會提早讀取第二個函數bar() (沒有提高)。第一個將被執行並返回。

問題 5:

var myVar = 'foo';(function() {
  console.log('Original value was: ' + myVar);
  var myVar = 'bar';
  console.log('New value is: ' + myVar);
})();

輸出: 「Original value was: undefined」, 「New value is: bar」

在這個例子中,全局變量myVar的值‘foo’出如今picture以外。這是由於變量myVar在函數做用域內部聲明和定義,並且被提高到了IIFE的頂端,值是‘undefined’,他被首先記錄。而後將值「bar」分配並記錄下來。

這是我這裏對JavaScript提高的總結。?

但願這兩篇(/)文章能對你有幫助。

若是你想學習箭頭函數和ES6其餘相關的功能,請查看下面的文章

Peace ✌️

至此,關於提高的兩篇文章結束了。後來一個讀者和做者就問題1問題3產生了分歧。到時候也搬過來吧。

相關文章
相關標籤/搜索