摘要:構造器的外表跟普通函數一樣,他們的區別在于被調用的方式。即,使用運算符創建對象時,就是將函數當作構造器調用。本節內容為設計模式與開發實踐第二章筆記。
this
JavaScript的this總是指向一個對象,至于指向哪個對象,是在運行時基于函數的執行環境的動態綁定的,而非函數被聲明時的環境。
this的指向this的指向大致可以分為以下4類:
作為對象的方法調用
作為普通函數調用
構造器調用
Function.prototype.call或Function.prototype.apply調用
1.作為對象的方法調用
當函數作為對象的方法被調用時,this指向該對象:
// 聲明obj對象 var obj = { a: "a屬性的值", // a 屬性 getA: function(){ // getA()方法 console.log(this === obj); // 輸出:true console.log(this.a); // 輸出: a屬性的值 } }; obj.getA();
2.作為普通函數調用
當函數不作為對象的屬性被調用時,也就是以普通函數方式,this指向全局對象。在瀏覽器的JavaScript里,全局對象是window對象。
window.name = "globalName"; // 聲明全局對象的name屬性 var getName = function(){ // 定義getName()函數 return this.name; }; // 調用函數 console.log(getName()); //輸出: globalName
window.name = "globalName"; // 聲明全局對象的name屬性 var myObject = { // 聲明myObject對象 name: "objectName"; getName: function(){ // 定義getName()方法 return this.name; } } var getName = myObject.getName; // 將getName()方法賦給變量getName console.log(getName()); // 輸出: globalName
3.構造器調用
JavaScript沒有類,但可以從構造器中創建對象,也提供了new運算符用于調用構造器。
大部分JavaScript函數都可以當作構造器使用。構造器的外表跟普通函數一樣,他們的區別在于被調用的方式。即,使用new運算符創建對象時,就是將函數當作構造器調用。當用new運算符調用函數時,該函數總會返回一個對象,此時,構造器里的this指向返回的這個對象。
var myClass = function(){ this.name = "className"; }; var obj = new myClass(); console.log(obj.name); // 輸出:seven
但,如果構造器顯式地返回了一個object類型的對象,那么此函數將返回這個object類型的對象,而不是函數本身所定義的對象,例如:
var myClass = function(){ this.name = "className"; return { //顯式地返回一個對象 name: "anne" } }; var obj = new myClass(); console.log(obj.name); // 輸出:anne
而,如果構造器不顯式地返回任何數據,或返回一個非對象類型的數據,就不會造成上述情形。
var myClass = function(){ this.name = "className"; return "anne"; // 返回string類型 }; var obj = new myClass(); console.log(obj.name); // 輸出:className
4.Function.prototype.call 或 Function.prototype.apply調用
跟普通函數調用相比,用 Function.prototype.call 或 Function.prototype.apply 可以動態地改變傳入函數的this。
var A = { name: "ObjectA", getName: function(){ return this.name; } }; var B = { name: "ObjectB" }; console.log(A.getName()); // 作為對象的方法調用,輸出:ObjectA console.log(A.getName.call(B)); // 輸出:ObjectB丟失的this
我們經常會因為this的指向與我們的期待不同,而出現undefined的情況,例如:
var obj = { name: "objName"; getName: function(){ return this.name; } }; // 作為對象的方法調用,指向obj對象 console.log(obj.getName()); // 輸出:objName // 作為普通函數調用,指向全局對象window,name屬性尚未定義 var getName2 = obj.getName; console.log(getName2()); // 輸出:Lundefinedcall 和 apply
ECAMScript3給Function的原型定義了兩個方法,分別是Function.prototype.call 或 Function.prototype.apply。在一些函數式風格的代碼編寫中,call和apply方法尤為有用。
call和apply的區別Function.prototype.call 或 Function.prototype.apply的作用一模一樣,區別僅在于傳入參數形式的不同。
apply接受兩個參數,第一個參數制定了函數體內this對象的指向,第二個函數為一個帶下標的集合,這個集合可以是數組,也可以是類數組。apply方法把這個集合中的元素作為參數傳遞給被調用的函數。
var func = function(a, b, c){ console.log([a, b, c]); // 輸出:[1,2,3] }; func.apply(null, [1, 2, 3]);
call傳入的參數數量不固定,第一個參數也是代表了函數體內的this指向,從第二個參數開始往后,每個參數依次被傳入函數:
var func = function(a, b, c){ console.log([a, b, c]); // 輸出:[1,2,3] }; func.call(null, 1, 2, 3);
當調用一個函數時,JavaScript的解釋器并不會計較形參和實參在數量、類型、以及順序上的區別,JavaScript的參數在內部就是用一個數組來表示的。從這個意義上說,apply比call的使用率更高,我們不必關心具體有多少參數被傳入函數,只要用apply一股腦地推過去就可以了。
當使用call或apply的時候,如果我們傳入的第一個參數為null,函數體內的this會指向默認的宿主對象,在瀏覽器中則是window:
var func = function(a, b, c){ console.log(this); }; func.apply(null, [1, 2, 3]); //輸出:window對象 func.call(null, 1, 2, 3); //輸出:window對象call和apply的用途
改變this指向
Function.prototype.bind
借用其他對象的方法
1.改變this指向
call和apply最常見的用途是改變函數內部的this指向:
var A = { name: "nameA"; }; var B = { name: "nameB"; }; window.name = "nameWindow"; var getName = function(){ conlole.log(this.name); }; getName(); // 以普通函數調用,指向了window對象,輸出:nameWindow getName.call(A); // 改變了this的指向,指向了傳入的對象,輸出:nameA getName.call(B); // 改變了this的指向,指向了傳入的對象,輸出:nameB
2.Function.prototype.bind
大部分高級瀏覽器都實現了內置的Function.prototype.bind,用來指定函數內部的this指向。
若沒有原生的Function.prototype.bind實現,可以通過模擬一個:
Function.prototype.bind = function(context){ var self = this; // 保存原函數 return function(){ // 返回一個新的函數 return self.apply(context, arguments); // 執行新函數的時候,會把之前傳入的context當作新函數體內的this } }; var obj = { name: "objName" }; var func = function(){ console.log(this.name); // 輸出:objName }.bind(obj); func();
我們通過Function.prototype.bind來“包裝”func函數,并且傳入一個對象context當作參數,這個context對象就是我們想要修正的this對象,即讓函數內部的this指向這個對象。
3.借用其他對象的方法
我們知道,杜鵑即不會筑巢,也不會孵雛,而是把自己的蛋寄托給云雀等其他鳥類,讓它們代為孵化和養育。在JavaScript中也存在類似的借用現象。
場景一:借用構造函數
通過這種技術,能夠實現一些類似繼承的效果:
var A = function(name){ this.name = name; }; var B = function(){ // 借用A的構造函數 A.apply(this, arguments); }; B.prototype.getName = function(){ return this.name; }; var b = new B("baby"); console.log(b.getName()); // 輸出:baby
場景二:類數組對象的操作
函數的參數列表arguments是一個類數組對象,雖然它也有下標,但它并非真正的數組,所以也不能像數組一樣,進行排序操作或者往集合里添加一個新的元素。這時,可以借用Array.prototype對象上的方法。
比如,想往arguments中添加一個新的元素,可以借用Array.prototype.push:
(function(){ Array.prototype.push.call(arguments, 3); console.log(arguments); // 輸出:[1,2,3] })(1, 2);
想把arguments轉成真正的數組的時候,可以借用Array.prototype.slice方法;想截去arguments列表中的頭一個元素時,可以借用Array.prototype.shift方法。
PS:本節內容為《JavaScript設計模式與開發實踐》第二章 筆記。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86139.html
摘要:發布者的狀態發生變化時就會通知所有的訂閱者,使得它們能夠自動更新自己。觀察者模式的中心思想就是促進松散耦合,一為時間上的解耦,二為對象之間的解耦。參考設計模式與開發實踐第章發布訂閱模式設計模式第章第節觀察者模式 概述 觀察者模式又叫發布 - 訂閱模式(Publish/Subscribe),它定義了一種一對多的關系,讓多個觀察者對象同時監聽某一個目標對象(為了方便理解,以下將觀察者對象叫...
摘要:在全局對象中調用,自然讀取的是全局對象的值構造器調用說明作為構造器調用時,指向返回的這個對象。最直觀的表現就是,去看一些優秀框架的源代碼時,不再是被繞的暈乎乎的。 學習起因: 在之前的JavaScript學習中,this,call,apply總是讓我感到迷惑,但是他們的運用又非常的廣泛。遂專門花了一天,來弄懂JavaScript的this,call,apply。中途參考的書籍也很多,以...
摘要:注意該方法的作用和方法類似,只有一個區別,就是方法接受的是若干個參數的列表,而方法接受的是一個包含多個參數的數組。指定的參數列表。返回值返回值是你調用的方法的返回值,若該方法沒有返回值,則返回。 溫馨提示:作者的爬坑記錄,對你等大神完全沒有價值,別在我這浪費生命溫馨提示-續:打call原本是屬于我們偶像宅文化中的專業名詞,指的是飯們在看live時在臺下配合愛豆演出的節奏喊口號,舉例:超...
摘要:訂閱模式的一個典型的應用就是后面會寫一篇相關的讀書筆記。享元模式享元模式的核心思想是對象復用,減少對象數量,減少內存開銷。適配器模式對目標函數進行數據參數轉化,使其符合目標函數所需要的格式。 設計模式 單例模式 JS的單例模式有別于傳統面向對象語言的單例模式,js作為一門無類的語言。使用全局變量的模式來實現單例模式思想。js里面的單例又分為普通單例和惰性單例,惰性單例指的是只有這個實例...
摘要:發生這種情況的條件是當引用類型值的對象恰好為活躍對象。總結本文介紹中的使用,更重要的是幫助我們能更好地理解值在全局函數構造函數以及一些特例的情況中值的變化。然而,由于對于來說沒有任何意義,因此會隱式轉換為全局對象。 接上一篇Javascript this 的一些學習總結02【轉自cnblogs的JKhuang】 引用類型以及this的null值 對于前面提及的情形,還有例外的情況,當調...
閱讀 3653·2021-10-11 10:58
閱讀 2245·2021-10-08 10:05
閱讀 2024·2021-09-27 13:34
閱讀 3558·2019-08-30 15:53
閱讀 2723·2019-08-30 14:02
閱讀 3551·2019-08-29 16:55
閱讀 614·2019-08-29 15:41
閱讀 1062·2019-08-29 15:23