1. 程式人生 > >重學Javascript - 極客時間《重學前端》筆記

重學Javascript - 極客時間《重學前端》筆記

cond 基於 內置 second www extends captures htm con

編程語言的一般規律是: 用一定的詞法和語法,表達一定的語義,從而操作運行時
技術分享圖片
Javascript文法(詞法+語法)
詞法 Lexical 第一類:標識符 第二類:常數
第三類:保留字 Token (不同語言的詞法分析主要是這裏不同)
第四類:界符 ‘/*’、‘//’、 () { } [ ] " " ‘ 等
第五類:運算符 <、<=、>、>=、=、+、-、*、/、^、等
https://www.cnblogs.com/winter-cn/archive/2012/04/17/2454229.html 技術分享圖片

語法

這裏講了語法分析的過程。主要是AST

https://www.cnblogs.com/fundebug/p/how-does-javascript-compile.html


Javascript語義

MDN裏可以查詢各種語義的使用方式 (關於具體語法的使用,建議查看權威網站,比如MDN,W3等)

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript

Javascript運行時 - 數據結構(類型+實例)
類型
Javascript有7種語言類型: 1)Undefined 任何變量在賦值之前都是Undefined類型,值為undefined. undefined是一個變量而非是一個關鍵字,這是一個設計失誤。建議用void 0代替。 2)Null 這是一個關鍵字 3)Boolean 4)String String的意義並非“字符串”,而是字符串的UTF16編碼, 字符串的最大長度實際上是受字符串的編碼長度影響的。
“因為計算機只能處理數字,如果要處理文本,就必須先把文本轉換為數字才能處理。最早的計算機在設計時采用8個比特(bit)作為一個字節(byte),所以,一個字節能表示的最大的整數就是255(二進制11111111=十進制255),0 - 255被用來表示大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A的編碼是65,小寫字母z的編碼是122。 如果要表示中文,顯然一個字節是不夠的,至少需要兩個字節,而且還不能和ASCII編碼沖突,所以,中國制定了GB2312編碼,用來把中文編進去。 類似的,日文和韓文等其他語言也有這個問題。為了統一所有文字的編碼,Unicode應運而生。Unicode把所有語言都統一到一套編碼裏,這樣就不會再有亂碼問題了。 Unicode通常用兩個字節表示一個字符,原有的英文編碼從單字節變成雙字節,只需要把高字節全部填為0就可以。
常見的有UTF16和UTF8,Unicode的碼點通常這樣來表示,U+0000 - U+FFFF,被稱為基本字符區域BMP 通過javascript進行UTF-8編碼
http://www.cnblogs.com/doublenet/p/5616451.html 5)Number 雙精度浮點型 非整數的Number類型無法用== (===也不行)來比較,比如在JS中, 0.1+0.2不等於0.3,除非這樣寫: Math.abs (0.1+0.2 - 0.3) <= Number.EPSILON 檢查等式左右兩邊差的絕對值是否小於最小精度,才是正確的比較浮點數的方法 6)Object 7)Symbol 這個東西主要用來幹嘛? -- 最初的目的是為了實現私有屬性. 後來, They are now known as unique symbols and their only intended use is to avoid name clashes between properties

https://stackoverflow.com/questions/21724326/what-is-the-motivation-for-bringing-symbols-to-es6

https://stackoverflow.com/questions/49615962/what-is-the-use-of-symbol-in-javascript-ecmascript-6

Symbol還可以用來實現叠代器 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols 除了這七種語言類型,還有一些語言的實現者更關心的規範類型。
List和Record: 用於描述函數傳參過程
Set: 主要用於解釋字符集等
Completion Record: 用於描述異常、跳出等語句執行過程
Reference: 用於描述對象屬性訪問、delete等
Property Descriptor: 用於描述對象的屬性
Lexical Environment和Environment Record: 用於描述變量和作用域
Data Block: 用於描述二進制數據 類型轉換
Number, String, Boolean, Symbol這幾個基本類型,都在對象類型中有一個“親戚”。 比如,我們必須認識到 3 與 new Number(3) 是完全不同的值,它們一個是Number類型,一個是對象類型。
點(.)運算符提供了裝箱操作,它會根據基礎類型構造一個臨時對象,使得我們能在基礎類型上調用對應對象的方法。但裝箱機制會頻繁產生臨時對象,在一些對性能要求較高的場景下,我們應該盡量避免裝箱操作。
StringToNumber
多數情況下,Number 是比 parseInt 和 parseFloat更好的方法。比如 a = Number(‘123‘) console.log(a+1) NumberToString用的很少,就不講了

PS: “==”運算試圖實現跨類型的比較,但這是一個失誤,很多編程規範中禁止使用“==”,而要求程序員進行顯式地類型轉換後,用===比較
識別對象對應的基本類型最準確的方法是 Object.prototype.toString().call() 比 instanceof還準確
var o = "123";
console.log(Object.prototype.toString.call(o)); //[object String]

裝箱機制會頻繁產生臨時對象,在一些對性能要求較高的場景下,我...

極客時間版權所有: https://time.geekbang.org/column/article/78884

下面這篇文章對JS中的類型轉換表述的比較清楚 https://www.jianshu.com/p/b161aeecb6d6 實例 - 即Javascript中的對象的原理
對象的三個特點 - 唯一標識性,狀態,行為 在JavaScript中,將狀態和行為統一抽象為“屬性”。尤其是在JavaScript中,函數也是一種屬性,跟其他屬性沒有區別。 JavaScript中的對象具有高度的動態性,因為JavaScript賦予了使用者在運行時為對象添改狀態和行為的能力。 為了提高抽象能力,JavaScript提供了數據屬性訪問器屬性兩類 對JavaScript來說,屬性並非只是簡單的名稱和值,JavaScript用一組特征來描述屬性 比如數據屬性,就有這四個特征 value, writable(可否被賦值), enumerable(可否被枚舉), configurable PS: 在Google的編程規範當中不建議使用訪問器屬性,更推薦的方式是This. 下面的例子展示了如何動態添加屬性
var o = { a: 1 };
Object.defineProperty(o, "b", {value: 2, writable: false, enumerable: false, configurable: true});
//a 和 b 都是數據屬性,但特征值變化了
Object.getOwnPropertyDescriptor(o,"a"); // {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(o,"b"); // {value: 2, writable: false, enumerable: false, configurable: true}
o.b = 3;
console.log(o.b); // 2 那什麽是原型呢? 看下面的例子 var cat = {
say(){
console.log("meow~");
},
jump(){
console.log("jump");
}
}

var tiger = Object.create(cat, {
say:{
writable:true,
configurable:true,
enumerable:true,
value:function(){
console.log("roar!");
}
}
})
var anotherCat = Object.create(cat);
anotherCat.say();
var anotherTiger = Object.create(tiger);
anotherTiger.say();
"基於原型的面向對象"和“基於類的面向對象”,前者的代表是JavaScript, 後者的代表是Java. 原型法的思想是“照貓畫虎”,類的思想是“分類”。 由於原型系統的強大,使得JavaScript又可以模擬“基於類的面向對象”。比如下面的例子: 用構造器模擬類的兩種方法 1)第一種方法是直接在構造器中修改 this,給 this 添加屬性
function c1(){
this.p1 = 1;
this.p2 = function(){
console.log(this.p1);
}
}
var o1 = new c1;
o1.p2();

2)第二種方法是修改構造器的 prototype 屬性指向的對象,它是從這個構造器構造出來的所有對象的原型 function c2(){
}
c2.prototype.p1 = 1;
c2.prototype.p2 = function(){
console.log(this.p1);
}

var o2 = new c2;
o2.p2(); ES6中的類 class Rex {
constructor(name,age) {
this.name = name;
this.age = age;
}

get area() {
return this.name;
}
sayHelloToRex() {
console.log("hello Rex");
}
}

var rex = new Rex(‘Wang Chen Long‘,36);
rex.sayHelloToRex();
console.log(rex.area); 通過括號和大括號來創建方法,數據型成員最好寫在構造器裏面, 通過this關鍵字來訪問
Javascript中的對象分類 宿主對象 (行為完全由宿主環境決定且遵循權威標準,比如JS標準,W3C標準)-- 通常是瀏覽器 固有的宿主對象,比如window https://developer.mozilla.org/zh-CN/docs/Web/API/Window 用戶可創建的宿主對象,比如

比如 document.createElement 就可以創...

極客時間版權所有: https://time.geekbang.org/column/article/80011

document.createElement就可以創建一些dom對象 下面的例子直接把 <p> 元素寫到 HTML 文檔輸出中:
<script>
document.write("<p>我的第一段 JavaScript</p>");
</script>
內置對象 -- 第一類,固有的內置對象,在任何 JS 代碼執行前就已經被創建出來了,可以通過ECMA標準查看 https://www.ecma-international.org/ecma-262/9.0/index.html#sec-well-known-intrinsic-objects 也可以用程序去查找,查找的標準是對象屬性的描述符 [Function: eval]
[Function: isFinite]
[Function: isNaN]
[Function: parseFloat]
[Function: parseInt]
[Function: decodeURI]
[Function: decodeURIComponent]
[Function: encodeURI]
[Function: encodeURIComponent]
[Function: Array]
[Function: Date]
[Function: RegExp]
[Function: Promise]
[Function: Proxy]
[Function: Map]
[Function: WeakMap]
[Function: Set]
[Function: WeakSet]
[Function: Function]
[Function: Boolean]
[Function: String]
[Function: Number]
[Function: Symbol]
[Function: Object]
{ [Function: Error] stackTraceLimit: 10 }
[Function: EvalError]
[Function: RangeError]
[Function: ReferenceError]
[Function: SyntaxError]
[Function: TypeError]
[Function: URIError]
[Function: ArrayBuffer]
[Function: SharedArrayBuffer]
[Function: DataView]
[Function: Float32Array]
[Function: Float64Array]
[Function: Int8Array]
[Function: Int16Array]
[Function: Int32Array]
[Function: Uint8Array]
[Function: Uint16Array]
[Function: Uint32Array]
[Function: Uint8ClampedArray]
Object [Atomics] {}
Object [JSON] {}
Object [Math] {}
{}
[]
[Function: isArray]
[Function: from]
[Function: of]
Date {}
[Function: now]
[Function: parse]
[Function: UTC]
RegExp {}
[Function: get input]
[Function: set input]
[Function: get $_]
[Function: set $_]
[Function: get lastMatch]
[Function: set lastMatch]
[Function: get $&]
[Function: set $&]
[Function: get lastParen]
[Function: set lastParen]
[Function: get $+]
[Function: set $+]
[Function: get leftContext]
[Function: set leftContext]
[Function: get $`]
[Function: set $`]
[Function: get rightContext]
[Function: set rightContext]
[Function: get $‘]
[Function: set $‘]
[Function: get $1]
[Function: set $1]
[Function: get $2]
[Function: set $2]
[Function: get $3]
[Function: set $3]
[Function: get $4]
[Function: set $4]
[Function: get $5]
[Function: set $5]
[Function: get $6]
[Function: set $6]
[Function: get $7]
[Function: set $7]
[Function: get $8]
[Function: set $8]
[Function: get $9]
[Function: set $9]
Promise {}
[Function: all]
[Function: race]
[Function: resolve]
[Function: reject]
[Function: revocable]
Map {}
WeakMap {}
Set {}
WeakSet {}
[Function]
[Boolean: false]
[String: ‘‘]
[Function: fromCharCode]
[Function: fromCodePoint]
[Function: raw]
[Number: 0]
[Function: isFinite]
[Function: isInteger]
[Function: isNaN]
[Function: isSafeInteger]
Symbol {}
[Function: for]
[Function: keyFor]
{}
[Function: assign]
[Function: getOwnPropertyDescriptor]
[Function: getOwnPropertyDescriptors]
[Function: getOwnPropertyNames]
[Function: getOwnPropertySymbols]
[Function: is]
[Function: preventExtensions]
[Function: seal]
[Function: create]
[Function: defineProperties]
[Function: defineProperty]
[Function: freeze]
[Function: getPrototypeOf]
[Function: setPrototypeOf]
[Function: isExtensible]
[Function: isFrozen]
[Function: isSealed]
[Function: keys]
[Function: entries]
[Function: values]
Error {}
[Function: captureStackTrace]
[EvalError]
[RangeError]
[ReferenceError]
[SyntaxError]
[TypeError]
[URIError]
ArrayBuffer {}
[Function: isView]
SharedArrayBuffer {}
DataView {}
Float32Array {}
Float64Array {}
Int8Array {}
Int16Array {}
Int32Array {}
Uint8Array {}
Uint16Array {}
Uint32Array {}
Uint8ClampedArray {}
[Function: load]
[Function: store]
[Function: add]
[Function: sub]
[Function: and]
[Function: or]
[Function: xor]
[Function: exchange]
[Function: compareExchange]
[Function: isLockFree]
[Function: wait]
[Function: wake]
[Function: notify]
[Function: parse]
[Function: stringify]
[Function: abs]
[Function: acos]
[Function: acosh]
[Function: asin]
[Function: asinh]
[Function: atan]
[Function: atanh]
[Function: atan2]
[Function: ceil]
[Function: cbrt]
[Function: expm1]
[Function: clz32]
[Function: cos]
[Function: cosh]
[Function: exp]
[Function: floor]
[Function: fround]
[Function: hypot]
[Function: imul]
[Function: log]
[Function: log1p]
[Function: log2]
[Function: log10]
[Function: max]
[Function: min]
[Function: pow]
[Function: random]
[Function: round]
[Function: sign]
[Function: sin]
[Function: sinh]
[Function: sqrt]
[Function: tan]
[Function: tanh]
[Function: trunc]
[Function: defineProperty]
[Function: deleteProperty]
[Function: apply]
[Function: construct]
[Function: get]
[Function: getOwnPropertyDescriptor]
[Function: getPrototypeOf]
[Function: has]
[Function: isExtensible]
[Function: ownKeys]
[Function: preventExtensions]
[Function: set]
[Function: setPrototypeOf]
[Function: concat]
[Function: find]
[Function: findIndex]
[Function: pop]
[Function: push]
[Function: shift]
[Function: unshift]
[Function: slice]
[Function: splice]
[Function: includes]
[Function: indexOf]
[Function: keys]
[Function: entries]
[Function: forEach]
[Function: filter]
[Function: map]
[Function: every]
[Function: some]
[Function: reduce]
[Function: reduceRight]
[Function: toString]
[Function: toLocaleString]
[Function: join]
[Function: reverse]
[Function: sort]
[Function: lastIndexOf]
[Function: copyWithin]
[Function: fill]
[Function: values]
[Function: toString]
[Function: toDateString]
[Function: toTimeString]
[Function: toISOString]
[Function: toUTCString]
[Function: getDate]
[Function: setDate]
[Function: getDay]
[Function: getFullYear]
[Function: setFullYear]
[Function: getHours]
[Function: setHours]
[Function: getMilliseconds]
[Function: setMilliseconds]
[Function: getMinutes]
[Function: setMinutes]
[Function: getMonth]
[Function: setMonth]
[Function: getSeconds]
[Function: setSeconds]
[Function: getTime]
[Function: setTime]
[Function: getTimezoneOffset]
[Function: getUTCDate]
[Function: setUTCDate]
[Function: getUTCDay]
[Function: getUTCFullYear]
[Function: setUTCFullYear]
[Function: getUTCHours]
[Function: setUTCHours]
[Function: getUTCMilliseconds]
[Function: setUTCMilliseconds]
[Function: getUTCMinutes]
[Function: setUTCMinutes]
[Function: getUTCMonth]
[Function: setUTCMonth]
[Function: getUTCSeconds]
[Function: setUTCSeconds]
[Function: valueOf]
[Function: getYear]
[Function: setYear]
[Function: toJSON]
[Function: toLocaleString]
[Function: toLocaleDateString]
[Function: toLocaleTimeString]
[Function: exec]
[Function: get dotAll]
[Function: get flags]
[Function: get global]
[Function: get ignoreCase]
[Function: get multiline]
[Function: get source]
[Function: get sticky]
[Function: get unicode]
[Function: compile]
[Function: toString]
[Function: test]
[Function: then]
[Function: catch]
[Function: finally]
[Function: get]
[Function: set]
[Function: has]
[Function: delete]
[Function: clear]
[Function: entries]
[Function: forEach]
[Function: keys]
[Function: get size]
[Function: values]
[Function: delete]
[Function: get]
[Function: has]
[Function: set]
[Function: has]
[Function: add]
[Function: delete]
[Function: clear]
[Function: entries]
[Function: forEach]
[Function: get size]
[Function: values]
[Function: delete]
[Function: has]
[Function: add]
[Function]
[Function: apply]
[Function: bind]
[Function: call]
[Function: toString]
[Function: toString]
[Function: valueOf]
[Function: anchor]
[Function: big]
[Function: blink]
[Function: bold]
[Function: charAt]
[Function: charCodeAt]
[Function: codePointAt]
[Function: concat]
[Function: endsWith]
[Function: fontcolor]
[Function: fontsize]
[Function: fixed]
[Function: includes]
[Function: indexOf]
[Function: italics]
[Function: lastIndexOf]
[Function: link]
[Function: localeCompare]
[Function: match]
[Function: normalize]
[Function: padEnd]
[Function: padStart]
[Function: repeat]
[Function: replace]
[Function: search]
[Function: slice]
[Function: small]
[Function: split]
[Function: strike]
[Function: sub]
[Function: substr]
[Function: substring]
[Function: sup]
[Function: startsWith]
[Function: toString]
[Function: trim]
[Function: trimStart]
[Function: trimEnd]
[Function: toLowerCase]
[Function: toUpperCase]
[Function: valueOf]
[Function: toLocaleLowerCase]
[Function: toLocaleUpperCase]
[Function: toExponential]
[Function: toFixed]
[Function: toPrecision]
[Function: toString]
[Function: valueOf]
[Function: toLocaleString]
[Function: toString]
[Function: valueOf]
[Function: __defineGetter__]
[Function: __defineSetter__]
[Function: hasOwnProperty]
[Function: __lookupGetter__]
[Function: __lookupSetter__]
[Function: isPrototypeOf]
[Function: propertyIsEnumerable]
[Function: toString]
[Function: valueOf]
[Function: get __proto__]
[Function: set __proto__]
[Function: toLocaleString]
[Function: toString]
[Function: get byteLength]
[Function: slice]
[Function: get byteLength]
[Function: slice]
[Function: get buffer]
[Function: get byteLength]
[Function: get byteOffset]
[Function: getInt8]
[Function: setInt8]
[Function: getUint8]
[Function: setUint8]
[Function: getInt16]
[Function: setInt16]
[Function: getUint16]
[Function: setUint16]
[Function: getInt32]
[Function: setInt32]
[Function: getUint32]
[Function: setUint32]
[Function: getFloat32]
[Function: setFloat32]
[Function: getFloat64]
[Function: setFloat64]
[Function: getBigInt64]
[Function: setBigInt64]
[Function: getBigUint64]
[Function: setBigUint64]
wake {}
notify {}

-- 第二類,“特權對象”。 在上面150+的固有內置對象中,我們把能夠通過語言本身的構造器創建的對象稱作“原生對象”,也叫“特權對象” 通過這些構造器,我們可以用 new 運算創建新的對象,幾乎所有這些構造器的能力都是無法用純JavaScript代碼實現的,它們也無法用class/extend語法來繼承。所以,它們其實是為了某些特定場景(比如使用“下標”)而設計出來的,也就是有“特權”。
技術分享圖片
用對象來模擬函數與構造器:函數對象與構造器對象 JavaScript 為這一類對象預留了私有字段機制
函數對象的定義是:具有[[call]]私有字段的對象,具有[[construct]]私有字段的對象

JavaScript 用對象模擬函數的設計代替了一般編程語言的函數,它們可以像其他語言的函數一樣被調用,傳參。任何宿主只要提供了“具有[[call]]私有字段的對象”,就可以被JavaScript函數調用語法支持。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
宿主對象或者內置對象,在作為函數調用和作為構造器使用的情況下,行為是不一樣的 比如內置對象 Date 在作為構造器調用時產生新的對象,作為函數時則產生字符串
Javascript運行時 - 執行過程(算法)
事件循環 一個 JavaScript 引擎會常駐於內存中,當宿主遇到一些事件時,就會把JavaScript代碼或者函數傳遞給它執行
在ES3和更早的版本中,JavaScript本身還沒有異步執行代碼的能力,當宿主環境傳遞給JavaScript引擎一段代碼,引擎就把代碼直接順次執行了,這個任務也就是宿主發起的任務。在ES5之後,JavaScript引入了Promise, 這樣,不需要瀏覽器的安排,JavaScript引擎本身也可以發起任務了。
Promise的總體思想是,需要進行io,等待或者其他異步操作的函數,不返回真實結果,而返回一個“承諾”
函數的調用方可以在合適的時機,選擇等待這個承諾兌現(通過Promise的then方法的回調) 基本用法如下:
function sleep(duration) {
 return new Promise(function(resolve, reject){
 setTimeout(resolve,duration);
 });
}
sleep(3000).then(()=> console.log(‘finished‘));

NodeJS中的process.nextTick方法可以在當前"執行棧"的尾部----下一次Event Loop(主線程讀取"任務隊列")之前----觸發回調函數。也就是說,它指定的任務總是發生在所有異步任務之前。setImmediate方法則是在當前"任務隊列"的尾部添加事件,也就是說,它指定的任務總是在下一次Event Loop時執行,這與setTimeout(fn, 0)很像


根據JSC引擎的術語,宿主發起的叫做宏觀任務,JavaScript引擎本身發起的叫做微觀任務
在JS引擎底層的C/C++代碼中,事件循環是一個跑在獨立線程中的循環,
while(true) {
r = wait();
execute(r);
}
整個循環做的事情基本上就是反復“等待-執行”。
這裏有篇文章講的不錯 http://www.ruanyifeng.com/blog/2014/10/event-loop.html
技術分享圖片 只有異步代碼會進入任務隊列。 雖然上圖中只有一個任務隊列,但我認為把它分解成宏任務隊列微任務隊列有更強的解釋力。 比如,Promise會添加到微任務隊列末尾。setTimeout等宿主API, 則會添加到宏任務隊列末尾。隊列總是先進先出,且微任務總是優先於宏任務,除非有定時器
下面這個例子證明了,“只有異步代碼會進入任務隊列”,“微任務優先於宏任務”
setTimeout(()=>console.log("d"), 0);
var r = new Promise(function(resolve, reject){
 console.log("a"); /*雖然這句寫在Promise中,但它不是異步代碼, 不會添加到任務隊列中 */
 resolve();
});

r.then(() => console.log("c"));
console.log("b");
下面這個例子證明了,“兩個隊列”,“隊列總是先進先出”
setTimeout(()=>console.log("d"), 0);
var r = new Promise(function(resolve, reject){
 resolve()
});
r.then(() => { 
 console.log("c1"); 
 new Promise(function(resolve, reject){
 resolve()
 }).then(setTimeout(()=>console.log("c2"),0));
});

async/await async函數必定返回Promise, 我們把所有返回Promise的函數都可以認為是異步函數 async函數強大的地方在於可以嵌套 下面的例子展示了如何嵌套。我們可以看到,如果把sleep這樣的異步操作放入某一個框架或者庫中,使用者幾乎不需要了解Promise的概念即可進行異步編程了 題目: 讓一個div改變顏色,綠色3秒,黃色1秒,紅色2秒 這裏必須用異步,如果不用異步模式就看不到顏色的變化了 Ps: 這個例子使用了JQuery, 只有JQuery可以用.style, document本身是沒有style屬性的
<script>
                $(document).ready(function(){
                function changeTrafficLight(color, duration) {
                        return new Promise(function(resolve, reject) {
                        document.getElementById("traffic-light").style.backgroundColor = color;
                        setTimeout(resolve, duration);
                        })
                    }
             
                async function trafficScheduler() {
                        await changeTrafficLight("green", 3000);
                        await changeTrafficLight("yellow", 1000);
                        await changeTrafficLight("red", 2000);
                        trafficScheduler();
                    }
             
                trafficScheduler();
            });
    </script>

函數的執行

我們可以這樣簡單理解一下,閉包其實只是一個綁定了執行環境的函...

極客時間版權所有: https://time.geekbang.org/column/article/83302

閉包
我們可以這樣簡單理解一下,閉包其實只是一個綁定了執行環境的函數,它與普通函數的區別是,它攜帶了執行的環境,就像人在外星中需要自帶吸氧的裝備一樣,這個函數也帶有在程序中生存的環境
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures JavaScript中的閉包包括下面這些部分: 環境部分
環境:函數的詞法環境(執行上下文的一部分)
標識符列表:函數中用到的未聲明的變量
表達式部分:函數體
實際上 JavaScript 中跟閉包對應的概念就是“函數”,不是執行上下文,也不是作用域
那什麽是執行上下文? 什麽又是作用域呢? 所謂的“執行上下文”,就是說同樣的代碼在不同的位置,會綁定不同的上下文,從而產生不同的行為。 技術分享圖片
最主要的還是詞法環境和變量環境。 詞法環境是實現閉包的必需條件。 VAR變量環境的例子如下: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var
Let變量環境的例子如下: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let 所謂作用域鏈, 就是指一個函數激活執行的時候去哪兒找變量的值。它也是實現閉包的必需條件。

class animal {
 constructor(name) {
 this.name = name;
 }

}

class dog extends animal {
 constructor(name){
 this.name = name
 }
}

function createEatFunction() {
 var desc = " is eating";
 return function eat(animal){
 console.log(animal.name+desc);
 };
}

var eat = createEatFunction();
//全局變量
var desc = "正在吃東西";
eat(dog); //這裏輸出什麽?

技術分享圖片

函數調用切換上下文
七種函數。區別在於this關鍵字的行為。
1)普通函數
function foo(){
// code
}
2) 箭頭函數
const foo = () => {
// code
}
3)方法。在class中定義的函數
class C {
foo(){
//code
}
}
4) 生成器函數。用function*定義的函數。
function foo*(){
// code
}
5) 類。用class定義的函數。實際上也是函數。
class Foo {
constructor(){
//code
}
}
6,7,8) 異步函數:普通函數、箭頭函數和生成器函數加上 async 關鍵字。
async function foo(){
// code
}
const foo = async () => {
// code
}
async function foo*(){
// code
}
This的運行機制 - this是執行上下文中很重要的一個組成部分。同一個函數調用方式不同,得到的this值也不同
JavaScript標準定義了[[thisModel]]私有屬性。
[[thisModel]]屬性的三個取值
lexical: 表示從上下文中找this, 這對應了箭頭函數
global:表示當this為undefined時,取全局對象,對應了普通函數
strict:當嚴格模式時使用,this嚴格按照調用時傳入的值,可能為null或者undefined
無論是否在嚴格模式下,在全局執行環境中(在任何函數體外部)this 都指向全局對象。
在函數內部,this的值取決於函數被調用的方式。
1) 不在嚴格模式下,且 this 的值不是由該調用設置的,所以 this 的值默認指向全局對象。
2) 所以,在嚴格模式下,如果 this 沒有被執行環境(execution context)定義,那它將保持為 undefined
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
我們看一個例子
function showThis(){
console.log(this);
}

var o = {
showThis: showThis
}

showThis(); // global
o.showThis(); // o
如果改成箭頭函數,結果就不一樣了。
const showThis = () => {
console.log(this);
}

var o = {
showThis: showThis
}

showThis(); // global
o.showThis(); // global

如果改成“方法”,結果又不一樣了
class C {
showThis() {
console.log(this);
}
}
var o = new C();
var showThis = o.showThis;

showThis(); // undefined
o.showThis(); // C的實例o

為什麽This有這樣的行為呢? 因為 在 JavaScript 標準中,為函數規定了用來保存定義時上下文的私有屬性[[Environment]]
當一個函數執行時,會創建一條新的執行環境記錄,記錄的外層詞法環境會被設置成函數的[[Environment]]
在下面的例子中, foo 能夠訪問 b(定義時詞法環境),卻不能訪問a (執行時的詞法環境),這就是執行上下文的切換機制
```js
// 這是 foo.js 文件裏的代碼
var b = 2;
module.exports = function() { // 導出function
console.log(b);
console.log(a);
};
```
```js
// 這是test.js 文件裏的代碼
var foo = require("./foo.js"); // 引入function 為foo
var a = 1;
foo();
// node 執行 test.js 輸出:
// -> 2
// -> ReferenceError: a is not defined
當函數調用時,會入棧一個新的執行上下文,函數調用結束時,執行上下文會出棧
技術分享圖片
Function.prototype.call和Function.prototype.apply可以指定函數調用時傳入的this值 function foo(a, b, c){
console.log(this);
console.log(a, b, c);
}
foo.call({}, 1, 2, 3);
foo.apply({}, [1, 2, 3]); MDN中關於call和apply的詳細解釋 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply JavaScript語句的執行 (這部分內容了解就好。個人感覺沒有太多實際意義。不過將來可以參照這種方式去分析別的語言的語句執行) JavaScript 語句執行機制涉及的一種基礎類型:Completion Record類型
function foo(){
try{
return 0;
} catch(err) {

} finally {
console.log("a")
}
}

console.log(foo());

JavaScript 正是依靠語句的 Completion ...

極客時間版權所有: https://time.geekbang.org/column/article/83860

JavaScript正是依靠語句的Completion Record類型,方才可以在語句的復雜嵌套結構中,實現各種控制。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements 技術分享圖片 技術分享圖片
帶標簽的語句 -- 任何JavaScript語句都是可以加標簽的,在語句前加冒號即可。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Loops_and_iteration

重學Javascript - 極客時間《重學前端》筆記