今天想着鄰近畢業季,估計又會有很多稀奇古怪的面試題,看掘金社區的時候看到一道賊有意思的題,一開始看的十分懵逼,后來捋了捋,將心得總結一下。
function Foo() {
getName = function () {
console.log(1);
}
return this;
}
Foo.getName = function () {
console.log(2);
}
Foo.prototype.getName = function () {
console.log(3);
}
var getName = function () {
console.log(4);
}
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
ok,下面我來一點一點分析一下這段代碼到底要做個啥。首先第一個
Foo.getName(); //2
第一個是2這肯定是沒問題的。
我們繼續往下
getName(); //4
第二個為4的原因為啥不是5呢,這就涉及到變量提升的問題了,因為函數聲明會提升到執行環境的頂部,而函數表達式只有變量var getName會提升看起來就像是這樣
function getName(){
console.log(5);
}
var getName;
getName = function(){
console.log(4);
}
所以函數聲明getName會被蓋掉,所以這里是4;
ok,繼續往下看
Foo().getName(); //1
這。。。。其實仔細看的話也是可以看出來的,我們在執行Foo()函數的時候getName這個變量提升到外部的全局作用域中了,因為在js中,如果對於一個變量沒用用var 或者 let等聲明的話,他就默認是全局屬性,就是window對象的一個屬性。所以在這里我們的全局的getName又被改了
因為我們Foo()執行的時候返回了this而這里的this就是window對象,關於為什么是window這里不再展開講,不然就寫不完了。。。。
我們需要知道的是在瀏覽器中所有全局的聲明都是window對象的屬性和方法,所以這里我們調用this.getName()就會返回1了。
這里需要注意的是如果你是在node環境下打印這些值的話,這里是會報錯的,因為在node環境下全局對象為global對象,而它區別window對象一個重要區別是,聲明的函數,變量不是global的屬性,所以你就找不到getName這個方法;而且會影響下一個的取值,下一個就會是4了。
接下來我們再一次執行
getName(); // 1
因為已經被修改了,所以不難看出來。接下來
new Foo.getName(); //2
在這里我們通過new 來返回一個 Foo.getName 的實例。因為new的過程中一步很重要的操作是通過call將this綁定到實例對象上,並將實例對象返回,ok,既然有call的過程那么就很容易明白他這里輸出了2;
我們可以驗證一下結果
var bar = new Foo.getName(); bar.__proto__.constructor === Foo.getName //true
這樣就能更直觀的看出,我們這樣通過原型鏈看出他是Foo.getName的實例;
接下來
new Foo().getName(); //3
在這里我們通過new Foo()操作返回一個實例對象,這樣我們就可以通過原型鏈__proto__找到getName這個方法並執行他。
最后
new new Foo().getName(); //3
這就是最讓人懵逼的地方,他到底在干啥呢,其實他和倒數第三個很像,他其實是對Foo().getName這個函數進行了實例化,具體可以看倒數第三個,所以這里會輸出3
而且我們可以在控制台上打印一下
var bar = new new Foo().getName();
打印這個bar看下結果吧。
這道題大致就是這樣,如果本人有寫錯的地方,還望指出,畢竟功力尚淺,歡迎留言進行交流。
