函數聲明與函數表達式的區別:
函數聲明提升:就是使用函數聲明時,函數會被解析器先讀取,以供執行環境使用,所以函數聲明的位置可以在函數調用的後面
函數表達式則不可以在函數調用的前面,必須先定義函數表達式,纔可以調用,不然瀏覽器會報錯。
函數表達式:
var sayHi = function(name){
console.log(name+",hi");
}
sayHi("john")
函數表達式創建的是一個匿名函數。然後將匿名函數賦值給一個變量,通過變量來調用。
(function(name){
console.log(name+",hi");
})("john")
(function(name){
console.log(name+",hi");
}("john"))
這兩種執行方式效果是相同的,是立即執行的意思,定義完之後直接執行,而不需要通過一個變量+()來調用。
閉包:
按照高程裏面的描述就是:一個函數可以訪問另外一個函數作用域裏面的變量。
function outerSum(a,b){
var sum = function(){
return a+b
}
return sum;
}
var result = outerSum(1,2)
result() // 3
函數調用時發生的事情:
首先會創建一個執行環境和相應的作用域鏈,然後使用arguments和其他命名參數的值來初始化函數的活動對象。然後外部函數的活動對象始終是第二個。
這是最簡單的閉包,sum內部函數可以獲取outerSum外部函數的活動變量。當outerSum執行完畢之後,它的執行環境被銷燬了,但是它的活動對象依然在內存中,沒有被銷燬,因爲匿名函數依然在引用着這些變量。直到匿名函數被銷燬,它的活動對象纔會被銷燬。
閉包的缺點:
function a() {
var arr = []
for(var i = 0;i < 10; i++){
arr[i] = function() {
console.log(i)
}
}
return arr
}
var b = a()
b[0]() //10
b[1]() //10
b[2]() //10
閉包只能取得包含函數中任何變量的最後一個值。比如這個例子。程序中先執行a(),這個函數,但是b()並沒有被調用。直到執行到return arr這條語句,a()已經執行完成,然後接下來繼續往下執行,就是b[0]()這個方法,但此時i=10,所以打印出來的都是10。可以把匿名函數設置爲立即執行函數,這樣就可以解決這個問題了。
function a() {
var arr = []
for(var i = 0;i < 10; i++){
arr[i] = function(num) {
return function(){
console.log(num)
}
}(i)
}
return arr
}
var b = a()
b[0]() //0
b[1]() //1
b[2]() //2
關於this對象
var name = 'The window'
var object = {
name: "My Object",
getNameFunc: function(){
return function(){
return this.name
}
}
}
console.log(object.getNameFunc()()) //The Window
這裏相當於:
var a = object.getNameFunc()
console.log(a())
這裏的a()是在全局作用域下調用的,並且不是dot調用,所以this就指向window。只要函數中指定this的值就可以了。
var name = 'The window'
var object = {
name: "My Object",
getNameFunc: function(){
var that = this
return function(){
return that.name
}
}
}
console.log(object.getNameFunc()()) // My Object
var name = 'The window'
var object = {
name: "My Object",
getNameFunc: function(){
return function(){
return this.name
}.bind(object)()
}
}
console.log(object.getNameFunc())
console.log(object.getNameFunc().call(object))
console.log(object.getNameFunc().apply(object))
只要修改this的值是指向object就可以返回object裏面的值。
模仿塊級作用域:
(function () {
//塊級作用域
}) ()
這是一個立即執行函數,模仿塊級作用域,其他作用域無法訪問這個作用域。
function() {
} () // 錯誤的寫法
這是個函數聲明,函數聲明後面加括號是錯誤的做法,函數聲明後面不能加圓括號。只有函數表達式纔可以。我們只要給函數聲明用圓括號括起來,那麼就可以轉換爲函數表達式。就像上面的形式那樣。
優點:模仿塊級作用域可以減少閉包占用的內存問題,因爲沒有指向匿名函數的引用,只要函數執行完畢,就可以立即銷燬其作用域鏈。
模塊模式:
爲單例創建私有變量和特權方法。所謂的單例,指的就是隻有一個實例的對象。
var singleton = function() {
//私有屬性和方法
var privateVariable = 10;
function privateFunction(){
return privateVariable;
}
return {
//公有方法和屬性
publicVariable: true,
publicMethods:function() {
privateVariable++;
return privateFunction()
}
}
}();
如果必須創建一個對象並以某些數據對其進行初始化,同時還要公開一些能夠訪問這些私有數據的方法,那麼就可以使用模塊模式。