持續更新一些有趣的js題
1.var a=function a(){
a=1;
return a;
}
console.log(a())//返回function a(){a=1;return a;}
稍微看了一下資料,這種函式表示式和函式宣告同時存在的時候,這個函式a是immutable型的,即不可改變的。故設定a=1無效;有興趣可以試試只用一種方式定義函式,我試了,結果都是返回1
2.function a(){}()報錯,unexpected token'(' 大概是這種錯誤
這裡會把function a(){}當作函式宣告來對待,轉化就成了
function a(){};
();
這種語法是不合js規範的,空括號就報錯了
3.function a(){}(1,2)
同上面一樣,這時候就會解析成
function a(){};
(1,2);其中(1,2)是函式表示式,又是運用了“,”運算子,返回最後一個逗號後面的,即返回2了
4.var num=1;
if(function f(){}){
num+=typeof f;
}
console.log(num)//1undefined
其中function f(){}是函式宣告,但是(function f(){})是表示式了,function f(){}是個物件,物件轉化就為true,
所以會走到num+=typeof f;其中因為函式f是以表示式的形式,故下面f訪問不到,typeof f==='undefined';
最後就是1+‘undefined’=1undefined;
5.
'use strict';
var a = 20;
function foo () {
var a = 1;
var obj = {
a: 10,
c: this.a + 20,
fn: function () {
return this.a;
}
}
return obj.c;
}
console.log(foo()); // ?
console.log(window.foo()); // ?
猜猜這段程式碼輸出什麼?
解析:這個題目很容易犯錯,知識點:{}不產生新的作用域,它裡面的this是指向全域性的,嚴格模式下為undefined,非嚴格模式為window。再來看這個題目,就會發現,foo()執行完返回this.a+20,其中obj.c不產生新的作用域,這裡的this還是全域性的,又是use strict模式下,故就是undefined.a+20,所以程式報錯退出。如果註釋第一個輸出,第二個輸出40,這個很好理解就不解釋了。
此文章純屬原創,後續會繼續更新。有問題歡迎指出,謝謝大家!!!
6.一道比較經典的連等賦值題
var a={c:1};
var b=a;
a.x=a={c:2};
console.log(a);
console.log(b);
注:連等賦值從右往左計算;
解析:js的基礎型別變數和值會儲存到變數物件中,引用型別的值會放入堆中,在變數物件中儲存著對堆中值的記憶體地址。
第一行和第二行為指的是a,b都指向堆中中的{c:1}物件,第三行a.x是為a新增屬性,這時候的a還是{c:1};根據從右往左計算,
首先知道堆中建立了一個新的物件為{c:2};並且將a的引用指向了它,故此時a就為{c:2},得到第一個輸出;而a.x的a還是保持著{c:1}的引用(為什麼呢?因為點運算子運算等級比=高,所以先給a.x賦值為undefined,這時的a還是{c:1}),a.x=a就將原來a的引用變成了{c:1,x:a},其中現在的a指向了{c:2},故原來a的引用(即b的指向)就變成了{c:1,x:{x:2}},這樣就得到了第二個輸出。
7.
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);
這段程式碼將輸出 456
(而不是 123
)。
原因為:當設定物件屬性時,JavaScript會暗中字串化引數值。在這種情況下,由於 b
和 c
都是物件,因此它們都將被轉換為"[object Object]"
。結果就是, a[b]
和a[c]
均相當於a["[object Object]"]
,並可以互換使用。因此,設定或引用 a[c]
和設定或引用 a[b]
完全相同。
8.
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //請寫出以下輸出結果: Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName();
new new Foo().getName();
//答案: Foo.getName();//2 getName();//4 Foo().getName();//1 getName();//1 new Foo.getName();//2 new Foo().getName();//3 new new Foo().getName();//3分析:1.Foo.getName()這個不用說,直接就是第五行,返回2
2.getName()這個就看最後兩行,一個函式宣告,一個函式表示式,函式宣告提前,表示式覆蓋宣告,故輸出4,也沒問題
3.Foo().getName()這裡Foo()返回this,沒有例項化,這個this指window,然後再呼叫getName();就是前面剛被賦值的getName(),故返回1
4.getName()在呼叫Foo().getName()時做了覆蓋,畢竟Foo函式內定義的全域性getName,故還是輸出1
5.new Foo.getName();首先提一下:點操作符優先於new操作符;故Foo.getName()跟第一個一樣,再經過new例項化,返回2
6.new Foo().getName() 這裡注意括號優先順序比new和點都高,故實際執行為(new Foo()).getName(),因為建構函式上沒有getName方法,就去原型上找,所有輸出3
7.new new Foo().getName()同上,實際執行為new (new Foo()).getName(),因為new Foo()返回this,故new Foo()還是指向Foo,這時候返回的物件接getName()還是去Foo的原型物件上去查詢,還是3