基礎(chǔ)面試題js

1、js的this是怎么工作的?(這個(gè)大概就是問this是什么)
答:1、this總是指向函數(shù)的直接調(diào)用者。2、如果有new關(guān)鍵字,this指向new出來的那個(gè)對(duì)象。3、在MOD實(shí)踐中,this指向目標(biāo)元素。4、箭頭函數(shù)的this指向它所在的函數(shù)作用域,并且不能改變,始終指向的是window。
2、原型繼承的原理?
答:在new一個(gè)函數(shù)的時(shí)候,會(huì)返回一個(gè)實(shí)例,這個(gè)實(shí)例里有一個(gè)porto指向它的原型,同時(shí)這個(gè)function會(huì)有一個(gè)prototype屬性指向這個(gè)原型,當(dāng)訪問這個(gè)實(shí)例上的屬性的時(shí)候如果沒有的話,會(huì)順著proto指向的原型上找,如果原型上沒有的話,會(huì)順著proto找原型的原型,一直指到object的實(shí)例,如果找到了,會(huì)把這個(gè)值賦值給你所需要的這個(gè)變量。優(yōu)點(diǎn):所有的對(duì)象都可以共享原型鏈上的方法,它的優(yōu)勢(shì)就是能夠節(jié)省內(nèi)存,非常方便。應(yīng)用場(chǎng)景:jquery的符號(hào),在源碼上就能看到是放在jquery的原型上,當(dāng)我們使用的時(shí)候就很方便的拿到一些屬性,vue開發(fā)的時(shí)候axios就是放在vue的實(shí)例上,我們通過$axios就可以在任何文件上使用這個(gè)方法。
3、什么是閉包,怎么用?
答:在js中函數(shù)可以訪問函數(shù)外部的變量的,但是在外部拿不到內(nèi)部的變量,A一個(gè)函數(shù),B是A里面的子函數(shù),在B里面把A的變量return出去,再把B函數(shù)給return出去,如果我要用A里面的變量就可以直接調(diào)用B函數(shù)了,這個(gè)就是閉包。
優(yōu)點(diǎn):可訪問函數(shù)內(nèi)部的變量,防止變量污染作用域(隱藏變量),從而實(shí)現(xiàn)封裝。
缺點(diǎn):本來局部變量會(huì)被垃圾回收機(jī)制給回收的,但是我們給返回到外面了,并且在引用的情況下,是不回被回收的,會(huì)造成內(nèi)存的一些浪費(fèi)
4、call、apply、bind的區(qū)別是什么?
答:call、apply、bind三個(gè)為改變this指向的方法。
共同點(diǎn):第一個(gè)參數(shù)都為改變this的指針。若第一參數(shù)為null/undefined,this默認(rèn)指向window
區(qū)別:call(無數(shù)個(gè)參數(shù))參數(shù)是一個(gè)一個(gè)傳遞。第一個(gè)參數(shù):改變this指向;第二個(gè)參數(shù):實(shí)參;使用之后會(huì)自動(dòng)執(zhí)行該函數(shù)

function fn(a,b,c){
        console.log(this,a+b+c); // this指向window
    }
    fn();
    fn.call(document,1,2,3);//call改變之后this指向document  
    //輸出 #document 6   1,2,3是實(shí)參 結(jié)果相加為6

apply(兩個(gè)參數(shù))是把所有參數(shù)組成一個(gè)數(shù)組傳遞。第一個(gè)參數(shù):改變this指向;第二個(gè)參數(shù):數(shù)組(里面為實(shí)參);使用時(shí)候會(huì)自動(dòng)執(zhí)行函數(shù)

function fn(a,b,c){
        console.log(this,a+b+c); 
    }
    fn();
    fn.apply(document,[1,2,3]); 

bind(無數(shù)個(gè)參數(shù)),第一個(gè)參數(shù):改變this指向;第二個(gè)參數(shù)之后:實(shí)參;返回值為一個(gè)新的函數(shù)

function fn(a,b,c){
    console.log(this,a+b+c); //window
}
let ff = fn.bind('小明',1,2,3); //手動(dòng)調(diào)用一下

使用的時(shí)候需要手動(dòng)調(diào)用下返回 的新函數(shù)(不會(huì)自動(dòng)執(zhí)行)
5、請(qǐng)指出js宿主對(duì)象(host object)和原生對(duì)象(native object)的區(qū)別?
答:宿主對(duì)象是指DOM和BOM;宿主對(duì)象不是引擎的原生對(duì)象,是宿主框架通過某種機(jī)制注冊(cè)到j(luò)s引擎里的對(duì)象。
原生對(duì)象是指Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、Math等對(duì)象。除了內(nèi)置對(duì)象,還有一些在運(yùn)行過程中創(chuàng)建出的對(duì)象。
6、請(qǐng)指出下面代碼的區(qū)別:function person()、var person = Parson()、var person = new Person()。
答:function person():聲明一個(gè)person函數(shù),this指向window;var person = Parson():將Parson方法返回的結(jié)果給person變量,如果沒有返回值則是undefined,this指向person;var person = new Person():創(chuàng)建一個(gè)構(gòu)造函數(shù),this指向Person。
7、請(qǐng)解釋變量聲明提升
答:舉一個(gè)例子:

foo(2);

function foo(a){
  console.log(a);
}

編譯階段:1、先遇見了foo(2),一個(gè)函數(shù)執(zhí)行,不是我編譯器的活,略過。2、發(fā)現(xiàn)了function,告訴JS引擎編譯器有函數(shù)要聲明,需要在當(dāng)前作用域內(nèi)的內(nèi)存中開辟一塊空間給foo,繼續(xù)編譯函數(shù)內(nèi)部(從上到下一行一行編譯),遇到形參a后在函數(shù)內(nèi)存里開辟一塊空間給a,只不過現(xiàn)在a的值為undefined,繼續(xù)走,結(jié)束。
執(zhí)行階段:1、遇到了foo(2),引擎:作用域,你見過foo沒?作用域:見過,剛才編譯器那小子剛聲明了他,我給你。引擎:好的。那我來執(zhí)行以下foo這個(gè)函數(shù)。作用域兄弟,你在foo中見過a嗎?作用域:有,編譯器也聲明他了,給你。引擎:謝了哥們,我把2復(fù)制給他。
來一個(gè)測(cè)試題

var a = new Object();
a.param = 123;

function foo(){
  get = function(){
      console.log(1);
  };
  return this;
}

foo.get = function(){
  console.log(2);
};

foo.prototype.get = function(){
  console.log(3);
};

var get = function(){
  console.log(4);
};

function get(){
  console.log(5);
}

foo.get();
get();
foo().get();
get();
new foo.get();
new foo().get();
new new foo().get();
// 2,4,1,1,2,3,3

8、什么是use strict?使用它的好處和壞處分別是什么?
答:只要在js文件的第一行加上use steict就是開啟嚴(yán)格模式,嚴(yán)格模式下的this不是指向window,指向的是undefined,幫助我們養(yǎng)成良好的代碼習(xí)慣,有一些不嚴(yán)謹(jǐn)?shù)拇a會(huì)報(bào)錯(cuò)。
9、什么是事件循環(huán)(event loop)?
js是單線程的,也就是說一個(gè)時(shí)間只能做一件事,做什么事都要排隊(duì),想要同一時(shí)間做多件事情,這就衍生出了異步,將事件放在消息隊(duì)列里,先執(zhí)行可以直接執(zhí)行的操作,之后不停的去詢問消息隊(duì)列:有沒有要執(zhí)行的?如果有就取出來放到主線程里執(zhí)行,如果沒有就繼續(xù)之前的操作。舉個(gè)例子:

console.log('start')

setTimeout(function() {
    console.log('setTimeout')
}, 0)

console.log('end')
// start
// end
// setTimeout

在上述的例子中兩個(gè)console是同步代碼,所以直接進(jìn)入主線程的執(zhí)行棧中執(zhí)行,setTimeout是異步代碼所以放到消息隊(duì)列里。在執(zhí)行棧中按照代碼從上到下的執(zhí)行順序,打印 start => end。執(zhí)行棧中的任務(wù)全部完成后,對(duì)消息隊(duì)里進(jìn)行輪詢,由于定時(shí)器設(shè)定的時(shí)間為0,所以會(huì)在執(zhí)行棧的任務(wù)清空后立即執(zhí)行,所以上述例子的打印結(jié)果為start => end => setTimeout

大致總結(jié)js事件線程循環(huán)的過程:
1、所有任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧。
2、所有執(zhí)行棧中的所有同步任務(wù)執(zhí)行完畢,js就會(huì)讀取息隊(duì)列中的異步任務(wù),如果有可以執(zhí)行的任務(wù)就把他放在執(zhí)行棧中并開始執(zhí)行。
3、主線程不斷重復(fù)上面第二步,這樣的一個(gè)循環(huán)成為事件循環(huán)。
js事件循環(huán)中有異步隊(duì)列有兩種:宏任務(wù)隊(duì)列(macro)和微任務(wù)隊(duì)列(micro)
常見的宏任務(wù)比如:setTimeout、setInterval、 setImmediate、script(整體代碼)、 I/O 操作、UI 渲染等。
常見的微任務(wù)比如:process。nextTick、Pormise、MutationObserver 等。
eventLoop循環(huán)過程:
1、初始狀態(tài):微任務(wù)隊(duì)列只有一個(gè)script腳本;(整體代碼為宏任務(wù))
2、【宏任務(wù)階段】執(zhí)行script代碼,創(chuàng)建宏任務(wù)到宏任務(wù)調(diào)用棧中,創(chuàng)建的微任務(wù)到微任務(wù)調(diào)用棧中。
3、【微任務(wù)階段】執(zhí)行微任務(wù),調(diào)出當(dāng)前微任務(wù)中的所有微任務(wù),一次性執(zhí)行,其中如果有宏任務(wù)推到宏任務(wù)棧中。
4、【宏任務(wù)階段】執(zhí)行宏任務(wù),調(diào)用當(dāng)前宏任務(wù)棧中的第一個(gè)宏任務(wù),其中有創(chuàng)見的微任務(wù)推到微任務(wù)棧中。
5、如果代碼沒結(jié)束,循環(huán)執(zhí)行3、4步驟。
總結(jié):
1、宏任務(wù)和微任務(wù)是交替進(jìn)行的
2、每個(gè)宏任務(wù)只執(zhí)行棧中的第一個(gè)任務(wù),執(zhí)行完就執(zhí)行微任務(wù)。微任務(wù)執(zhí)行棧中所有的微任務(wù)。

console.log('sync');
 
setTimeout(function () {
   console.log('setTimeout')
}, 0);
 
var promise = new Promise(function (resolve, reject) {
   console.log('promise');
   setTimeout(function() {
      promise.then(function(){
        console.log('promise-setTimeout-then')    
      })
      console.log('promise-setTimeout')
   }, 0);
   resolve();
});
 
promise.then(function() {
    setTimeout(function() { 
            promise.then(function(){
                console.log('then-setTimeout-then')    
            })
            console.log('then-setTimeout')
    }, 0);
    console.log('then');
    promise.then(function(){
        console.log('then-than')    
    })
})
////宏任務(wù)階段
sync  
promise
//微任務(wù)階段
then
then - than
//宏任務(wù)階段
setTimeout
//微任務(wù)階段(沒有任務(wù),沒有輸出)
//宏任務(wù)階段
promise - setTimeout
//微任務(wù)階段
promise - setTimeout - then
//宏任務(wù)階段
then - setTimeout
//微任務(wù)階段
then-setTimeout-then

如果不清楚請(qǐng)?zhí)D(zhuǎn)鏈接:https://blog.csdn.net/webjhh/article/details/116759051
10、請(qǐng)解釋同步和異步的區(qū)別。
11、http的狀態(tài)碼
答:1信息,服務(wù)器接收到請(qǐng)求,請(qǐng)求者繼續(xù)吧
2
成功。
3重定向,需要進(jìn)一步操作來完成請(qǐng)求
4
客戶端錯(cuò)誤
5**服務(wù)端錯(cuò)誤
16、瀏覽器的垃圾回收機(jī)制是什么?
12、構(gòu)造函數(shù)new的過程
1、創(chuàng)建一個(gè)新對(duì)象
2、把當(dāng)前構(gòu)造函數(shù)的作用域給新的對(duì)象(此時(shí)this指向就會(huì)指向新對(duì)象)
3、執(zhí)行構(gòu)造函數(shù)中的代碼(給新的對(duì)象添加方法或者是屬性)
4、返回新對(duì)象
通過new關(guān)鍵字創(chuàng)建的對(duì)象會(huì)有一個(gè)constructor屬性,該屬性指向的是當(dāng)前的這個(gè)對(duì)象。
注意:原本構(gòu)造函數(shù)是window對(duì)象的方法,如果不是用new調(diào)用,那么指向的還是window,那么this指向也是window。如果是用new關(guān)鍵字調(diào)用,那么this指向就是當(dāng)前的對(duì)象。
13、如果后端給前端一個(gè)很大的數(shù),前端要怎么處理?
答:分頁(yè),滾動(dòng)加載,加lodding,節(jié)流。
14、函數(shù)的節(jié)流和防抖
函數(shù)節(jié)流是指一定時(shí)間內(nèi)js方法只跑一次
函數(shù)防抖是指頻繁觸發(fā)的情況下,只有足夠的空閑時(shí)間,才執(zhí)行代碼一次。
函數(shù)防抖的要點(diǎn),也是需要一個(gè)setTimeout來輔助實(shí)現(xiàn)。延遲執(zhí)行需要跑的代碼。
如果方法多次觸發(fā),則把上次記錄的延遲執(zhí)行代碼用clearTimeout清掉,重新開始。
如果計(jì)時(shí)完畢,沒有方法進(jìn)來訪問觸發(fā),則執(zhí)行代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容