數(shù)據(jù)類(lèi)型
數(shù)據(jù)類(lèi)型
基本類(lèi)型 Boolean,object, number, null, undefined, string, symbol (es6)
Symbol值通過(guò)Symbol( )生成,只要屬性名是屬于Symbol類(lèi)型的,就是獨(dú)一無(wú)二的,可以保證不會(huì)與其他屬性名產(chǎn)生沖突。
引用類(lèi)型: 數(shù)組,函數(shù),對(duì)象
判斷變量的類(lèi)型
typeof 返回的是字符串,描述數(shù)據(jù)類(lèi)型。需要注意的是,對(duì)于null,object,function, array都返回object,并不會(huì)明確告訴是哪一種對(duì)象
instanceof及原理 ,判斷摸個(gè)對(duì)象是否是另一個(gè)對(duì)象的實(shí)例,返回布爾值
Object.prototype.toString.call() 判斷對(duì)象屬于哪種內(nèi)置類(lèi)型
深拷貝 淺拷貝
淺拷貝拷貝的是內(nèi)存地址,兩者指向同一塊內(nèi)存空間,改變其中一個(gè)對(duì)象的值,另一個(gè)對(duì)象的值也會(huì)隨之變化。Object.assign() 方法用于將所有可枚舉的屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。它將返回目標(biāo)對(duì)象
深拷貝是將一個(gè)對(duì)象從內(nèi)存中完整的拷貝出來(lái),并開(kāi)辟一個(gè)新的內(nèi)存空間儲(chǔ)存,修改器其中一個(gè)對(duì)象的值不會(huì)影響別的對(duì)象。JSON.parse(JSON.stringify()),對(duì)帶函數(shù)的對(duì)象只能用遞歸的方法實(shí)現(xiàn)
-
手寫(xiě)深拷貝
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="js" cid="n26" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判斷復(fù)制的目標(biāo)是數(shù)組還是對(duì)象
for(let keys in source){ // 遍歷目標(biāo)
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是對(duì)象,就遞歸一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接賦值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}</pre>
數(shù)據(jù)類(lèi)型轉(zhuǎn)換
相等==和全等===
強(qiáng)制轉(zhuǎn)換和隱式轉(zhuǎn)換
作用域
變量提升
- js引擎會(huì)先對(duì)文件進(jìn)行預(yù)編譯,其中一部分工作邊便是將函數(shù)和變量的聲明提升到其對(duì)應(yīng)作用域的頂部。全局變量提升到全局作用域頂部,函數(shù)內(nèi)聲明的變量提升到函數(shù)作用域的頂部。其中變量會(huì)被先定義為undifined。es6中引用的塊作用域的概念。 let 和const不允許有這樣的操作,let 聲明以后不允許多次聲明同一名稱的變量,這樣避免了引起混亂。let只在let所在的代碼域內(nèi)有效,const聲明一個(gè)只讀的常量,一旦聲明常量的值無(wú)法改變
閉包
閉包的形成:函數(shù)里面再定義一個(gè)函數(shù),內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)作用域的變量。如果外部函數(shù)不暴露這個(gè)內(nèi)部函數(shù)的話,外界就不知道這個(gè)內(nèi)部函數(shù)。這個(gè)內(nèi)部函數(shù)和函數(shù)所能訪問(wèn)的變量的總和被稱為閉包。
閉包的作用:閉包常常用來(lái)隱藏變量,給內(nèi)部函數(shù)的變量設(shè)置里只讀屬性,外部只能返回他的值,而不能修改他的值,從而起到了保護(hù)作用。但是閉包會(huì)造成某一塊內(nèi)存長(zhǎng)期被占用,消耗內(nèi)存
作用域鏈
全局作用域 :
- 瀏覽器打開(kāi)一個(gè)頁(yè)面時(shí),會(huì)給js代碼提供一個(gè)全局的運(yùn)行環(huán)境,這個(gè)環(huán)境就是全局作用域。一個(gè)頁(yè)面只有一個(gè)全局作用域,這個(gè)全局作用域下有window對(duì)象,全局作用域下的變量和函數(shù)都會(huì)儲(chǔ)存在window下。
函數(shù)作用域:
- 函數(shù)作用域指聲明在函數(shù)內(nèi)部的變量,函數(shù)作用域包含在全局作用域下,全局作用域下可以有多個(gè)函數(shù)作用域
塊級(jí)作用域 var/let/const
- 塊級(jí)作用域可通過(guò)let和const聲明,所聲明的變量在指定塊的作用域外無(wú)法被訪問(wèn),在一個(gè)函數(shù)內(nèi)部或一個(gè)代碼塊內(nèi)部被創(chuàng)建。const和let的作用域是一致的,不同的是const聲明一個(gè)只讀的常量,常量一旦聲明不允許改變 let的特點(diǎn):1. 變量提升不會(huì)提升到代碼塊的頂部,所以let一定要先聲明再使用,需要手動(dòng)放置到作用域頂部。2. var可以在同一個(gè)作用域內(nèi)重復(fù)聲明,let禁止在相同的作用域內(nèi)重復(fù)聲明。
作用域鏈?zhǔn)鞘裁?/strong>
- 自由變量指當(dāng)前作用域中沒(méi)有定義的變量。 作用域鏈指由子作用域?qū)訉酉蚋缸饔糜蛑姓易杂米兞康年P(guān)系。
作用域和執(zhí)行上下文的區(qū)別
- javascript屬于解釋性語(yǔ)言,js的執(zhí)行分為兩個(gè)階段:解釋階段和執(zhí)行階段。作用域在解釋階段,在函數(shù)定義時(shí)已經(jīng)確定,但是執(zhí)行上下文在函數(shù)執(zhí)行之前才被創(chuàng)建。 執(zhí)行上下文可能隨時(shí)會(huì)變,但時(shí)作用域在定義時(shí)就被確定,不會(huì)變。同一個(gè)作用域下,不同的調(diào)用會(huì)產(chǎn)生不同的執(zhí)行上下文。
原型
原型和原型鏈
舉例:描述構(gòu)造函數(shù)、實(shí)例和原型之間的關(guān)系
原型指將所有對(duì)象的公共屬性儲(chǔ)存到一個(gè)對(duì)象中,然后讓每一個(gè)對(duì)象的protp記錄這個(gè)對(duì)象的地址,而這個(gè)公共屬性就是原型。
protp和constructor是對(duì)象的屬性,prototype是函數(shù)獨(dú)有的屬性。 proto 是一個(gè)對(duì)象指向另一個(gè)對(duì)象,指向它的原型對(duì)象。prototype是給其他對(duì)象提供共享屬性的對(duì)象,所有的對(duì)象都可以作用另一個(gè)對(duì)象的prototype來(lái)用。函數(shù)創(chuàng)建的對(duì)象.protp === 函數(shù).protptype。 constructor指向該對(duì)象的構(gòu)造函數(shù)。函數(shù).protptype.constructor === 該函數(shù)本身
原型減少了不必要的內(nèi)存消耗。
原型鏈指通過(guò)proto不斷向上(父類(lèi)對(duì)象)查找原型對(duì)象的方式,要是找到Object的父類(lèi)對(duì)象null, 還是沒(méi)找到想到的屬性或者方法則會(huì)返回undefined
js創(chuàng)建對(duì)象的幾種方式(常見(jiàn))
工廠模式
構(gòu)造函數(shù)模式
原型模式
組合模式
寄生構(gòu)造模式
繼承
js如何實(shí)現(xiàn)繼承
js如何實(shí)現(xiàn)一個(gè)類(lèi)
js實(shí)現(xiàn)繼承的多種方式
call/apply/bind
call、apply和bind都用來(lái)改變this的指向
call和apply都是對(duì)函數(shù)直接的調(diào)用,bind()返回一個(gè)函數(shù),需要對(duì)函數(shù)進(jìn)行調(diào)用
call和bind可以一個(gè)個(gè)傳入,apply要以數(shù)組的格式傳參
new, this
new操作符
new的模擬實(shí)現(xiàn)
this對(duì)象的理解
this指向
this永遠(yuǎn)指向運(yùn)行時(shí)最后調(diào)用它的那個(gè)對(duì)象
在全局變量中this等價(jià)于window對(duì)象,var聲明變量賦值給window,未聲明的變量直接復(fù)制給window
在構(gòu)造函數(shù)中this指向new的對(duì)象
改變this的指向:箭頭函數(shù),call,bind,apply, new實(shí)例化一個(gè)對(duì)象
事件
DOM
- Document Object Model 文檔對(duì)象模型
DOM事件
- 事件是用戶或者瀏覽器執(zhí)行某種動(dòng)作,是文檔或者瀏覽器發(fā)生交互的一瞬間,不如點(diǎn)擊一下鼠標(biāo)(click)。html和js之間的交互是通過(guò)事件實(shí)現(xiàn)的。
DOM事件流
- DOM是一個(gè)樹(shù)型結(jié)構(gòu),當(dāng)html產(chǎn)生事件時(shí),該事件就會(huì)在dom樹(shù)中的根節(jié)點(diǎn)和元素節(jié)點(diǎn)之間傳播,這種事件的傳遞被稱為事件流。
DOM diff
用一個(gè)對(duì)象來(lái)描述DOM樹(shù)即為虛擬DOM。
比對(duì)(diff算法)虛擬DOM和真實(shí)DOM的差異,產(chǎn)生差異補(bǔ)丁對(duì)象,再將差異補(bǔ)丁對(duì)象應(yīng)用到真實(shí)的dom節(jié)點(diǎn)上。因?yàn)椴僮鱠om的代價(jià)是昂貴的,虛擬dom將多次更新的diff內(nèi)容保存到一個(gè)對(duì)象中,這樣可以盡可能地減少dom操作。
事件的三個(gè)階段
捕獲階段、目標(biāo)階段、冒泡階段
在DOM標(biāo)準(zhǔn)事件模型中,是先捕獲后冒泡。但是如果要實(shí)現(xiàn)先冒泡后捕獲的效果, 對(duì)于同一個(gè)事件,監(jiān)聽(tīng)捕獲和冒泡,分別對(duì)應(yīng)相應(yīng)的處理函數(shù),監(jiān)聽(tīng)到捕獲事件,先暫緩執(zhí)行,直到冒泡事件被捕獲后再執(zhí)行捕獲事件。
Event loop
進(jìn)程與線程
- 線程是一個(gè)程序執(zhí)行的最小單位,一個(gè)進(jìn)程可由一個(gè)或多個(gè)線程組成。不同的進(jìn)程互相獨(dú)立,但是不同的線程之間共享內(nèi)存空間。
執(zhí)行棧
- 任務(wù)進(jìn)入執(zhí)行棧后分為同步任務(wù)和異步任務(wù)。同步任務(wù)進(jìn)入主線程開(kāi)始執(zhí)行。異步任務(wù)進(jìn)入Event Table并注冊(cè)函數(shù),然后Event Table會(huì)將這個(gè)函數(shù)移入Event Queue。當(dāng)主線程任務(wù)執(zhí)行完畢,會(huì)去Event Queue調(diào)取對(duì)應(yīng)的函數(shù)并放入主線程執(zhí)行。上述過(guò)程不斷重復(fù)就是事件循環(huán)。
為什么js是單線程不是多線程
- 因?yàn)樽鳛闉g覽器的腳本語(yǔ)言,js主要功能是與用戶交互和操作DOM,多線程會(huì)帶來(lái)很多問(wèn)題,比如在其中一個(gè)線程修改了DOM樹(shù),另一個(gè)線程不變,瀏覽器不知道以哪個(gè)線程為準(zhǔn)。
微任務(wù)/宏任務(wù)
宏任務(wù) js代碼、setTimeout、setInterval、setImmediate
微任務(wù) Promise
事件循環(huán)的順序:進(jìn)入宏任務(wù)后開(kāi)始第一次循環(huán),接著執(zhí)行所有的微任務(wù)。然后再次從宏任務(wù)開(kāi)始。
瀏覽器與node.js的事件循環(huán)
-
process.nextTick(callback)類(lèi)似node.js版的"setTimeout",在事件循環(huán)的下一次循環(huán)中調(diào)用 callback 回調(diào)函數(shù)。
Promise
Promise的基本用法
他是ES6中新增加的一個(gè)類(lèi), 目的是為了管理JS中的異步編程的。
Promise 有三個(gè)狀態(tài):pending(準(zhǔn)備狀態(tài):初始化成功、開(kāi)始執(zhí)行異步的任務(wù))、fullfilled(成功狀態(tài))、rejected(失敗狀態(tài))。pending是初始狀態(tài),可以轉(zhuǎn)化為fullfilled和rejected,轉(zhuǎn)化后狀態(tài)不可改變。
Promise函數(shù)天生有兩個(gè)參數(shù),resolve(當(dāng)異步操作執(zhí)行成功,執(zhí)行resolve方法),rejected(當(dāng)異步操作失敗,執(zhí)行reject方法) then()方法中有兩個(gè)函數(shù)onFulfilled和onRejected,第一個(gè)傳遞的函數(shù)是resolve,第二個(gè)傳遞的函數(shù)是reject。
-
https://juejin.cn/post/6844903625769091079#heading-9
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="js" cid="n185" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
return promise2;
}
catch(fn){
return this.then(null,fn);
}
}
function resolvePromise(promise2, x, resolve, reject){
if(x === promise2){
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if(called)return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if(called)return;
called = true;
reject(err);
})
} else {
resolve(x);
}
} catch (e) {
if(called)return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
//resolve方法
Promise.resolve = function(val){
return new Promise((resolve,reject)=>{
resolve(val)
});
}
//reject方法
Promise.reject = function(val){
return new Promise((resolve,reject)=>{
reject(val)
});
}</pre><pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="js" cid="n187" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">//race方法
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
};
})
}
?</pre><pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="js" cid="n190" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">//all方法(獲取所有的promise,都執(zhí)行then,把結(jié)果放到數(shù)組,一起返回)
Promise.all = function(promises){
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
i++;
if(i == promises.length){
resolve(arr);
};
};
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(data=>{
processData(i,data);
},reject);
};
});
}</pre><pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="js" cid="n208" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">function getData(url) {
var xhr = new XMLHttpRequest(); // 創(chuàng)建一個(gè)對(duì)象,創(chuàng)建一個(gè)異步調(diào)用的對(duì)象
xhr.open('get', url, true) // 設(shè)置一個(gè)http請(qǐng)求,設(shè)置請(qǐng)求的方式,url以及驗(yàn)證身份
xhr.send() //發(fā)送一個(gè)http請(qǐng)求
xhr.onreadystatechange = function () { //設(shè)置一個(gè)http請(qǐng)求狀態(tài)的函數(shù)
if (xhr.readyState == 4 && xhr.status ==200) {
console.log(xhr.responseText) // 獲取異步調(diào)用返回的數(shù)據(jù)
}
}
}
Promise(getData(url)).resolve(data => data)
?
AJAX狀態(tài)碼:0 - (未初始化)還沒(méi)有調(diào)用send()方法
1 - (載入)已調(diào)用send方法,正在發(fā)送請(qǐng)求
2 - (載入完成呢)send()方法執(zhí)行完成
3 - (交互)正在解析相應(yīng)內(nèi)容
4 - (完成)響應(yīng)內(nèi)容解析完成,可以在客戶端調(diào)用了
?
?</pre>減少http請(qǐng)求數(shù),合理設(shè)置http緩存
合并css圖片,壓縮資源
多圖片的頁(yè)面使用懶加載
減少閉包的使用
減少對(duì)dom的操作
使用svg圖片而不是png圖片
前端有哪些頁(yè)面優(yōu)化的方法
Set本身結(jié)構(gòu)類(lèi)似于數(shù)組,但成員的值唯一。Set利用set構(gòu)造函數(shù)生成set數(shù)據(jù)結(jié)構(gòu),常用來(lái)數(shù)組去重。add 添加元素,delete 刪除元素,has 判斷元素,clear 清除所有元素
JavaScript 的對(duì)象(Object),本質(zhì)上是鍵值對(duì)的集合(Hash 結(jié)構(gòu)),但是傳統(tǒng)上只能用字符串當(dāng)作鍵。這給它的使用帶來(lái)了很大的限制。為了解決這個(gè)問(wèn)題,ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu),使各種類(lèi)型的值(包括對(duì)象)都可以當(dāng)作鍵。也就是說(shuō),Object 結(jié)構(gòu)提供了“字符串—值”的對(duì)應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對(duì)應(yīng),是一種更完善的 Hash 結(jié)構(gòu)實(shí)現(xiàn)。keys()返回所有鍵名的遍歷器,values()返回所有鍵值得遍歷器,entries()返回所有成員的遍歷器,forEach()返回map中所有成員
Map與Set
es6 module
Promise
它的語(yǔ)法標(biāo)志是 ...
以數(shù)組為例,擴(kuò)展運(yùn)算符可以拆開(kāi)這個(gè)數(shù)組,變成一個(gè)元素集合。 擴(kuò)展剩余運(yùn)算符和擴(kuò)展運(yùn)算符的區(qū)別就是,剩余運(yùn)算符會(huì)收集這些集合,放到數(shù)組中。
剩余/擴(kuò)展運(yùn)算符
箭頭函數(shù)
let/const
es6
寫(xiě)一個(gè)簡(jiǎn)單的ajax請(qǐng)求
缺點(diǎn) 1.無(wú)法使用回退按鈕 2.不利于網(wǎng)頁(yè)的SEO 3.不能發(fā)送跨域請(qǐng)求
ajax的優(yōu)勢(shì) 1.無(wú)刷新頁(yè)面請(qǐng)求,使產(chǎn)品更快,更小更友好 2.服務(wù)器端的任務(wù)轉(zhuǎn)嫁到客戶端處理 3.減輕瀏覽器負(fù)擔(dān),節(jié)約帶寬 4.基于標(biāo)準(zhǔn)化對(duì)象,不需要安裝特定的插件 5.徹底將頁(yè)面與數(shù)據(jù)分離
創(chuàng)建一個(gè)ajax對(duì)象 ,使用XMLHttpRequest這個(gè)函數(shù)創(chuàng)建一個(gè)實(shí)例對(duì)象。
告訴ajax對(duì)象要發(fā)送的請(qǐng)求地址以及請(qǐng)求方式 ,調(diào)用xhr下面的open方法
發(fā)送請(qǐng)求,使用send方法。
獲取服務(wù)器響將要響應(yīng)到客戶端的數(shù)據(jù),使用xhr下的resopnsText屬性就是服務(wù)器響應(yīng)給客戶端的數(shù)據(jù)。
AJAX實(shí)現(xiàn)
AJAX
async await
Promise.all
promise.race