JavaScript規(guī)定了幾種語言類型
基本類型(null,undefined,string,number,Boolean,symbol(符號,標記))
引用類型(object,Array,F(xiàn)unction)
javaScript中的變量在內(nèi)存中的具體存儲形式(理解值類型和引用類型)
變量主要分為兩種類型,基本類型和引用類型
基本類型在內(nèi)存中占據(jù)的空間大小是一定的,所以保存在棧內(nèi)存中
從一個變量向另一個變量復制基本來下的值 會創(chuàng)建這個值得一個副本。
引用類型保存在堆內(nèi)存中
包含引用類型值得變量實際上包含的并不是對象本身 而是一個指向該對象的指針。
從一個變量向另一個變量復制引用類型的值 復制的其實是指針 因此兩個變量最終都指向同一個對象
javascript為什么使用單線程
防止dom渲染沖突問題
描述:javascript主要的任務是處理用戶的交互,而用戶的交互無非就是響應DOM的增刪改,
如果是多線程的話,用戶一邊對dom進行刪除,一邊對dom進行修改,渲染就會有沖突
可以使用web worker實現(xiàn)多線程
什么是任務隊列
任務隊列"是一個先進先出的數(shù)據(jù)結(jié)構(gòu),排在前面的事件,優(yōu)先被主線程讀取。
主線程的讀取過程基本上是自動的,只要執(zhí)行棧一清空,"任務隊列"上第一位的事件就自動進入主線程。
同步和異步任務
同步任務指的是,在主線程上排隊執(zhí)行的任務,只有前一個任務執(zhí)行完畢,才能執(zhí)行后一個任務;
異步任務指的是,不進入主線程、而進入"任務隊列"的任務,只有"任務隊列"通知主線程,某個異步任務可以執(zhí)行了,該任務才會進入主線程執(zhí)行
js運行機制
- js運行會分為同步任務和異步任務
- 所有同步任務都在主線程上執(zhí)行,形成一個執(zhí)行棧。
- 主線程之外,還存在一個"任務隊列"。只要異步任務有了運行結(jié)果,就在"任務隊列"之中放置一個事件。
- 一旦"執(zhí)行棧"中的所有同步任務執(zhí)行完畢,系統(tǒng)就會讀取"任務隊列",看看里面有哪些事件。那些對應的異步任務,于是結(jié)束等待狀態(tài),進入執(zhí)行棧,開始執(zhí)行。
主線程不斷重復上面的第三步。
主線程從"任務隊列"中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為Event Loop(事件循環(huán))。
js引擎在一次事件循環(huán)中,會先執(zhí)行js線程的主任務,然后會去查找是否有微任務microtask(promise),如果有那就優(yōu)先執(zhí)行微任務,如果沒有,在去查找宏任務macrotask(setTimeout、setInterval)進行執(zhí)行。
宏任務 or 微任務
這里需要注意的是new Promise是會進入到主線程中立刻執(zhí)行,而promise.then則屬于微任務
宏任務(macro-task):整體代碼script、setTimeOut、setInterval
微任務(mincro-task):promise.then、promise.nextTick(node)
null和undefined的區(qū)別
null字面表示的是空值,如何要釋放一個空對象的話,將變量設置為null,表示對象已經(jīng)被清空,垃圾回收機制可以被收回
underfined字面表示的是沒有被定義的,主要有以下幾種情況
(1)聲明了變量,但沒有給變量賦值
(2)訪問對象中不存在的屬性
(3)定義的形參,但沒有傳實參
(4)使用viod對表達式求值
window.onload和DOMContentLoded區(qū)別
DOM完整的解析過程
1、解析HTML結(jié)構(gòu)。
2、加載外部腳本和樣式表文件。
3、解析并執(zhí)行腳本代碼。//js之類的
4、DOM樹構(gòu)建完成。//DOMContentLoaded事件會被觸發(fā)
5、加載圖片等外部文件。
6、頁面加載完畢。//load事件會被觸發(fā)
1、當 onload事件觸發(fā)時,頁面上所有的DOM,樣式表,腳本,圖片,flash都已經(jīng)加載完成了。
2、當 DOMContentLoaded 事件觸發(fā)時,僅當DOM加載完成,不包括樣式表,圖片,flash。
至少可以說出三種判斷JavaScript數(shù)據(jù)類型的方式,以及他們的優(yōu)缺點,如何準確的判斷數(shù)組類型
typeof(一般判斷基本類型)(null返回object,function返回function,其他的基本類型,返回正常的結(jié)果,其他的引用類型,返回的都是object)
instanceeOf(后面一定是對象類型,檢測的是原型)(instanceof 只能用來判斷兩個對象是否屬于實例關(guān)系, 而不能判斷一個對象實例具體屬于哪種類型。)

constructor(類繼承中會發(fā)生錯誤)
Object.prototype.toString.call() 適合所有的類型
熟練應用map、reduce、filter等高階函數(shù)解決問題
arr.forEach(callback) 遍歷數(shù)組,無return 即使有return,也不會返回任何值,并且會影響原來的數(shù)組
arr.map(callback) 映射數(shù)組(遍歷數(shù)組),有return 返回一個新數(shù)組 。
arr.filter(callback) 過濾數(shù)組,返回一個滿足要求的數(shù)組
arr.every(callback) 依據(jù)判斷條件,數(shù)組的元素是否全滿足,若滿足則返回ture
arr.some(callback) 依據(jù)判斷條件,數(shù)組的元素是否有一個滿足,若有一個滿足則返回ture
arr.reduce(callback, initialValue) 迭代數(shù)組的所有項,累加器,數(shù)組中的每個值(從左到右)合并,最終計算為一個值
callback的參數(shù): value --當前索引的值
index --索引
array --原數(shù)組(只有forEach和map)
arr.indexOf() 查找某個元素的索引值,若有重復的,則返回第一個查到的索引值若不存在,則返回 -1
arr.lastIndexOf() 和arr.indexOf()的功能一樣,不同的是從后往前查找
Array.from() 將偽數(shù)組變成數(shù)組,就是只要有l(wèi)ength的就可以轉(zhuǎn)成數(shù)組。
Array.of() 將一組值轉(zhuǎn)換成數(shù)組,類似于聲明數(shù)組
arr.find(callback) 找到第一個符合條件的數(shù)組成員
arr.findIndex(callback) 找到第一個符合條件的數(shù)組成員的索引值
arr.includes() 判斷數(shù)中是否包含給定的值
arr.keys() 遍歷數(shù)組的鍵
arr.values() 遍歷數(shù)組鍵值
理解原型設計模式以及JavaScript中的原型規(guī)則
無論什么時候,只要創(chuàng)建了一個新函數(shù),就會根據(jù)特定的規(guī)則為該函數(shù)創(chuàng)建一個 prototype 屬性,這個屬性指向函數(shù)的原型對象(構(gòu)造函數(shù)原型)。
當你對構(gòu)造函數(shù)new出一個實例時候,這個實例會有一個屬性proto,它指向的是構(gòu)造函數(shù)的prototype,就是指向所謂的自己原型對象
在默認情況下,所有原型對象都會自動獲得一個 constructor (構(gòu)造函數(shù))屬性,這個屬性包含一個指向 prototype 屬性所在函數(shù)的指針
prototype
People--------------------> People.prototype(構(gòu)造函數(shù)的原型)
↑
xiaoming --------- proto(原型對象)
xiaoming.constructor === People.prototype.constructor
People.prototype.constructor === People
proto是神器,有原型鏈查找功能,當xiaoli身上沒有某個屬性的時候,系統(tǒng)會沿著proto去尋找它的原型對象上有沒有這個屬性
instanceof的底層實現(xiàn)原理,手動實現(xiàn)一個instanceof
instanceof主要用于判斷某個實例是否屬于某個類型,也可用于判斷某個實例是否是其父類型或者祖先類型的實例。
instanceof 主要的實現(xiàn)原理就是只要右邊變量的 prototype 在左邊變量的原型鏈上即可。因此,instanceof 在查找的過程中會遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果查找失敗,則會返回 false。
function food(name,color,size) {
this.name = name;
this.color = color;
this.size = size;
}
var apple = new food('redApple', 'RED', 'small');
console.log(apple instanceof food);
function instance_of(L, R) {//L 表示左表達式,R 表示右表達式
var O = R.prototype;
L = L.__proto__;
while (true) {
if (L === null)
return false;
if (O === L) // 這里重點:當 O 嚴格等于 L 時,返回 true
return true;
L = L.__proto__;
}
}
//此方法用來判斷繼承
function instance_of(L, R) {//L 表示左表達式,R 表示右表達式
var O = R;
L = L.__proto__;
while (true) {
if (L === null)
return false;
if (O === L.constructor) // 這里重點:當 O 嚴格等于 L 時,返回 true
return true;
L = L.__proto__;
}
}
實現(xiàn)繼承的幾種方式以及他們的優(yōu)缺點
1.原型鏈繼承(核心: 將父類的實例作為子類的原型)
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
缺點:構(gòu)造函數(shù)原型上的屬性在所有該構(gòu)造函數(shù)構(gòu)造的實例上是共享的,即屬性沒有私有化,原型上屬性的改變會作用到所有的實例上。
2.構(gòu)造繼承(核心:使用父類的構(gòu)造函數(shù)來增強子類實例,等于是復制父類的實例屬性給子類(沒用到原型))
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
優(yōu)缺點:實現(xiàn)了屬性的私有化,但是子類無法訪問父類原型上的屬性。
3.組合繼承(核心:通過調(diào)用父類構(gòu)造,繼承父類的屬性并保留傳參的優(yōu)點,然后通過將父類實例作為子類原型,實現(xiàn)函數(shù)復用)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
描述new一個對象的詳細過程,手動實現(xiàn)一個new操作符
(new過程中會新建對象,此對象會繼承構(gòu)造器的原型與原型上的屬性,最后它會被作為實例返回這樣一個過程。)
? 創(chuàng)建一個空對象,將它的引用賦給 this,繼承函數(shù)的原型。
? 通過 this 將屬性和方法添加至這個對象
? 最后返回 this 指向的新對象,也就是實例(如果沒有手動返回其他的對象)
// 構(gòu)造器函數(shù)
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定義的new方法
let newMethod = function (Parent, ...rest) {
// 1.以構(gòu)造器的prototype屬性為原型,創(chuàng)建新對象;
let child = Object.create(Parent.prototype);
// 2.將this和調(diào)用參數(shù)傳給構(gòu)造器執(zhí)行
Parent.apply(child, rest);
// 3.返回第一步的對象
return child;
};
//創(chuàng)建實例,將構(gòu)造函數(shù)Parent與形參作為參數(shù)傳入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';
詞法作用域和動態(tài)作用域
詞法作用域是在寫代碼或者說定義時確定的,而動態(tài)作用域是在運行時確定的。
詞法作用域關(guān)注函數(shù)在何處聲明,而動態(tài)作用域關(guān)注函數(shù)從何處調(diào)用。
JavaScript的作用域和作用域鏈
作用域是在運行時代碼中的某些特定部分中變量,函數(shù)和對象的可訪問性
作用域鏈是javascript內(nèi)部中一種變量、函數(shù)查找機制
this的原理以及幾種不同使用場景的取值
① 通過函數(shù)名()直接調(diào)用:this指向window
② 通過對象.函數(shù)名()調(diào)用的:this指向這個對象
③ 函數(shù)作為數(shù)組的一個元素,通過數(shù)組下標調(diào)用的:this指向這個數(shù)組
④ 函數(shù)作為window內(nèi)置函數(shù)的回調(diào)函數(shù)調(diào)用:this指向window( setInterval setTimeout 等)
⑤ 函數(shù)作為構(gòu)造函數(shù),用new關(guān)鍵字調(diào)用時:this指向新new出的對象
閉包的實現(xiàn)原理和作用,可以列舉幾個開發(fā)中閉包的實際應
閉包:函數(shù)作為返回值;函數(shù)作參數(shù)(所有的自由變量查找,是在函數(shù)定義的地方,不是在執(zhí)行的地方)
閉包的概念
①函數(shù)嵌套函數(shù)
②函數(shù)內(nèi)部可以引用函數(shù)外部的參數(shù)和變量
③參數(shù)和變量不會被垃圾回收機制回收
閉包的作用:訪問函數(shù)內(nèi)部的變量,保持函數(shù)在環(huán)境中一直存在,不會被垃圾回收機制收回
(因為函數(shù)內(nèi)部聲明 的變量是局部的,只能在函數(shù)內(nèi)部訪問到,但是函數(shù)外部的變量是對函數(shù)內(nèi)部可見的,這就是作用域鏈的特點了。)
閉包的優(yōu)點:
(1)方便調(diào)用上下文中聲明的局部變量
(2)邏輯緊密,可以在一個函數(shù)中再創(chuàng)建個函數(shù),避免了傳參的問題
(3)加強了封裝性
(4)變量始終保持在內(nèi)存中,不會被清除
閉包的缺點:因為使用閉包,可以使函數(shù)在執(zhí)行完后不被銷毀,保留在內(nèi)存中,如果大量使用閉包就會造成內(nèi)存泄露,內(nèi)存消耗很大
堆棧溢出和內(nèi)存泄漏的原理,如何防止
1、內(nèi)存泄露:是指申請的內(nèi)存執(zhí)行完后沒有及時的清理或者銷毀,占用空閑內(nèi)存,內(nèi)存泄露過多的話,就會導致后面的進程申請不到內(nèi)存。因此內(nèi)存泄露會導致內(nèi)部內(nèi)存溢出
2、堆棧溢出:是指內(nèi)存空間已經(jīng)被申請完,沒有足夠的內(nèi)存提供了
js一半常用的垃圾回收方法是標記清除
標記清除法:在一個變量進入執(zhí)行環(huán)境后就給它添加一個標記:進入環(huán)境,進入環(huán)境的變量不會被釋放,因為只要執(zhí)行流進入響應的環(huán)境,就可能用到他們。當變量離開環(huán)境后,則將其標記為“離開環(huán)境”
3、常見的內(nèi)存泄露的原因
全局變量引起的內(nèi)存泄露
閉包
沒有被清除的計時器
4、解決方法
減少不必要的全局變量
減少閉包的使用(因為閉包會導致內(nèi)存泄露)
避免死循環(huán)的發(fā)生
設計模式
1 、單例模式( 調(diào)用static 可以使用this嗎)
要求一個類只有一個實例化對象存在
這個實例化對象必須提供一個全局對外訪問方式
這個實例化對象應當是私有的,不能被外界直接訪問或者更改
每個實例只允許被實例化一次
(1)類中定義一個私有化屬性 。 static private interface = 類名
(2)定義一個靜態(tài)方法 static getinterface(){ if(!類名.interface){new 類名} return 類名 + 屬性(interface)
(3) constructor 是一個私有化
使用場景
- 引用第三方庫(多次引用只會使用一個庫引用,如 jQuery)
- 彈窗(登錄框,信息提升框)
- 購物車 (一個用戶只有一個購物車)
- 全局態(tài)管理 store (Vuex / Redux)
2、工廠模式
工廠模式是創(chuàng)建對象的常用設計模式,為了不暴露創(chuàng)建對象的具體邏輯,將邏輯封裝在一個函數(shù)中,這個函數(shù)就稱為一個工廠。本質(zhì)上是一個負責生產(chǎn)對象實例的工廠
let factory = function (role) {
function User(obj) {
this.name = obj.name;
this.role = obj.role;
}
switch(role) {
case 'superman':
return new User({ name: '平臺用戶', role: ['主頁', '登錄頁'] })
break;
case 'man':
return new User({ name: '游客', role: ['登錄頁']})
break;
default:
throw new Error('參數(shù)錯誤')
}
}
let superman = factory('superman');
let man = factory('man');
3、策略模式:
策略模式的本意將算法的使用與算法的實現(xiàn)分離開來,避免多重判斷調(diào)用哪些算法
適用場景:
- 多重條件語句判斷,執(zhí)行對應的算法場景
- 表單校驗(validator)
4、代理模式:
代理模式是為其他對象提供一種代理,也就是當其他對象直接訪問該對象時,如果開銷較大,就可以通過這個代理層控制對該對象的訪問。常見的使用場景為懶加載,合并http請求和緩存。
(function(){
// 目標對象,是真正被代理的對象
function Subject(){}
Subject.prototype.request = function(){};
function Proxy(realSubject){
this.realSubject = realSubject;
}
Proxy.prototype.request = function(){
this.realSubject.request();
};
}());
5、觀察者模式
一個對象(目標對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知
js兼容性問題
1、event對象
IE是把event事件對象作為全局對象window的一個屬性;可以使用event或window.event來訪問;
FireFox和Chrome等主流瀏覽器是通過把【事件對象】作為【事件響應函數(shù)】的【參數(shù)】進入傳入的;
兼容性的寫法示例:
domElement.onclick = function( e ){
e = e || window.event;//或(||)書寫順序有講究,不能隨意換
}
2、事件源(獲取事件作用的元素)
非IE event.target IE event.srcElement
3、添加事件監(jiān)聽器的函數(shù)
標準(IE8以上):obj.addEventListener(事件名稱,事件函數(shù),是否捕獲);
1.有捕獲
2.事件名稱沒有on
3.this觸發(fā)該事件的對象
IE:obj.attachEvent(事件名稱,事件函數(shù));
1.沒有捕獲
2.事件名稱有on
3.this指向window
4、取消事件的默認行為
function stopDefault(e) {
if ( e && e.preventDefault ) {
e.preventDefault();
} else {
window.event.returnValue = false; // IE中阻止函數(shù)器默認動作的方式
}
return false;
}
5、阻止事件冒泡
FF、Chrome:event.stopPropagation();
IE:event.cancelBubble
6、ajax請求
IE: new ActiveXObject()
FF、Chrome:new XMLHttpRequest()
瀏覽器的組成
用戶界面 瀏覽器引擎 渲染引擎(內(nèi)核) 網(wǎng)絡 UI 后端 JS引擎 數(shù)據(jù)存儲
瀏覽器內(nèi)核
1、IE瀏覽器內(nèi)核:Trident內(nèi)核,也是俗稱的IE內(nèi)核;
2、Chrome瀏覽器內(nèi)核:以前是Webkit內(nèi)核,現(xiàn)在是Blink內(nèi)核;
3、Firefox瀏覽器內(nèi)核:Gecko內(nèi)核,俗稱Firefox內(nèi)核;
4、Safari瀏覽器內(nèi)核:Webkit內(nèi)核;
5、Opera瀏覽器內(nèi)核:最初是自己的Presto內(nèi)核,后來是Webkit,現(xiàn)在是Blink內(nèi)核;
6、360瀏覽器、獵豹瀏覽器內(nèi)核:IE+Chrome雙內(nèi)核;
7、搜狗、遨游、QQ瀏覽器內(nèi)核:Trident(兼容模式)+Webkit(高速模式);
8、百度瀏覽器、世界之窗內(nèi)核:IE內(nèi)核;
9、2345瀏覽器內(nèi)核:以前是IE內(nèi)核,現(xiàn)在也是IE+Chrome雙內(nèi)核;