做者 | Jeskson前端
來源 | 達達前端小酒館程序員
1
到底是怎麼樣的一道面試題,能讓我拿出來講說呢?下面請看代碼:web
function fun(a,b) { console.log(b) return { fun: function(c) { return fun(c,a); } }; } var d = fun(0); d.fun(1); d.fun(2); d.fun(3); var d1 = fun(0).fun(1).fun(2).fun(3); var d2 = fun(0).fun(1); d2.fun(2); d2.fun(3);
function fun(a,b) { console.log(b) return { fun: function(c) { return fun(c,a); } }; } var d = fun(0); d.fun(1); d.fun(2); d.fun(3); var d1 = fun(0).fun(1).fun(2).fun(3); var d2 = fun(0).fun(1); d2.fun(2); d2.fun(3);
undefined VM1036:2 0 VM1036:2 0 VM1036:2 0 VM1036:2 undefined VM1036:2 0 VM1036:2 1 VM1036:2 2 VM1036:2 undefined VM1036:2 0 VM1036:2 1 VM1036:2 1 {fun: ƒ}
//答案: //undefined,0,0,0 //undefined,0,1,2 //undefined,0,1,1
JS函數分兩種:具名函數(命名函數)和匿名函數。面試
能夠用fn.name來判斷,若是有name就是具名函數,若是沒有name就是匿名函數。算法
須要注意的是在IE瀏覽器上沒法獲取具名函數的name,會返回undefined的結果,而在谷歌瀏覽器上就能夠獲取。編程
// 獲取名稱 function getFunctionName(fun){ if(fun.name !== undefined) return fun.name; var funName = fun.toString(); funName = funName.substr('function '.length); funName = funName.substr(0, funName.indexOf('(')); return funName; }
2
第一種是:聲明函數,聲明函數方法,包括函數名和函數體。segmentfault
function funDa() {}
第二種是:建立匿名函數的表達式。設計模式
建立一個變量,這個變量的內容是一個函數,爲匿名函數瀏覽器
var funDa = function() {}
這樣這個函數就沒有了name
網絡
var funDa = function(){} getFunctionName(funDa).length; // 0
第三種是:建立具名函數表達式。
var funDa = function dada(){};
建立一個變量,變量賦值的內容爲一個帶有名稱的函數。具名函數表達式的函數名只能在建立函數內部使用,函數的外層只能使用funData,dada函數名只能在建立函數內部使用。
在對象內定義函數,也是屬於函數表達式。
第四種是:Function構造函數
Function("alert(1)"); ƒ anonymous() { alert(1) } Function("dada"); ƒ anonymous() { dada } new Function("alert(1)"); ƒ anonymous() { alert(1) } new Function("dada"); ƒ anonymous() { dada }
Function構造函數傳一個函數字符串,返回包含這個字符串命令的函數。
第五種是:自執行函數
( function(){ alert(1); })(); undefined ( function(){ alert(1); }) ƒ (){ alert(1); }
(function da1(){ alert(1); })();
自執行函數也是「函數表達式」。
第六種是:其餘
運用eval,setTimeout,setInterval等方法。
3
第一個fun函數是屬於標準的具名函數聲明,是新建立的函數,返回的是一個對象字面量表達式,屬性一個新的Object。
這返回,對象內部包含一個fun的屬性,屬於匿名函數表達式,這個fun屬性存放的是一個新建立匿名函數表達式,全部聲明的匿名函數都是一個新函數。則第一個fun函數和第二個fun函數不一樣,都是新建立的函數。
4
函數做用域鏈的問題
對象內部的函數表達式:
var d = { fn: function(){ console.log(fn); } }; d.fn(); VM1879:3 Uncaught ReferenceError: fn is not defined at Object.fn (<anonymous>:3:21) at <anonymous>:6:3 fn @ VM1879:3 (anonymous) @ VM1879:6 var d1 = { fn: function(){ console.log("dada"); } }; d1.fn(); VM1973:3 dada undefined
非對象內部的函數表達式:
var da = function () { console.log(da); }; da(); VM2270:2 ƒ () { console.log(da); } undefined
使用var能夠訪問到存放當前函數的變量,var da,da()訪問函數的變量,在對象內部不能訪問到。
5
函數做用域鏈:
function fun(a,b) { console.log(b) return { fun: function(c) { return fun(c,a); } }; } var d = fun(0); d.fun(1); d.fun(2); d.fun(3); var d1 = fun(0).fun(1).fun(2).fun(3); var d2 = fun(0).fun(1); d2.fun(2); d2.fun(3);
var d = fun(0); d.fun(1); d.fun(2); d.fun(3);
undefined VM2273:2 0 VM2273:2 0 VM2273:2 0
第一個fun(0)在調用第一層fun函數,第二個fun(1)是在調用前一個fun的返回值的fun函數。即就是fun(1),fun(2),fun(3)函數獨使在調用第二層fun函數,第一次調用fun(0)時,b爲undefined,第二次調用fun(1),c爲1,a爲0。
var d = fun(0);調用的是第一層
而d.fun->fun(0).fun調用第二層
fun:function(1),return fun(1,a),fun(1,0),此時fun閉包了外層函數的a,也就是第一次調用的a=0。這樣子第一層fun函數爲fun(1,0),因此爲0。
第一次:
function fun(0,undefined) { console.log(undefined) return { fun: function(c) { return fun(c,0); } }; }
fun(0),b爲undefined,fun(0).fun(1),c=1,此時fun閉包外層函數的a,也就是第一次調用的a=0,即c=1,a=0,並在內部調用第一層fun函數fun(1,0),因此b=0。
function fun(a,b) { console.log(b) return { fun: function(1) { return fun(1,0); } }; }
第三次調用fun(2)時,c爲2,仍是調用d.fun,仍是閉包了第一次調用時的a,fun(2,0)因此輸出b爲0。
function fun(a,b) { console.log(b) return { fun: function(c) { return fun(c,a); } }; } var d = fun(0); d.fun(1); d.fun(2); d.fun(3);
6
var d1 = fun(0).fun(1).fun(2).fun(3);
從fun(0)調用第一層fun函數,返回值爲一個對象,第二個fun(1)調用的是第二層fun函數,後面的也是第二層fun函數。
第一層fun(0),b爲undefined,第二層.fun(1)時c爲1,c=1,a=0,內部調用第一層fun函數fun(1,0),因此b爲0。
調用你.fun(2)時,c爲2,此時當前的fun函數不是第一次執行的返回對象,而是第二次執行的返回對象,第二次執行第一層fun函數是:
fun(1,0),a=1,b=0。第三次執行fun函數,c=2,a=1
function fun(a,b) { console.log(b) return { fun: function(1) { return fun(1,0); } }; } fun(1,0),a=1,b=0。第三次執行fun函數,c=2,a=1 function fun(a,b) { console.log(b) return { fun: function(2) { return fun(2,1); } }; } // 1 function fun(2,1) a=2, b=1
第四次調用.fun(3)爲c爲3
// a=2 function fun(a,b) { console.log(b) return { fun: function(3) { return fun(3,2); } }; }
7
var d2 = fun(0).fun(1); d2.fun(2); d2.fun(3);
var d2=fun(0).fun(1);
// undefined, 0
此時的return(c=1,a=0),return fun(1,0),因此b爲0
d2.fun(2);
第三次調用.fun(2),c爲2
// c爲2,a=1,b=0 function fun(a,b) { console.log(b) return { fun: function(2) { return fun(2,a); } }; } 因此return fun(2,1) function fun(a=2,b=1),因此爲 1
d2.fun(3),c爲3,仍是調用的第二次的返回值,最終調用第一層的
fun(a,b)
// c爲3,a=1,b=0 function fun(a,b) { console.log(b) return { fun: function(3) { return fun(3,a); } }; } 因此return fun(3,1) function fun(a=3,b=1),因此爲 1
注意這裏的:
// c爲3,a=1,b=0 這是調用這個代碼的結果 a=1,b=0
var d2 = fun(0).fun(1);
好了,這樣就知道大概的答案和講解了:
8
總的來講,你明白了!講的好辛苦,給個贊哦!求獎勵,我來了
function fun(a,b) { console.log(b) return { fun: function(c) { return fun(c,a); } }; } var d = fun(0); d.fun(1); d.fun(2); d.fun(3); var d1 = fun(0).fun(1).fun(2).fun(3); var d2 = fun(0).fun(1); d2.fun(2); d2.fun(3); undefined VM1036:2 0 VM1036:2 0 VM1036:2 0 VM1036:2 undefined VM1036:2 0 VM1036:2 1 VM1036:2 2 VM1036:2 undefined VM1036:2 0 VM1036:2 1 VM1036:2 1 {fun: ƒ}
一名喜好編程技術與專一於前端的程序員,將web前端領域、數據結構與算法、網絡原理等通俗易懂的呈現給小夥伴。分享web前端相關的技術文章、工具資源,精選課程、熱點資訊。
推薦閱讀
一、你知道多少this,new,bind,call,apply?那我告訴你
二、爲何學習JavaScript設計模式,由於它是核心
三、一篇文章把你帶入到JavaScript中的閉包與高級函數
四、大廠HR面試ES6中的深刻淺出面試題知識點
做者Info:
【做者】:Jeskson
【原創公衆號】:達達前端小酒館。
【福利】:公衆號回覆 「資料」 送自學資料大禮包(進羣分享,想要啥就說哈,看我有沒有)!
【轉載說明】:轉載請說明出處,謝謝合做!~
大前端開發,定位前端開發技術棧博客,PHP後臺知識點,web全棧技術領域,數據結構與算法、網絡原理等通俗易懂的呈現給小夥伴。謝謝支持,承蒙厚愛!!!
若本號內容有作得不到位的地方(好比:涉及版權或其餘問題),請及時聯繫咱們進行整改便可,會在第一時間進行處理。
這是一個有質量,有態度的博客