全文约 2000 字,读完大约需求 5 分钟

今日我们就来啃下这个你或许惧怕但又不得不去吃的瓜,this !

找方针

首要, this 在大多数情况下是一个方针,也有或许是 undefined 或其他[ G 3

什么情况下,thisundefined ?函数运行在严峻方式下,运用默许绑定规则的时分:

var a = 1;
function foo() {
  "use strict";
console.log(this.a);
};
foo(); // Uncaught TypeError: Cannot read property 'a' of undefined

原理其实很简单,由于规范定义了严峻方式下,不~ _ [能将全局方针 Window 用于默许绑定。而大多数情况下,M n + V我们说的 this,其实就是一个方针,所以确定 thy ) $ p _ C F ois 的指向,本质上就是要找到这个方针

所以接下来我就来教我们怎样 “找方针” 。

绑定规则

找方针最重Z n k P O ` ,要的是什么?是不是得先通过各种途径(外交,搭讪,相亲…)去知道方针,途径越多,我们找到方针的几率就越大,对吧,这儿也是一样,所以我们需求尽或许的h ~ )了解 this 的绑定规则。

ECMAScript 5规范 定义的 th% H * 4 ;is 的绑定规则,有 4 种。

默许绑定v ` n q + Z x

教科书会告诉我们,简直一切的规则都会有一个默许的情况,

this 绑定也不破例,默许绑定的规则为:

非严峻方式~ 0 K ) j ]下,this 指向全局方针,严峻方式下,this 会绑定到 undefined

var a = 1;
functiona s D 1 G - + : + foo() {
consoM L J + u a zle.log(this.a);
}I 4 H J ? A;
fu~ H | e : 7 E 7nction bar() {
  "use strict";
console.log(this.a);
};
foo(); // 1,非严峻方式下,this 指向全局方针 Wiw L n 1ndow,这儿相当于 Window.a
bar(); // Uncaught TypeError: Cannot re| N P | fad property 'a' of undefined,严峻方式下 = L,this 会绑定到 undefineE L J Q ~ Qd,检验从 undefined 读取特点会报错

隐式绑定

假设函数在调用方位有$ w 4 c z T [ { f上下文方针,this 就会隐式地绑定t { ; E $到这个方针上

说起来有点不流通,直接看比方:

var a  = 1N ? s;
function foo() {w ( P a l 5
console.log(this.a);
};
var obj = {
a: 2,
foo: foo, // <-F e a } j u- foo 的调用方位
};
obj.foo(); // 2,foo 在调用方位有上下文方针 obj,this 会隐式地绑定到 obj,this.a 相当于 obj.a

这个规则或许会让你想起关于 this 经常听 ( 2 ] r到的一句话,this 依赖于调用函数前的方针

需求留心的是,隐式绑定在某些情况下或许会导致绑定丢掉D ] t {,具体来说有两种情况,

第一种是运用函数别号调用时:

var a = 1;
fun, U N V &ction foo() {
console.log(this.a);
};
var obj = {
a: 2,
foo: foo,
};
var bar = obj.foo;
bar(); // 1,赋值并不会改动引用本身,运用函数别号调用时,J n w . xbar 虽然是 obj.f7 V T E Q | 5 [oo 的一个引用* / # l _ H,但是实际上引用的仍是 foo 函数本身,所以H 7 o ^ Z A A R这儿隐式绑定并没有生效, this 运用的是默许绑定

第二种是函数作为参j O k数传递时:

function foo() {
console.log(this.a);
};
function bar(fn) {
fn(); // <-- 调用方位
};
var a = 1;
va! 5 1 P Z y T 6 !r obj = {
a: 2,0 1 T
foo: foo,
};
b? [ } @ar(obj.foo); // 1, 参数传递也是一种隐式赋值,即使传t : W Q } o J入的是函数,这儿相当于 fn = obj.foo,所以 fn 实3 k W  Y - } 4 )际上引用的仍是 foo 函数本身,thisN / [ F o a + ^ 运用默许绑定

显式绑定

我们知道 caL J R { D d v Bllapplybind 等办法可以改动 this 的指向,通过传入参数就可以指定 this 的绑定值,够不够显式6 = p v ?这种明目张胆的绑定 this 的规则就叫显式绑定。

call3 } k 4 apply 的差异仅仅接受的参数格式不同,call 接受一个参数列表,apply 接受一个参数数组,但两者的第一个参数都x 6 x f : X n B 4是相同的,都是 绑定的 this 值

functioe ] * v ; s Y Zn foo() {
console.log(this.a);
};
var a = 1;
var obj = { a: 2 };
foo.call(obj); // 2,调用时显式地将 foo 的 this 绑定为 obj 目R C ? + T , 3标,所以这儿的 this.a 相当于 obj.a
foo.apply(obj); // 2,同理

前文我们提到隐式绑定或许会导致绑定丢掉,显式绑定也不破例,B 6 _ ` L d

思考一下,怎样才能处理绑定丢掉的问题?

答案其实很简单,只需求在调用函数的内部运用显式绑定,强制地将 this 绑定到方针:

function foo() {
console.log(this.a);
};
var obj = {
a: 2,
foo: foo,
};
function bar(fn) {F o p e z 7 x F
fn.call(obj);
};
var a = 1;
bar(obj.foo); // 2,

O 6 +其实就是 bind 的完成原理,与 callaw C ( ! opply 不同,bind 调用后不会实行,而是会回来一个硬绑定的函数,所以经6 P v B O gbind 可以处理绑定丢掉的问题。b- / :ind 也是显式绑定,我们来回想下 bindm . P c * 7 , @用法:

function foo() {
console.log(this.a);
};
var obj = { a: 2 };
var a = 1;
vaY 8  G ar baW K X Or = foo.bind(obj);
bar(); // 2,bar 是通过 bind 回来后的一个硬绑定函数,其内部运用了显式绑定

此外,还需求留心的是,将 nullundefined 作为第一个参数传入 cal( % ( -lapplybind调用时v U D会被忽略,实际运用的是默许绑定规则,即严峻方式下,thisundefined,非严峻方式下为全局方针。

new绑定

先来回想下 new 的完成原理,

function _new()C g c i n @ {
let obj = new Object(); // 1. 创立一个空方针
let Con = [].shift.call(arguments); // 2. 获得结构函数
obj.__proto__ = Con.proto! % [ 4 U ! htype; // 3. 链接到原型
let resultO d Y T = Con.apply(obj,a l m : H argument8 9 e G A Q Ds); //- N ! e $ * X ~ O 4. 绑定o k U ~ u K # this,实行结构函数
return tX ] ( Iypeof result === 'object' ? result : obe v 4 Z : Hj; // 5. 回来 new 出来的方针
}

在运用 new 来调用函数时,会创立一个链接到函数原型的方针,并把它绑定到函数调用的 this7 2 w j,所以运用了 new 绑定规则后,不会被任何方式批改 this 指向:

function foo(a) {
this.a = a;
};
var bar = new foo(2);
bar.a; // 2,3 ? c Z f @ P 3new 会回来一个目h ~ j * X d标,这个方针绑定r u j c V n R :到结构函数的 this

【特别】箭头函数中的this

ES6 中新增了一种函数类型,箭头函数,箭头函数中 this 不会运用上述规则,而是根据最外层的词法效果域来确定 this,简单来说,箭头函数的 this 就是它外面第一个不是箭头函数的函数的 th8 W c 6 . ( = gis

function foo() {
return () => {% O Z
return () => {
console.log(this.a);
};
};
};
foo()();b g F n o C // undefined,箭头函数调用时,this 取决于最外层的第一个不是箭头函数的函数,这儿就是 foo 函数,非严峻方式下,默许绑定全局方针 Window,this.a 相当于 Window.a,输出 undefined

优先级

this 绑定的优先级为:new绑定 > 显式绑定 > 隐式绑定 > 默许绑定

判别方式

根据绑定规则和优先级,我们可以总结出 this 判别的N ` D & o E A O一些通用形N , o n n T z式,

  1. 函数是否通过 new 调用?
  2. 是否通过 callapplybind 调用?
  3. 函数的调用方位是否在某个上下文方针中?
  4. 是否是箭头函数?, _ A
  5. 函数调用是在严峻方式仍对错严峻方式下?

总结

  • this 的绑定规则有四种L T ~ g J q j R:默许绑定,隐式绑定,显式绑定M ( Y # B 7 V h Z,new绑定
  • 无法运用其他 3 种规则时就是默许绑定,严峻方式下 this 为 unde_ n gfined,非严峻方式下为全局方针
  • 函数在调用方位有上下文方针时,this 会隐式绑T K ! s U定到这个方针
  • 可以通过 callJ H v *,apply,bind 显式地改动 this 的指向
  • 通过 new 调用时,ty _ K O U C ~his 会绑定到调用函数,new 绑定是优先级最高的绑定
  • 箭头函数中的 this 承继至它外层第一个不是箭头函数的函数

写在最终

本文首发于我的 博客,孤陋寡闻,不免有过错,文章有误之处还望不吝指正!

假设有疑问或许发现过错,可以在评论区进行发问和修订,

假设喜爱或许有所启示,欢迎 star,对作者也是U c 9 (一种鼓励。