數(shù)據(jù)類型
數(shù)據(jù)類型
基本類型 Boolean腾供,object, number, null, undefined, string, symbol (es6)
Symbol值通過Symbol( )生成,只要屬性名是屬于Symbol類型的,就是獨一無二的佳魔,可以保證不會與其他屬性名產(chǎn)生沖突。
引用類型: 數(shù)組晦炊,函數(shù)鞠鲜,對象
判斷變量的類型
typeof 返回的是字符串宁脊,描述數(shù)據(jù)類型。需要注意的是贤姆,對于null榆苞,object,function, array都返回object霞捡,并不會明確告訴是哪一種對象
instanceof及原理 坐漏,判斷摸個對象是否是另一個對象的實例,返回布爾值
Object.prototype.toString.call() 判斷對象屬于哪種內(nèi)置類型
深拷貝 淺拷貝
淺拷貝拷貝的是內(nèi)存地址,兩者指向同一塊內(nèi)存空間碧信,改變其中一個對象的值赊琳,另一個對象的值也會隨之變化。Object.assign() 方法用于將所有可枚舉的屬性的值從一個或多個源對象復(fù)制到目標(biāo)對象砰碴。它將返回目標(biāo)對象
深拷貝是將一個對象從內(nèi)存中完整的拷貝出來躏筏,并開辟一個新的內(nèi)存空間儲存,修改器其中一個對象的值不會影響別的對象呈枉。JSON.parse(JSON.stringify())趁尼,對帶函數(shù)的對象只能用遞歸的方法實現(xiàn)
-
手寫深拷貝
<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ù)組還是對象
for(let keys in source){ // 遍歷目標(biāo)
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是對象,就遞歸一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是猖辫,就直接賦值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}</pre>
數(shù)據(jù)類型轉(zhuǎn)換
相等==和全等===
強制轉(zhuǎn)換和隱式轉(zhuǎn)換
作用域
變量提升
- js引擎會先對文件進行預(yù)編譯酥泞,其中一部分工作邊便是將函數(shù)和變量的聲明提升到其對應(yīng)作用域的頂部。全局變量提升到全局作用域頂部啃憎,函數(shù)內(nèi)聲明的變量提升到函數(shù)作用域的頂部婶博。其中變量會被先定義為undifined。es6中引用的塊作用域的概念荧飞。 let 和const不允許有這樣的操作,let 聲明以后不允許多次聲明同一名稱的變量名党,這樣避免了引起混亂叹阔。let只在let所在的代碼域內(nèi)有效,const聲明一個只讀的常量传睹,一旦聲明常量的值無法改變
閉包
閉包的形成:函數(shù)里面再定義一個函數(shù)耳幢,內(nèi)部函數(shù)可以訪問外部函數(shù)作用域的變量。如果外部函數(shù)不暴露這個內(nèi)部函數(shù)的話欧啤,外界就不知道這個內(nèi)部函數(shù)睛藻。這個內(nèi)部函數(shù)和函數(shù)所能訪問的變量的總和被稱為閉包。
閉包的作用:閉包常常用來隱藏變量邢隧,給內(nèi)部函數(shù)的變量設(shè)置里只讀屬性店印,外部只能返回他的值,而不能修改他的值倒慧,從而起到了保護作用按摘。但是閉包會造成某一塊內(nèi)存長期被占用包券,消耗內(nèi)存
作用域鏈
全局作用域 :
- 瀏覽器打開一個頁面時,會給js代碼提供一個全局的運行環(huán)境炫贤,這個環(huán)境就是全局作用域溅固。一個頁面只有一個全局作用域,這個全局作用域下有window對象兰珍,全局作用域下的變量和函數(shù)都會儲存在window下侍郭。
函數(shù)作用域:
- 函數(shù)作用域指聲明在函數(shù)內(nèi)部的變量,函數(shù)作用域包含在全局作用域下掠河,全局作用域下可以有多個函數(shù)作用域
塊級作用域 var/let/const
- 塊級作用域可通過let和const聲明亮元,所聲明的變量在指定塊的作用域外無法被訪問,在一個函數(shù)內(nèi)部或一個代碼塊內(nèi)部被創(chuàng)建口柳。const和let的作用域是一致的苹粟,不同的是const聲明一個只讀的常量,常量一旦聲明不允許改變 let的特點:1. 變量提升不會提升到代碼塊的頂部跃闹,所以let一定要先聲明再使用嵌削,需要手動放置到作用域頂部。2. var可以在同一個作用域內(nèi)重復(fù)聲明望艺,let禁止在相同的作用域內(nèi)重復(fù)聲明苛秕。
作用域鏈?zhǔn)鞘裁?/strong>
- 自由變量指當(dāng)前作用域中沒有定義的變量。 作用域鏈指由子作用域?qū)訉酉蚋缸饔糜蛑姓易杂米兞康年P(guān)系找默。
作用域和執(zhí)行上下文的區(qū)別
- javascript屬于解釋性語言艇劫,js的執(zhí)行分為兩個階段:解釋階段和執(zhí)行階段。作用域在解釋階段惩激,在函數(shù)定義時已經(jīng)確定店煞,但是執(zhí)行上下文在函數(shù)執(zhí)行之前才被創(chuàng)建。 執(zhí)行上下文可能隨時會變风钻,但時作用域在定義時就被確定顷蟀,不會變。同一個作用域下骡技,不同的調(diào)用會產(chǎn)生不同的執(zhí)行上下文鸣个。
原型
原型和原型鏈
舉例:描述構(gòu)造函數(shù)、實例和原型之間的關(guān)系
原型指將所有對象的公共屬性儲存到一個對象中布朦,然后讓每一個對象的protp記錄這個對象的地址囤萤,而這個公共屬性就是原型。
protp和constructor是對象的屬性是趴,prototype是函數(shù)獨有的屬性涛舍。 proto 是一個對象指向另一個對象,指向它的原型對象唆途。prototype是給其他對象提供共享屬性的對象做盅,所有的對象都可以作用另一個對象的prototype來用缤削。函數(shù)創(chuàng)建的對象.protp === 函數(shù).protptype。 constructor指向該對象的構(gòu)造函數(shù)吹榴。函數(shù).protptype.constructor === 該函數(shù)本身
原型減少了不必要的內(nèi)存消耗亭敢。
原型鏈指通過proto不斷向上(父類對象)查找原型對象的方式,要是找到Object的父類對象null, 還是沒找到想到的屬性或者方法則會返回undefined
js創(chuàng)建對象的幾種方式(常見)
工廠模式
構(gòu)造函數(shù)模式
原型模式
組合模式
寄生構(gòu)造模式
繼承
js如何實現(xiàn)繼承
js如何實現(xiàn)一個類
js實現(xiàn)繼承的多種方式
call/apply/bind
call图筹、apply和bind都用來改變this的指向
call和apply都是對函數(shù)直接的調(diào)用帅刀,bind()返回一個函數(shù),需要對函數(shù)進行調(diào)用
call和bind可以一個個傳入远剩,apply要以數(shù)組的格式傳參
new, this
new操作符
new的模擬實現(xiàn)
this對象的理解
this指向
this永遠指向運行時最后調(diào)用它的那個對象
在全局變量中this等價于window對象扣溺,var聲明變量賦值給window,未聲明的變量直接復(fù)制給window
在構(gòu)造函數(shù)中this指向new的對象
改變this的指向:箭頭函數(shù)瓜晤,call,bind,apply, new實例化一個對象
事件
DOM
- Document Object Model 文檔對象模型
DOM事件
- 事件是用戶或者瀏覽器執(zhí)行某種動作锥余,是文檔或者瀏覽器發(fā)生交互的一瞬間,不如點擊一下鼠標(biāo)(click)痢掠。html和js之間的交互是通過事件實現(xiàn)的驱犹。
DOM事件流
- DOM是一個樹型結(jié)構(gòu),當(dāng)html產(chǎn)生事件時足画,該事件就會在dom樹中的根節(jié)點和元素節(jié)點之間傳播雄驹,這種事件的傳遞被稱為事件流。
DOM diff
用一個對象來描述DOM樹即為虛擬DOM淹辞。
比對(diff算法)虛擬DOM和真實DOM的差異医舆,產(chǎn)生差異補丁對象,再將差異補丁對象應(yīng)用到真實的dom節(jié)點上象缀。因為操作dom的代價是昂貴的蔬将,虛擬dom將多次更新的diff內(nèi)容保存到一個對象中,這樣可以盡可能地減少dom操作央星。
事件的三個階段
捕獲階段霞怀、目標(biāo)階段、冒泡階段
在DOM標(biāo)準(zhǔn)事件模型中等曼,是先捕獲后冒泡。但是如果要實現(xiàn)先冒泡后捕獲的效果凿蒜, 對于同一個事件禁谦,監(jiān)聽捕獲和冒泡,分別對應(yīng)相應(yīng)的處理函數(shù)废封,監(jiān)聽到捕獲事件州泊,先暫緩執(zhí)行,直到冒泡事件被捕獲后再執(zhí)行捕獲事件漂洋。
Event loop
進程與線程
- 線程是一個程序執(zhí)行的最小單位遥皂,一個進程可由一個或多個線程組成力喷。不同的進程互相獨立,但是不同的線程之間共享內(nèi)存空間演训。
執(zhí)行棧
- 任務(wù)進入執(zhí)行棧后分為同步任務(wù)和異步任務(wù)弟孟。同步任務(wù)進入主線程開始執(zhí)行。異步任務(wù)進入Event Table并注冊函數(shù)样悟,然后Event Table會將這個函數(shù)移入Event Queue拂募。當(dāng)主線程任務(wù)執(zhí)行完畢,會去Event Queue調(diào)取對應(yīng)的函數(shù)并放入主線程執(zhí)行窟她。上述過程不斷重復(fù)就是事件循環(huán)陈症。
為什么js是單線程不是多線程
- 因為作為瀏覽器的腳本語言,js主要功能是與用戶交互和操作DOM震糖,多線程會帶來很多問題录肯,比如在其中一個線程修改了DOM樹,另一個線程不變吊说,瀏覽器不知道以哪個線程為準(zhǔn)论咏。
微任務(wù)/宏任務(wù)
宏任務(wù) js代碼、setTimeout疏叨、setInterval潘靖、setImmediate
微任務(wù) Promise
事件循環(huán)的順序:進入宏任務(wù)后開始第一次循環(huán),接著執(zhí)行所有的微任務(wù)蚤蔓。然后再次從宏任務(wù)開始卦溢。
瀏覽器與node.js的事件循環(huán)
-
process.nextTick(callback)
類似node.js版的"setTimeout",在事件循環(huán)的下一次循環(huán)中調(diào)用 callback 回調(diào)函數(shù)秀又。
Promise
Promise的基本用法
他是ES6中新增加的一個類, 目的是為了管理JS中的異步編程的单寂。
Promise 有三個狀態(tài):pending(準(zhǔn)備狀態(tài):初始化成功、開始執(zhí)行異步的任務(wù))吐辙、fullfilled(成功狀態(tài))宣决、rejected(失敗狀態(tài))。pending是初始狀態(tài)昏苏,可以轉(zhuǎn)化為fullfilled和rejected尊沸,轉(zhuǎn)化后狀態(tài)不可改變。
Promise函數(shù)天生有兩個參數(shù)贤惯,resolve(當(dāng)異步操作執(zhí)行成功洼专,執(zhí)行resolve方法),rejected(當(dāng)異步操作失敗,執(zhí)行reject方法) then()方法中有兩個函數(shù)onFulfilled和onRejected孵构,第一個傳遞的函數(shù)是resolve,第二個傳遞的函數(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)建一個對象雾袱,創(chuàng)建一個異步調(diào)用的對象
xhr.open('get', url, true) // 設(shè)置一個http請求,設(shè)置請求的方式官还,url以及驗證身份
xhr.send() //發(fā)送一個http請求
xhr.onreadystatechange = function () { //設(shè)置一個http請求狀態(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 - (未初始化)還沒有調(diào)用send()方法
1 - (載入)已調(diào)用send方法芹橡,正在發(fā)送請求
2 - (載入完成呢)send()方法執(zhí)行完成
3 - (交互)正在解析相應(yīng)內(nèi)容
4 - (完成)響應(yīng)內(nèi)容解析完成,可以在客戶端調(diào)用了
?
?</pre>減少http請求數(shù)妻枕,合理設(shè)置http緩存
合并css圖片僻族,壓縮資源
多圖片的頁面使用懶加載
減少閉包的使用
減少對dom的操作
使用svg圖片而不是png圖片
前端有哪些頁面優(yōu)化的方法
Set本身結(jié)構(gòu)類似于數(shù)組,但成員的值唯一屡谐。Set利用set構(gòu)造函數(shù)生成set數(shù)據(jù)結(jié)構(gòu)述么,常用來數(shù)組去重。add 添加元素愕掏,delete 刪除元素度秘,has 判斷元素,clear 清除所有元素
JavaScript 的對象(Object)饵撑,本質(zhì)上是鍵值對的集合(Hash 結(jié)構(gòu))剑梳,但是傳統(tǒng)上只能用字符串當(dāng)作鍵。這給它的使用帶來了很大的限制滑潘。為了解決這個問題垢乙,ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu),使各種類型的值(包括對象)都可以當(dāng)作鍵语卤。也就是說追逮,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對應(yīng)粹舵,是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)钮孵。keys()返回所有鍵名的遍歷器,values()返回所有鍵值得遍歷器眼滤,entries()返回所有成員的遍歷器巴席,forEach()返回map中所有成員
Map與Set
es6 module
Promise
它的語法標(biāo)志是 ...
以數(shù)組為例,擴展運算符可以拆開這個數(shù)組诅需,變成一個元素集合漾唉。 擴展剩余運算符和擴展運算符的區(qū)別就是,剩余運算符會收集這些集合堰塌,放到數(shù)組中赵刑。
剩余/擴展運算符
箭頭函數(shù)
let/const
es6
寫一個簡單的ajax請求
缺點 1.無法使用回退按鈕 2.不利于網(wǎng)頁的SEO 3.不能發(fā)送跨域請求
ajax的優(yōu)勢 1.無刷新頁面請求,使產(chǎn)品更快蔫仙,更小更友好 2.服務(wù)器端的任務(wù)轉(zhuǎn)嫁到客戶端處理 3.減輕瀏覽器負擔(dān)料睛,節(jié)約帶寬 4.基于標(biāo)準(zhǔn)化對象丐箩,不需要安裝特定的插件 5.徹底將頁面與數(shù)據(jù)分離
創(chuàng)建一個ajax對象 摇邦,使用XMLHttpRequest這個函數(shù)創(chuàng)建一個實例對象恤煞。
告訴ajax對象要發(fā)送的請求地址以及請求方式 ,調(diào)用xhr下面的open方法
發(fā)送請求施籍,使用send方法居扒。
獲取服務(wù)器響將要響應(yīng)到客戶端的數(shù)據(jù),使用xhr下的resopnsText屬性就是服務(wù)器響應(yīng)給客戶端的數(shù)據(jù)丑慎。
AJAX實現(xiàn)
AJAX
async await
Promise.all
promise.race