因?yàn)榻梃b的文章比較多加上個人理解整理,沒有備注來源。如侵權(quán)比刪!
一、js的數(shù)據(jù)類型
ES5的5種:
基本數(shù)據(jù)類型: Number、 String、 Boolean、 undefined、 null
復(fù)雜數(shù)據(jù)類型:Object、function、Array
ES6新增
Symbol 表示獨(dú)一無二
ES10新增:
BigLnt 表示任意大的數(shù)
ps:存儲區(qū)別
基本數(shù)據(jù)類型和引用數(shù)據(jù)類型存儲在內(nèi)存中的位置不同:
- 基本數(shù)據(jù)類型存儲在棧中
- 引用類型的對象存儲于堆中
二、undefined和null的區(qū)別
相同點(diǎn):
1、在if語句中null和undefined都會轉(zhuǎn)換為false
2、用 == 比較 返回true
null ==undefined // true
不同點(diǎn):
undefined:代表的含義未定義
null: 表示一個空對象指針
typeof undefined //'undefined'
typeof null // "object"
三、判斷 JavaScript 的數(shù)據(jù)類型typeof
typeof:type的返回值是字符串(String)
typeof undefined // "undefined"
typeof null // "object"
typeof 1 // "number"
typeof "1" // "string"
typeof Symbol() // "symbol"
typeof function() {} // "function"
typeof {} // "object"
typeof 不能識別 null,如何識別 null?[使用全等即可]
let a = null
a === null
instanceof: 可以用來判斷對象的類型:
instanceof 只能正確判斷引用數(shù)據(jù)類型 而不能判斷基本數(shù)據(jù)類型,
原因:其內(nèi)部運(yùn)行機(jī)制是判斷在其原型鏈中能否找到該類型的原型
或者可以理解為:一個對象是否在一個類的示例上
var date = new Date()
date instanceof Date // true
var number = new Number()
number instanceof Number // true
var string = new String()
string instanceof String // true
四、js數(shù)據(jù)類型轉(zhuǎn)換
強(qiáng)制類型轉(zhuǎn)換
轉(zhuǎn)化成字符串 toString() String()
轉(zhuǎn)換成數(shù)字 Number()、 parseInt()、 parseFloat()
轉(zhuǎn)換成布爾類型 Boolean()
隱式類型轉(zhuǎn)換
"" + - / % === =
例子:
轉(zhuǎn)換為數(shù)字
Number():可以把任意值轉(zhuǎn)換成數(shù)字,如果有不是數(shù)字的值,則會返回NaN
------------------
Number('1') // 1
Number(true) // 1
Number('123a') // NaN
// 空字符串轉(zhuǎn)為0
Number('') // 0
// 布爾值:true 轉(zhuǎn)成 1,
//false 轉(zhuǎn)成 0
Number(true) // 1
Number(false) // 0
// undefined:轉(zhuǎn)成 NaN
Number(undefined) // NaN
// null:轉(zhuǎn)成0
Number(null) // 0
parseInt(string):解析一個字符串并返回指定基數(shù)的十進(jìn)制整數(shù),
------------------
parseInt('a123') // NaN 如果第一個字符不是數(shù)字或者符號就返回NaN
parseInt('123a') // 123
parseInt('32a3') //32
String()//轉(zhuǎn)換字符串
-------------------
String(1) // "1"
String("a") // "a" 字符串轉(zhuǎn)換后還是原來的值
String(true) // "true"
String(false) // "false"
String(undefined) // "undefined"
String(null) // "null"
Boolean()可以將任意類型的值轉(zhuǎn)為布爾值
-------------------
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(1)// true
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
隱式類型轉(zhuǎn)換
-------------------
- * 不進(jìn)行演示
一旦存在字符串,進(jìn)行字符串拼接操作
'1' + 1 // '11'
'1' + true // "1true"
'1' + false // "1false"
'1' + {} // "1[object Object]"
'1' + [] // "1"
'1' + undefined // "1undefined"
'1' + null // "1null"
5、==和===的區(qū)別
(==)相等操作符會做類型轉(zhuǎn)換,再進(jìn)行值的比較,
(===)全等運(yùn)算符不會做類型轉(zhuǎn)換
'' == '0' // false
1 == '1' // true
1 === '1' // false
6、作用域
簡單的理解就是變量的有效范圍。
可分為:全局作用域、函數(shù)作用域、塊級作用域
全局作用域
函數(shù)最外層定義的變量擁有全局作用域,函數(shù)內(nèi)部是可以訪問的
let inVariable = "全局作用域";
function fn() {
console.log(inVariable); // 全局作用域
}
fn();
函數(shù)作用域
函數(shù)作用域也叫局部作用域:如果一個變量是在函數(shù)內(nèi)部聲明的,那這些變量只能在函數(shù)下訪問,不能再函數(shù)以外去訪問
function fn() {
let inVariable = "函數(shù)內(nèi)部變量";
console.log(inVariable)// "函數(shù)內(nèi)部變量";
}
fn();
//報(bào)錯
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
塊級作用域
ES6引入了塊級作用域 ,【由大括號包裹,比如:if(){},for(){}等】
let和const關(guān)鍵字,和var關(guān)鍵字不同【后續(xù)講解let、const、var區(qū)別】
在大括號中使用let和const聲明的變量存在于塊級作用域中。在大括號之外不能訪問這些變量
{
// 塊級作用域中的變量
let a = 'Hello';
var b = 'World';
console.log(a); // Hello
}
console.log(b); // 'World'
console.log(a); // 報(bào)錯:Uncaught ReferenceError: a is not defined
如何訪問函數(shù)內(nèi)部的變量(本人面試時候有問到)
1、通過return訪問:
function bar(value) {
var testValue = "hello word";
return testValue;
}
console.log(bar());//hello word
2、通過 閉包 訪問函數(shù)內(nèi)部變量
閉包的定義:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
可以理解成“定義在一個函數(shù)內(nèi)部的函數(shù)“
閉包是將函 數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁。”
function bar() {
var testValue = "hello";
var rusult = testValue + "Word";
function innser() {
console.log(rusult);//helloWord
return rusult;
}
return innser();
}
console.log(bar()); // "helloWord"
7、var、let、const之間的區(qū)別
var 聲明變量可以重復(fù)聲明,而 let 不可以重復(fù)聲
var 是不受限于塊級作用域的,而 let 是受限于塊級作用域
var 可以在聲明的上面訪問變量,而 let 有暫存死區(qū),在聲明的上面訪問變量會報(bào)錯
var在全局作用域下聲明變量會導(dǎo)致變量掛載在 window 上let、const 不會
const 聲明之后必須賦值,否則會報(bào)錯 const 定義不可變的量,改變了就會報(bào)錯
const 和 let 一樣支持塊級作用域、在聲明的上面訪問變量會報(bào)錯
上代碼var篇
//用var聲明的變量既是全局變量也是頂層變量,所謂頂層就是window
var a = 10;
console.log(window.a) // 10
//使用var聲明的變量存在變量提升的情況
console.log(a) // undefined
var a = 20
//在編譯階段,編譯器會將其變成以下執(zhí)行
var a;
console.log(a); //undefined
a = 20;
console.log(a); //20
//var 多次聲明
var a = 20
var a = 30
console.log(a) // 30
//在函數(shù)中使用使用var聲明變量時候,該變量是局部的
var a = 20
function change(){
var a = 30
}
change()
console.log(a) // 20
let篇
//let 具有塊級作用域
{
let a = 20
}
console.log(a) // 報(bào)錯Uncaught ReferenceError: a is not defined
//不存在變量提升
console.log(a) // 報(bào)錯Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 2
// let 不允許相同作用域下聲明
let a = 20
let a = 30 //報(bào)錯【累了具體報(bào)錯不打印了】
const篇
//const聲明一個只讀的常量,一旦聲明,常量的值就不能改變
const a = 1
a = 3 // 報(bào)錯
//const一旦聲明變量,就必須立即初始化,不能留到以后賦值
const a;
// SyntaxError: Missing initializer in const declaration
const實(shí)際上保證的并不是變量的值不能改動,而是變量指向的那個內(nèi)存地址的數(shù)據(jù)不能改動。
復(fù)雜數(shù)據(jù)類型變量指向的是內(nèi)存地址,保存的只是一個指向?qū)嶋H數(shù)據(jù)的指針,const只能保證這個指針是固定的,并不能確保改變量的結(jié)構(gòu)不變
const foo = {};
// 為 foo 添加一個屬性,可以成功
foo.a = 123;
foo.a // 123
// 將 foo 指向另一個對象,就會報(bào)錯
foo = {}; //報(bào)錯
8、閉包
閉包的概念就是:有權(quán)利訪問另一個函數(shù)作用域中的變量,一般就是函數(shù)包裹著函數(shù)[之前有提到過閉包的定義]
閉包可以重用一個變量,且保證這個變量不會被污染的一種機(jī)制。這些變量的值始終保持在內(nèi)存中,不會被垃圾回收機(jī)制處理
閉包的缺點(diǎn):由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題
使用場景 :
防抖、節(jié)流、函數(shù)套函數(shù)避免全局污染
創(chuàng)建私有變量
延長變量的生命周期
//簡單的閉包
function bar() {
var testValue = "hello";
var rusult = testValue + "Word";
function innser() {
console.log(rusult);//helloWord
return rusult;
}
return innser();
}
console.log(bar()); // "helloWord"
9、什么是內(nèi)存泄漏
內(nèi)存泄漏指任何對象在您不再擁有或需要它之后仍然存在
10、原型、原型鏈
原型
- 每個函數(shù)都有一個
prototype屬性,被稱為顯示原型 - 每個引用數(shù)據(jù)類型都會有
_ _proto_ _屬性,其被稱為隱式原型 - 每個引用數(shù)據(jù)類型,它的
_ _ proto_ _屬性指向它的構(gòu)造函數(shù)的’prototype’屬性。 - 每個prototype原型都有一個
constructor屬性,指向它關(guān)聯(lián)的構(gòu)造函數(shù)。 - 當(dāng)試圖得到一個對象的屬性時,如果這個對象本身不存在這個屬性,那么就會去它的
_ _ proto_ _屬性(也就是它的構(gòu)造函數(shù)的’prototype’屬性)中去尋找。
原型鏈
當(dāng)訪問一個對象的某個屬性時,會先在這個對象本身屬性上查找,如果沒有找到,則會去它的proto隱式原型上查找,即它的構(gòu)造函數(shù)的prototype,如果還沒有找到就會再在構(gòu)造函數(shù)的prototype的proto中查找,這樣一層一層向上查找就會形成一個鏈?zhǔn)浇Y(jié)構(gòu),我們稱為原型鏈。直到頂層返回null
11、什么是遞歸
遞歸:如果一個函數(shù)在內(nèi)部可以調(diào)用其本身,那么這個函數(shù)就是遞歸函數(shù)。簡單理解:函數(shù)內(nèi)部自己調(diào)用自己, 這個函數(shù)就是遞歸函數(shù)
注意:遞歸函數(shù)的作用和循環(huán)效果一樣,由于遞歸很容易發(fā)生“棧溢出”錯誤(stack overflow),所以必須要加退出條件return。
12、什么是防抖什么是節(jié)流
防抖:觸發(fā)高頻事件后 n 秒內(nèi)函數(shù)只會執(zhí)行一次,如果 n 秒內(nèi)高頻事件再次被觸發(fā),則重新計(jì)算時間
應(yīng)用場景:用戶在輸入框中連續(xù)輸入一串字符時,可以通過防抖策略,只在輸入完后,才執(zhí)行查詢的請求,這樣可以有效減
少請求次數(shù),節(jié)約請求資源;
節(jié)流:高頻事件觸發(fā),但在 n 秒內(nèi)只會執(zhí)行一次,所以節(jié)流會稀釋函數(shù)的執(zhí)行頻率(也可以理解:可以減少一段時間內(nèi)事件的觸發(fā)頻率)
應(yīng)用場景: 鼠標(biāo)連續(xù)不斷地觸發(fā)某事件(如點(diǎn)擊),只在單位時間內(nèi)只觸發(fā)一次;
區(qū)別:防抖動是將多次執(zhí)行變?yōu)樽詈笠淮螆?zhí)行,節(jié)流是將多次執(zhí)行變成每隔一段時間執(zhí)行。
13、call,apply和bind的區(qū)別
相同點(diǎn): 三個函數(shù)都會改變this的指向(調(diào)用這三個函數(shù)的函數(shù)內(nèi)部的this)
不同點(diǎn):
- call、apply與bind的差別
call和apply改變了函數(shù)的this上下文后便執(zhí)行該函數(shù),而bind則是返回改變了上下文后的一個函數(shù)。(bind不會立即執(zhí)行)
- call、apply的區(qū)別
他們倆之間的差別在于參數(shù)的區(qū)別,call和apply的第一個參數(shù)都是要改變上下文的對象,而call從第二個參數(shù)開始以參數(shù)列表的形式展現(xiàn),apply則是把除了改變上下文對象的參數(shù)放在一個數(shù)組里面作為它的第二個參數(shù)
14、Localstorage、sessionStorage、cookie 的區(qū)別
共同點(diǎn):都是保存在瀏覽器端的,且同源
localStorage:聲生命周期永久生效,除非手動刪除 否則關(guān)閉頁面也會存在
sessionStorage:生命周期為關(guān)閉瀏覽器窗口。
存儲大小:sessionStorage約5M、localStorage約20M
Cookie是服務(wù)器發(fā)給客戶端的特殊信息,cookie是以文本的方式保存在客戶端(儲存量4k左右)。cookie可以設(shè)置過期時間,到達(dá)時間后自動銷毀,如果沒有設(shè)置會隨瀏覽器的關(guān)閉而銷毀。cookei中儲存的數(shù)據(jù)會伴隨著每一次http請求發(fā)送到服務(wù)端。
15、同源策略
MDN官方給定的概念:同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進(jìn)行交互。這是一個用于隔離潛在惡意文件的重要安全機(jī)制
通俗的理解:瀏覽器規(guī)定,A 網(wǎng)站的 JavaScript,不允許和非同源的網(wǎng)站 C 之間,進(jìn)行資源的交互
同源:協(xié)議,域名,端口,三者必須一致
跨域
同源指的是兩個 URL 的協(xié)議、域名、端口一致,反之,則是跨域
跨域,指的是從一個域名去請求另外一個域名的資源。即跨域名請求
跨域時,瀏覽器不能執(zhí)行其他域名網(wǎng)站的腳本,是由瀏覽器的同源策略造成的,也是瀏覽器施加的安全限制。
怎么解決跨域
1、需要后臺配置cors進(jìn)行跨域,原理是給服務(wù)器設(shè)置一個響應(yīng)頭,然后瀏覽器將會允許跨域請求
2、jsonp:原理動態(tài)創(chuàng)建一個script標(biāo)簽,利用script標(biāo)簽的src屬性不受同源策略的限制。所有的src屬性和href都不會受同源策略的限制??梢哉埱蟮谌椒?wù)器的數(shù)據(jù)內(nèi)容
3、反向代理
4、ngex
16、常見的http狀態(tài)碼
200:這個是最常見的http狀態(tài)碼,表示服務(wù)器已經(jīng)成功接受請求,并將返回客戶端所請求的最終結(jié)果
202:表示服務(wù)器已經(jīng)接受了請求,但是還沒有處理,而且這個請求最終會不會處理還不確定
204:服務(wù)器成功處理了請求,但沒有返回任何實(shí)體內(nèi)容 ,可能會返回新的頭部元信息
301:客戶端請求的網(wǎng)頁已經(jīng)永久移動到新的位置,當(dāng)鏈接發(fā)生變化時,返回301代碼告訴客戶端鏈接的變化,客戶端保存新的鏈接,并向新的鏈接發(fā)出請求,已返回請求結(jié)果
404:請求失敗,客戶端請求的資源沒有找到或者是不存在
500:服務(wù)器遇到未知的錯誤,導(dǎo)致無法完成客戶端當(dāng)前的請求。
503:服務(wù)器由于臨時的服務(wù)器過載或者是維護(hù),無法解決當(dāng)前的請求,
17、事件循環(huán)機(jī)制EventLoop
Event Loop即事件循環(huán),是解決javaScript單線程運(yùn)行阻塞的一種機(jī)制。
首先我們需要知道JS 是??阻塞單線程語?,就是同一時間只能做一件事情
同步任務(wù)和異步任務(wù)
javascript是單線程。單線程就意味著,所有任務(wù)需要排隊(duì),前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)。如果前一個任務(wù)耗時很長,后一個任務(wù)就意味著等待。于是javascript所有任務(wù)分為兩種:同步任務(wù),異步任務(wù)
同步任務(wù):不需要等待可立即看到執(zhí)行結(jié)果,比如console、promise 的回調(diào)
異步任務(wù):異步任務(wù)需要等待一定的時候才能看到結(jié)果,比如setTimeout、setInterval、網(wǎng)絡(luò)請求(ajax)、script 腳本的執(zhí)行
異步任務(wù)分為:宏任務(wù)與微任務(wù)
宏任務(wù):script(整體代碼)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 環(huán)境)
微任務(wù):Promise、 MutaionObserver、process.nextTick(Node.js環(huán)境)
- 在執(zhí)行同步代碼的時候,如果遇到了異步事件,js 引擎并不會一直等待其返回結(jié)果,而是會將這個事件掛起,繼續(xù)執(zhí)行執(zhí)行棧中的其他任務(wù)
- 當(dāng)同步事件執(zhí)行完畢后,再將異步事件對應(yīng)的回調(diào)加入到與當(dāng)前執(zhí)行棧中,不同的另一個任務(wù)隊(duì)列中等待執(zhí)行。
- 任務(wù)隊(duì)列可以分為宏任務(wù)對列和微任務(wù)對列,當(dāng)當(dāng)前執(zhí)行棧中的事件執(zhí)行完畢后,js 引擎首先會判斷微任務(wù)對列中是否有任務(wù)可以執(zhí)行,如果有就將微任務(wù)隊(duì)列的事件壓入棧中執(zhí)行。
- 當(dāng)微任務(wù)對列中的任務(wù)都執(zhí)行完成后再去判斷宏任務(wù)對列中的任務(wù)。
執(zhí)行順序
1、先執(zhí)行主線程
2、遇到宏隊(duì)列,放到宏隊(duì)列
3、遇到微隊(duì)列,放到微隊(duì)列,
4、主線程執(zhí)行完畢
5、執(zhí)行微隊(duì)列,微隊(duì)列執(zhí)行完畢
6、執(zhí)行一次宏隊(duì)列,中的一個任務(wù),執(zhí)行完畢
7、執(zhí)行微隊(duì)列,執(zhí)行完畢
8、依次循環(huán)
上代碼:
setTimeout(function () {
console.log(1);
});
new Promise(function (resolve, reject) {
console.log(2);
resolve(3);
}).then(function (val) {
console.log(val);
});
console.log(4);
// 2431
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function(resolve, reject) {
console.log(2);
resolve()
}).then(function() {
console.log(3)
});
process.nextTick(function () {
console.log(4)
})
console.log(5)
//25431
1、主線程開始執(zhí)行,遇到setTimeout,將setTimeout的回調(diào)函數(shù)丟到宏任務(wù)隊(duì)列中
2、在往下執(zhí)行new Promise立即執(zhí)行,輸出2
3、.then的回調(diào)函數(shù)丟到微任務(wù)隊(duì)列中,再繼續(xù)執(zhí)行
4、遇到process.nextTick,同樣將回調(diào)函數(shù)扔到為任務(wù)隊(duì)列,再繼續(xù)執(zhí)行,輸出5
5、當(dāng)所有同步任務(wù)執(zhí)行完成后看有沒有可以執(zhí)行的微任務(wù)
6、因?yàn)閜rocess.nextTick指定的異步任務(wù)總是發(fā)生在所有異步任務(wù)之前,因此先執(zhí)行process.nextTick輸出4,然后執(zhí)行.then函數(shù)輸出3,第一輪執(zhí)行結(jié)束。
7、第二輪:從宏任務(wù)隊(duì)列開始,發(fā)現(xiàn)setTimeout回調(diào),輸出1執(zhí)行完畢,因此結(jié)果是25431