近期準備好好的讀一讀《你不知道的JavaScript(上卷)》這本書,俗話說的好,好記性不如爛筆頭,讀到this這章感受是時候須要一些筆記了。文中若有錯誤之處,歡迎指出。javascript
什麼是this,咱們先來看看做者的回答。前端
當一個函數被調用時,會建立一個活動記錄(有時候也稱爲執行上下文)。這個記錄會包含函數在哪裏被調用(調用棧)、函數的調用方法、傳入的參數等信息。this就是記錄的其中一個屬性,會在函數執行的過程當中用到。vue
function foo() { console.log( this.a ); } var a = 2; foo(); // 2
這段代碼輸出2 , 說明this默認綁定到了全局對象java
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; obj.foo(); // 2
這段代碼this綁定到了obj對象,當函數引用有上下文對象(context)時,隱式綁定規則會把this綁定到這個上下文對象。react
隱式丟失問題git
// 例子1111 function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 函數別名!  var a = "oops, global"; // a是全局對象的屬性」 bar(); // "oops, global" //2222 function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var a = "oops, global"; // a是全局對象的屬性 setTimeout( obj.foo, 100 ); // "oops, global
例子1111雖然bar是obj.foo的一個引用,可是實際上,它引用的是foo函數自己,所以此時的bar()實際上是一個不帶任何修飾的函數調用,所以應用了默認綁定。例子222在setTimeout函數中丟失了this綁定,道理是同樣的,咱們能夠把參數傳遞當作一種隱式賦值。es6
在分析隱式綁定時,咱們必須在一個對象內部包含一個指向函數的屬性,並經過這個屬性間接引用函數,從而把this間接(隱式)綁定到這個對象上。github
那麼若是咱們不想在對象內部包含函數引用,而想在某個對象上強制調用函數,該怎麼作呢?windows
學過js的估計對 call,apply和bind都不陌生,js提供的這些原生方法就給咱們提供了顯式綁定this的途徑。閉包
function foo() { console.log( this.a ); } var obj = { a:2 }; foo.call( obj ); // 2
經過foo.call(..),咱們能夠在調用foo時強制把它的this綁定到obj上。
若是你傳入了一個原始值(字符串類型、布爾類型或者數字類型)來看成this的綁定對象,這個原始值會被轉換成它的對象形式(也就是new String(..)、new Boolean(..)或者new Number(..))。這一般被稱爲「裝箱」。
「從this綁定的角度來講,call(..)和apply(..)是同樣的,它們的區別體如今其餘的參數上,可是如今咱們不用考慮這些。」
咱們已經知道了this綁定的4種方式,可是使用call和apply咱們仍是不能解決隱式丟失的問題,思考如下例子:
function foo() { console.log( this.a ); } var obj = { a:2 }; var bar = function() { foo.call( obj ); }; bar(); // 2 setTimeout( bar, 100 ); // 2 bar.call(windows) //沒法再修改this
經過call咱們在函數bar內強制指定了foo的this,以後不論如何調用bar,它總會在obj上手動調用foo,這種綁定是一種顯示強制綁定,所以稱爲硬綁定。這就是bind的產生來由(由於硬綁定比較經常使用,es5中新增了bind方法),經過硬綁定製定this,上面代碼如今就變成了
...略 var bar = foo.bind(obj) bar(); // 2 setTimeout( bar, 100 ); // 2
關於new,咱們首先梳理一下基礎知識。
new操做符幹了什麼?
建立(或者說構造)一個全新的對象。
這個新對象會被執行[[原型]]鏈接。
這個新對象會綁定到函數調用的this。
若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象」
function foo(a) { this.a = a } var bar = foo(2) var baz = new foo(2) bar.a //Cannot read property 'a' of undefined baz.a // 2 this綁定到了foo
綁定規則優先級和es6的this新特性
若是要判斷一個運行中函數的this綁定,就須要找到這個函數的直接調用位置。找到以後就能夠順序應用下面這四條規則來判斷this的綁定對象。
ES6中的箭頭函數並不會使用四條標準的綁定規則,而是根據當前的詞法做用域來決定this,具體來講,箭頭函數會繼承外層函數調用的this綁定(不管this綁定到什麼)。
function showThis () { console.log(this) } function showStrictThis () { 'use strict' console.log(this) } showThis() // window showStrictThis() // undefined
優先級最小的this對象,默認綁定。
思考下面的例子:
var person = { name: '11', showThis () { return this } } person.showThis() === person //true var person2 = { name: '22', showThis () { return person.showThis() } } var person3 = { name: '33', showThis () { var retrunThis = person.showThis return retrunThis() } } person.showThis() //person person2.showThis() //? person3.showThis() //?
咱們首先要找到調用位置,在2裏是這句return person.showThis(),隱式綁定了一個person對象,因此輸出person ,3 是return retrunThis() ,this默認綁定到全局,返回window.
function showThis () { return this } var person = { name: 'person' } showThis() // window showThis.call(p1) // person showThis.apply(p1) // person
經過顯式綁定指定了context object。
function showThis () { return this } var person = { name: 'person' } var personBind = showThis.bind(person) personBind() //person var person2 = { name: 'person2' } personBind.call(person2) //person
bind方法強綁定了this,已經沒法再經過顯式綁定切換this。
function showThis () { return this } var person = { name: 'person' } var person2 = { name: 'person2' } var personBind = showThis.bind(person) personBind() //person new personBind() //showThis
new優先級高於bind,因此能夠覆蓋this。
function foo() { setTimeout(() => { // 這裏的this在詞法上繼承自foo() console.log( this.a ); },100); } var obj = { a:2 }; foo.call( obj ); // 2
箭頭函數並非使用function關鍵字定義的,而是使用被稱爲「胖箭頭」的操做符=>定義的。箭頭函數不使用this的四種標準規則,而是根據外層(函數或者全局)做用域來決定this。箭頭函數的綁定沒法被修改,包括new。 關於箭頭函數網上有不少詳細全面的講解。這裏再也不展開。
原型鏈,閉包,堆棧結構等等。。。javascript入門咱們都以爲是比較簡單的,找一份敲代碼的工做真不難,只要努力搬磚就行了。可是若是不去深刻了解js的底層機制,這條道路恐怕是走不遠的。沒有好的基礎,咱們能夠學會使用react,使用vue,使用別人的插件,可是本身造輪子仍是遠遠達不到的。
前端水很深,須要學的的東西太多太雜,真要學,幾乎都能學都有用,都不學,照樣敲代碼沒什麼大問題,工做業務積累應付工做仍是足夠的,不夠?那就加個班唄。加油吧~爲了避免被淘汰,共勉~
若是以爲本文對你有所幫助,就star一下吧~大傳送之術! 個人博客Github