摘要:于是決定寫個歸納。如果懂了,那么下面的例子也就會做了已知調(diào)用函數(shù)的對象是,所以指向,即。相當(dāng)于在全局作用域聲明了變量,并且賦值。實際上,調(diào)用函數(shù)的是關(guān)鍵字。使用來調(diào)用函數(shù),即函數(shù)的構(gòu)造調(diào)用時,我們會構(gòu)造一個新對象,并把它綁定到調(diào)用中的上。
對this的理解,我一直都是用一句話概括:誰調(diào)用它,它就指向誰。
好像也沒有什么問題,但仔細看了<你不知道的JavaScript>這本書和網(wǎng)上一些文章后,發(fā)現(xiàn)this的原理還挺講究的。于是決定寫個歸納。
(對了,無知的我實在沒想到原來bind也和this扯上關(guān)系。。)
this的綁定規(guī)則有4種。分別是:
1、默認綁定
2、隱式綁定
3、顯示綁定
4、new綁定
需要明確:this的值雖然會隨著函數(shù)使用場合的不同而發(fā)生變化,但有一個原則,它指向的是調(diào)用它所在的函數(shù)的那個對象。
1、默認綁定(純函數(shù)調(diào)用)function test(){ console.log(this.a); } var a = 1; test();
當(dāng)調(diào)用test()時,因為應(yīng)用了this的默認綁定,this.a被解析成全局變量a,this指向全局對象window。所以結(jié)果為1。
怎么知道應(yīng)用了默認綁定呢?當(dāng)前test()是直接使用不帶任何修飾的函數(shù)引用進行調(diào)用的。這個簡單來說就是沒有任何前綴啊等東西,很純粹!而其調(diào)用位置是全局作用域,更能確定除了默認綁定,無法應(yīng)用其他規(guī)則了。
如果懂了,那么下面的例子也就會做了
function test(){ this.a = 2; console.log(a); } test();
已知調(diào)用函數(shù)test()的對象是window,所以this指向window,即this.a===window.a。由于window可以省略,因此簡寫成a。
相當(dāng)于在全局作用域聲明了變量a,并且賦值a=2。
this.a = 2
-->window.a = 2
-->a = 2
所以結(jié)果為2。
可能說得太啰嗦了,直接說下一種綁定。
2、隱式綁定(作為方法調(diào)用)通俗地說就是一個函數(shù),被當(dāng)作引用屬性添加到了一個對象中了,然后以 “對象名.函數(shù)名()” 形式進行調(diào)用,這時如果函數(shù)引用有上下文對象,隱式綁定規(guī)則會把函數(shù)調(diào)用中的this綁定到這個上下文對象。
下面看下例子。
function test(){ console.log(this.a); } var obj = { a:3, test:test }; obj.test();
對象obj中包含兩個屬性a和test,其值分別是3和一個函數(shù)test()。
obj.test()表示函數(shù)引用時有上下文對象(也就是這里的obj)。
根據(jù)隱式綁定規(guī)則,會把test()中的this綁定到這個上下文對象,即this被綁定到obj,this.a===obj.a。
所以結(jié)果為3。
如果懂了,那么下面的例子也就會做了
function test(){ console.log(this.a); } var obj2 = { a:4, test:test }; var obj1 = { a:400, obj2:obj2 } obj1.obj2.test()
看起來復(fù)雜了些,不過只要記住下面這個準(zhǔn)則就可以了。
對象屬性引用鏈中,只有上一層或者說最后一層在調(diào)用位置中起作用
所以只有obj2這個上下文對象生效,結(jié)果為4。
隱式綁定的番外篇——隱式丟失這個網(wǎng)上貌似很少提及,隱式丟失,通俗來講就是“變low”了!
原本應(yīng)用隱式綁定的,因為丟失綁定對象,變回應(yīng)用默認綁定了!把this綁定到全局對象或undefined。
當(dāng)對象將其引用屬性給了新的引用,再次調(diào)用這個新的引用時,原本this指向的對象就會改為指向window。
到底在說什么,看個例子。
function foo(){ console.log(this.a); } var obj = { a:5, foo:foo }; var bar = obj.foo; //這句就是關(guān)鍵 var a = "oops, global"; bar(); // "oops, global"
b引用了test()函數(shù)本身,此時的b()是一個不帶任何修飾的函數(shù)調(diào)用,因此應(yīng)用了默認綁定。
如果懂了,那么下面的例子也就會做了
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo); // "oops, global"
其實和上面的沒什么區(qū)別,只是這里把函數(shù)作為參數(shù)傳遞了,參數(shù)傳遞是一種隱式賦值。此時的this指向的是調(diào)用它的函數(shù)的對象即全局對象,因此應(yīng)用了默認綁定。
3、顯示綁定(apply/call調(diào)用)回顧一下隱式綁定,其關(guān)鍵是把函數(shù)當(dāng)作引用屬性添加到了對象中,通過這個屬性間接引用這個函數(shù),把this簡介(隱式)綁定到這個對象上。
如果不想在對象內(nèi)部包含函數(shù)引用,而想簡單粗暴地在某個對象強制調(diào)用一個函數(shù),這時就用到了函數(shù)的call()和apply()方法。
call和apply,以往我單純理解為“控制this的指向”,現(xiàn)在才發(fā)現(xiàn)是原來是this綁定規(guī)則中的一種。
call和apply區(qū)別
apply接收的是數(shù)組參數(shù),call接收的是連續(xù)參數(shù)。所以當(dāng)傳入的參數(shù)數(shù)目不確定時,多使用apply。
(tips:這里推薦個方法記憶apply和call各自接收的參數(shù):apply為a開頭,數(shù)組Array也是a開頭,所以apply接收的是數(shù)組參數(shù))
看下面例子。
function test(){ console.log(this.a); } var obj = { a:6 }; test.call(obj);
當(dāng)調(diào)用test時強制把它的this綁定到obj上。所以this.a===obj.a,結(jié)果為6。
注意:后續(xù)參數(shù)傳入的是原始值的話,會被轉(zhuǎn)換成它的對象形式(如字符串類型-->new String()如此類推)
這次我們不用call,用apply。
var a = 0; function test(){ console.log(this.a); } var obj = {}; obj.a = 7; obj.m = test; obj.m.apply(); obj.m.apply(obj);
同樣的,對象obj包含了兩個屬性a和m,m引用了函數(shù)test()。
obj.m.apply(obj)即把test()這個函數(shù)中的this綁定到對象obj上。即this指向的是obj。所以this.a===obj.a。
“apply沒有參數(shù)怎么辦,基本語法都是包含參數(shù)的啊,至少給個對象,讓this有個指向啊。”
apply定義了當(dāng)沒有參數(shù)時,全局對象會自動默認成為其第一個參數(shù),apply()等價于apply(window)。
因此obj.m.apply(),test()中的this就指向了全局對象window,結(jié)果為0。
干嘛用的?解決前面提到的隱式丟失問題。
回顧當(dāng)初隱式丟失的第二個例子。
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo); // "oops, global"
將其改一下變成:
function foo(){ console.log(this.a); } function doFoo(fn){ fn.call(obj); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo);
依舊是創(chuàng)建了doFoo()這個函數(shù),但在其內(nèi)部手動調(diào)用了 obj.foo.call(obj),
把foo()強制綁定到了obj對象,之后無論如何調(diào)用doFoo(),它總會手動在obj上調(diào)用foo。
對于硬綁定,ES5提供了一個內(nèi)置方法Function.prototype.bind,我們把上面的例子再改!
function foo(){ console.log(this.a); } function doFoo(fn){ fn.bind(obj); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo);
一執(zhí)行,我的天!啥也沒有????
這就對了,這是bind的“效果”,也是和apply、call的主要區(qū)別——延遲調(diào)用。
也就是說bind其實只是函數(shù)的引用,要想執(zhí)行需要進行回調(diào),即fn.bing(obj)(),
此時就能輸出5了。
好,現(xiàn)在把隱式丟失的第一個例子改動
function foo(){ console.log(this.a); } var obj = { a:5, foo:foo }; var bar = obj.foo.bind(obj); var a = "oops, global"; bar(); //5
為了能證明bind的特點,函數(shù)在回調(diào)時執(zhí)行,把bind改成call,最后3行代碼變成
var bar = obj.foo.call(obj); var a = "oops, global"; bar(); //Uncaught TypeError: bar is not a function
結(jié)果報錯了,對,因為call和apply都是綁定后立刻執(zhí)行的,都執(zhí)行完了,bar就只是一個沒有賦值的變量而已。
總結(jié)下“顯示綁定三人組”:
共同點: 1、都用于控制this指向; 2、第一個參數(shù)都是this需要指向的對象,也就是上下文; 3、都可以后續(xù)參數(shù)傳遞; 4、沒有任何參數(shù)時,this都指向全局對象window 區(qū)別: 1、call、apply綁定后立刻執(zhí)行,bind是延遲執(zhí)行。換言之,當(dāng)你希望改變上下文環(huán)境之后并非立即執(zhí)行,而是回調(diào)執(zhí)行的時候,就使用bind()方法吧。4、new綁定(作為構(gòu)造函數(shù)調(diào)用)
什么是構(gòu)造函數(shù),一個函數(shù)被new調(diào)用時,該函數(shù)就是構(gòu)造函數(shù)。
(變身前:普通函數(shù);使用地攤貨new變身器,變身后:構(gòu)造函數(shù))
function test(){ this.x = 8; } var obj = new test(); console.log(obj.x);
你可以簡單粗暴理解為:就相當(dāng)于test()被對象obj調(diào)用,其this指向obj。
但貌似很不規(guī)范。。
實際上,調(diào)用函數(shù)的是new關(guān)鍵字。
使用new來調(diào)用函數(shù),即函數(shù)的“構(gòu)造調(diào)用”時,我們會構(gòu)造一個新對象,
并把它綁定到test()調(diào)用中的this上。所以在代碼中,this就指向了新對象obj
判斷this的4種規(guī)則根據(jù)優(yōu)先級從高到低排序如下:
1、函數(shù)在 new 中調(diào)用,this綁定的是這個新對象
2、函數(shù)通過 call、apply或bind 調(diào)用,this綁定的是指定對象
3、函數(shù)在某上下文對象中調(diào)用,this綁定的是這個上下文對象
4、以上都不是,使用默認綁定。(在全局作用域調(diào)用,this綁定window對象)
關(guān)于this書中還提及到很多其他知識,例如軟綁定、this詞法箭頭函數(shù)等,就不歸納到這里了
歸納前看過的相關(guān)書籍或文章:
1、<你不知道的JavaScript>(上)第二部分第二章 this全面解析
2、阮一峰的網(wǎng)絡(luò)日志-JavaScript的this用法
http://www.ruanyifeng.com/blo...
3、chanzen的個人博客-call,apply,bind用法和意義
http://ovenzeze.coding.me/use...
4、腳本之家-JS中的this變量的使用介紹
http://www.jb51.net/article/4...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/82958.html
摘要:從現(xiàn)在開始,養(yǎng)成寫技術(shù)博客的習(xí)慣,或許可以在你的職業(yè)生涯發(fā)揮著不可忽略的作用。如果想了解更多優(yōu)秀的前端資料,建議收藏下前端英文網(wǎng)站匯總這個網(wǎng)站,收錄了國外一些優(yōu)質(zhì)的博客及其視頻資料。 前言 寫文章是一個短期收益少,長期收益很大的一件事情,人們總是高估短期收益,低估長期收益。往往是很多人堅持不下來,特別是寫文章的初期,剛寫完文章沒有人閱讀會有一種挫敗感,影響了后期創(chuàng)作。 從某種意義上說,...
摘要:從現(xiàn)在開始,養(yǎng)成寫技術(shù)博客的習(xí)慣,或許可以在你的職業(yè)生涯發(fā)揮著不可忽略的作用。如果想了解更多優(yōu)秀的前端資料,建議收藏下前端英文網(wǎng)站匯總這個網(wǎng)站,收錄了國外一些優(yōu)質(zhì)的博客及其視頻資料。 前言 寫文章是一個短期收益少,長期收益很大的一件事情,人們總是高估短期收益,低估長期收益。往往是很多人堅持不下來,特別是寫文章的初期,剛寫完文章沒有人閱讀會有一種挫敗感,影響了后期創(chuàng)作。 從某種意義上說,...
摘要:迭代器在原有的數(shù)據(jù)結(jié)構(gòu)類型上新增了兩種類型,我們在使用的時候還可以通過自由組合的形式使用這些結(jié)構(gòu)類型達到自己想要的數(shù)據(jù)結(jié)構(gòu),這就需要一種統(tǒng)一的接口機制供我們調(diào)用處理不同的數(shù)據(jù)結(jié)構(gòu)。 引言 萬丈高樓平地起,欲練此功,必先打好基本功: ) 在了解 ES6 新增的變量類型前,我們必須先知道 JavaScript 在ES6之前,有如下六種基本數(shù)據(jù)類型:Null、Undefined、Number...
摘要:即將立秋的課多周刊第期我們的微信公眾號,更多精彩內(nèi)容皆在微信公眾號,歡迎關(guān)注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。課多周刊機器人運營中心是如何玩轉(zhuǎn)起來的分享課多周刊是如何運營并堅持下來的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號:fed-talk,更多精彩內(nèi)容皆在微信公眾號,歡迎關(guān)注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大...
閱讀 2051·2023-04-26 02:32
閱讀 714·2021-11-18 13:12
閱讀 2603·2021-10-20 13:48
閱讀 2693·2021-10-14 09:43
閱讀 4004·2021-10-11 10:58
閱讀 3863·2021-09-30 10:00
閱讀 3071·2019-08-30 15:53
閱讀 3655·2019-08-30 15:53