1. JS 有哪些資料型別?
根據 JavaScript 中的變數型別傳遞方式,分為基本資料型別和參考資料型別兩大類七種,
基本資料型別包括Undefined、Null、Boolean、Number、String、Symbol (ES6新增)六種,
參考資料型別只有Object一種,主要包括物件、陣列和函式,
判斷資料型別采用typeof運算子,有兩種語法:
typeof 123;//語法一
const FG = 123;
typeof FG;//語法二
typeof(null) //回傳 object;
null == undefined //回傳true,因為undefined派生自null;
null === undefined //回傳false,
2. 基本資料型別和參考資料型別有什么區別?
(1)兩者作為函式的引數進行傳遞時:
基本資料型別傳入的是資料的副本,原資料的更改不會影響傳入后的資料,
參考資料型別傳入的是資料的參考地址,原資料的更改會影響傳入后的資料,
(2)兩者在記憶體中的存盤位置:
基本資料型別存盤在堆疊中,
參考資料型別在堆疊中存盤了指標,該指標指向的資料物體存盤在堆中,
3. 判斷資料型別的方法有哪些?
(1)利用typeof可以判斷資料的型別;
(2)A instanceof B可以用來判斷A是否為B的實體,但它不能檢測 null 和 undefined;
(3)B.constructor == A可以判斷A是否為B的原型,但constructor檢測 Object與instanceof不一樣,還可以處理基本資料型別的檢測,
不過函式的 constructor 是不穩定的,這個主要體現在把類的原型進行重寫,在重寫的程序中很有可能出現把之前的constructor給覆寫了,這樣檢測出來的結果就是不準確的,
(4)Object.prototype.toString.call()
Object.prototype.toString.call()是最準確最常用的方式,
4. 與深拷貝有何區別?如何實作?
淺拷貝只復制指向某個物件的指標,而不復制物件本身,淺拷貝的實作方式有:
(1)Object.assign():需注意的是目標物件只有一層的時候,是深拷貝;
(2)擴展運算子;
深拷貝就是在拷貝資料的時候,將資料的所有參考結構都拷貝一份,深拷貝的實作方式有:
(1)手寫遍歷遞回賦值;
(2)結合使用JSON.parse()和JSON.stringify()方法,
5. let、const的區別是什么?
var、let、const都是用于宣告變數或函式的關鍵字,其區別在于:
| var | let | const | |
|---|---|---|---|
| 作用域 | 函式作用域 | 塊級作用域 | 塊級作用域 |
| 作用域內宣告提升 | 有 | 無(暫時性死區) | 無 |
| 是否可重復宣告 | 是 | 否 | 否 |
| 是否可重復賦值 | 是 | 是 | 否(常量) |
| 初始化時是否必需賦值 | 否 | 否 | 是 |
6. 什么是執行背景關系和執行堆疊?
變數或函式的執行背景關系,決定了它們的行為以及可以訪問哪些資料,每個背景關系都有一個關聯的變數物件,而這個背景關系中定義的所有變數和函式都存在于這個物件上(如DOM中全域背景關系關聯的便是window物件),
每個函式呼叫都有自己的背景關系,當代碼執行流進入函式時,函式的背景關系被推到一個執行堆疊中,在函式執行完之后,執行堆疊會彈出該函式背景關系,在其上的所有變數和函式都會被銷毀,并將控制權返還給之前的執行背景關系, JS的執行流就是通過這個執行堆疊進行控制的,
7. 什么是作用域和作用域鏈?
作用域可以理解為一個獨立的地盤,可以理解為識別符號所能生效的范圍,作用域最大的用處就是隔離變數,不同作用域下同名變數不會有沖突,ES6中有全域作用域、函式作用域和塊級作用域三層概念,
當一個變數在當前塊級作用域中未被定義時,會向父級作用域(創建該函式的那個父級作用域)尋找,如果父級仍未找到,就會再一層一層向上尋找,直到找到全域作用域為止,這種一層一層的關系,就是作用域鏈 ,
8. 作用域和執行背景關系的區別是什么?
(1)函式的執行背景關系只在函式被呼叫時生成,而其作用域在創建時已經生成;
(2)函式的作用域會包含若干個執行背景關系(有可能是零個,當函式未被呼叫時),
9. this指向的各種情況都有什么?
this的指向只有在呼叫時才能被確定,因為this是執行背景關系的一部分,
(1)全域作用域中的函式:其內部this指向window:
var a = 1;
function fn(){
console.log(this.a)
}
fn() //輸出1
(2)物件內部的函式:其內部this指向物件本身:
var a = 1;
var obj = {
a:2,
fn:function(){
console.log(this.a)
}
}
obj.fn() //輸出2
(3)建構式:其內部this指向生成的實體:
function createP(name,age){
this.name = name //this.name指向P
this.age = age //this.age指向P
}
var p = new createP("老李",46)
(4)由apply、call、bind改造的函式:其this指向第一個引數:
function add(c,d){
return this.a + this.b + c + d
}
var o = {a:1,b:2)
add.call(o,5,7) //輸出15
(5)箭頭函式:箭頭函式沒有自己的this,看其外層的是否有函式,如果有,外層函式的this就是內部箭頭函式的this,如果沒有,則this是window,
10.如何改變this指標的指向?
可以使用apply、call、bind方法改變this指向(并不會改變函式的作用域),比較如下:
(1)三者第一個引數都是this要指向的物件,也就是想指定的背景關系,背景關系就是指呼叫函式的那個物件(沒有就指向全域window);
(2)apply和bind的第二個引數都是陣列,call接收多個引數并用逗號隔開;
(3)apply和call只對原函式做改動,bind會回傳新的函式(要生效還得再呼叫一次),
11.什么是閉包?
閉包就是參考了其他函式作用域中變數的函式,這種模式通常在函式嵌套結構中實作,里面的函式可以訪問外面函式的變數,外面的變數的是這個內部函式的一部分,閉包有如下作用:
(1)加強封裝,模擬實作私有變數;
(2)實作常駐記憶體的變數,
閉包不能濫用,否則會導致記憶體泄露,影響網頁的性能,閉包使用完了后,要立即釋放資源,將參考變數指向null,
12. 什么是原型、原型鏈?
原型:JS宣告建構式(用來實體化物件的函式)時,會在記憶體中創建一個對應的物件,這個物件就是原函式的原型,建構式默認有一個prototype屬性,prototype的值指向函式的原型,同時原型中也有一個constructor屬性,constructor的值指向原函式,
通過建構式實體化出來的物件,并不具有prototype屬性,其默認有一個__proto__屬性,__proto__的值指向建構式的原型物件,在原型物件上添加或修改的屬性,在所有實體化出的物件上都可共享,
當在實體化的物件中訪問一個屬性時,首先會在該物件內部尋找,如找不到,則會向其__proto__指向的原型中尋找,如仍找不到,則繼續向原型中__proto__指向的上級原型中尋找,直至找到或Object.prototype為止,這種鏈狀程序即為原型鏈,
13. 何為防抖和節流?如何實作?
防抖和節流都是防止短時間內高頻觸發事件的方案,
防抖的原理是:如果一定時間內多次執行了某事件,則只執行其中的最后一次,
節流的原理是:要執行的事件每隔一段時間會被冷卻,無法執行,
應用場景有:搜索框實時搜索,滾動改變相關的事件,
//@fn: 要執行的函式
//@delay: 設定的時限
//防抖函式
function debunce(fn,delay){
let flag = null;
return function(){
if(flag) clearTimeout(flag)
//利用apply改變函式指向,使得封裝后的函式可以接收event本身
flag = setTimeout(()=>fn.apply(this,arguments),delay)
}
}
//節流函式
function throttle(fn,delay){
let flag = true;
return function(){
if(!flag) return false;
flag = false;
setTimeout(()=>{
fn.apply(this,arguments)
flag=true
},delay)
}
}
14. 如何理解同步和異步?
同步:按照代碼書寫順序一一執行處理指令的一種模式,上一段代碼執行完才能執行下一段代碼,
異步:可以理解為一種并行處理的方式,不必等待一個程式執行完,可以執行其它的任務,
JS之所以需要異步的原因在于JS是單執行緒運行的,常用的異步場景有:定時器、ajax請求、事件系結,
15. JS是如何實作異步的?
JS引擎是單執行緒的,但又能實作異步的原因在于事件回圈和任務佇列體系,
事件回圈:
JS 會創建一個類似于 while (true) 的回圈,每執行一次回圈體的程序稱之為 Tick,每次 Tick 的程序就是查看是否有待處理事件,如果有則取出相關事件及回呼函式放入執行堆疊中由主執行緒執行,待處理的事件會存盤在一個任務佇列中,也就是每次 Tick 會查看任務佇列中是否有需要執行的任務,
任務佇列:
異步操作會將相關回呼添加到任務佇列中,而不同的異步操作添加到任務佇列的時機也不同,如 onclick, setTimeout, ajax 處理的方式都不同,這些異步操作是由瀏覽器內核的 webcore 來執行的,瀏覽器內核包含3種 webAPI,分別是 DOM Binding、network、timer模塊,
onclick 由 DOM Binding 模塊來處理,當事件觸發的時候,回呼函式會立即添加到任務佇列中,
setTimeout 由 timer 模塊來進行延時處理,當時間到達的時候,才會將回呼函式添加到任務佇列中,
ajax 由network 模塊來處理,在網路請求完成回傳之后,才將回呼添加到任務佇列中,
主執行緒:
JS 只有一個執行緒,稱之為主執行緒,而事件回圈是主執行緒中執行堆疊里的代碼執行完畢之后,才開始執行的,所以,主執行緒中要執行的代碼時間過長,會阻塞事件回圈的執行,也就會阻塞異步操作的執行,
只有當主執行緒中執行堆疊為空的時候(即同步代碼執行完后),才會進行事件回圈來觀察要執行的事件回呼,當事件回圈檢測到任務佇列中有事件就取出相關回呼放入執行堆疊中由主執行緒執行,
16. 什么是AJAX?如何實作?
ajax是一種能夠實作區域網頁重繪的技術,可以使網頁異步重繪,
ajax的實作主要包括四個步驟:
(1)創建核心物件XMLhttpRequest;
(2)利用open方法打開與服務器的連接;
(3)利用send方法發送請求;("POST"請求時,還需額外設定請求頭)
(4)監聽服務器回應,接識訓傳值,
//1-創建核心物件
//該物件有兼容問題,低版本瀏覽器應使用ActiveXObject
const xthhp = new XMLHttpRequest();
//2-連接服務器
//open(method,url,async)
xhttp.open("POST","http://localhost:3000",true)
//設定請求頭
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//3-發送請求
//send方法發送請求引數,如為GET方法,則在open中url后拼接
xhttp.send({_id:123})
//4-接收服務器回應
//onreadystatechange事件,會在xhttp的狀態發生變化時自動呼叫
xhttp.onreadystatechange =function(){
//狀態碼共5種:0-未open 1-已open 2-已send 3-讀取回應 4-回應讀取結束
if(xhttp.readyState == 4 && xhttp.status == 200){
alert("ajax請求已完成")
}
}
17. 實作異步的方式有哪些?
(1)回呼函式模式:將需要異步執行的函式作為回呼函式執行,其缺點在于處理復雜邏輯異步邏輯時,會造成回呼地獄(回呼嵌套層數太多,代碼結構混亂);
(2)事件監聽模式:采用事件驅動的思想,當某一事件發生時觸發執行異步函式,其缺點在于整個代碼全部得變為事件驅動模式,難以分辨主流程;
(3)發布訂閱模式:當異步任務執行完成時發布訊息給信號中心,其他任務通過在信號中心中訂閱訊息來確定自己是否開始執行;
(4)Promise(ES6):Promise物件共有三種狀態pending(初始化狀態)、fulfilled(成功狀態)、rejected(失敗狀態),
(5)async/await(ES7):基于Promise實作的異步函式;
(6)利用生成器實作,
18. 怎么理解Promise物件?
Promise物件有如下兩個特點:
(1)物件的狀態不受外界影響,Promise物件共有三種狀態pending、fulfilled、rejected,狀態值只會被異步結果決定,其他任何操作無法改變,
(2)狀態一旦成型,就不會再變,且任何時候都可得到這個結果,狀態值會由pending變為fulfilled或rejected,這時即為resolved,
Promise的缺點有如下三個缺點:
(1)Promise一旦執行便無法被取消;
(2)不可設定回呼函式,其內部發生的錯誤無法捕獲;
(3)當處于pending狀態時,無法得知其具體發展到了哪個階段,
Pomise中常用的方法有:
(1)Promise.prototype.then():Promise實體的狀態發生改變時,會呼叫then內部的回呼函式,then方法接受兩個引數(第一個為resolved狀態時時執行的回呼,第一個為rejected狀態時時執行的回呼)
(2)Promise.prototype.catch():.then(null, rejection)或.then(undefined, rejection)的別名,用于指定發生錯誤時的回呼函式,
19. 怎么理解宏任務,微任務???
宏任務有:script(整體代碼)、setTimeout、setInterval、I/O、頁面渲染;
微任務有:Promise.then、Object.observe、MutationObserver,
執行順序大致如下:
主執行緒任務——>宏任務——>微任務——>微任務里的宏任務——>.......——>直到任務全部完成
20. 什么是跨域?怎么解決跨域問題?
跨域問題實際是由同源策略衍生出的一個問題,當傳輸協議、域名、埠任一部分不一致時,便會產生跨域問題,從而拒絕請求,但<img src=https://www.cnblogs.com/stjerne/archive/2021/01/02/XXX> <link href=XXX><script src=XXX>;天然允許跨域加載資源,解決方案有:
(1)JSONP
原理:利用<script>;標簽沒有跨域限制的漏洞,使得網頁可以得到從其他來源動態產生的JSON資料(前提是服務器支持),
優點:實作簡單,兼容性好,
缺點:僅支持get方法,容易受到XSS攻擊,
(2)CORS
原理:服務器端設置Access-Control-Allow-Origin以開啟CORS,該屬性表示哪些域名可以訪問資源,如設定通配符則表示所有網站均可訪問,
實作實體(express):
//app.js中設定
var app = express();
//CORS跨域-------------------------------------------------------------------------------------
// CORS:設定允許跨域中間件
var allowCrossDomain = function (req, res, next) {
// 設定允許跨域訪問的 URL(* 表示允許任意 URL 訪問)
res.header("Access-Control-Allow-Origin", "*");
// 設定允許跨域訪問的請求頭
res.header("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept,Authorization");
// 設定允許跨域訪問的請求型別
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
// 設定允許服務器接收 cookie
res.header('Access-Control-Allow-Credentials', 'true');
next();
};
app.use(allowCrossDomain);
//------------------------------------------------------------------------------------
(3)Node中間件代理
原理:同源策略僅是瀏覽器需要遵循的策略,故搭建中間件服務器轉發請求與回應,達到跨域目的,
/* server1.js 代理服務器(http://localhost:3000)*/
const http = require('http')
// 第一步:接受客戶端請求
const server = http.createServer((request, response) => {
// 代理服務器,直接和瀏覽器直接互動,需要設定CORS 的首部欄位
response.writeHead(200,{
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods':'*',
'Access-Control-Allow-Headers':'Content-Type'
})
// 第二步:將請求轉發給服務器
const proxyRequest = http.request({
host:'127.0.0.1',
port:4000,
url:'/',
method:request.method,
headers:request.headers
},
serverResponse =>{
// 第三步:收到服務器的回應
var body = ''
serverResponse.on('data', chunk =>{
body += chunk
})
serverResponse.on('end',()=> {
console.log('The data is '+ body)
// 第四步:將回應結果轉發給瀏覽器
response.end(body)
})
})
.end()
})
server.listen(3000,()=>{console.log('中間件服務器地址: http://localhost:3000')})
// server2.js(http://localhost:4000)
const http = require("http");
const data = https://www.cnblogs.com/stjerne/archive/2021/01/02/{ title:"fontend", password: "123456" };
const server = http.createServer((request, response) => {
if (request.url === "/") {
response.end(JSON.stringify(data));
}
});
server.listen(4000, () => {
console.log("The server is running at http://localhost:4000");
});
(4)nginx反向代理
原理:類似Node中間件服務器,通過nginx代理服務器實作,
實作方法:下載安裝nginx,修改配置,
21. 實作繼承的方法有哪些???
實作繼承的方法有:
(1)class+extends繼承(ES6)
//類模板
class Animal {
constructor(name){
this.name = name
}
}
//繼承類
class Cat extends Animal{//重點,extends方法,內部用constructor+super
constructor(name) {
super(name);
//super作為函式呼叫時,代表父類的建構式
}//constructor可省略
eat(){
console.log("eating")
}
}
(2)原型繼承
//類模板
function Animal(name) {
this.name = name;
}
//添加原型方法
Animal.prototype.eat = function(){
console.log("eating")
}
function Cat(furColor){
this.color = color ;
};
//繼承類
Cat.prototype = new Animal()//重點:子實體的原型等于父類的實體
(3)借用建構式繼承
function Animal(name){
this.name = name
}
function Cat(){
Animal.call(this,"CatName")//重點,呼叫父類的call方法
}
(4)寄生組合式繼承(重點)
22. DOM事件模型和事件流?
DOM事件模型包括事件捕獲(自上而下觸發)與事件冒泡(自下而上觸發,ie用的就是冒泡)機制,基于事件冒泡機制可以完成事件代理,
事件捕獲
事件冒泡
DOM事件流包括三個階段事件捕獲階段、處于目標階段、事件冒泡階段,
23. EventLoop事件回圈是什么?
js是一門單執行緒的需要,它的異步操作都是通過事件回圈來完成的,整個事件回圈大體由執行堆疊、訊息佇列和微任務佇列三個部分組成,
同步代碼會直接在執行堆疊中呼叫執行,
定時器中的回呼會在執行堆疊被清空且定時達成時推入執行堆疊中執行,
promise、async異步函式的回呼會被推入到微任務佇列中,當執行堆疊被清空且異步操作完成時立即執行,
24. require/import之間的區別?
(1)require是CommonJS語法,import是ES6語法;
(2)require只在后端服務器支持,import在高版本瀏覽器及Node中都可以支持;
(3)require引入的是原始匯出值的復制,import則是匯出值的參考;
(4)require時運行時動態加載,import是靜態編譯;
(5)require呼叫時默認不是嚴格模式,import則默認呼叫嚴格模式.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/243804.html
標籤:其他
上一篇:css筆記:回應式、媒體查詢
