函數表達式和函數聲明
變量/函數聲明都會提早
console.log(a) let a =1
那麼打印出來的a爲 undefined,由於會將a提到前面並賦予默認值undefined
函數聲明:函數聲明會將函數提到調用函數變量的前面
fn('里斯')//不會報錯 function fn(name) { console.log(); }
函數表達式:
fn1();//會保錯,由於fn1爲undefeated const fn1=function fn(name) { console.log(); }
執行上下文(變量提高)
- 範圍:一段<script> 或者一個函數
- 全局:變量定義、函數聲明 一段<script>
- 函數:變量定義、函數聲明、this、arguments
this
- this須要在執行時才能確認值,定義時沒法確認
- this做爲普通函數執行,指向的是window
- this做爲對象執行,指向的是調用者對象
- this做爲構造函數執行,指向的是構造函數對象
- call apply bind
做用域
- 無塊級做用域
if(true) { const name='zhangsan' } console.log(name);// 'zhangsan'
- 全局做用域和函數做用域
const a=12;// 全局變量,全局均可以訪問,該變量容易被污染 function() { const b =32; // 函數做用域 函數內部能夠訪問 console.log(a); /// a變量在函數中沒有定義,那麼該變量稱之爲自由變量 console.log(b); }
- 指向函數對象與函數調用
var xiaoming = { name: '小明', birth: 1990, age: function () { var y = new Date().getFullYear(); return y - this.birth; } }; xiaoming.age; // 只是一個Func對象 xiaoming.age(); // Func的執行
同時strict模式下(use strict)讓函數的this指向undefined,事實是不管是strict模式,this指向undefined或window,能夠經過apply和call來控制this的指向的,完成this的綁定。
- apply
它接收兩個參數,第一個參數就是須要綁定的this變量,第二個參數是Array,表示函數自己的參數。
用apply修復getAge()調用
function getAge() { var y = new Date().getFullYear(); return y - this.birth; } var user= { name: '小明', birth: 1990, age: getAge }; user.age(); // 25 getAge.apply(user, []); // 25, this指向user, 參數爲空
- call
apply是經過將參數進行打包成數組Array的方式傳入,call是經過將參數以順序的方式傳入Func
Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5
對於普通的函數this的指向通常設置爲null
默認值
function test(x,y='world'){ console.log(''); }
- bind
bind 經過函數變量的方式調用bind函數綁定this對象指向
const ageFunc=function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}.bind({birth:20})
當咱們調用時ageFunc()執行函數時,函數指向{birth:20}這個Object對象
- rest參數
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);//3,4,5
}
foo(1, 2, 3, 4, 5);
- This對象的綁定
function getAge() { var y = new Date().getFullYear(); return y - this.birth; } var user= { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 當經過user對象去調用指向age,那麼getAge Func中的this對象指向的是調用者,即詞法做用域 getAge(); // 若是直接getAge()執行,那麼至關this屬於window對象
單獨調用函數,好比getAge(),此時,該函數的this指向全局對象,也就是window
閉包
定義: 內部函數能夠引用外部函數的參數和局部變量,當內部函數返回函數時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」
- 經過返回一個函數而後延遲執行
- 若是裏函數引用了外函數的某個變量,那這個變量就能享受和全局變量同樣的特權,不會被回收!由於該變量一直被Child函數一直訪問着。同時享受全局變量不會被銷燬的特權的閉包變量多到必定數量了,那內存就要撐爆了,一旦超過了計算機能接受的內存閥值,就會致使內存泄漏
函數做爲返回值javascript
函數做爲參數傳遞java
返回函數是根據做用域鏈一層一層往上找,找到便可,不是執行時生效,而是定義時生效數組
- 函數做爲返回值
function F() { let a = 10 return function() { console.log(a) // 自由變量 父級做用域中尋找 } }
let a =23 let f = new F() f() // 10
- 函數做爲參數傳遞
function F() { let a = 10 return function() { console.log(a) // 自由變量 父級做用域中尋找 } }
function F1(fn) { let a = 120 fn() } let f = new F() let f1 = new F1(f) f1() // 10
自由變量是由定義時的做用域來決定的,不是由執行時的做用域來決定的.
特性
根據閉包相關特性,總結其特色以下:
懶執行
function lazy_sum(arr) { var sum = function () { return arr.reduce(function (x, y) { return x + y; }); } return sum; } var results=lazy_sum([1,2,3,4,5]);
當去調用results()時纔會去真正計算求和
私有變量
在面嚮對象語言中,通常在函數內部經過private定義私有變量,而在閉包中則經過內部函數攜帶狀態返回。
function rememberCount(initial ) { var count = initial || 0; return { inc: function() { count = count + 1; return count; } } } var rc=rememberCount(); rc.inc(); //1 rc.inc(); //2 rc.inc(); //3
在返回的對象中,實現了一個閉包,該閉包攜帶了局部變量count,而且,從外部代碼根本沒法訪問到變量count。換句話說,閉包就是攜帶狀態的函數,而且它的狀態能夠徹底對外隱藏起來
注意
在閉包中,函數內部定義了數組的變量,當函數返回函數,內部的變量還被新的函數所引用,而是直到調用了f()才執行
function count() { var arr = []; for (var i=1; i<=3; i++) { arr.push(function () { return i * i; }); } return arr; } var results = count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2];
返回結果並不是是咱們想象的1,4,9
f1(); // 16 f2(); // 16 f3(); // 16
所有都是16!緣由就在於返回的函數引用了變量i,但它並不是馬上執行。等到3個函數都返回時,它們所引用的變量i已經變成了4,所以最終結果爲16。
返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。
經過建立並當即執行,建立一個匿名函數並馬上執行:
(function (x) { return x * x; })(3); // 9 function (x) { return x * x } (3);// 語法會解析錯誤 (function (x) { return x * x }) (3); function count() { var arr = []; for (var i=1; i<=3; i++) { //建立了3個function對象,保護了i變量的污染 arr.push((function (n) { return function () { return n * n; } })(i)); } return arr; } var results = count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2]; f1(); // 1 f2(); // 4 f3(); // 9
實際開發中的閉包應用
- 用於封裝變量,收斂權限
function isHasWatchList(){ const _list =[]; return function (id) { if(_list.indexOf(id) >= 0){ return false; } else { _list.push(id); return true; } } } // 使用 const hasWatchList = new isHasWatchList(); hasWatchList('10') // true hasWatchList('10') // false hasWatchList('10') // false