JS目錄
給 JavaScript 做一個(gè)頂層目錄盲赊,可這樣劃分:文法碟渺、語義叔扼、運(yùn)行時(shí)
再細(xì)分為:
建立知識框架
兩個(gè)目標(biāo):一是理解原理和背景,二是把不方便查閱和記憶的內(nèi)容整理好
前端知識在總體上分成基礎(chǔ)部分和實(shí)踐部分
基礎(chǔ)部分包含3大塊: JS語言狈定、CSS 及 HTML颂龙、瀏覽器的實(shí)現(xiàn)原理及API
一、類型
JS分7 種類型是:
Undefined掸冤;Null厘托;Boolean友雳;String稿湿;Number;Symbol押赊;Object
Undefined與null
Undefined 類型表示未定義饺藤,它的類型只有一個(gè)值包斑,就是 undefined。任何變量在賦值前是 Undefined 類型涕俗、值為 undefined
編程規(guī)范要求用 void 0 代替 undefined罗丰,因?yàn)椋篔S 的代碼 undefined 是一個(gè)變量,而并非是一個(gè)關(guān)鍵字再姑,這是 js 語言公認(rèn)的設(shè)計(jì)失誤之一萌抵,所以,為了避免無意中被篡改元镀,建議用 void 0 來獲取 undefined 值
Undefined 跟 Null 有一定的表意差別绍填,Null 表示的是:“定義了但是為空”。
Null與 undefined 不同栖疑,null 是 JavaScript 關(guān)鍵字讨永,所以在任何代碼中,你都可以放心用 null 關(guān)鍵字來獲取 null 值遇革。
String
String 用于表示文本數(shù)據(jù)卿闹。String 有最大長度是 2^53 - 1。
String 的意義并非“字符串”萝快,而是字符串的 UTF16 編碼
字符串的操作 charAt锻霎、charCodeAt、length 等方法針對的都是 UTF16 編碼杠巡。所以量窘,字符串的最大長度,實(shí)際上是受字符串的編碼長度影響的氢拥。
JavaScript 中的字符串是永遠(yuǎn)無法變更的蚌铜,一旦字符串構(gòu)造出來,無法用任何方式改變字符串的內(nèi)容嫩海,所以字符串具有值類型的特征冬殃。
Number
JavaScript 為了表達(dá)幾個(gè)額外的語言場景(比如不讓除以 0 出錯(cuò),而引入了無窮大的概念)
規(guī)定了幾個(gè)例外情況:
NaN叁怪,占用了 9007199254740990审葬,它不是個(gè)數(shù)字
Infinity,無窮大奕谭;
-Infinity涣觉,負(fù)無窮大。
Symbol
Symbol 是 ES6 中引入的新類型血柳,它是一切非字符串的對象 key 的集合
在 ES6 規(guī)范中官册,整個(gè)對象系統(tǒng)被用 Symbol 重塑。
我們創(chuàng)建 Symbol 的方式是使用全局的 Symbol 函數(shù)难捌。
例如: var mySymbol = Symbol("my symbol");
Object
Object 的定義是“屬性的集合”膝宁。屬性分為數(shù)據(jù)屬性和訪問器屬性鸦难,二者都是 key-value 結(jié)構(gòu),key 可以是字符串或者 Symbol 類型员淫。
我們必須認(rèn)識到 3 與 new Number(3) 是完全不同的值合蔽,它們一個(gè)是 Number 類型, 一個(gè)是對象類型介返。
因?yàn)?C++ 和 Java 的成功拴事,在這兩門語言中,每個(gè)類都是一個(gè)類型圣蝎,二者幾乎等同挤聘,以至于很多人常常會把 JavaScript 的“類”與類型混淆。
Number捅彻、String 和 Boolean组去,三個(gè)構(gòu)造器是兩用的,當(dāng)跟 new 搭配時(shí)步淹,它們產(chǎn)生對象从隆,當(dāng)直接調(diào)用時(shí),它們表示強(qiáng)制類型轉(zhuǎn)換缭裆。
Symbol 函數(shù)比較特殊键闺,直接用 new 調(diào)用它會拋出錯(cuò)誤,但它仍然是 Symbol 對象的構(gòu)造器澈驼。
JavaScript 中的“類”僅僅是運(yùn)行時(shí)對象的一個(gè)私有屬性辛燥,而 JavaScript 中是無法自定義類型的。
JS 語言設(shè)計(jì)上試圖模糊對象和基本類型之間的關(guān)系缝其,可以把對象的方法在基本類型上使用
比如:console.log("abc".charAt(0)); //a
其原理是挎塌,. 點(diǎn)運(yùn)算符提供了裝箱操作,它會根據(jù)基礎(chǔ)類型構(gòu)造一個(gè)臨時(shí)對象内边,能在基礎(chǔ)類型上調(diào)用對應(yīng)對象的方法
二榴都、裝箱、拆箱
前文提到漠其,全局的 Symbol 函數(shù)無法使用 new 來調(diào)用嘴高,但我們?nèi)钥梢岳醚b箱機(jī)制來得到一個(gè) Symbol 對象,我們可以利用一個(gè)函數(shù)的 call 方法來強(qiáng)迫產(chǎn)生裝箱和屎。
var symbolObject = (function(){ return this; }).call(Symbol("a"));
使用內(nèi)置的 Object 函數(shù)拴驮,我們可以在 JS 中顯式調(diào)用裝箱能力
var symbolObject = Object(Symbol("a"));
console.log(typeof symbolObject); //object
拆箱轉(zhuǎn)換
拆箱轉(zhuǎn)換,它是對象類型到基本類型的轉(zhuǎn)換柴信。
會嘗試調(diào)用 valueOf 和 toString 來獲得拆箱后的基本類型套啤。如果 valueOf 和 toString 都不存在,或者沒有返回基本類型颠印,則會產(chǎn)生類型錯(cuò)誤 TypeError纲岭。
在 ES6 之后,還允許對象通過顯式指定 Symbol.toPrimitive 來覆蓋原有的行為(用這方法就不會執(zhí)行valueOf和toString方法)
三线罕、對象屬性:
對象屬性分為:數(shù)據(jù)屬性止潮、訪問器屬性
數(shù)據(jù)屬性:它比較接近于其它語言的屬性概念,具有四個(gè)特征
value:就是屬性的值钞楼。
writable:決定屬性能否被賦值喇闸。
enumerable:決定 for in 能否枚舉該屬性。
configurable:決定該屬性能否被刪除或者改變特征值询件。
在大多數(shù)情況下燃乍,我們只關(guān)心數(shù)據(jù)屬性的值即可
訪問器屬性:
屬性在讀和寫時(shí)執(zhí)行,它允許使用者在寫和讀屬性時(shí)宛琅,得到完全不同的值刻蟹,可視為一種函數(shù)的語法糖
getter:函數(shù)或 undefined,在取屬性值時(shí)被調(diào)用嘿辟。
setter:函數(shù)或 undefined舆瘪,在設(shè)置屬性值時(shí)被調(diào)用。
enumerable:決定 for in 能否枚舉該屬性红伦。
configurable:決定該屬性能否被刪除或者改變特征值英古。
對象分類
1、宿主對象(host Objects):由 JavaScript 宿主環(huán)境提供的對象昙读,它們的行為完全由宿主環(huán)境決定召调。
2、內(nèi)置對象(Built-in Objects):由 JavaScript 語言提供的對象蛮浑。
- 2.1唠叛、固有對象(Intrinsic Objects ):由標(biāo)準(zhǔn)規(guī)定,隨著 JavaScript 運(yùn)行時(shí)創(chuàng)建而自動創(chuàng)建的對象實(shí)例沮稚。
- 2.2玻墅、原生對象(Native Objects):可以由用戶通過 Array、RegExp 等內(nèi)置構(gòu)造器或者特殊語法創(chuàng)建的對象壮虫。
- 2.3澳厢、普通對象(Ordinary Objects):由{}語法、Object 構(gòu)造器或者 class 關(guān)鍵字定義類創(chuàng)建的對象囚似,它能夠被原型繼承剩拢。
宿主對象,分為固有宿主的和用戶可創(chuàng)建的宿主兩種
固有宿主如:window
用戶創(chuàng)建宿主如: document.createElement 就可以創(chuàng)建一些 DOM 對象
固有對象饶唤,在任何 JavaScript 代碼執(zhí)行前就已經(jīng)被創(chuàng)建出來了徐伐,它們通常扮演者類似基礎(chǔ)庫的角色
三個(gè)值:Infinity、NaN募狂、undefined
九個(gè)函數(shù):eval办素、isFinite角雷、isNaN、parseFloat性穿、parselnt勺三、decodeURl、decodeURIComponent需曾、encodeURI吗坚、encodeURlComponent
構(gòu)造器:Array、Date呆万、RegExp商源、Promise、Proxy谋减、Map牡彻、WeakMap、Set出爹、WeakSet讨便、Function、Boolean以政、String霸褒、Number、Symbol盈蛮、Object废菱、Error、EvalError....
四個(gè)用于當(dāng)作命名空間的對象:Atomics抖誉、JSON殊轴、Math、Reflect
原生對象袒炉,分成以下類:
JavaScript 用對象模擬函數(shù)的設(shè)計(jì)代替了一般編程語言中的函數(shù)旁理,它們可以像其它語言的函數(shù)一樣被調(diào)用、傳參我磁。
任何對象只需要實(shí)現(xiàn)[[call]]孽文,它就是一個(gè)函數(shù)對象,可以去作為函數(shù)被調(diào)用夺艰。而如果它能實(shí)現(xiàn)[[construct]]芋哭,它就是一個(gè)構(gòu)造器對象,可以作為構(gòu)造器被調(diào)用郁副。
用戶用 function 關(guān)鍵字創(chuàng)建的函數(shù)必定同時(shí)是函數(shù)和構(gòu)造器减牺,但它們表現(xiàn)出來的行為效果卻并不相同。
JS 的執(zhí)行
Promise:是 JavaScript 語言提供的一種標(biāo)準(zhǔn)化的異步管理方式,它的總體思想是拔疚,需要進(jìn)行 io肥隆、等待或者其它異步操作的函數(shù),不返回真實(shí)結(jié)果稚失,而返回一個(gè)“承諾”栋艳,函數(shù)的調(diào)用方可以在合適的時(shí)機(jī),選擇等待這個(gè)承諾兌現(xiàn)(通過 Promise 的 then 方法的回調(diào))
var r = new Promise(function(resolve, reject){
console.log("a");
resolve()
});
setTimeout(()=>console.log("d"), 0) //0秒的setTimeout
r.then(() => console.log("c"));
console.log("b")
打印順序是abcd墩虹,不論代碼順序如何,d 必定發(fā)生在 c 之后憨琳,哪怕Promise返回需要3秒诫钓,也仍然是先c后d。
因?yàn)?Promise 產(chǎn)生的是 JavaScript 引擎內(nèi)部的微任務(wù)篙螟,而 setTimeout 是瀏覽器 API菌湃,它產(chǎn)生宏任務(wù)。
generator/iterator 也常常被跟異步一起來講遍略,但它們并非異步代碼惧所,只是在缺少 async/await 的時(shí)候,一些框架使用這樣的特性來模擬 async/await绪杏。但是 generator 并非被設(shè)計(jì)成實(shí)現(xiàn)異步下愈,所以有了 async/await 之后,generator/iterator 就不應(yīng)用來模擬異步的方法蕾久,只用于迭代器势似。
通常把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),JS引擎發(fā)起的任務(wù)稱為微觀任務(wù)
Promise 產(chǎn)生異步代碼僧著,JS保證這些異步代碼在一個(gè)宏觀任務(wù)中完成 (每個(gè)宏觀任務(wù)包含1個(gè)微觀任務(wù)隊(duì)列)
異步執(zhí)行的順序:
1履因、首先我們分析有多少個(gè)宏任務(wù);
2盹愚、在每個(gè)宏任務(wù)中栅迄,分析有多少個(gè)微任務(wù);
3皆怕、根據(jù)調(diào)用次序毅舆,確定宏任務(wù)中的微任務(wù)執(zhí)行次序;
4愈腾、根據(jù)宏任務(wù)的觸發(fā)規(guī)則和調(diào)用次序朗兵,確定宏任務(wù)的執(zhí)行次序;
5顶滩、確定整個(gè)順序
在只有 var余掖,沒有 let 的舊 JavaScript 時(shí)代,誕生了一個(gè)技巧,叫做:立即執(zhí)行的函數(shù)表達(dá)式(IIFE)盐欺,通過創(chuàng)建一個(gè)函數(shù)赁豆,并且立即執(zhí)行,來構(gòu)造一個(gè)新的域冗美,從而控制 var 的范圍魔种。
(function(){
var a;
}());
或者
(function(){
var a;
})();
推薦用法:
void function(){
var a;
}();
有了let,就不需要用這種立即執(zhí)行函數(shù)了
執(zhí)行上下文
一旦上下文被切換粉洼,整個(gè)語句的效果可能都會發(fā)生改變节预。所以切換上下文的時(shí)機(jī)就顯得非常重要。切換上下文最主要的場景是函數(shù)調(diào)用属韧。
1安拟、看個(gè)簡單的切換上下文代碼:
//a.js
var a = 1;
function foo(){
console.log(a);
console.log(b);
}
//b.js
引入a.js
var b = 2;
foo(); //這里就是切換上下文
foo打印a 為1,打印b 報(bào)錯(cuò)
看以下的這段 JS代碼:
var b = {}
let c = 1
this.a = 2;
//1宵喂、var 把 b 聲明到哪里糠赦;
//2、b 表示哪個(gè)變量锅棕;
//3拙泽、b 的原型是哪個(gè)對象慷垮;
//4钾恢、let 把 c 聲明到哪里耸棒;
//5攻旦、this 指向哪個(gè)對象
2啃擦、回答上面的問題忱叭,先從理解this關(guān)鍵字開始
this 是執(zhí)行上下文中很重要的一個(gè)組成部分澄阳。同一個(gè)函數(shù)調(diào)用方式不同斗遏,得到的 this 值也不同
this 跟面向?qū)ο蠛翢o關(guān)聯(lián)脆炎,它是與函數(shù)調(diào)用時(shí)使用的表達(dá)式相關(guān)
普通函數(shù)的 this 值由 ‘ 調(diào)用它所使用的引用 ’ 決定梅猿,下面示例
function showThis(){
console.log(this);
}
var o = {
x:2,
showThis: showThis
}
showThis(); // global
o.showThis(); // Reference 類型 o //{ x: 2, showThis: [Function: showThis] }
注意:上面獲取函數(shù)的表達(dá)式,它返回的并非函數(shù)本身秒裕,而是一個(gè) Reference 類型
Reference 類型由兩部分組成:一個(gè)對象和一個(gè)屬性值
上面代碼中 o.showThis 產(chǎn)生的 Reference 類型袱蚓,即由對象 o 和屬性“showThis”構(gòu)成
3、JavaScript 用一個(gè)棧來管理執(zhí)行上下文几蜻,這個(gè)棧中的每一項(xiàng)又包含一個(gè)鏈表喇潘。如下圖所示:
當(dāng)函數(shù)調(diào)用時(shí),會入棧一個(gè)新的執(zhí)行上下文梭稚,函數(shù)調(diào)用結(jié)束時(shí)颖低,執(zhí)行上下文被出棧。
而 this 則是一個(gè)更為復(fù)雜的機(jī)制
4弧烤、操作 this 的內(nèi)置函數(shù)
Function.prototype.call 和 Function.prototype.apply 可以指定函數(shù)調(diào)用時(shí)傳入的 this 值
function foo(a, b, c) {
console.log(this)
console.log(a, b, c)
}
foo(1, 2, 3) //this為 global
foo.call({}, 1, 2, 3) //this為{}
foo.apply({}, [1, 2, 3]) //this為{}
call 和 apply 作用是一樣的忱屑,只是傳參方式有區(qū)別
來源: winter 老師的 重學(xué)前端