一直以來本人認為想深入瞭解一門語言,不光是讓自己變成擼sir,更需要時間的錘煉。能經得起時間考驗的東西更值得擁有。學習和使用Javascript一晃都7年了,最近才感覺自己對他才有頓悟,不知道是否來得有點遲。本文歸納了我對 JS中作用的理解,希望得學習有所幫助。 特別說明:這是從另一個側面(函數域的 ...
一直以來本人認為想深入瞭解一門語言,不光是讓自己變成擼sir,更需要時間的錘煉。能經得起時間考驗的東西更值得擁有。學習和使用Javascript一晃都7年了,最近才感覺自己對他才有頓悟,不知道是否來得有點遲。本文歸納了我對 JS中作用的理解,希望得學習有所幫助。
特別說明:這是從另一個側面(函數域的覆蓋範圍)來解釋和說明執行上下文。
一、作用域的理論理解
從入門Javascript時,無論是學校老師,還是你工作的老司機,都會很認真的考慮你,Js中有一個全局作用域,然後他包含很多的子域(如:由function、object創建作用域的),子域是可以很輕鬆的訪問父級域中的任何對象。這種表述你是否感覺很是空洞,難以理解。今本人就用浩瀚無邊的地域來舉個粟子:
通過上面這個Image,讓我一步一步帶你領略作用域的魅力。
1. 首先,地球:我們大家都很熟悉,他包含著你,我,以及世界上所有的國家(這兒就不論宇宙,否則會Hold不住),這肯定就是Js中的全局作用域了。地球包含天空、空氣,正如Js中全局作用包含包含Number、String、Function、Object、Boolean、Regex等對象。
2. 再者:中國、美國、北韓是三個平等的子作用域,他們都有利用地球 這個全局作用域的權利,如發射衛星。但美國再看不懂北韓,也不可能到北韓去乾什麼事情的。如Js的子作用域中可以任意使用Number、String等對象,但是沒辦法直接調用一個作用域平級的對象的方法。
3. 再次:你做為一個中國公民,可以依法使用中國內部的資源(只不過要麼交錢買,要麼就是拿了稅的),如果你想買點美國的的東西,那就需要new一個什麼寶、或者什麼東的來做中國與美國之間的引用,然後您懂的。如Js中需要訪問一個平級作用域的內容時,那你就需要拿到被訪問的引用。
4. 最後:bind,apply,call,可以這樣理解,bind就是在正規海購實體商場東西(預先可以看到實體的物品),而apply和call則是代購(只能視頻看)。bind改變一個作用但此函數不會立刻執行,而apply和call則會立刻執行。
二、示例代碼說明作用域
function Card(){ this.name = 'name'; } Card.prototype.getName =function(callback){ callback(); return this.name; } function PostCard(card){ this.name = "postCard" this.card =card; } PostCard.prototype.getName =function(){ var _name = this.card.getName(function(){ console.log("callback: " + this.name); }); console.log(_name + " " + this.name); } var card = new Card(); var postCard = new PostCard(card); postCard.getName();
1. 上述代碼Card和PostCard就像第一部分提及的中國、美國一樣,他們都有一個共同的父級作用域,就是window(地球)
2. Card和PostCard是平級作用域,如PostCard的getName方法想訪問Card的getName方法,就需要拿到Card的引用,如PostCard構造函數傳入的card實例就是為了完成這個事情。
3. Card的getName接受一個callback的高階函數(這又是另外一個話題,函數式編程了),這裡只要記住傳入就是一個類型為function的形參就行了。然後在Card的getName方法中執行了callback,但這裡你要註意,執行callback時沒有通過任何方式或手段來指定他的作用域,所以callback執行作用域為window(地球)。
4.輸出結果:
callback:
name postname
三、示例代碼說明bind\apply\call
function Direction(){ this.direction = "mid"; } Direction.prototype.set =function(val){ this.direction = val; } var direction = new Direction(); var dir02 = new Direction(); direction.set.call(direction, "right"); direction.set.apply(dir02, ["bottom"]); console.log(direction.direction); console.log(dir02.direction); //bind var tmp = direction.set.bind(dir02); tmp("bind after"); console.log('..........................'); console.log(direction.direction); console.log(dir02.direction);
本示例代碼提供了一個Direction函數,然後對this(當前實例)綁定了direction屬性,且在Direction原型上綁定了set方法(用於修改this上的direction).
1. 通過 new 創建了Direction的兩個實例direction,dir02
2. 通過call和apply改變作用域的指向,進行set方法的執行,call指向了direction實例,而apply指向了dir02的實例。這也展示了call與apply的區別,就是參數傳送用一個列表,一個是數組。
3. 通過輸出結果大家會發現,雖然都是調用direction實例的set方法,但改變的this.direction卻是對應的實例屬性。
4. 通過bind方法把direction實例的set方法與dir02相綁定,然後執行這個綁定,會發現改變是仍然是dir02實例的direction屬性。
5. 輸出結果
right
bottom
scope
..........................
right
bind after
四、作用域的總結
1. 只有一個全局作用域,但有無數個子作用域。
2. 作用域的創建與執行:
2.1 創建階段[函數被調用,但內部代碼還沒開始執行]
2.2 創建 作用域鏈
2.3 創建變數 函數 以及參數
2.4 決定this的值(也就是作用域,或者是執行上下文)
2.5 代碼執行[賦值、尋找函數引用以及解釋/執行代碼]