Q1:(function(){}()最后這個()有什么意義跋涣?必須有這個()才是閉包?铜靶?
var sum = (function(){
var add=function(i,j){
return i+j;
}
return function(i,j){
add(i,j);
}
})()//(function(){}()最后這個()有什么意義玄柠?必須有這個()才是閉包?敏弃?)```
1.函數(shù)定義
注:函數(shù)實例化很少用

Q1:函數(shù)聲明在函數(shù)定義前能被調(diào)用,為什么噪馏?
Q2:函數(shù)表達式和實例化在函數(shù)定義前不能被調(diào)用麦到,為什么绿饵?
2.代碼執(zhí)行過程
?代碼執(zhí)行前先預(yù)解析(將變量、變量聲明瓶颠、函數(shù)定義 提前處理)拟赊,然后單步執(zhí)行JS代碼。
?函數(shù)聲明被前置到頂部執(zhí)行步清。
?當(dāng)用函數(shù)聲明重復(fù)定義一個函數(shù)時要门,只有最后一次定義有效。



>A:執(zhí)行結(jié)果:
函數(shù)聲明:101
函數(shù)表達式:11
函數(shù)表達式:11
——
var add1; //最頂部的變量聲明
function add1(i) {
console.log("函數(shù)聲明:"+(i+1));
}
function add1(i) {
console.log("函數(shù)聲明:"+(i+100));
}
add1(1); //當(dāng)執(zhí)行這句時廓啊,上面兩個相同的函數(shù)聲明欢搜,后面的覆蓋了前面的聲明,因此按照第二個函數(shù)的結(jié)果輸出 為101
add1 = function (i) { //代碼所在位置的變量賦值
console.log("函數(shù)表達式:"+(i+10));
};
add1(1); //此時的add1指向了最后定義的匿名函數(shù)了谴轮,因此執(zhí)行結(jié)果為 11
add1(1); //沒有其他代碼改變add1的指向炒瘟,因此add1(1)的執(zhí)行結(jié)果還是11```
3.函數(shù)定義之間的區(qū)別
4.函數(shù)調(diào)用4種模式
注:自定義構(gòu)造函數(shù)建議首字母大寫,便于相互理解
⑴方法調(diào)用模式
var myNumber = {
value: 1,
add: function(i){
console.log(this);
this.value += i;
}
}
myNumber.add(1);```
⑵apply調(diào)用模式
apply:Function構(gòu)造函數(shù)原型對象上的一個方法第步。構(gòu)造函數(shù)的原型對象會被它創(chuàng)建的對象的原型鏈引用疮装,而任何函數(shù)都是Function構(gòu)造函數(shù)的實例,所以任何函數(shù)都可以直接調(diào)用apply方法粘都。
apply功能:函數(shù)借用廓推。將函數(shù)借用給一個對象,幫助它實現(xiàn)函數(shù)所定義的邏輯翩隧。

⑶函數(shù)調(diào)用模式的區(qū)別
函數(shù)執(zhí)行時,JS引擎在函數(shù)本地作用域自動添加this堆生、arguments兩個臨時變量专缠,函數(shù)調(diào)用模式本質(zhì)區(qū)別就體現(xiàn)在this變量的指向上。

![arguments作用:用于獲取函數(shù)實參
arguments[index]實參
arguments.length實參個數(shù)](http://upload-images.jianshu.io/upload_images/316258-2c25fa02ba161f1c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>
>1.this指向Window全局變量
2.不能
3.實現(xiàn)方法
//方法一:可以把helper調(diào)整為方法函數(shù)淑仆,這樣helper就可以正確引用myNumber為this了
var myNumber = {
value:1,
helper:function(i) {
console.log(this);
this.value +=i;
},
add: function(i) {
this.helper(i);
}
}
myNumber.add(1);
//方法二:使用閉包
var myNumber = {
value: 1,
add: function(i){
var thisnew = this;
// 構(gòu)建閉包
var helper = function(i){
console.log(thisnew);
thisnew.value += i;
}
helper(i);
}
}
//方法三:使用apply(call)調(diào)用模式涝婉,將當(dāng)前helper方法借用給myNumber對象使用,這樣this指向的就是myNumber對象
var myNumber = {
value: 1,
add: function(i){
var helper = function(i){
console.log(this);
this.value += i;
}
// myNumber對象借用helper方法蔗怠,helper中的this將指向myNumber對象
helper.apply(myNumber,[i]); //apply方法
helper.call(myNumber,i); //call方法
}
}
//方法4墩弯,最笨的一種,針對這個題目只要不用this.value改成myNumber.value就可以了
var myNumber = {
value: 1,
add: function(i){
var helper = function(i);
console.log(this);
myNumber.value += i;
}
helper(i);
}
}
myNumber.add(1);```
5.函數(shù)傳參
按值傳遞call by value:一個外部變量傳遞給一個函數(shù)時寞射,函數(shù)實參獲取的時這個外部變量的副本最住,在函數(shù)內(nèi)部對實參修改,不會反映在外部變量怠惶。
引用傳遞:一個外部變量傳遞給一個函數(shù)時,函數(shù)實參實際上是這個外部變量的引用轧粟,在函數(shù)內(nèi)部對實參修改策治,都會反應(yīng)到外部變量脓魏。
6.閉包Closure
⑴函數(shù)內(nèi)部定義的子函數(shù)用到了父函數(shù)變量所形成的特定的作用域履腋。
珊燎??⑵閉包有哪些功能遵湖?
基于js執(zhí)行性能考慮悔政,被頻繁調(diào)用的函數(shù)內(nèi)部定義和調(diào)用的幫助函數(shù),如果不需要保存狀態(tài)延旧,應(yīng)該將這些幫助函數(shù)保存到閉包作用域谋国。
①閉包使用舉例1
將字符串中的一些特定字符按順序用數(shù)組中的元素替換,例如:
var arr = ['c','f','h','o'];
var str = 'ab4de8g4ijklmn7';
替換后 str == 'abcdefghijklmno';
/*var arr = ['c','f','h','o'];
var str = 'ab4de8g4ijklmn1';
console.log(str);
var func = (function(){
// count變量會保存在閉包作用域內(nèi)迁沫,表示func被調(diào)用次數(shù)(即正在替換第幾個字符)
var count = 0;
return function(){
return arr[count++];
}
})();
str = str.replace(/\d/g, func)
console.log(str);```
②閉包使用舉例2 -- 封裝
1.暴露type類型和start, stop, getStatus方法
2.隱藏status芦瘾,light對象狀態(tài)
var Car = function(type){
var status = "stop",
light = "off";
return {
type: type,
start: function(){
status = "driving";
light = "on";
},
stop: function(){
status = "stop";
light = "off";
},
getStatus: function(){
console.log(type + " is " + status + " with light " + light);
}
}
}
var audi = new Car("audi");
audi.start();
audi.getStatus();
audi.stop();
audi.getStatus();```
③閉包使用舉例3 -- 性能優(yōu)化1
減少函數(shù)定義時間和內(nèi)存消耗
// 不使用閉包
function sum(i, j) {
var add = function(i, j){
return i+j;
}
return add(i, j)
}
var startTime = new Date();
for(var i = 0; i< 1000000; i++) {
sum(1,1);
}
var endTime = new Date();
console.log(endTime - startTime);
// // // 使用閉包
var sum = (function() {
var add = function(i, j){
return i+j;
}
return function(i,j) {
add(i, j);
}
})()
var startTime = new Date();
for(var i = 0; i< 1000000; i++) {
sum(1,1);
}
var endTime = new Date();
console.log(endTime - startTime);```
閉包使用舉例3 -- 性能優(yōu)化2
普通遞歸函數(shù)跟使用閉包記錄調(diào)用返回結(jié)果的遞歸函數(shù)調(diào)用次數(shù)對比
// // 普通遞歸函數(shù)
// var factorial = (function(){
// var count = 0;
// var fac = function(i){
// count++;
// if (i==0) {
// console.log('調(diào)用次數(shù):' + count);
// return 1;
// }
// return i*factorial(i-1);
// }
// return fac;
// })();
// for(var i=0;i<=10;i++){
// console.log(factorial(i));
// }
// // 使用閉包記錄調(diào)用返回結(jié)果的遞歸函數(shù) -- 記憶函數(shù)
var factorial = (function(){
var memo = [1];
var count = 0;
var fac = function(i){
count++;
var result = memo[i];
if(typeof result === 'number'){
console.log('調(diào)用次數(shù):' + count);
return result;
}
result = i*fac(i-1);
memo[i] = result;
return result;
}
return fac;
})();
for(var i=0;i<=10;i++){
console.log(factorial(i));
}```
7.first-class function:JS中函數(shù)可以當(dāng)做普通變量來使用
8.Function.prototype.bind 所有函數(shù)都可以調(diào)用bind方法
bind返回的是函數(shù)的引用。
9集畅、函數(shù)柯里化近弟??
函數(shù)柯里化通常是指把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的并且返回一個接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)挺智。