中高級JavaScript易錯面試題

寫出下題的輸出html

 

一、函數的實參與形參lengthpromise

 

var length = 10; function fn() { console.log(this.length); } var obj = { length: 5, method: function(fn) { fn(); arguments[0](); } }; console.log(obj.method(fn, 1));  // 0 2

 

 

咱們都知道,[1, 2, 3].length能夠獲得3,"123".length能夠獲得3,那麼函數的length獲得什麼呢?瀏覽器

function test(a,b,c) {} test.length // 3
function test(a,b,c,d) {} test.length // 4

能夠看到,函數的length彷佛返回了參數的個數,那麼對於形參和實參有沒有區別呢?答案是有。異步

 

function test() { console.log( arguments.length );} test(1,2,3); // 輸出 3
test(1,2,3,4); // 輸出 4

能夠看到,在函數中,用arguments.length取到的是函數的實際參數的個數。async

另外,咱們要知道var length = 10 這樣寫是不行的,由於length是JavaScript內置的屬性,不能用做變量名或函數名。戳這裏http://www.runoob.com/js/js-reserved.html查看JavaScript有哪些保留關鍵字、內置屬性等。編輯器

因此,當執行fn()時,this.length打印的是fn這個函數的形參的個數,爲0;而執行arguments[0]()時,其實是obj.method()這個方法的arguments調用了fn函數,this.length的this指向的是arguments,他的實際參數個數爲2。函數

 

 

二、函數的解析與預解析過程(變量提高)oop

 

function fn(a) {
    console.log(a);     // function a() {alert(1)}
    var a = 2;
    function a() {alert(1)}
    console.log(a);     //2
}
fn(1);

 

這道題仍是挺吊炸天的。。我也想了半天。。下面我來說一下,涉及到函數的解析和預解析過程。this

首先遇到function fn這樣的函數聲明,會進行函數預解析這麼個過程,什麼是函數預解析?通俗的說就是,從函數體裏找變量和函數聲明的過程,找到的變量(遇到var就找到了變量)不會去讀具體的值,只會賦爲undefined;找到的函數聲明會賦值爲整個函數體,這裏有個知識點就是,若是找到的變量和聲明同名,那麼聲明會覆蓋變量(個人理解是,畢竟函數體比undefined的強嘛)。spa

好比此例中,預解析時找到了變量a,而且賦值爲undefined,找到了聲明function a(){alert(1)},爲整個函數體;二者同名,因此聲明覆蓋了變量a的值,a再也不是undefined的,而是函數體。

預解析完成後調用了方法,開始一步一步走方法。首先console.log(a),這時打印出的是函數體;接着var a = 2,a的值從函數體被改爲了 2 ;接着是個function a(){}函數聲明,注意,聲明不能改變變量的值,因此走完這一句,a的值仍是2,接着打印出了2。

 

 

有人確定有這樣的疑惑,爲何a=1傳進去沒起做用呢?這裏有一個原則,就是局部變量優先,基於這個原則,咱們再來分析一下a的變化過程。預解析中,a=undefined,a=function(){alert(1)},此時參數有值等於1,本應該將a賦值爲1,但卻沒有,緣由是此時的a已經等於局部函數聲明function(){alert(1)},因此外部傳進來的參數1並無取代a的值;假如本例沒有function(){alert(1)}這一句,打印出的將是1,  2。

局部變量優先原則,原理同下:

var a = 5;
function fn(){
  var a = 10;
  console.log(a)  // 10,局部變量優先,在局部找到a後,不會再向外查找
}

 

 

三、變量提高、window的變量

if('a' in window) {   
    var a = 10; 
}  
console.log(a);  // 10

首先,if(){}的花括號並不像function(){}的花括號同樣,具備本身的塊級做用域,if的花括號仍是全局的環境。根據JavaScript的變量提高機制,var a會被js引擎解釋到第一行,以下:

var a;
if ('a' in window) {
  a = 10;
}

接着有個知識點,全局變量是window對象的屬性,因此'a'  in  window會返回true,答案就很直白了。

 

這道題我在作的時候踩了個坑,我在代碼編輯器裏寫了以下代碼:

window.onload = function(){
  if('a' in window){
    var a = 10;
  }  
 console.log(a)  // undefined
}

這時候,a這個變量是定義在匿名函數function(){}裏的,屬於該函數的局部變量,因此a再也不是window的對象。你們必定要注意細節。

 

 

四、基本類型無屬性

var a = 10;
a.pro = 10;
console.log(a.pro + a);  // NaN var s = 'hello';
s.pro = 'world';
console.log(s.pro + s)  // undefinedhello

變量a與s都是基本類型,沒法給他們添加屬性,因此a.pro和s.pro都是undefined。

undefined + 10 獲得NaN(not a number)。

undefined + 'hello' 獲得undefinedhello,其中undefined被轉化爲字符串類型。

 

若是實在想給字符串添加屬性,咱們須要將字符串定義爲對象類型的字符串,以下:

var a= new String('objectString')
a.pro = "aaaaaaa"
console.log(a.pro)    // aaaaaaa

 

 

五、async與await的執行

async function sayHello() {
  console.log('Hello')
  await sleep(1000)
  console.log('world!')
}
function sleep(ms) {
   return new Promise(resolve =>  {
  console.log("666666");
  setTimeout(resolve, ms);
  console.log("888888")})
}
sayHello()  // hello 666666 888888 world!

 

async 表示這是一個async函數,await只能用在這個函數裏面。

await 表示在這裏等待promise返回結果了,再繼續執行。

 

 

首先打出hello,到了await,會等待promise的返回,因此「world」不會馬上打出,接着進入sleep函數,打出666,接着開了一個1秒的定時器,雖然js是單線程的,但setTimeout是異步的,在瀏覽器中,異步操做都是被加入到一個稱爲「events loop」隊列的地方,瀏覽器只會在全部同步代碼執行完成以後採起循環讀取的方式執行這裏面的代碼,因此resolve被加入任務隊列,先打印了888,一秒後執行了resolve,表示promise成功返回,打出了world。

 

 

以上每道題都是本渣本身的想法和理解,若有不正確的地方煩請讀者指正,大佬輕噴~

相關文章
相關標籤/搜索