前端面試中讓你困惑的閉包、原型、原型鏈究竟是什麼?

前段時間我朋友從上家公司離職,上週開始了前端面試(現在已經上班了),一天我下班回到出租房時,他問我原型鏈是什麼?一時半會我竟然也不知道從何說起能夠讓他很清楚的明白,又忽然想起之前我一個朋友也問過我閉包的問題,因此在這裡記錄解惑一下,下面我會以面試官和應聘者的口吻進行介紹理解......

前端面試中讓你困惑的閉包、原型、原型鏈究竟是什麼?

一.閉包

面試官:什麼是閉包?閉包你瞭解嗎?

應聘者:閉包就是能夠讀取其他函數內部變量的函數。

面試官:通俗一點呢?

應聘者:通俗的講就是函數a的內部函數b,被函數a外部的一個變量引用的時候,就創建了一個閉包。

面試官:是這樣,沒錯,那你知道什麼情況下會用到閉包嗎?

應聘者:最常見的是函數封裝的時候,再就是在使用定時器的時候,會經常用到...

面試官:那你簡單寫一個閉包吧

應聘者:

function a(){
 var i=0;
 function b(){
 alert(++i);
 }
 return b;
}
var c = a();
c();//外部的變量

面試官:你這個寫法是正確的,那我衍生一下,你回答一下依次會彈出什麼:

function a(){
 var i=0;
 function b(){
 i++;
 alert(i);
 }
 return b;
}
var c = a();
c();//?
c();//?
c();//?

應聘者:應該是會依次彈出1,2,3。

面試官:沒錯,你回答的很對,你知道其中的原理嗎?能否解釋一下

應聘者:好的,i是函數a中的一個變量,它的值在函數b中被改變,函數b每執行一次,i的值就在原來的基礎上累加 1 。因此,函數a中的i變量會一直保存在內存中。

當我們需要在模塊中定義一些變量,並希望這些變量一直保存在內存中但又不會 “汙染” 全局的變量時,就可以用閉包來定義這個模塊。

面試官:非常棒,你說的這是它的一個用處,你能說一下閉包的用處有哪些嗎?

應聘者:它的最大用處有兩個,一個是它可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。

面試官:那我順便再出個問題考考你吧,請看題:

前端面試中讓你困惑的閉包、原型、原型鏈究竟是什麼?

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]();

應聘者:答案為2,1,0,3(這個時候你瞭解清楚閉包之後,這個答案應該就是隨口而出),解釋如下:

//創建數組元素
var num = new Array();
for(var i=0; i<4; i++){
 //num[i] = 閉包;//閉包被調用了4次,就會生成4個獨立的函數
 //每個函數內部有自己可以訪問的個性化(差異)的信息
 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

面試官:那你知道閉包的優缺點嗎?

應聘者:

優點:

① 減少全局變量;

② 減少傳遞函數的參數量;

③ 封裝;

缺點:

① 使用閉包會佔有內存資源,過多的使用閉包會導致內存溢出等

面試官:正好你提到了內存洩漏,說說你的解決方法

應聘者:簡單的說就是把那些不需要的變量,但是垃圾回收又收不走的的那些賦值為null,然後讓垃圾回收走;

面試官:看來閉包已經難不倒你了,閉包就到這裡告一段落了,接下來考考你原型和原型鏈。

二.原型和原型鏈

面試官:你好,接下來考考你原型和原型鏈

應聘者:好的,請問

面試官:你解釋一下原型的概念吧!

應聘者: 原型(prototype)是function對象的一個屬性,它定義了構造函數製造出的對象的公共祖先(公共的屬性和方法)通過該構造函數產生的對象,可以繼承改原型的屬性和方法。 原型也是對象。

面試官:通俗一點呢?

應聘者:通俗的說,原型就是一個模板,更準確的說是一個對象模板

面試官:那你接著說一下原型鏈是什麼?

應聘者:每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數想指針(constructor),而實例對象都包含一個指向原型對象的內部指針(__proto__)。如果讓原型對象等於另一個類型的實例,此時的原型對象將包含一個指向另一個原型的指針(__proto__),另一個原型也包含著一個指向另一個構造函數的指針(constructor)。假如另一個原型又是另一個類型的實例,如此層層遞進,這就構成了實例與原型的鏈條。這就是原型鏈的基本概念;

面試官:你這個說太多了,簡單一點

應聘者:就是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法;

舉例說明: Student → Person → Object ,學生繼承人類,人類繼承對象類

面試官:我看你說到了繼承,那你簡單寫一個原型鏈繼承的例子

應聘者:好,如下

function Animal(name) {
 this.name = name
}
Animal.prototype.getName = function() {
 console.log(this.name)
}
var animal1 = new Animal('Kate')
var animal2 = new Animal('Lucy')
//對象animal1 和 animal2共享方法getName
animal1.getName()
animal2.getName()

面試官:ok,那我出個問題考考你,你告訴我結果,題目如下:

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)//多少

應聘者:答案為1,undefined,2,3。原因是b繼承A,所以b.n就為1,而m在A中找不到,所以為undefined,以此類推,c繼承的時候A添加了n和m,所以c.n和c.m分別是2和3;

面試官:你回答的很棒,你知道null和undefined的區別嗎?

應聘者:undefined是一個表示"無"的原始值,

null用來表示尚未存在的對象,

面試官:很正確,再出個問題給你,問題如下:

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()//?

應聘者:答案應該為a()、報錯找不到b這個函數、a()、b()。(如果你能瞬間回答出這個答案就代表你已經瞭解原型和原型鏈了)

面試官:看來原型原型鏈也難不倒你了,那面試就到這裡告一段落了。

前端面試中讓你困惑的閉包、原型、原型鏈究竟是什麼?

三.總結

閉包和原型原型鏈的介紹就到這裡介紹了,如果上面有哪些解釋有錯誤麻煩大家指正一下,謝謝了!

在這裡我相信有很多想要學習前端的小夥伴,我自己是一名從事了多年開發的前端老架構師,辭職目前在做自己的前端私人定製課程,今年年初我花了一個月整理了一份最適合2019年學習的前端學習乾貨,從最基礎的html到js各種框架都有整理,送給每一位前端小夥伴

想要獲取的可以關注我的頭條號並在後臺私信我:【 前端 】,即可免費獲取。


分享到:


相關文章: