作用域鏈和函數內部this指向問題以及bind、call、apply方法
作用域鏈
-
作用域是相對於變量而言的, 其意義就在與查找變量(確定變量的來處, 變量是否可以訪問到, 確定變量在當前位置是否可以取到值)
JS分函數作用域和全局作用域JS變量又遵循就近使用的原則- 首先在使用該變量的當前作用域查找 , 如果當前作用域聲明了這個變量,就可以確定結果;如果沒有查找到,進入步驟2
- 查找當前作用域的上級作用域,也就是當前函數的上級函數,看看上級函數中有沒有聲明
- 再查找上級函數的上級函數,直到全局作用域為止
- 如果全局作用域中也沒有,我們就認為這個變量未聲明
xxx變量 is not defined
-
如何確定函數作用域的上一級作用域
-
函數的上一次作用域取決於函數聲明時的位置,而不是函數調用時的位置。
-
函數就聲明在全局作用域內
<script> function fn(callback){ var age=18; callback() } fn(function(){ console.log(age); //age is not defined //分析:age變量: //1、查找當前作用域:並沒有 //2、查找上一級作用域:全局作用域 (此時函數就聲明在全局作用域內) //3、此時age變量就獲取不到fn函數內部的age變量 //看上一級作用域,不是看函數在哪里調用,而是看函數在哪里編寫,函數聲明在全局,它的上一級作用域就是全局作用域。 //-->因為這種特別,我們通常會把作用域說成是:詞法作用域 }) </script> -
函數聲明在其它的函數內 (閉包就是個典型案例)
<script> function fn(){ var a=5; return function(){ a++; console.log(a); //a變量肯定是可以訪問的 } } var f1=fn(); //f1指向匿名函數 f1(); //6 f1(); //7 f1(); //8 //分析:a變量: //1、查找當前作用域:並沒有 //2、查找上一級作用域:fn函數作用域 (此時函數就聲明在fn函數內) //-->一般認為函數執行完畢,變量就會釋放,但是此時由於js引擎發現匿名函數要使用a變量,所以a變量並不能得到釋放,而是把a變量放在匿名函數可以訪問到的地方去了 //-->a變量存在於f1函數可以訪問到的地方,當然此時a變量只能被f1函數訪問 </script>
-
函數內部的this指向問題(箭頭函數不適用)
-
函數的4種調用方式
-
普通函數調用的方式
<script> var age = 18; //該age聲明在全局作用域下,其就會被掛載到window對象上,即window.age=18 var p = { age: 15, say: function () { console.log(this.age); //this指向window } } var s1 = p.say //s1 = function () {console.log(this.age);} s1(); //函數調用, 結果為 18 </script> -
方法的調用
<script> var age = 18; //該age聲明在全局作用域下,其就會被掛載到window對象上,即window.age=18 var p = { age: 15, say: function () { console.log(this.age); //this指向p對象 } } p.say() //結果為15 //p.say獲取的就是p對象內部的say方法,然后方法后+()就是方法的調用。 </script> -
new調用(構造函數)
<script> var age = 18; var p = { age: 15, say: function () { console.log(this.age); //此時this指向構造函數new出來的實例,而實例中並沒有初始化age屬性 } } new p.say() //構造函數調用, 結果為undefined </script><script> var age = 18; var p = { age: 15, say: function (age) { this.age = age console.log(this.age); } } new p.say(22) //構造函數調用, 結果為22 </script> -
上下文方式(call、apply、bind)
<script> var length = 21; //window.length = 21 function f1() { console.log(this.length); } f1.call([1, 3, 5]) //結果為3,此時this指向[1,3,5]數組 f1.apply(this) //結果為21,此時this指向window f1.call(5) //undefinde , 此時this指向 Number,其沒有length屬性 f1.bind(this)() //結果為21 //bind方法只改變函數f1內部this指向,而不會讓函數執行,想讓其執行,需要在后面手動調用+() </script>
-
bind、call、apply共同點和區別
- 三個方法都可以改變函數內部this指向
- bind和call、apply的區別是,bind方法不會讓函數執行,而call、apply的調用會讓函數也跟着執行
- call和apply的區別就在與給函數傳參的方式不同
- call(this指向對象, 參數1,參數2,參數3) 參數以逗號分隔,依次排下去
- apply(this指向對象, [參數1,參數2,參數3]) 參數包裹在一個數組中
