undefined。var myOtherVar = 10
function a() { console.log('myVar', myVar) b()}
function b() { console.log('myOtherVar', myOtherVar) c()}
function c() { console.log('Hello world!')}
a()
var myVar = 5變量聲明的位置(一個(gè)在上,一個(gè)在下)
函數a調用下面定義的函數b, 函數b調用函數c
b在a之后聲明或者一切正常? console.log 打印的變量又是怎么樣?'myVar' undefined'myOtherVar' 10'Hello world!'
undefined之外,尚未為變量分配值。 因此,myVar在被打印時(shí)的值是undefined,因為JS引擎從頂部開(kāi)始逐行執行代碼。var myOtherVar = undefinedvar myVar = undefined
function a() {...}function b() {...}function c() {...}全局對象(瀏覽器中是 window 對象,NodeJs 中是 global 對象)
this 指向全局對象
myOtherVar = 10在全局上下文中,myOtherVar被賦值為10a()function a() {console.log('myVar', myVar)b()}
創(chuàng )建新的函數上下文
a 函數里面沒(méi)有聲明變量和函數
函數內部創(chuàng )建了 this 并指向全局對象(window)
接著(zhù)引用了外部變量 myVar,myVar 屬于全局作用域的。
接著(zhù)調用函數 b ,函數b的過(guò)程跟 a一樣,這里不做分析。
創(chuàng )建全局上下文,全局變量和函數。
每個(gè)函數的調用,會(huì )創(chuàng )建一個(gè)上下文,外部環(huán)境的引用及 this。
函數執行結束后會(huì )從堆棧中彈出,并且它的執行上下文被垃圾收集回收(閉包除外)。
當調用堆棧為空時(shí),它將從事件隊列中獲取事件。
function a() { var myOtherVar = 'inside A'
b()}
function b() { var myVar = 'inside B'
console.log('myOtherVar:', myOtherVar)
function c() { console.log('myVar:', myVar) }
c()}
var myOtherVar = 'global otherVar'var myVar = 'global myVar'a()全局作用域和函數內部都聲明了變量
函數c現在在函數b中聲明
myOtherVar: 'global otherVar'myVar: 'inside B'
全局創(chuàng )建和聲明 - 創(chuàng )建內存中的所有函數和變量以及全局對象和 this
執行 - 它逐行讀取代碼,給變量賦值,并執行函數a
函數a創(chuàng )建一個(gè)新的上下文并被放入堆棧,在上下文中創(chuàng )建變量myOtherVar,然后調用函數b
函數b 也會(huì )創(chuàng )建一個(gè)新的上下文,同樣也被放入堆棧中
myVar 變量,并聲明函數c函數b試圖打印myOtherVar,但這個(gè)變量并不存在于函數b中,函數b 就會(huì )使用它的外部引用上作用域鏈向上找。由于函數b是全局聲明的,而不是在函數a內部聲明的,所以它使用全局變量myOtherVar。
函數c執行步驟一樣。由于函數c本身沒(méi)有變量myVar,所以它它通過(guò)作用域鏈向上找,也就是函數b,因為myVar是函數b內部聲明過(guò)。
a -> global
c -> b -> global
function loopScope () { var i = 50 var j = 99
for (var i = 0; i < 10; i ) {}
console.log('i =', i)
for (let j = 0; j < 10; j ) {}
console.log('j =', j)}
loopScope()i = 10j = 99
var i,對于不知情的開(kāi)發(fā)人員來(lái)說(shuō),這可能會(huì )導致bug。let關(guān)鍵字,它與var相同,只是let有自己的塊作用域。 另一個(gè)關(guān)鍵字是const,它與let相同,但const常量且無(wú)法更改(指內存地址)。function blockScope () { let a = 5 { const blockedVar = 'blocked' var b = 11
a = 9000 }
console.log('a =', a) console.log('b =', b) console.log('blockedVar =', blockedVar)}
blockScope()a = 9000b = 11ReferenceError: blockedVar is not defined
a是塊作用域,但它在函數中,而不是嵌套的,本例中使用var是一樣的。
對于塊作用域的變量,它的行為類(lèi)似于函數,注意var b可以在外部訪(fǎng)問(wèn),但是const blockedVar不能。
在塊內部,從作用域鏈向上找到 a 并將let a更改為9000。
function logMessage2 () { console.log('Message 2')}
console.log('Message 1')
setTimeout(logMessage2, 1000)
console.log('Message 3')setTimeout函數來(lái)延遲一條消息。 我們知道js是同步,來(lái)看看輸出結果Message 1Message 3Message 2
打印 Message 1
調用 setTimeout
打印 Message 3
打印 Message 2
setTimeout是一個(gè) API,和大多數瀏覽器 API一樣,當它被調用時(shí),它會(huì )向瀏覽器發(fā)送一些數據和回調。我們這邊是延遲一秒打印 Message 2。setTimeout 后,我們的代碼繼續運行,沒(méi)有暫停,打印 Message 3 并執行一些必須先執行的操作。function exponent (x) { return function (y) { //和math.pow() 或者x的y次方是一樣的 return y ** x }}
const square = exponent(2)
console.log(square(2), square(3)) // 4, 9
console.log(exponent(3)(2)) // 8function blockingCode() {const startTime = new Date().getSeconds()// 延遲函數250毫秒setTimeout(function() {const calledAt = new Date().getSeconds()const diff = calledAt - startTime// 打印調用此函數所需的時(shí)間console.log(`Callback called after: ${diff} seconds`)}, 250)// 用循環(huán)阻塞堆棧2秒鐘while(true) {const currentTime = new Date().getSeconds()// 2 秒后退出if(currentTime - startTime >= 2) break}}blockingCode() // 'Callback called after: 2 seconds'
250毫秒之后調用一個(gè)函數,但因為我們的循環(huán)阻塞了堆棧所花了兩秒鐘,所以回調函數實(shí)際是兩秒后才會(huì )執行,這是JavaScript應用程序中的常見(jiàn)錯誤。setTimeout不能保證在設置的時(shí)間之后調用函數。相反,更好的描述是,在至少經(jīng)過(guò)這段時(shí)間之后調用這個(gè)函數。setTimeout 的設置為0,情況是怎么樣?function defer () { setTimeout(() => console.log('timeout with 0 delay!'), 0) console.log('after timeout') console.log('last log')}
defer()after timeoutlast logtimeout with 0 delay!
add。調用add(1,2)返回3,當再次使用相同的參數add(1,2)調用它,這次不是重新計算,而是記住1 2是3的結果并直接返回對應的結果。 Memoization可以提高代碼運行速度,是一個(gè)很好的工具。// 緩存函數,接收一個(gè)函數const memoize = (func) => { // 緩存對象 // keys 是 arguments, values are results const cache = {}
// 返回一個(gè)新的函數 // it remembers the cache object & func (closure) // ...args is any number of arguments return (...args) => { // 將參數轉換為字符串,以便我們可以存儲它 const argStr = JSON.stringify(args)
// 如果已經(jīng)存,則打印 console.log('cache', cache, !!cache[argStr])
cache[argStr] = cache[argStr] || func(...args)
return cache[argStr] }}
const add = memoize((a, b) => a b)
console.log('first add call: ', add(1, 2))
console.log('second add call', add(1, 2))cache {} falsefirst add call: 3cache { '[1,2]': 3 } truesecond add call 3
add 方法,緩存對象是空的,它調用我們的傳入函數來(lái)獲取值3.然后它將args/value鍵值對存儲在緩存對象中。add函數來(lái)說(shuō),有無(wú)緩存看起來(lái)無(wú)關(guān)緊要,甚至效率更低,但是對于一些復雜的計算,它可以節省很多時(shí)間。這個(gè)示例并不是一個(gè)完美的緩存示例,而是閉包的實(shí)際應用。聯(lián)系客服