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