javascript中的this和函數息息相關,因此今天,我就給你們詳細地講述一番:javascript函數中的this
一談到this,不少讓人暈暈乎乎的抽象概念就跑出來了,這裏我就只說最核心的一點——函數中的this總指向調用它的對象,接下來的故事都將圍繞這一點展開
(提醒前排的筒子們準備好茶水和西瓜,我要開始講故事啦!!)
【故事】有一個年輕人叫"迪斯"(this),有一天,迪斯不當心穿越到一個叫 「伽瓦斯克利」(javascript)的 異世界,此時此刻迪斯身無分文, 他首先要作的事情就是——找到他的住宿的地方——調用函數的對象
this的默認綁定
【故事——線路1】若是迪斯(this)直到天黑前都沒有找到能收留本身的住所,他眼看就要過上非洲難民的生活, 這時候,一位樂善好施的魔法師村長——window救世主通常地出現了:先住在我家吧!
【正文】
當一個函數沒有明確的調用對象的時候,也就是單純做爲獨立函數調用的時候,將對函數的this使用默認綁定:綁定到全局的window對象
function fire () {
console.log(this === window)
}
fire(); // 輸出true
上面的例子我相信對大多數人都很簡單,但有的時候咱們把例子變一下就會具備迷惑性:
function fire () {
// 我是被定義在函數內部的函數哦!
function innerFire() {
console.log(this === window)
}
innerFire(); // 獨立函數調用
}
fire(); // 輸出true
函數 innerFire在一個外部函數fire裏面聲明且調用,那麼它的this是指向誰呢? 仍然是window
許多人可能會顧慮於fire函數的做用域對innerFire的影響,但咱們只要抓住咱們的理論武器——沒有明確的調用對象的時候,將對函數的this使用默認綁定:綁定到全局的window對象,即可得正確的答案了
下面這個增強版的例子也是一樣的輸出true
var obj = {
fire: function () {
function innerFire() {
console.log(this === window)
}
innerFire(); // 獨立函數調用
}
}
obj.fire(); //輸出 true
【注意】在這個例子中, obj.fire()的調用實際上使用到了this的隱式綁定,這就是下面我要講的內容,這個例子我接下來還會繼續講解
【總結】 凡事函數做爲獨立函數調用,不管它的位置在哪裏,它的行爲表現,都和直接在全局環境中調用無異
this的隱式綁定
【故事——線路2】 迪斯(this)穿越來異世界「伽瓦斯克利」(javascript)的時候,恰好身上帶了一些錢,因而他找到一個旅館住宿了下來
當函數被一個對象「包含」的時候,咱們稱函數的this被隱式綁定到這個對象裏面了,這時候,經過this能夠直接訪問所綁定的對象裏面的其餘屬性,好比下面的a屬性
var obj = {
a: 1,
fire: function () {
console.log(this.a)
}
}
obj.fire(); // 輸出1
如今咱們須要對日常司空見慣的的代碼操做作一些更深的思考,首先,下面的這兩段代碼達到的效果是相同的:
// 我是第一段代碼
function fire () {
console.log(this.a)
}
var obj = {
a: 1,
fire: fire
}
obj.fire(); // 輸出1
// 我是第二段代碼
var obj = {
a: 1,
fire: function () {
console.log(this.a)
}
}
obj.fire(); // 輸出1
fire函數並不會由於它被定義在obj對象的內部和外部而有任何區別,也就是說在上述隱式綁定的兩種形式下,fire經過this仍是能夠訪問到obj內的a屬性,這告訴咱們:
1. this是動態綁定的,或者說是在代碼運行期綁定而不是在書寫期
2. 函數於對象的獨立性, this的傳遞丟失問題
(下面的描述可能帶有我的的情感傾向而顯得不太嚴謹,但這是由於我但願閱讀者儘量地理解我想表達的意思)
隱式綁定下,做爲對象屬性的函數,對於對象來講是獨立的
基於this動態綁定的特色,寫在對象內部,做爲對象屬性的函數,對於這個對象來講是獨立的。(函數並不被這個外部對象所「徹底擁有」)
我想表達的意思是:在上文中,函數雖然被定義在對象的內部中,但它和「在對象外部聲明函數,而後在對象內部經過屬性名稱的方式取得函數的引用」,這兩種方式在性質上是等價的(而不只僅是效果上)
定義在對象內部的函數只是「剛好能夠被這個對象調用」而已,而不是「生來就是爲這個對象所調用的」
借用下面的隱式綁定中的this傳遞丟失問題來講明:
var obj = {
a: 1, // a是定義在對象obj中的屬性 1
fire: function () {
console.log(this.a)
}
}
var a = 2; // a是定義在全局環境中的變量 2
var fireInGrobal = obj.fire;
fireInGrobal(); // 輸出 2
上面這段簡單代碼的有趣之處在於: 這個於obj中的fire函數的引用( fireInGrobal)在調用的時候,行爲表現(輸出)徹底看不出來它就是在obj內部定義的,其緣由在於:咱們隱式綁定的this丟失了!! 從而 fireInGrobal調用的時候取得的this不是obj,而是window
上面的例子稍微變個形式就會變成一個可能困擾咱們的bug:
var a = 2;
var obj = {
a: 1, // a是定義在對象obj中的屬性
fire: function () {
console.log(this.a)
}
}
function otherFire (fn) {
fn();
}
otherFire(obj.fire); // 輸出2
在上面,咱們的關鍵角色是otherFire函數,它接受一個函數引用做爲參數,而後在內部直接調用,但它作的假設是參數fn仍然可以經過this去取得obj內部的a屬性,但實際上, this對obj的綁定早已經丟失了,因此輸出的是全局的a的值(2),而不是obj內部的a的值(1)
在一串對象屬性鏈中,this綁定的是最內層的對象
在隱式綁定中,若是函數調用位置是在一串對象屬性鏈中,this綁定的是最內層的對象。以下所示:
var obj = {
a: 1,
obj2: {
a: 2,
obj3: {
a:3,
getA: function () {
console.log(this.a)
}
}
}
}
obj.obj2.obj3.getA(); // 輸出3
this的顯式綁定:(call和bind方法)
【故事——線路3】 迪斯(this)穿越來異世界「伽瓦斯克利」(javascript),通過努力的打拼,積累了必定的財富,因而他買下了本身的房子
上面咱們提到了this的隱式綁定所存在的this綁定丟失的問題,也就是對於 「 fireInGrobal = obj.fire」
fireInGrobal調用和obj.fire調用的結果是不一樣的,由於這個函數賦值的過程沒法把fire所綁定的this也傳遞過去。這個時候,call函數就派上用場了
call的基本使用方式: fn.call(object)
fn是你調用的函數,object參數是你但願函數的this所綁定的對象。
fn.call(object)的做用:
1.即刻調用這個函數(fn)
2.調用這個函數的時候函數的this指向object對象
例子:
var obj = {
a: 1, // a是定義在對象obj中的屬性
fire: function () {
console.log(this.a)
}
}
var a = 2; // a是定義在全局環境中的變量
var fireInGrobal = obj.fire;
fireInGrobal(); // 輸出2
fireInGrobal.call(obj); // 輸出1
本來丟失了與obj綁定的this參數的fireInGrobal再次從新把this綁回到了obj
可是,咱們其實不太喜歡這種每次調用都要依賴call的方式,咱們更但願:可以一次性 返回一個this被永久綁定到obj的fireInGrobal函數,這樣咱們就沒必要每次調用fireInGrobal都要在尾巴上加上call那麼麻煩了。
怎麼辦呢? 聰明的你必定能想到,在fireInGrobal.call(obj)外面包裝一個函數不就能夠了嘛!
var obj = {
a: 1, // a是定義在對象obj中的屬性
fire: function () {
console.log(this.a)
}
}
var a = 2; // a是定義在全局環境中的變量
var fn = obj.fire;
var fireInGrobal = function () {
fn.call(obj) //硬綁定
}
fireInGrobal(); // 輸出1
若是使用bind的話會更加簡單
var fireInGrobal = function () {
fn.call(obj) //硬綁定
}
能夠簡化爲:
var fireInGrobal = fn.bind(obj);
call和bind的區別是:在綁定this到對象參數的同時:
1.call將當即執行該函數
2.bind不執行函數,只返回一個可供執行的函數
【其餘】:至於apply,由於除了使用方法,它和call並無太大差異,這裏不加贅述
在這裏,我把顯式綁定和隱式綁定下,函數和「包含」函數的對象間的關係比做買房和租房的區別。
由於this的緣故
在隱式綁定下:函數和只是暫時住在「包含對象「的旅館裏面,可能過幾天就又到另外一家旅館住了
在顯式綁定下:函數將取得在「包含對象「裏的永久居住權,一直都會」住在這裏「
new綁定
【故事】 迪斯(this)組建了本身的家庭,並生下多個孩子(經過構造函數new了許多個對象)
執行new操做的時候,將建立一個新的對象,而且將構造函數的this指向所建立的新對象
function foo (a) {
this.a = a;
}
var a1 = new foo (1);
var a2 = new foo (2);
var a3 = new foo (3);
var a4 = new foo (4);
console.log(a1.a); // 輸出1
console.log(a2.a); // 輸出2
console.log(a3.a); // 輸出3
console.log(a4.a); // 輸出4
謝謝你們哦!!
【完】