前端面試中讓你困惑的閉包、原型匀奏、原型鏈究竟是什么鞭衩?
前段時(shí)間我朋友從上家公司離職,上周開始了前端面試(現(xiàn)在已經(jīng)上班了)娃善,一天我下班回到出租房時(shí)论衍,他問我原型鏈?zhǔn)鞘裁矗恳粫r(shí)半會(huì)我竟然也不知道從何說起能夠讓他很清楚的明白聚磺,又忽然想起之前我一個(gè)朋友也問過我閉包的問題坯台,因此在這里記錄解惑一下,下面我會(huì)以面試官和應(yīng)聘者的口吻進(jìn)行介紹理解......
一.閉包
面試官:什么是閉包瘫寝?閉包你了解嗎蜒蕾?
應(yīng)聘者:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
面試官:通俗一點(diǎn)呢焕阿?
應(yīng)聘者:通俗的講就是函數(shù)a的內(nèi)部函數(shù)b咪啡,被函數(shù)a外部的一個(gè)變量引用的時(shí)候,就創(chuàng)建了一個(gè)閉包捣鲸。
面試官:是這樣瑟匆,沒錯(cuò),那你知道什么情況下會(huì)用到閉包嗎栽惶?
應(yīng)聘者:最常見的是函數(shù)封裝的時(shí)候愁溜,再就是在使用定時(shí)器的時(shí)候,會(huì)經(jīng)常用到...
面試官:那你簡(jiǎn)單寫一個(gè)閉包吧
應(yīng)聘者:
<pre style="margin: 0px; padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "Courier New"
!important; font-size: 12px !important;">
function a(){ var i=0; function b(){
alert(++i);
} return b;
} var c = a();
c();//外部的變量
</pre>
面試官:你這個(gè)寫法是正確的外厂,那我衍生一下冕象,你回答一下依次會(huì)彈出什么:
<pre style="margin: 0px; padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "Courier New"
!important; font-size: 12px !important;">
function a(){ var i=0; function b(){
i++;
alert(i);
} return b;
} var c = a();
c();//?
c();//汁蝶?
c();//渐扮?
</pre>
應(yīng)聘者:應(yīng)該是會(huì)依次彈出1,2,3。
面試官:沒錯(cuò)掖棉,你回答的很對(duì)墓律,你知道其中的原理嗎?能否解釋一下
應(yīng)聘者:好的幔亥,i是函數(shù)a中的一個(gè)變量耻讽,它的值在函數(shù)b中被改變,函數(shù)b每執(zhí)行一次帕棉,i的值就在原來的基礎(chǔ)上累加 1 针肥。因此饼记,函數(shù)a中的i變量會(huì)一直保存在內(nèi)存中。
當(dāng)我們需要在模塊中定義一些變量慰枕,并希望這些變量一直保存在內(nèi)存中但又不會(huì) “污染” 全局的變量時(shí)具则,就可以用閉包來定義這個(gè)模塊。
面試官:非常棒具帮,你說的這是它的一個(gè)用處博肋,你能說一下閉包的用處有哪些嗎?
應(yīng)聘者:它的最大用處有兩個(gè)匕坯,一個(gè)是它可以讀取函數(shù)內(nèi)部的變量束昵,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。
面試官:那我順便再出個(gè)問題考考你吧葛峻,請(qǐng)看題:
<pre style="margin: 0px;
padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "Courier New"
!important; font-size: 12px !important;">
var num = new Array(); for(var i=0; i<4; i++){
num[i] = f1(i);
} function f1(n){ function f2(){
alert(n);
} return f2;
}
num[2]();
num[1]();
num[0]();
num[3]();</pre>
應(yīng)聘者:答案為2,1,0,3(這個(gè)時(shí)候你了解清楚閉包之后锹雏,這個(gè)答案應(yīng)該就是隨口而出),解釋如下:
<pre style="margin: 0px; padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "Courier New"
!important; font-size: 12px !important;">//創(chuàng)建數(shù)組元素
var num = new Array();
for(var i=0; i<4; i++){ //num[i] = 閉包;//閉包被調(diào)用了4次术奖,就會(huì)生成4個(gè)獨(dú)立的函數(shù)
//每個(gè)函數(shù)內(nèi)部有自己可以訪問的個(gè)性化(差異)的信息
num[i] = f1(i);
} function f1(n){ function f2(){
alert(n);
} return f2;
}
num[2](); //2
num[1](); //1
num[0](); //0
num[3](); //3</pre>
面試官:那你知道閉包的優(yōu)缺點(diǎn)嗎礁遵?
應(yīng)聘者:
優(yōu)點(diǎn):
① 減少全局變量;
② 減少傳遞函數(shù)的參數(shù)量采记;
③ 封裝佣耐;
缺點(diǎn):
① 使用閉包會(huì)占有內(nèi)存資源,過多的使用閉包會(huì)導(dǎo)致內(nèi)存溢出等
面試官:正好你提到了內(nèi)存泄漏唧龄,說說你的解決方法
應(yīng)聘者:簡(jiǎn)單的說就是把那些不需要的變量兼砖,但是垃圾回收又收不走的的那些賦值為null,然后讓垃圾回收走既棺;
面試官:看來閉包已經(jīng)難不倒你了讽挟,閉包就到這里告一段落了,接下來考考你原型和原型鏈丸冕。
二.原型和原型鏈
面試官:你好耽梅,接下來考考你原型和原型鏈
應(yīng)聘者:好的,請(qǐng)問
面試官:你解釋一下原型的概念吧胖烛!
應(yīng)聘者: 原型(prototype)是function對(duì)象的一個(gè)屬性眼姐,它定義了構(gòu)造函數(shù)制造出的對(duì)象的公共祖先(公共的屬性和方法)通過該構(gòu)造函數(shù)產(chǎn)生的對(duì)象,可以繼承改原型的屬性和方法佩番。 原型也是對(duì)象众旗。
面試官:通俗一點(diǎn)呢?
應(yīng)聘者:通俗的說趟畏,原型就是一個(gè)模板逝钥,更準(zhǔn)確的說是一個(gè)對(duì)象模板
面試官:那你接著說一下原型鏈?zhǔn)鞘裁矗?/p>
應(yīng)聘者:每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)想指針(constructor),而實(shí)例對(duì)象都包含一個(gè)指向原型對(duì)象的內(nèi)部指針(proto)艘款。如果讓原型對(duì)象等于另一個(gè)類型的實(shí)例,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型的指針(proto)沃琅,另一個(gè)原型也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針(constructor)哗咆。假如另一個(gè)原型又是另一個(gè)類型的實(shí)例,如此層層遞進(jìn)益眉,這就構(gòu)成了實(shí)例與原型的鏈條晌柬。這就是原型鏈的基本概念;
面試官:你這個(gè)說太多了郭脂,簡(jiǎn)單一點(diǎn)
應(yīng)聘者:就是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法年碘;
舉例說明: Student → Person → Object ,學(xué)生繼承人類展鸡,人類繼承對(duì)象類
面試官:我看你說到了繼承屿衅,那你簡(jiǎn)單寫一個(gè)原型鏈繼承的例子
應(yīng)聘者:好,如下
<pre style="margin: 0px; padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "
Courier New"
!important; font-size: 12px !important;">
function Animal(name) { this.name = name
}
Animal.prototype.getName = function() {
console.log(this.name)
} var animal1 = new Animal('Kate') var animal2 = new Animal('Lucy')
//對(duì)象animal1 和 animal2共享方法getName
animal1.getName()
animal2.getName()</pre>
面試官:ok莹弊,那我出個(gè)問題考考你涤久,你告訴我結(jié)果,題目如下:
<pre style="margin: 0px;
padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "Courier New"
!important; font-size: 12px !important;">
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)//多少</pre>
應(yīng)聘者:答案為1,undefined,2,3忍弛。原因是b繼承A,所以b.n就為1响迂,而m在A中找不到,所以為undefined细疚,以此類推蔗彤,c繼承的時(shí)候A添加了n和m,所以c.n和c.m分別是2和3;
面試官:你回答的很棒疯兼,你知道null和undefined的區(qū)別嗎然遏?
應(yīng)聘者:undefined
是一個(gè)表示"無"的原始值,
`` null用來表示尚未存在的對(duì)象,
面試官:很正確镇防,再出個(gè)問題給你啦鸣,問題如下:
<pre style="margin: 0px; padding: 0px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: "Courier New"
!important; font-size: 12px !important;">
var F=function(){};
Object.prototype.a=function(){
console.log('a()')
};
Function.prototype.b=function(){
console.log('b()')
} var f=new F();
f.a()//?
f.b()//来氧?
F.a()//诫给?
F.b()//?</pre>
應(yīng)聘者:答案應(yīng)該為a()啦扬、報(bào)錯(cuò)找不到b這個(gè)函數(shù)中狂、a()、b()扑毡。(如果你能瞬間回答出這個(gè)答案就代表你已經(jīng)了解原型和原型鏈了)
面試官:看來原型原型鏈也難不倒你了胃榕,那面試就到這里告一段落了。
三.總結(jié)
閉包和原型原型鏈的介紹就到這里介紹了瞄摊,如果上面有哪些解釋有錯(cuò)誤麻煩大家指正一下勋又,謝謝了苦掘!