首先一塊兒回顧下預解析和做用域吧:es6
預解析:
瀏覽器每讀到一個script標籤或function,先不執行任何代碼,會先把整個代碼快速的瀏覽一遍,而後從中 挑出 var 和 function兩個關鍵字 .
var: 預解析遇到 var 就把 var 連同它後邊的名字一塊 提到script(或function) 的最前邊,預解析完成以後,在從上向下一行一行執行代碼,若是碰到了 = 就賦值;
function:預解析遇到function,就把整個函數提到提到script(或function) 的最前邊(跟在var的後邊預解析先解析var 在解析 function)瀏覽器
全局做用域 --- 在任何地方都能訪問
函數外定義的變量擁有全局做用域
不使用var定義的變量擁有全局做用域
全部window對象上的屬性擁有全局做用域
沒有聲明在任何函數內部的函數擁有全局做用域
局部做用域 --- 只能在函數內部訪問
使用var在函數內部定義的變量,和使用function在函數內部聲明的函數,擁有局部做用域函數
[[Scopes]] : 做用域
當咱們聲明一個函數的時候,同時該函數就會建立一個屬性這個屬性是[[Scopes]](做用域),咱們在這個函數中 聲明的變量都會被存入這個函數的[[Scopes]]屬性中
變量與函數的查找規則:
當咱們調用一條數據的時候,js首先會在當前做用域中進行查找,若是找不到,就向上找到父級的做用域,若是在父級的做用域中也找不到,就繼續向上查找,直 到window的做用域。若是在window中也找不到,就報錯了this
小例子中有涉及到箭頭函數,因此咱們先認識一下ES6中的箭頭函數吧。spa
// 箭頭函數 var fn3 = (a, b) => { console.log(a, b); } // 幾種有條件的簡化寫法 // 當參數只有一個的時候 var fn4 = a => { console.log(a); } // 當參數有多個的時候 var fn5 = (a, b) => { console.log(a, b); } // 當沒有參數的時候 var fn6 = () => { console.log(1); } // 有且僅有一個形參的時候,能夠省略參數的小括號 // var fn7 = r => { // return r * r * Math.PI; // } // 只有一條語句的時候,能夠省略{},同時該條語句的結果將做爲該函數的返回值 var fn7 = r => r * r * Math.PI;
下面是箭頭函數的特性,主要是this須要多加註意code
在es6中,提供了一種新的函數格式:箭頭函數 注意: 1. 箭頭函數不能做爲構造函數,也就是箭頭函數不能使用new運算符 2. 箭頭函數的this永遠指向當前申明做用域對象 帶大括號的範圍 1. 普通函數this指向取決於調用 2. 箭頭函數this指向取決於申明 3. 箭頭函數沒有arguments對象 也會由於call的改變指向而改變this。即便在構造函數中。所謂的聲明時候的this,也多是被變化掉了。
接着,咱們一塊兒來看一個小例子吧!對象
var name='window'; var person1 = { name:'person1', show1: function() { console.log(this.name) }, show2: () => console.log(this.name), show3: function() { return function() { console.log(this.name)//這裏的this跟外層函數的this不要緊 } }, show4: function(){ return () => console.log(this.name) } }; //call是更改this指向的。 //對象屬性中的函數,寫法至關於 person1.show=function(){} //函數是全局做用域,掛載到window下,方便理解後面的this指向window var person2 = {name:'person2'}; person1.show1()//person1 //person1是個對象因此這裏指向的是對象的this person1 person1.show1.call(person2)//person2 //函數執行前先改了this指向 person1.show2()//window //函數聲明是掛載到window下的,箭頭函數與聲明做用域對象有關 //即便這裏調用該函數執行的是對象,但this仍是指的window person1.show2.call(person2)//window //同上,雖然執行函數時更改了this指向,可是這一套對箭頭很差使。因此仍然是window person1.show3()()//window //person1.show3()獲得的實際上是function(){console.log(this.name)} //函數執行,this指向的就是window person1.show3().call(person2)//person2 //person1.show3()獲得的實際上是function(){console.log(this.name)} //然而接着在執行function前更改了指向 person1.show3.call(person2)()//window //person1.show3.call(person2)更改了function() { // return function() { // console.log(this.name) // } // } //但再次調用括號執行本質仍是跟上面同樣,執行的是return出來的內部函數 //先更改的指向是障眼法而已 person1.show4()()//person1 //person1.show4()先執行獲得的是() => console.log(this.name) //再指向該箭頭函數,它的聲明做用域this指向的是person1 person1.show4().call(person2)//person1 //箭頭函數this在聲明時候指向的是person1。 //person1.show4()先執行獲得的是() => console.log(this.name) //這裏的call是更改箭頭函數自己的指向,是改不了的。 person1.show4.call(person2)()//person2 //person1.show4.call(person2)第一步執行這個的時候就先更改了指向 //在執行() => console.log(this.name)前先更改了person1的指向
對比: 上面第一個是一個對象而已,因此show2的箭頭函數聲明時直接指向的window, 下面的是構造函數,可以爲s1的箭頭函數提供執行環境,因此這裏指向的是TImer對象自己。blog
以上兩種狀況是須要注意區別的。ip
有錯誤或者不足之處請你們多多指正,謝謝啦!!!作用域