第七章 函數(shù)表達(dá)式
問答
1. 函數(shù)聲明的語法是?
function test() {
// ...
}
2. 函數(shù)表達(dá)式(匿名函數(shù))的語法是?
var test = function() {
// ...
};
3. 以下遞歸語句是否報(bào)錯, 報(bào)錯原因是? 如何改進(jìn)?
function factorial (num) {
if (num <= 1) {
return 1
} else {
return num * factorial(num - 1)
}
}
var anotherFactorial = factorial
factorial = null
anotherFactorial(4)
錯誤: Uncaught TypeError: factorial is not a function
原因: 函數(shù)factorial內(nèi)部的factorial指向null, 因此報(bào)錯.
方法1: arguments.callee, 它指向當(dāng)前正在執(zhí)行的函數(shù)指針. 但是嚴(yán)格模式不允許使用.
function factorial (num) {
if (num <= 1) {
return 1
} else {
return num * arguments.callee(num - 1)
}
方法2: 函數(shù)表達(dá)式
var factorial = function f (num) {
if (num <= 1) {
return 1
} else {
return num * f(num - 1)
}
}
4. 閉包的概念?
有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)(或其他). 只要不被垃圾回收機(jī)制回收即可, 不一定需要返回函數(shù), 方式有:
- 在一個函數(shù)中返回另一個函數(shù), 返回的函數(shù)中包含上層函數(shù)的活動對象和全局對象(層層向外, 不止兩層)
- 在函數(shù)中將內(nèi)部狀態(tài)保存在外部變量中.
var test = (function () {
var test = 123
function fn () {
return test
}
TEST = {
test: test,
fn: fn
}
})()
console.log(TEST.fn()) // 123 , 注意: window.TEST === TEST
console.log(TEST) // {test:123, fn:f}
5. 在閉包中, 函數(shù)被調(diào)用的時候都發(fā)生了什么?
function createComparisonFunction (propertyName) {
return function (object1, object2) {
var value1 = object1[propertyName]
var value2 = object2[propertyName]
if (value1 < value2) {
return -1
} else if (value1 > value2) {
return 1
} else {
return 0
}
}
}
// 創(chuàng)建函數(shù)
var compareNames = createComparisonFunction('name')
// 調(diào)用函數(shù)
var result = compareNames({ name: 'Nicholas' }, { name: 'Greg' })
// 解除對匿名函數(shù)的引用,釋放內(nèi)存
compareNames = null
- 當(dāng)創(chuàng)建
createComparisonFunction
函數(shù)時, 會創(chuàng)建一個預(yù)先包含全局變量對象的作用域鏈, 這個作用域鏈被保存在當(dāng)前函數(shù)內(nèi)部的[[Scopes]]
屬性中.
當(dāng)執(zhí)行
createComparisonFunction
函數(shù)時, 會創(chuàng)建一個執(zhí)行環(huán)境(execution context), 通過復(fù)制函數(shù)的[[Scopes]]
屬性中的對象構(gòu)建起執(zhí)行環(huán)境的作用域鏈.使用
arguments
和其他 形參/變量/函數(shù)聲明 初始化函數(shù)的 活動對象(activation object), 即另一種局部變量對象, 并被推入執(zhí)行環(huán)境作用域鏈的頂端. 這里需要補(bǔ)充變量提升及優(yōu)先級的說明.-
此時,
createComparisonFunction
函數(shù)的執(zhí)行環(huán)境中的作用域鏈包含多個變量對象:- 0: 本地活動對象
- 1: 引用的外層函數(shù)變量對象
- n-1: ...
- n: 全局變量對象.
顯然, 作用域鏈的本質(zhì)是一個指向變量對象的指針列表, 它只包含引用但不實(shí)際包含變量對象.
當(dāng)在函數(shù)中訪問一個變量時, 就會從作用域鏈中搜索具有相應(yīng)名字的變量(變量標(biāo)識符匹配過程). 這里可以補(bǔ)充優(yōu)化機(jī)制.
當(dāng)
createComparisonFunction
函數(shù)執(zhí)行完畢時, 其執(zhí)行環(huán)境的作用域鏈會被銷毀.
- 當(dāng)匿名函數(shù)從
createComparisonFunction
函數(shù)中返回后, 它的作用域鏈被初始化為包含createComparisonFunction
函數(shù)的活動對象和全局變量對象.
- 因此, 即使
createComparisonFunction
函數(shù)執(zhí)行完畢, 但是它的活動對象仍然會留在內(nèi)存中, 直到匿名函數(shù)主動銷毀,createComparisonFunction()
的活動對象才會被銷毀. 這里需要補(bǔ)充JS垃圾回收及標(biāo)記清除機(jī)制.
注意:
- 閉包保存的是整個變量對象, 而不是某個特殊的變量.
- 只要沒有形成閉包, 當(dāng)函數(shù)執(zhí)行完畢, 就可以立即銷毀其作用域鏈和活動對象(變量對象).
- 如果形成閉包, 作用域鏈也會被銷毀, 但是活動對象及變量對象不會銷毀.
- 作用域鏈決定哪些數(shù)據(jù)能被函數(shù)訪問
6. 下面函數(shù)返回的是什么, 為什么?
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
// this只有執(zhí)行時在確定指向誰, 當(dāng)前this指定window
// 活動對象中只有window
return this.name;
};
}
};
console.log(object.getNameFunc()()); // The Window
匿名函數(shù)的執(zhí)行環(huán)境具有全局性, 因此其this
對象通常指向window
(這里可以使用call/apply
主動改變this).
this的概念: 在函數(shù)運(yùn)行時牍汹,基于調(diào)用位置的條件自動生成的內(nèi)部對象,可以理解為動態(tài)綁定對象到this上瓦胎。
每個函數(shù)在被調(diào)用時都會自動取得兩個特殊的變量: this
和arguments
. 內(nèi)部函數(shù)在搜索這兩個變量時, 只會搜索到其活動變量為止, 因此最后一個函數(shù)執(zhí)行時, 外層function的活動對象只有window, this為window, 即, this === window
.
因此, 函數(shù)執(zhí)行時才能確定this, 且this指向調(diào)用函數(shù)最近的一個對象.
7. IIFE能模仿塊級作用域的原因?
JavaScript是函數(shù)級作用域, 在立即執(zhí)行函數(shù)內(nèi)定義的變量是局部變量, 一般情況下, 使用完畢后會自動銷毀.
8. 關(guān)于作用域鏈優(yōu)化部分的理解?
- 緩存深層作用域鏈上的變量
變量使用前都會在作用域鏈中查找, 因此將常用的跨作用域變量緩存為局部變量能減少變量查找時間
- 避免使用with
with能動態(tài)改變作用域鏈, 將指定的對象壓入作用域鏈頂端, 造成原來局部變量對象轉(zhuǎn)為第二位, 影響查找效率.
- catch內(nèi)部使用簡化代碼
當(dāng)try代碼中發(fā)生錯誤時, 執(zhí)行過程會自動跳轉(zhuǎn)到catch中, 然后把異常對象推入一個變量對象并置于作用域的首位, 建議catch中不要放復(fù)雜的語句
-
盡量不使用eval()
- 非嚴(yán)格模式下, eval中的語句只有在執(zhí)行時才知道是否有內(nèi)部變量污染外部環(huán)境;
- 嚴(yán)格模式為eval中的變量為局部作用域
- 此外, js引擎無法對動態(tài)作用域進(jìn)行優(yōu)化, 只能按照作用域鏈查找的傳統(tǒng)模式進(jìn)行執(zhí)行.