1. 程式人生 > >持續更新一些有趣的js題

持續更新一些有趣的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