JavaScript:this(二)

  本文接着上篇文章繼續和你們聊聊this,若是是經過上篇文章來到了本篇文章那麼應該對this有必定的理解了,確定還帶着些對this的疑惑,下面將講述關於this的剩下內容,完全的理解this。若是沒有看過上篇文章的同窗,建議先看上篇文章的內容。java

  在講this以前咱們先了解一個前置知識,簡單賦值,咱們先來看看規範中怎麼定義的:面試

11.13.1 Simple Assignment ( = )數組

The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:數據結構

  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let rref be the result of evaluating AssignmentExpression.
  3. Let rval be GetValue(rref).
  4. Throw a SyntaxError exception if the following conditions are all true:
  • Type(lref) is Reference is true
  • IsStrictReference(lref) is true
  • Type(GetBase(lref)) is Environment Record
  • GetReferencedName(lref) is either "eval" or "arguments"
  1. Call PutValue(lref, rval).
  2. Return rval.

產生式AssignmentExpression : LeftHandSideExpression = AssignmentExpression按照下面的過程執行 :app

  1. lref爲解釋執行LeftHandSideExpression的結果
  2. rref爲解釋執行AssignmentExpression的結果
  3. rvalGetValue(rref)
  4. 拋出一個SyntaxError異常,當如下條件都成立:
  • Type(lref)Reference
  • IsStrictReference(lref)true
  • Type(GetBase(lref))爲環境記錄項
  • GetReferencedName(lref)爲"eval"或"arguments"
  1. 調用PutValue(lref, rval)
  2. 返回rval

  對於簡單賦值A=B,能夠這麼理解:ide

  1. 計算表達式A,獲得一個引用lrefA
  2. 計算表達式B,獲得一個值rvalB
  3. rvalB賦給lrefA指向的名稱綁定
  4. 返回結果rvalB

  上篇文章講到了this的默認綁定和隱性綁定,默認綁定和隱性綁定中還有幾個容易出錯的場景,咱們先來看看默認綁定:函數

默認綁定

// 實例一
var x = 1;
var obj = {
  x: 2,
  foo: function() {
    console.log(this.x); // 2
    function bar() {
      console.log(this.x); // 1
    }
    bar();
  }
}
obj.foo();
複製代碼

  實例一就是默認綁定的一個很經典的場景,第一個控制檯輸出是2,這個你們應該都沒有疑問,是隱性綁定,上篇文章已經分析過了。學習

  有疑惑的是第二個控制檯輸出爲何是1,也相信不少人也知道是1,說這就是默認綁定,函數bar在函數foo中調用,函數bar是獨立調用,bar沒有帶有任何修飾符的函數調用,因此barthis是指向全局對象window,因此輸出是1。對,這種解釋沒毛病,大部分文章都是這麼解釋的,可是這種解釋沒有依據,不能很好的說服我,感受就是在強迫我就是這麼理解的,時間長了,好像是這麼回事。下面從規範中解析爲何第二個輸出是1,從根本上理解這種默認綁定。ui

  前面的文章提到函數調用的時候會建立想要的執行上下文:this

executionContext: {
    variable object:vars, functions, arguments
    scope chain: variable object + all parents scopes
    thisValue: context object
}
複製代碼

  每一個執行上下文中都有一個變量對象(Variable object),變量對象包含了函數形參、函數聲明以及全部的變量聲明。

  咱們回到咱們的實例一中來,obj.foo()調用的時候,會建立函數foo的執行上下文,函數的變量對象以下:

foo_VO = {
  bar: <reference to FunctionDeclaration 'bar'> } 複製代碼

  foo函數調用有變量對象,天然也會有活動對象這個概念,活動對象前面的文章也講過了,咱們來看看ECMAScript規範是怎麼說的,關於執行上下文、變量對象等概念在ECMAScript5ECMAScript6都沒有說起到,在早版本ECMAScript3.1中有講解:

10.1.6 Activation Object

When control enters an execution context for function code, an object called the activation object is created and associated with the execution context. The activation object is initialised with a property with name arguments and attributes { DontDelete }. The initial value of this property is the arguments object described below.

  當函數調用時建立了執行上下文,活動對象建立並與執行上下文相關聯。活動對象被初始化,並含有一個名爲arguments屬性和不能刪除的屬性,初始化的屬性就是arguments對象。

The activation object is then used as the variable object for the purposes of variable instantiation.

  爲了變量實例化,活動對象被用做變量對象。

  ECMAScript3.1中解釋了函數調用的時候,活動對象被用做變量對象。咱們再來看看規範中進入了執行上下文,函數調用是怎麼說的:

10.2.3 Function Code

  • The scope chain is initialised to contain the activation object followed by the objects in the scope chain stored in the [[Scope]] property of the Function object.
  • Variable instantiation is performed using the activation object as the variable object and using property attributes { DontDelete }.
  • The caller provides the this value. If the this value provided by the caller is not an object (including the case where it is null), then the this value is the global object.

  咱們只要先看第二點,變量實例化,活動對象被用做了變量對象。這麼說建立執行上下文的時候,有變量對象, 當執行的時候,活動對象被用做變量對象。

  咱們再看看函數foo執行上下文中的活動對象是啥:

foo_AO = {
  arguments: {
    length: 0
  },
  bar: <reference to FunctionExpression "bar"> } 複製代碼

  在建立函數執行上下文階段,變量對象被建立,變量對象的屬性不能被訪問,此時的函數尚未執行,當函數來到執行階段,變量對象被激活,變成了活動對象,而且裏面的屬性都能訪問到,開始進行執行階段的操做。

  因此在實例一中,函數bar()等價於foo_AO.bar(),實例一能夠這麼的改寫:

// 實例一改寫
var x = 1;
var obj = {
  x: 2,
  foo: function() {
    console.log(this.x); // 2
    function bar() {
      console.log(this.x); // 1
    }
    foo_AO.bar(); // bar()
  }
}
obj.foo();
複製代碼

  當函數obj.foo()被調用後,同時函數bar也會被調用,也就是foo_AO.bar()被調用,這樣也就回到了咱們熟悉的模式了,函數barfoo_AO修飾了,foo_AO.bar是屬性訪問,foo_AO.bar會被解釋執行爲一個Reference

foo_AO.bar_reference = {
  base: foo_AO,
  name: 'bar
}
複製代碼

  由於foo_AO.bar會被解釋執行爲一個Referencefoo_AO也是一個對象,因此thisValue = GetBase(foo_AO.bar_reference),也就是foo_AO,然而在ECMAScript程序中咱們是不能直接接觸到活動對象,只是內部實現,那this真正的指向是指向哪裏呢,咱們來看看規範(3.1)中怎麼說的:

10.1.6 Activation Object

The activation object is purely a specification mechanism. It is impossible for an ECMAScript program to access the activation object. It can access members of the activation object, but not the activation object itself. When the call operation is applied to a Reference value whose base object is an activation object, null is used as the this value of the call.

  活動對象純粹是一種規範機制。ECMAScript程序是可能直接訪問活動對象。它能夠訪問活動對象的成員,可是不能訪問活動對象自己。當調用操做應用到其基(base)值是活動對象的引用(Reference)時,將null做爲this的值。

  因此實例一的this指向null,最終會不會是null呢,咱們再來看看下面的:

10.2.3 Function Code

The caller provides the this value. If the this value provided by the caller is not an object (including the case where it is null), then the this value is the global object.

  當thisnull的是,this又會指向全局對象,也就是window。因此實例一中第二個控制檯輸出的this是執行window,即this.x = 1

  講到這裏,把實例一中的第二個控制檯輸出的this爲何是window的真正緣由講完了,你們應該能真正的理解是什麼緣由了,也不須要硬記。

隱性綁定

// 實例二
function foo() {
  console.log(this.a);
}
var obj2 = {
    a: 1,
    foo: foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};
obj1.obj2.foo(); // 1
複製代碼

  實例二也是隱性綁定的一個經典的應用場景,控制檯最後輸出的值是1,爲何是1,大部分文章的解釋是在對象屬性引用鏈中只有最後一層在調用位置中起做用,是this的隱性綁定,在實例二中起做用的是obj2,因此this指向的obj2,即控制檯輸出的是1,這也是在強行解釋,並無給出合理的解釋緣由。

  其實實例二考察的不只僅是this的指向,還考察了JavaScript的運算符優先級,若是能把運算符的優先級捋清楚,這種隱性綁定是很好理解的。

  obj1.obj2.foo()執行,其執行順序是怎麼樣的呢,能夠參考JavaScript運算符的優先級彙總表,優先級最高的是(),優先級是20,因此obj1.obj2.foo()先執行的是函數,因此其函數調用後,產生的MemberExpression就是obj1.obj2.foo,那麼這個表達式的執行順序是怎樣的?

  看到出來這就是屬性訪問表達式,成員表達式,鏈式成員訪問,其優先級是19,因此obj1.obj2.foo後於()執行,而且成員訪問的執行順序是從左到右的。

  即obj1.obj2.foo是先執行obj1.obj2獲得執行結果(result)後再執行結果的result.foo。這就是obj1.obj2.foo()的基本執行順序,按照此順序咱們能夠把實例二改寫成:

// 改寫實例二
function foo() {
  console.log(this.a);
}
var obj2 = {
    a: 1,
    foo: foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};
var result = obj1.obj2;
result.foo(); // 1
複製代碼

  咱們須要先知道result的值,result = obj1.obj2很明顯,這就是一個簡單賦值,上面有介紹簡單賦值。

  一開始,聲明瞭變量obj1和變量obj2,並都作了簡單賦值,後面又聲明瞭變量result也作了簡單賦值,都按照上面的簡單賦值的定義,賦值後最終內容以下:

obj1.obj2 = {
  a: 1,
  foo: function() {
    console.log(this.a);
  }
}
result = {
  a: 1,
  foo: function() {
    console.log(this.a);
  }
}
複製代碼

  也就是說對於result = obj1.obj2result的值是一個對象。

  而後咱們再來看看result.foo()的執行,也就是等價於:

result = {
  a: 1,
  foo: function() {
    console.log(this.a);
  }
}
reslut.foo();
複製代碼

  這種方式也就是咱們上篇文章分析過的方式,result.foo執行解釋結果是一個Reference,其數據結構是:

reference_result_foo = {
	base: result,
	name: "foo"
}
複製代碼

  這裏的this的最終指向就是result,就再也不具體分析了,參考上篇文章的內容,this.a等價於result.a,控制檯輸出的也就是1

  咱們再回到實例二中,

// 實例二
function foo() {
  console.log(this.a);
}
var obj2 = {
    a: 1,
    foo: foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};
obj1.obj2.foo(); // 1
複製代碼

  這裏的this指向也就是obj2,控制檯輸出的也就是1了。

隱式丟失

  還有一個很常見的場景:

// 實例三
function foo() {
  console.log(this.a); // 1
}
var obj = {
  a: 2,
  foo: foo
};
var bar = obj.foo;
var a = 1;
bar();
複製代碼

  這裏控制檯輸出是1,大部分解釋是this綁定隱式丟失,而後指向了全局,因此是1。這種解釋也有點牽強。

  其實仍是能夠從簡單賦值的角度來解釋,bar的值是obj.foo的值,是obj.foo的真正的值,是經過GetValue()方法獲得的值,不是引用,這裏也就是函數foo,並非對象obj的方法foo。若是這個能理解的話,這裏很好解釋了,執行函數bar其實就是執行函數foothis指向也就是全局對象windowthis.a也就是window.a,輸出的值也就是1了。具體的緣由就不展開解釋了,都已經寫到這裏了,應該很容易理解了。這也就是所謂的隱式丟失。

顯式綁定

  在函數原型上定義了三個方法容許手動設置函數調用的this值:apply()call()bind(),這個三個方法做用是一致的,強行修改函數調用的this值。咱們下面來看看這三個方法:

apply()

15.3.4.3 Function.prototype.apply (thisArg, argArray)

When the apply method is called on an object func with arguments thisArg and argArray, the following steps are taken:

  1. If IsCallable(func) is false, then throw a TypeError exception.
  2. If argArray is null or undefined, then

a. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and an empty list of arguments.

  1. ...
  1. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.

NOTE The thisArg value is passed without modification as the this value. This is a change from Edition 3, where a undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value.

  我只把apply()調用的部分步驟列出來了,只須要看第二步和第三步就能夠了。

  當以thisArgargArray爲參數在一個func對象上調用apply方法,採用以下步驟:

  1. 若是IsCallable(func)false, 則拋出一個TypeError異常
  2. 若是argArraynullundefined, 則
  • 返回提供thisArg做爲this值並以空參數列表調用 func[[Call]]內部方法的結果
  1. 提供thisArg做爲this值並以argList做爲參數列表,調用func[[Call]]內部方法,返回結果

  規範把apply方法的調用寫的明明白白的,傳了兩個參數,一是thisArg,最終會把thisArg做爲this值,二是argArray,是一個數組,做爲參數列表,最後調用func[[Call]]內部方法,返回結果。咱們來看看下面的實例:

// 實例四
function foo(param){
  console.log(this.a); // 10
  console.log(param); // 1
}
var obj = {
  a : 10
};
foo.apply(obj, [1]);
複製代碼

  實例四中函數foo調用後this強行被指向了obj

  須要注意的是,thisArgundefinednull時它會被替換成全局對象,全部其餘值會被應用ToObject 並將結果做爲this值。參考實例五:

// 實例五
function foo(param){
  console.log(this.a); // 2
  console.log(param); // 1
}
var obj = {
  a : 10
};
var a = 2;
foo.apply(null, [1]);
複製代碼

call()

15.3.4.4 Function.prototype.call(thisArg[ , arg1 [ , arg2, … ]])

When the call method is called on an object func with argument thisArg and optional arguments arg1, arg2 etc, the following steps are taken:

  1. If IsCallable(func) is false, then throw a TypeError exception.
  2. t argList be an empty List.
  3. If this method was called with more than one argument then in left to right order starting with arg1 append each argument as the last element of argList
  4. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.

NOTE The thisArg value is passed without modification as the this value. This is a change from Edition 3, where a undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value.

  當以thisArg和可選的arg1arg2等等做爲參數在一個func對象上調用call方法,採用以下步驟:

  1. 若是IsCallable(func)false, 則拋出一個TypeError異常
  2. argList爲一個空列表
  3. 若是調用這個方法的參數多餘一個,則從arg1開始以從左到右的順序將每一個參數插入爲argList的最後一個元素
  4. 提供thisArg做爲this值並以argList做爲參數列表,調用func[[Call]]內部方法,返回結果

  一樣規範把call方法的調用寫的明明白白的,能夠接收多個參數,第一個參數是thisArg,最終會做爲this值,從第二個參數開始全部的參數都是func的參數列表,調用func[[Call]]內部方法,返回結果。咱們來看看下面的實例:

// 實例六
function foo(param){
  console.log(this.a); // 10
  console.log(param); // 1
}
var obj = {
  a : 10
};
foo.call(obj, 1);
複製代碼

  實例六中函數foo調用後this強行被指向了obj

  須要注意的是,thisArgundefinednull時它會被替換成全局對象,全部其餘值會被應用ToObject並將結果做爲this值。

bind()

15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, …]])

The bind method takes one or more arguments, thisArg and (optionally) arg1, arg2, etc, and returns a new function object by performing the following steps:

  1. Let Target be the this value.
  2. If IsCallable(Target) is false, throw a TypeError exception.
  3. Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
  4. Let F be a new native ECMAScript object
  5. ...
  6. Return F

  只把bind()執行的部分步驟列出來了,只要看上面幾個步驟就能夠了。

  bind方法須要一個或更多參數,thisArg和(可選的)arg1, arg2, 等等,執行以下步驟返回一個新函數對象:

  1. Targetthis
  2. 若是IsCallable(Target)false, 拋出一個TypeError異常
  3. A爲一個(可能爲空的)新內部列表,它包含按順序的thisArg 後面的全部參數(arg1, arg2等等)
  4. F爲一個新原生ECMAScript對象
  5. ...
  6. 返回F

  一樣規範也把bind方法的調用寫的明明白白的,bind最終返回的是一個新的函數對象,這個和上面的apply方法和call方法的調用不同。

  將值綁定到了函數的this上,並將綁定好的函數返回,因此bind只是一個函數,不會馬上執行。咱們來看看下面的實例:

// 實例七
function foo(param){
  console.log(this.a); // 10
  console.log(param); // 1
}
var obj = {
  a : 10
};
var foo = foo.bind(obj, 1);
foo();
複製代碼

  實例七中函數foo調用後this強行被指向了obj

  上面就是關於顯示綁定的全部內容,主要是經過函數原型上的applycallbind三個方法實現的,面試中對這三個方法也是常常會問到,這三個方法原生實現怎麼寫,能夠參考規範中對這三個方法的執行步驟,在這裏暫時不展開了,後期考慮。

new綁定

  咱們先看個實例:

// 實例八
function Foo() {
  this.x = 10;
}
var foo = new Foo();
console.log(foo.x); // 10
複製代碼

  對於輸出是10,緣由你們都知道,咱們來看看new具體作來什麼,使得Foo函數中的this指向了foo

11.2.2 The new Operator

The production NewExpression : new NewExpression is evaluated as follows:

  1. Let ref be the result of evaluating NewExpression.
  2. Let constructor be GetValue(ref).
  3. If Type(constructor) is not Object, throw a TypeError exception
  4. If constructor does not implement the [[Construct]] internal method, throw a TypeError exception
  5. Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments)

  產生式NewExpression : new NewExpression按照下面的過程執行:

  1. ref爲解釋執行NewExpression的結果
  2. constructorGetValue(ref)
  3. 若是Type(constructor) 不是Object,拋出一個TypeError異常
  4. 若是constructor沒有實現[[Construct]]內置方法 ,拋出一個TypeError異常
  5. 返回調用constructor[[Construct]]內置方法的結果 , 傳入按無參數傳入參數列表 ( 就是一個空的參數列表 )

  按照規範中的定義,就實例八而言,ref最終解釋結果是一個引用(reference),constructorGetValue(ref),也就是函數Foo

constructor = function Foo() {
  this.x = 10;
}
複製代碼

  最後是返回調用constructor[[Construct]] 內置方法的結果。咱們再來看看[[Construct]]內置方法是怎麼執行的:

13.2.2 [[Construct]]

When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:

  1. Let obj be a newly created native ECMAScript object
  2. Set all the internal methods of obj as specified in 8.12
  3. Set the [[Class]] internal property of obj to "Object"
  4. Set the [[Extensible]] internal property of obj to true
  5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype"
  6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.
  7. Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4
  8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args
  9. If Type(result) is Object then return result
  10. Return obj

  當以一個可能的空的參數列表調用函數對象F[[Construct]] 內部方法,採用如下步驟

  1. obj爲新建立的ECMAScript原生對象
  2. 依照8.12設定obj的全部內部方法
  3. 設定obj[[Class]]內部方法爲"Object"
  4. 設定obj[[Extensible]]內部方法爲true
  5. proto爲以參數"prototype"調用F[[Get]]內部屬性的值
  6. 若是Type(proto)Object,設定obj[[Prototype]]內部屬性爲proto
  7. 若是Type(proto)不是Object,設定obj[[Prototype]]內部屬性爲15.2.4描述的標準內置的Objectprototype對象
  8. objthis值,調用[[Construct]]的參數列表爲args,調用F[[Call]]內部屬性,令result爲調用結果
  9. 若是Type(result)Object,則返回result
  10. 返回obj

  函數對象F[[Construct]]作了this指向的綁定,內部建立了一個新的對象obj,新對象內置原型屬性指向了F的原型對象prototype,最後新建立的對象objthis的值,並調用F[[Call]]內部屬性,最後返回obj。上面的實現步驟能夠簡單的理解爲下面程序:

var foo = new Foo() = {
  var obj = {};
  obj.__proto__ = Foo.prototype;
  var result = Foo.call(obj);
  return typeof result === 'Object' ? result : obj;
}
複製代碼

  上面也就是面試常常問到的手動實現new原理,能夠作爲參考。因此當執行var foo = new Foo()時,this就指向新的對象,最後把執行結果返回給了foofoo真實的值也就是對象。

  也就是說函數Foothis指向的是foo,因此控制檯輸出的值是foo.x,也就是10了。

箭頭函數

  箭頭函數是ECMAScript6提出來的,在這裏不對箭頭函數作詳細的介紹,只說明箭頭函數關於this的狀況。

  在ECMAScript6中有這麼一句話:

8.1.1.3Function Environment Records

A function Environment Record is a declarative Environment Record that is used to represent the top-level scope of a function and, if the function is not an ArrowFunction, provides a this binding.

  函數環境記錄是聲明式環境記錄,用於表示函數的頂級範圍。若是函數不是箭頭函數,那麼會提供this綁定。也就是說在箭頭函數中不存在this綁定這個說法。

[[thisBindingStatus]]

If the value is "lexical", this is an ArrowFunction and does not have a local this value.

  當值是lexical時,表示是箭頭函數而且沒有本身的this值綁定

9.2.4FunctionInitialize (F, kind, ParameterList, Body, Scope)

  1. If kind is Arrow, set the [[ThisMode]] internal slot of F to lexical.   當箭頭函數初始化時,[[ThisMode]]設置爲lexical

[[ThisMode]]

lexical means that this refers to the this value of a lexically enclosing function.

  當[[ThisMode]]是lexical時,表示this值是當前封閉函數lexical scope

9.2.1.2OrdinaryCallBindThis ( F, calleeContext, thisArgument )

  1. If thisMode is lexical, return NormalCompletion(undefined)
  2. Return envRec.BindThisValue(thisValue)

  在函數執行前綁定this的時候,傳入的thisArgument會被直接忽略。

  再來看一段話:

14.2.16 Runtime Semantics: Evaluation

An ArrowFunction does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.

  箭頭函數不會爲argumentssuperthis或者new.target定義本地綁定。對於箭頭函數中的argumentssuperthis或者new.target的任何引用都必須解析爲lexically封閉環境中綁定。一般這將是一個當即封閉的函數的函數環境。

  總之,箭頭函數初始化,在lexical環境中,沒有自身的this綁定,箭頭函數也無法修改this,函數內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

  咱們來看看下面的實例:

// 實例九
function foo() {
  setTimeout(() => {
    console.log(this.a); // 2
  }, 100);
}
var a = 1;
foo.call({ a: 2 });
複製代碼

  實例九中控制檯輸出的是2,不是1,若是定時器裏面的函數是普通函數的話,那麼this的指向會是window,輸出的值1。在這裏輸出的是2

  再來看看一個實例,有點意思:

// 實例十
var obj= {
  that: this,
  bar: function() {
    return () => {
      console.log(this); 
    }
  },
  baz: () => {
    console.log(this); 
  },
  bam: function() {
    console.log(this);
  }
}
console.log(obj.that);  // window
obj.bar()(); // obj
obj.baz(); // window
obj.bam(); // obj
複製代碼

  看到實例九,應該有人會對結果感到奇怪,這個能夠思考下。

  1. obj的當前做用域是windowobj.that === window
  2. 若是不用functionfunction有本身的函數做用域)將其包裹起來,this會指向window,如上面的baz函數
  3. function包裹的目的就是將箭頭函數綁定到當前的對象上。函數的做用域是當前這個對象,而後箭頭函數會自動指向函數所在做用域的this,即obj

  把箭頭函數的相關定義理解清楚,就不會存在疑惑了。

  到這裏已經把關於this的內容幾乎所有梳理完了。

結語

  文章若有不正確的地方歡迎各位大佬指正,也但願有幸看到文章的同窗也有收穫,一塊兒成長!

-------------------------------本文首發於我的公衆號----------------------------

最後,歡迎你們關注個人公衆號,一塊兒學習交流。
相關文章
相關標籤/搜索