>undefined與null的區(qū)別
undefined? 代表已經(jīng)定義,但未賦值
null? 代表已經(jīng)定義并且賦值,只是值為null
>什么時候給變量賦值為null
- 初始賦值,表明將要為一個變量賦值,使之成為一個對象
- 結(jié)束前,讓某個變量指向的對象成為垃圾對象(被垃圾回收器回收)
>什么是函數(shù)?
- 實現(xiàn)特定功能的n條語句的封裝體
- 只有函數(shù)是可以執(zhí)行的,其他類型的數(shù)據(jù)不能執(zhí)行
>為什么要用函數(shù)?
- 提高代碼的復(fù)用
- 便于閱讀交流
>如何定義函數(shù)?
- 函數(shù)聲明 function fn (){}
- 表達(dá)式 var obj = function(){};
>如何調(diào)用(執(zhí)行)函數(shù)?
- test():直接調(diào)用
- obj.test():通過對象調(diào)用
- new test():new調(diào)用
- test.call/apply(obj):臨時讓test成為obj的方法進(jìn)行調(diào)用
>什么函數(shù)才是回調(diào)函數(shù)?
1).你定義的
2).你沒有調(diào)用
3).但最終它執(zhí)行了
>常見的回調(diào)函數(shù)
- dom事件回調(diào)函數(shù)(onclick 等等)
- 定時器回調(diào)函數(shù)? (延時定時器,循環(huán)定時器)
- ajax請求回調(diào)函數(shù)
- 生命周期回調(diào)函數(shù)
>什么叫IIFE
1)理解
立即執(zhí)行函數(shù)表達(dá)式
2)作用
- 隱藏實現(xiàn)(可以將函數(shù),變量聲明放入其中,使其只在局部作用)
- 不會污染外部(全局)命名空間
( function( ){
? ? ? ? var obj = xxx;
} )( )
>函數(shù)中的this
1)this是什么?
- 任何函數(shù)本質(zhì)上都是通過某個對象來調(diào)用的,如果沒有直接指定就是window
- 所有函數(shù)內(nèi)部都有一個變量this
- 它的值是調(diào)用函數(shù)的當(dāng)前對象
2)如何確定thie的值?
- test( ): // window
- p.test( ): // p
- var p = new test( )://新創(chuàng)建的對象p
- p.call(obj): // obj
>函數(shù)的prototype屬性
- 每個函數(shù)都有一個prototype屬性,它默認(rèn)指向一個Object空對象(即稱為原型對象)
- 原型對象中有一個屬性constructor,它指向函數(shù)對象
? 給原型對象添加屬性(一般都是方法)
- 作用:函數(shù)的所有實例對象自動擁有原型中的屬性(方法)
function Fun ( ) {? } //聲明一個Fun函數(shù)(構(gòu)造函數(shù))
Fun.prototype.test=function ( ) { //為Fun的原型對象添加test方法
console.log( " abc " );
};
var fun = new Fun( ); //新建一個Fun函數(shù)的實例
fun.test( ); //返回abc
>顯式原型與隱式原型
- 每個函數(shù)function都有一個prototype,即顯式原型(屬性)
- 每個實例對象都有一個__proto__,成為隱式原型(屬性)
- 對象的隱式原型的值為其對應(yīng)構(gòu)造函數(shù)的顯式原型的值
- 內(nèi)存結(jié)構(gòu)(圖)
- 總結(jié)
* 函數(shù)的prototype屬性:在定義時自動添加的,默認(rèn)值是一個空Object實例對象
* 對象的__proto__屬性:創(chuàng)建對象時自動添加的,默認(rèn)值為其構(gòu)造函數(shù)的prototype屬性值
* 程序員能直接操作顯示原型,但不能直接操作隱式原型(ES6之前)
>原型鏈
* 訪問一個對象屬性時,先在自身屬性中查找,找到返回,如果沒有,再沿著__proto__這條鏈向上查找,找到返回,如果最終沒有找到,返回undefined
* 別名:隱式原型連
* 作用:查找對象的屬性(方法)
* 補充:
1)函數(shù)的顯式原型指向的對象默認(rèn)是空Object實例對象(但Object不滿足)
2)所有函數(shù)都是Function的實例(包含F(xiàn)unction也是其自身的實例)
3)Object的原型對象時原型鏈的盡頭
* 原型鏈的屬性問題
1)讀取對象的屬性值時,會自動到原型鏈中查找
2)設(shè)置對象的屬性值時,不會查找原型鏈,如果當(dāng)前對象沒有此屬性值,直接添加此屬性并設(shè)置其值
3)方法一般定義在原型中,屬性一般通過構(gòu)造函數(shù)定義在對象本身上
>instanceof
- 如何判斷
1)表達(dá)式:A instanceof B
2)如果B函數(shù)的顯示原型對象,在A對象的原型鏈上,返回true,否則返回false
- Function是通過new自己產(chǎn)生的實例? ( var Function = new Function( ); )
>變量提升與函數(shù)提升
- 變量聲明提升
* 通過var定義(聲明)的變量,在定義語句之前就可以訪問到
* 值:undefined
- 函數(shù)聲明提升
* 通過function聲明的函數(shù),在之前就可以直接調(diào)用
* 值:函數(shù)定義(對象)
>執(zhí)行上下文
- 代碼分類(位置)
* 全局代碼
* 函數(shù)(局部)代碼
- 全局執(zhí)行上下文
* 在執(zhí)行全局代碼前將window確定為全局執(zhí)行上下文
* 對全局?jǐn)?shù)據(jù)進(jìn)行預(yù)處理
1)var 定義的全局變量 ==>undefined,添加為window的屬性
2)function聲明的全局函數(shù)==>賦值(fun),添加為window的方法
3)this==>賦值(window)
* 開始執(zhí)行全局代碼
- 函數(shù)執(zhí)行上下文
* 在調(diào)用函數(shù),準(zhǔn)備執(zhí)行函數(shù)體之前,創(chuàng)建對應(yīng)的函數(shù)執(zhí)行上下文對象
* 對局部數(shù)據(jù)進(jìn)行預(yù)處理
1)形參變量==>賦值(實參)==>添加為執(zhí)行上下文的屬性
2)arguments==>賦值(實參列表),添加為執(zhí)行上下文的屬性
3)var 定義的局部變量==>undefined,添加為執(zhí)行上下文的屬性
4)function聲明的函數(shù)==>賦值(fun),添加為執(zhí)行上下文的方法
5)this==>賦值(調(diào)用函數(shù)的對象)
* 開始執(zhí)行函數(shù)體代碼
>執(zhí)行上下文棧
* 在全局代碼執(zhí)行前,JS引擎就會創(chuàng)建一個棧來存儲管理所有的執(zhí)行上下文對象
* 在全局執(zhí)行上下文(window)確定后,將其添加到棧中(壓棧)
* 在函數(shù)執(zhí)行上下文創(chuàng)建后,將其添加到棧中(壓棧)
* 在當(dāng)前函數(shù)執(zhí)行完后,將棧頂?shù)膶ο笠瞥ǔ鰲#?/p>
* 當(dāng)所有的代碼執(zhí)行完后,棧中只剩下window
>作用域與作用域鏈
- 理解: * 就是一塊“地盤”,一個代碼段所在的區(qū)域
* 它是靜態(tài)的(相對于上下文對象),在編寫代碼的時候就確定了
- 分類: * 全局作用域
* 函數(shù)作用域
* 沒有塊作用域(ES6有了)
- 作用:隔離變量,不同作用域下同名變量不會有沖突
- 區(qū)別:
1)區(qū)別1
* 全局作用域外,每個函數(shù)都會創(chuàng)建自己的作用域,作用域在定義函數(shù)的時候就已經(jīng)確定了,而不是在函數(shù)調(diào)用時
* 全局執(zhí)行上下文環(huán)境是在全局作用域確定之后,JS代碼馬上執(zhí)行之前創(chuàng)建
* 函數(shù)執(zhí)行上下文時在調(diào)用函數(shù)時,函數(shù)體代碼執(zhí)行之前創(chuàng)建
2)區(qū)別2
* 作用域是靜態(tài)的,只要函數(shù)定義好了就一直存在,且不會再變化
* 執(zhí)行上下文是動態(tài)的,調(diào)用函數(shù)時創(chuàng)建,函數(shù)調(diào)用結(jié)束時上下文環(huán)境就會自動釋放
3)聯(lián)系
* 執(zhí)行上下文(對象)是從屬于所在的作用域
* 全局上下文環(huán)境==>全局作用域
* 函數(shù)上下文環(huán)境==>對應(yīng)的函數(shù)作用域
>閉包
1)如何產(chǎn)生閉包
* 當(dāng)一個嵌套的內(nèi)部(子)函數(shù)引用了嵌套的外部(父)函數(shù)的變量(函數(shù))時,就產(chǎn)生了閉包
2)閉包到底是什么?
* 使用chrome調(diào)試查看
* 理解一:閉包時嵌套的內(nèi)部數(shù)據(jù)(絕大部分人)
* 理解二:包含被引用變量(函數(shù))的對象(極少數(shù)人)
* 注意:閉包存在于嵌套的內(nèi)部函數(shù)中
3)產(chǎn)生閉包的條件?
* 函數(shù)嵌套
* 內(nèi)部引用了外部函數(shù)的數(shù)據(jù)(變量/函數(shù))
* 調(diào)用了外部函數(shù)
4)閉包的作用
* 使函數(shù)內(nèi)部的變量在函數(shù)執(zhí)行完之后,仍然存活在內(nèi)存中(延長了局部變量的生命周期)
* 讓函數(shù)外部可以操作(讀寫)到函數(shù)內(nèi)部的數(shù)據(jù)(變量/函數(shù))
5)問題
* 函數(shù)執(zhí)行完后,函數(shù)內(nèi)部聲明的局部變量是否還存在?
答:一般是不存在,存在于閉包中的變量才可能存在
* 在函數(shù)外部能直接訪問函數(shù)內(nèi)部的局部變量嗎?
答:不能,但我們可以通過閉包讓外部操作它
>閉包的生命周期
- 產(chǎn)生:在嵌套內(nèi)部函數(shù)定義執(zhí)行完成時就產(chǎn)生了(不是在調(diào)用,僅僅是定義)
- 死亡:在嵌套的內(nèi)部函數(shù)成為垃圾對象時
>閉包的應(yīng)用
- 定義JS模塊
* 具有特定功能的JS文件
* 將所有的數(shù)據(jù)和功能都封裝在一個函數(shù)內(nèi)部(私有的)
* 只向外暴露一個包含n個方法的對象或函數(shù)
* 模塊的使用者,只需要通過模塊暴露的對象調(diào)用方法來實現(xiàn)對應(yīng)的功能
>閉包的缺點
1)缺點:
* 函數(shù)執(zhí)行完后,函數(shù)內(nèi)的局部變量沒有釋放,占用內(nèi)存時間會變長
* 容易造成內(nèi)存泄漏
2)解決:
* 能不用閉包就不用閉包(很難不會去用到閉包,所以及時釋放就好)
* 及時釋放
>內(nèi)存溢出與內(nèi)存泄漏
1)內(nèi)存溢出:
* 一種程序運行出現(xiàn)的錯誤
* 當(dāng)程序員需要的內(nèi)存超過了剩余的內(nèi)存時,就出現(xiàn)內(nèi)存溢出的錯誤
2)內(nèi)存泄漏:
* 占用的內(nèi)存沒有及時釋放
* 內(nèi)存泄漏積累多了就容易導(dǎo)致內(nèi)存溢出
* 常見的內(nèi)存泄漏:
- 意外的全局變量(例如函數(shù)中聲明的變量沒有帶 var )
- 沒有及時清理的計時器或回調(diào)函數(shù)
- 閉包
>對象創(chuàng)建模式
1)Object構(gòu)造函數(shù)模式
* 套路:先創(chuàng)建空Object對象,再動態(tài)添加屬性/方法
* 適用場景:起始時不確定對象內(nèi)部數(shù)據(jù)
* 問題:語句太多
例:
var p = new Object( );
p.name = "tom";
p.age = 12;
p.setName = function( ){
this.name = name
}
2)對象字面量模式:
* 套路:使用{ }創(chuàng)建對象,同時制定屬性/方法
* 適用場景:起始時對象內(nèi)部數(shù)據(jù)是確定的
* 問題:如果創(chuàng)建多個對象,有重復(fù)代碼
例:
var p = {
name : "tom",
age: 12,
setName : function( name ){
this.name = name;
}
}
3)工廠模式:
* 套路:通過工廠函數(shù)動態(tài)創(chuàng)建對象并返回
* 適用場景:需要創(chuàng)建多個對象
* 問題:對象沒有一個具體的類型,都是Object類型
例:
function createPerson( name , age ){
var obj = {
name : name,
age : age,
setName : function ( name ){
this.name = name;
}
}
return obj;
}
createPerson( "jack","12");
createPerson("bob","18");
4)自定義構(gòu)造函數(shù)模式
5)構(gòu)造函數(shù)+原型的組合模式
>線程與進(jìn)程
1)進(jìn)程:
* 程序的一次執(zhí)行,它占有一片獨有的內(nèi)存空間
* 可以通過windows任務(wù)管理器查看進(jìn)程
2)線程:
* 是進(jìn)程內(nèi)的一個獨立執(zhí)行單元
* 是程序執(zhí)行的一個完整流程
* 是CPU的最小調(diào)度單元
3)關(guān)系:
* 一個進(jìn)程至少有一個線程(主線程)
* 程序是在某個進(jìn)程中的某個線程執(zhí)行的
>瀏覽器內(nèi)核模塊組成
1)主線程
* js引擎模塊:負(fù)責(zé)js程序的編譯與運行
* html,css文檔解析模塊:負(fù)責(zé)頁面文本的解析
* DOM/CSS模塊:負(fù)責(zé)dom/css在內(nèi)存中的相關(guān)處理
* 布局和渲染模塊:負(fù)責(zé)頁面的布局和效果的繪制(內(nèi)存中的對象)
2)分線程
* 定時器模塊:負(fù)責(zé)定時器的管理
* 事件響應(yīng)模塊:負(fù)責(zé)時間的管理
* 網(wǎng)絡(luò)請求模塊:Ajax請求
>js線程
* js是單線程執(zhí)行的(回調(diào)函數(shù)也是在主線程)
* H5提出了實現(xiàn)多線程的方法:Web Workers
* 只能是主線程更新界面
>定時器問題
* 定時器并不真正完全定時
* 如果在主線程執(zhí)行了一個長時間的操作,可能導(dǎo)致延時才處理
>事件處理機(jī)制(圖)
- 代碼分類
* 初始化執(zhí)行代碼:包含綁定dom事件監(jiān)聽,設(shè)置定時器,發(fā)送ajax請求的代碼
* 回調(diào)執(zhí)行代碼:處理回調(diào)邏輯
- js引擎執(zhí)行代碼的基本流程
* 初始化代碼===>回調(diào)代碼
- 模型的2個重要組成部分:
* 事件的管理模塊
* 回調(diào)隊列
- 模型的運轉(zhuǎn)流程
* 執(zhí)行初始化代碼,將事件回調(diào)函數(shù)交給對應(yīng)的模塊管理
* 當(dāng)事件發(fā)生時,管理模塊會將回調(diào)函數(shù)及其數(shù)據(jù)添加到回到隊列中
* 只有當(dāng)初始化代碼執(zhí)行完成(可能需要一定的時間),才會遍歷讀取回調(diào)隊列中的回調(diào)函數(shù)并執(zhí)行
>H5 Web Workers
* 可以讓js在分線程執(zhí)行
* Worker
* 問題
- worker內(nèi)代碼不能操作DOM更新UI
- 不是每個瀏覽器都支持這個新特性
- 不能跨域加載JS