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