這篇文章主要介紹了解析JavaScript物件導向概念中的參考類型與作用域,文中重點講解了擴充函數運行作用域的需要的call和apply方法,朋友可以參考下
引用類型
引用類型主要包括:Object 類型、Array 類型、Date 類型、RegExp 類型、Function 類型等等。
引用類型使用時,需要從它們身上產生一個物件(實例)。也就是說,引用型別相當於一個模版,當我們想要用某個引用型別的時候,就需要用這個模版來產生一個物件來使用,所以引用型別有時候也稱作物件定義。
例如,我們需要產生一個person 對象,來定義某人的個人資訊和行為,那麼我們就需要依賴Object 類型:
var person = new Object(); person.name = "jiangshui"; person.sayName = function(){ console.log(this.name); }
上面的這個person 對象,透過new 運算子使用Object 類型這個「模版」定義。之後就可以對這個物件添加屬性 name 和方法 sayName 了。屬性和方法是 Object 類型具有的“功能”,所以透過 Object 等引用類型創建的物件就可以用這個了。
創建對像不一定非得需要用new 操作符,有一些類型可以簡化的創建,例如創建一個上面那樣的Object 類型的對象,也可以使用下面兩種方法:
var person = {}; person.name = "jiangshui"; person.sayName = function(){ console.log(this.name); }
或
var person = { name : "jiangshui", sayName : function(){ console.log(this.name); } };
{}運算子的功能就跟new Object() 一樣,簡化了運算。上面兩種寫法也有一些區別,第一種是“追加”,也就是在先前的定義中,繼續添加屬性或方法,如果之前已經存在了同名屬性方法,則會覆蓋。而第二種是“取代”,就是不管前面是否定義 person 物件的屬性和方法,這個方法會用新定義的內容,整個替換掉先前定義的。因為引用類型產生的對象,是儲存在記憶體中的一塊區域,然後將其指標保存在某變數中(person),第二種寫法,是產生了一個新物件(新記憶體區域),然後將person 變量指向了新記憶體區域,所以就把之前的取代了。了解這一點對後面理解,至關重要。
其他引用類型的用法大致一致,例如 Array 類型,也可以用 [] 來產生對象,或直接定義。產生陣列物件之後,就可以依照陣列的格式儲存資訊內容,此外物件會得到Array 類型中定義的那些方法,例如push、shift、sort 等等,就可以呼叫這些方法,例如:
#var colors = []; colors.push('red','green'); console.log(colors);
上面程式碼就是透過Array 類型建立一個陣列類型的對象,然後呼叫Array 類型裡面之前定義的push 方法,在物件裡面加入了red 和green 兩個值,最後在控制台印出來,就可以看到了。
call 和 apply 方法
這兩個方法是 Function 類型提供的,也就是說,可以在函數上面使用。 call 和 apply 方法的功能一樣,就是可以擴充函數運作的作用域,差別就在於使用 call 的時候,傳遞給函數的參數必須逐一列舉出來,而 apply 方法卻不用。這樣可以根據自己函數的要求來決定使用 call 或 apply。
擴充函數運行的作用域是什麼意思?舉個例子你就明白了。
你可以這樣理解,函數被包裹在一個容器(作用域)裡面,在這個容器裡面存在一些變數或其他東西,當函數運行,呼叫這些變數等,就會在目前容器裡面找這個東西。這個容器其實外面還包裹了一個更大的容器,如果當前小容器沒有的話,函數會到更大的容器裡面尋找,依次類推,一直找到最大的容器 window 物件。但是如果函數在目前小容器裡面運行的時候,小容器裡面有對應變數等,就算是大容器裡面也有,函數還是會呼叫自己容器裡面的。
而 call 和 apply 方法,就是解決這個問題,突破容器的限制。就前面例子:
var person = { name : "jiangshui", sayName : function(){ console.log(this.name); } };
開啟Chrome 的Console 之後,貼上進去執行一下,之後再執行person.sayName() 可以看到
這時候,person 就是一個容器,其中創建了一個sayName 方法(函數),執行的時候,必須在person 作用域下面執行。在最下面直接執行的時候,也就是在 window 的作用域下面執行會報錯 not defined,因為 window 下面沒有定義 sayName 方法。而裡面的 this 指針,是比較特殊的東西,它指向目前作用域,this.name 的意思,就是呼叫目前作用域下面的 name 值。
下面我們為window 物件新增name 屬性:
window.name = "yujiangshui";
或直接
name = "yujiangshui";
因为 window 是最大的容器,所以 window 可以省略掉,所有定义的属性或者变量,都挂靠到 window 上面去了,不信可以看:
那现在我们就想在 window 这个大容器下面,运行 person 小容器里面的 sayName 方法,就需要用 call 或 apply 来扩充 sayName 方法的作用域。执行下面语句:
person.sayName.call(window);
或者
person.sayName.call(this);
输出的结果都是一样的,你也可以换用 apply 看看效果,因为这个 demo 太简单的,不需要传递参数,所以 call 和 apply 功能效果就完全一致了。
解释一下上面代码,sayName 首先是 Function 类型的实例,也就具有了 call 方法和 apply 方法,call 和 apply 方法既然是 Function 类型的方法,所以就需要用这种方式调用 person.sayName.call(window) 而不是什么 person.sayName().call(window) 之类的。
然后 call 和 apply 方法的参数,就是一个作用域(对象),表示将前面的函数在传递进去的作用域下面运行。将 window 这对象传递进去之后,sayName 方法中的 this.name 指向的就是 window.name,于是就扩充了作用域。
为什么传递 window 和 this 都是一样的效果?因为我们当前执行这个函数的位置是 window,前面说过 this 指针指向的是当前作用域,所以 this 指向的就是 window,所以就等于 window。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
设计模式中的组合模式在JavaScript程序构建中的使用(高级篇)
详细解读JavaScript设计模式开发中的桥接模式(高级篇)
以上是全面分析JavaScript物件導向概念中的Object型別與作用域(附有範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!