感謝大家批注指正,我不想誤導大家,我只是說我遇到過類似的題目,然后這里分析分析這些題目,並沒有說我們平時要這樣去寫代碼。js這門語言很靈活,有很多奇葩(坑),如果你不知道,哪天你無意間調到坑里去了你還不知道,有經驗的人應該都踩過無數js的坑。
去面試的時候有一些公司會給出面試題叫你做,前端的話一般有輸出結果的題,叫你寫出題目的輸出結果。我遇到過幾次,可是每次都感覺自己迷迷糊糊的,現在看來還是自己基礎不夠好導致了的。閑來沒事,總結總結,大神們,莫拍磚,我只是個前端小菜鳥。
先來看一組題目,如果你們全部答對,那直接看最底部的一題。
//1、------------------------------------------------------------------------------------- a() var a = c = function() { console.log(2) } a() function a() { console.log(1) } a(); (function(b) { b(), c() var b = c = function a() { console.log(3) } b() })(a) c() //2、------------------------------------------------------------------------------------- var A = function() {} A.prototype.n = 1; var b = new A() A.prototype = { n: 2, m: 3 } var c = new A() console.log(b.n, b.m, c.n, c.m) //3、------------------------------------------------------------------------------------- (function f() { function f() { return 1; } return f(); function f() { return 2; } })(); //4、------------------------------------------------------------------------------------- if (!(a in window)) { var a = 1; } console.log(a) //5、------------------------------------------------------------------------------------- function a() {} var a; console.log(typeof a) //6、------------------------------------------------------------------------------------- (function(b) { console.log(b) var b = c = 2 console.log(b) })(1) //7、------------------------------------------------------------------------------------- (function(b) { console.log(b) var b = c = 2 console.log(b) function b() {} console.log(b) })(1) //8、------------------------------------------------------------------------------------- var a = 10; function fn() { console.log(1) var a = 100; console.log(a) } fn(); //9、------------------------------------------------------------------------------------- var a = 1 function c(a, b) { console.log(a) a = 2 console.log(a) } c()
以上題目考的知識點相對單一,無非就是預編譯和作用域,還沒牽扯太多東西。
研究以上題目我總結幾點
js對:var 后面的變量、函數參數、函數進行預編譯
來看看一些測試例子
只是var聲明的變量相關
var 聲明的變量a,就是顯示聲明的, 被預解析,賦值為undefined,既是常說變量提升。你在一個作用域任何一個地方聲明變量,都會被提升到作用域開始(接受意見意見加上)。
console.log(a)//undefined var a=1; console.log(a)//1
以上代碼約等於
var a = undefined console.log(a) //undefined a = 1; console.log(a) //1
再看以下代碼,script標簽
<script> console.log(typeof a)//undefined var a='littlebear'; console.log(a)//littlebear </script> <script> console.log(typeof a)//string var a=1; console.log(a)//1 </script>
感覺以上代碼輸出正在,第二個script標簽里面的a 是string類型,是第一個script標簽先預編譯。第二個<script>標簽里的a但會往上查找。
在看下面這個
<script> console.log(typeof a)//undefined console.log(a)//報錯,遇到<script>標簽對時,會先對這一塊進行預解析,下面沒預解析,所以找不到聲明過的a,於是報錯了 </script> <script> console.log(typeof a)//undefined var a=1; console.log(a)//1 </script>
第一個script標簽里的a,undefined,直接輸出啊,立馬報錯。因為在第一個script標簽的時候沒有發現a聲明過,沒聲明直接使用就會報錯。 第二個script標簽里面的輸出是我們預料之中的
得出一個結論:js的預編譯,是從有順序的,根據script標簽的順序從上到下執行的
var聲明的變量和函數在一起的時候
<script> console.log(a)//function a(){} var a=1; console.log(a)//1 function a(){} console.log(a)//1 </script>
第一個輸出的是function a(){},為什么會是這樣!! 我感覺是先預編譯變量,先給a賦值undefined,后來預編譯函數,函數a覆蓋了之前的變量a的默認值; 然后代碼順序執行到a=1;,這個時候賦值表達式給a賦值為1,所以后面輸出都是1。
var 聲明的變量和函數參數在一起的時候
<script> var a=10; function fn(a,b){ console.log(a)//undefined a會預編譯,此時輸出a,當前作用域能夠找到a,值是undefined。 所以不會向外搜索,也就不會是外部的10 var a=10; console.log(a)//10 } fn(); </script>
上面一種情況比較好理解,下面這種情況比較容易讓人混淆。
<script> function fn(a,b){ console.log(a)//容易上當 var a=10; console.log(a)//10 } fn('容易上當'); </script>
函數有兩個參數a和b,內部有聲明了啊。調用fn的時候傳入了一個字符串。 看看內部的輸出,顯示輸出:容易上當,再是輸出10。這是為啥呢
預編譯階段會預編譯,var后面的變量,函數參數、函數。調用fn的時候傳入的參數a,b。 a和b會預編譯賦值undefined,調用發你,傳入'容易上當',a就被賦值為容易上當了。函數里面變量a的默認值就是參數a的值了,開始輸出a,也就是參數a值。后來遇到賦值表達式a=10,此時a就變成10了。
看下面這段代碼就想對簡單了
<script> var a=10; function fn(a,b){ console.log(a)//undefined 這里不是10喲。 當前作用域能夠找到a的 a=10; console.log(a)//10 } fn(); </script>
函數,函數參數,var聲明的變量在一起的時候
<script> var a=10; function fn(a,b){ console.log(a)//function a(){} 這里輸出function a(){}, 不是參數a的值,哈哈 var a=10; console.log(a)//10 function a(){} console.log(a)//10 } fn(15); </script>
fn里面有參數a,也有變量a,也有函數a,這種迷惑性更高。其實當你知道三者先后順序之后就不迷惑了
經過測試,我發現:參數a會覆蓋預編譯變量a的默認值,如果有函數,函數會覆蓋參數a的值,這個就是先后順序而已。
函數形參聲明--->函數聲明---->變量聲明
來看一個綜合一點的,找找感覺
<script> a();//1 var a = c = function() { console.log(2) }; a();//2 function a() { console.log(1) } a();//2 (function(b) { b();//2 其實就是外部的a c();//2 c為什么是2? 也許有人以為這里會報錯,其實不會。 原因就在於var b=c=xxx。 c相當於沒有加var 不會預編譯,這里c直接查找到外部作用域的c var b = c = function a() { console.log(3) } b()//3 })(a);//走到這里 a已經被賦值表達式重新賦值 c();//3 由於沒加var 的原因 c已經被立即執行函數內部的賦值表達式改變了值 這里是3 </script>
return 和函數、var聲明的變量在一起的時候
由於預編譯,后面的a覆蓋了前面的a,所以return a的值 其實就是預編譯之后a的值,預編譯之后a的值就是第二個a。
<script> function fn(){ function a(){console.log(1)} return a; function a(){console.log(2)} } fn()();//2 </script>
在看這個,也是一個容易坑人的題目
<script> var a=10; function fn(){ //預編譯a 賦值undefined,內部作用域存在a這個變量,所以這里 !a 就是 !undefined,就是true,進入函數a=20; //但是后面的a怎么就是20呢,js沒有塊級作用域!! 不要小看js各種細節,夠折騰的 if (!a) { var a=20 } console.log(a)// 這里是20 , } fn() </script>
看起勁了,收不到了,再來一個
<script> //a in window ==>true 、 !true ===》false if (!(a in window)) { var a = 1; } console.log(a)// undefined </script>
說了這么多,其實就是圍繞着js預編譯函數,var 聲明的變量就是顯示聲明的,會有變量提示、函數參數,搞懂先后順序,以后遇到類似的題目基本都是沒問題。
還有很多很多關於輸出結果的,基本圍繞着js的預編譯,作用域,函數上下文對象,以及這門語言的一些特性來展開的。多看看《Javascript語言精髓與編程實踐》 《Javascript高級程序設計第三版》加強基礎,題目萬變不離其中,都是考那些知識點。
接受大家的批評,我是菜鳥,不想誤人子弟。在一個作用域內變量的聲明順序,函數形參聲明--->函數聲明---->變量聲明 聲明是有先后順序的。任何一種聲明,如果在前面出現,都不會再次聲明。
再來一個 高級的,很多人都見過,但是我估計第一次能夠全部答對的人應該很少,即使答對,但是全部知道為什么的更少!
<script> var a = 10; var foo = { a: 20, bar: function () { var a = 30; return this.a; } }; console.log( foo.bar(), // 1. (foo.bar)(), // 2. (foo.bar = foo.bar)(), // 3. (foo.bar, foo.bar)() // 4. ); </script>