JavaScript之this使用

1、調(diào)用位置


在理解this的綁定之前,首先理解調(diào)用位置,決定this的綁定

function a() {

  // 當(dāng)前調(diào)用棧是:a, 因此,當(dāng)前調(diào)用位置是全局作用域

  console.log('a');

  b(); // b的調(diào)用位置

}

function b() {

  // 當(dāng)前調(diào)用棧是:a=>b, 因此,當(dāng)前調(diào)用位置是a中

  console.log('b');

  c(); // c的調(diào)用位置

}

function c() {

  // 當(dāng)前調(diào)用棧是:a=>b=>c, 因此,當(dāng)前調(diào)用位置是b中

  console.log('c')

}

a();

2、綁定規(guī)則


2.1 默認(rèn)綁定

最常用的函數(shù)調(diào)用類(lèi)型:獨(dú)立函數(shù)調(diào)用??梢钥醋瞿J(rèn)規(guī)則

this.a被解析成了全局變量a,因?yàn)楸纠?,函?shù)調(diào)用應(yīng)用了this的默認(rèn)綁定,因此this指向全局對(duì)象

function foo() {

  console.log(this.a);

}

var a = 2;

foo(); // 2

?如果在嚴(yán)格模式下,全局對(duì)象無(wú)法使用默認(rèn)綁定,因此this會(huì)綁定到undefined

function foo() {

  "use strict"

  console.log(this.a);

}

var a = 2;

foo(); // TypeError: this is undefined

this的綁定規(guī)則完全取決于調(diào)用位置,但是運(yùn)行在非嚴(yán)格模式下,默認(rèn)綁定才能綁定到全局對(duì)象;嚴(yán)格模式下與foo()的調(diào)用位置無(wú)關(guān)

function foo() {

  console.log(this.a);

}

(function () {

  "use strict"

  foo(); // 2

})();

2.2 隱式綁定

另一條需要考慮的規(guī)則是調(diào)用位置是否有上下文對(duì)象,或者說(shuō)是否被某個(gè)對(duì)象擁有或者包含,不過(guò)這種說(shuō)法可能會(huì)造成一些誤導(dǎo).

foo函數(shù)嚴(yán)格來(lái)說(shuō)都不屬于obj對(duì)象,然而調(diào)用位置會(huì)使用obj上下文來(lái)引用函數(shù),因此函數(shù)被調(diào)用時(shí)obj對(duì)象“擁有”或“包含”foo

當(dāng)foo()被調(diào)用時(shí),它的落腳點(diǎn)確實(shí)指向obj對(duì)象,當(dāng)函數(shù)引用有上下文對(duì)象時(shí),隱式綁定規(guī)則會(huì)把函數(shù)調(diào)用中的this綁定到這個(gè)上下文對(duì)象。

function foo() {

  console.log(this.a);

}

var obj = {

  a: 2,

  foo: foo

}

obj.foo(); // 2

對(duì)象屬性引用鏈中只有最頂層或者說(shuō)最后一層會(huì)影響調(diào)用位置

function foo() {

  console.log(this.a)

}

var obj1 = {

  a: 42,

  foo: foo

}

var obj2 = {

  a: 2,

  obj1: obj1

}

obj2.obj1.foo(); // 42

隱式丟失

一個(gè)最常見(jiàn)的this綁定問(wèn)題就是被隱式綁定的函數(shù)丟失綁定的對(duì)象,也就是說(shuō)她回默認(rèn)綁定。從而把this綁定到全局對(duì)象或者undefined上,取決于是否嚴(yán)格模式

雖然bar是obj.foo的一個(gè)引用,到時(shí)實(shí)際上,它引用的是foo函數(shù)本身,因此此時(shí)的bar()其實(shí)是一個(gè)不帶任何修飾的函數(shù)調(diào)用,因此應(yīng)用了默認(rèn)綁定。

function foo() {

  console.log(this.a)

}

var obj = {

  a: 2,

  foo: foo

}

var bar = obj.foo; // 函數(shù)別名

var a = 'global'; // a是全局對(duì)象的屬性

bar(); // 'global

參數(shù)傳遞其實(shí)就是一種隱式賦值,因此我們傳入函數(shù)是也會(huì)是隱式賦值,所以結(jié)果和上述一樣

function foo() {

  console.log(this.a);

}

function doFoo(fn) {

  fn();

}

var obj = {

  a: 2,

  foo: foo

}

var a = 'global';

doFoo(obj.foo); // 'global'

如果把函數(shù)傳入語(yǔ)言?xún)?nèi)置的函數(shù)而不是自己的函數(shù)也是一樣的結(jié)果.

function foo() {

  console.log(this.a);

}

var obj = {

  a: 2,

  foo: foo

}

var a = 'global';

setTimeout(obj.foo, 1000) // 'global'

javascript環(huán)境中內(nèi)置的函數(shù)實(shí)現(xiàn)偽代碼類(lèi)似:

function setTimeout(fn, delay) {

  // 等待delay毫秒

  fn(); //調(diào)用位置

}

2.3顯示綁定

call()、apply()他們第一個(gè)參數(shù)是一個(gè)對(duì)象,他們會(huì)把這個(gè)對(duì)象綁定到this,接著調(diào)用函數(shù)時(shí)制定這個(gè)this,因?yàn)榭梢灾苯又付╰his的綁定對(duì)象,因此我們稱(chēng)之為顯示綁定。

function foo() {

  console.log(this.a);

}

var obj = {

  a: 2

}

foo.call(obj); // 2

1、硬綁定

創(chuàng)建了函數(shù)bar(),并在它的內(nèi)部手動(dòng)調(diào)用了foo.call(obj),因此強(qiáng)制把foo的this綁定到obj。無(wú)論之后如何調(diào)用函數(shù)bar,他總會(huì)手動(dòng)在obj上調(diào)用foo。

這種綁定是一種顯示的強(qiáng)制綁定,因此我們稱(chēng)之為硬綁定。

function foo() {

  console.log(this.a)

}

var obj = {

  a: 2

}

var bar = function () {

  foo.call(obj)

}

bar(); // 2

setTimeout(bar, 100); // 2

硬綁定的bar不可能再修改它的this

bar.call(window); // 2

硬綁定的典型應(yīng)用場(chǎng)景是創(chuàng)建一個(gè)包裹函數(shù),傳入所有的參數(shù)并返回接收到的所有值。

function foo(something) {

  console.log(this.a, something);

  return this.a + something;

}

var obj = {

  a: 2

}

var bar = function () {

  return foo.apply(obj, arguments);

}

var b = bar(3); // 2 3

console.log(b); // 5

另一種使用方法是創(chuàng)建一個(gè)重復(fù)使用的函數(shù)

function foo(something) {

  console.log(this.a, something);

  return this.a + something;

}

function bind(fn, obj) {

  return function () {

    return fn.apply(obj, arguments)

  }

}

var obj = {

  a: 2

}

var bar = bind(foo, obj)

var b = bar(3); // 2 3

console.log(b); // 5

由于硬綁定是一種非常常用的模式,所以在es5提供Function.prototype.bind,用法

function foo(something) {

  console.log(this.a, something);

  return this.a + something;

}

var obj = {

  a: 2

}

var bar = foo.bind(obj); // 2 3

console.log(bar); // 5

?2、API調(diào)用"上下文"


function foo(el) {

  console.log(el, this.id);

}

var obj = {

  id: 'winerss'

}

調(diào)用foo()時(shí)把this綁定到obj

[1,2,3].forEach(foo, obj);??// 1 winerss 2 winerss 3 winerss

3、優(yōu)先級(jí)


顯示綁定優(yōu)先級(jí)高于隱式綁定

function foo(params) {

  console.log(this.a);

}

var obj1 = {

  a: 2,

  foo: foo

}

var obj2 = {

  a: 3,

  foo: foo

}

obj1.foo(); // 2

obj2.foo(); // 3

obj1.foo.call(obj2); // 3

obj2.foo.call(obj1); // 2

new綁定比隱式綁定優(yōu)先級(jí)高

function foo(something) {

  this.a = something;

}

var obj1 = {

  foo: foo

}

var obj2 = {}

obj1.foo(2)

console.log(obj1.a); // 2

obj1.foo.call(obj2, 3);

console.log(obj2.a); // 3

var bar = new obj1.foo(4);

console.log(obj1.a); // 2

console.log(bar.a); // 4

new綁定比顯示綁定優(yōu)先級(jí)高

function foo(something) {

  this.a = something

}

var obj1 = {};

var bar = foo.bind(obj1);

bar(2);

console.log(obj1.a); // 2

var baz = new bar(3)

console.log(obj1.a); // 2

console.log(baz.a); // 3

4、綁定例外


?4、1被忽略的this

如果把null undefined 作為this綁定的對(duì)象傳入call apply或者bind,這些值在調(diào)用時(shí)會(huì)被忽略,應(yīng)用默認(rèn)綁定規(guī)則

function foo() {

  console.log(this.a);

}

var a = 2;

foo.call(null); // 2

傳null使用的情況

function foo(a, b) {

  console.log(`a:${a}, b:$`);

}

把數(shù)組展開(kāi)成參數(shù)

foo.apply(null, [2,3]); //a: 2, b: 3

var bar = foo.bind(null, 2); //?使用bind()進(jìn)行柯里化

bar(3); // a: 2,b: 3

總是使用null來(lái)忽略this綁定可能產(chǎn)生一些副作用,如果某個(gè)函數(shù)確實(shí)使用了this,(比如第三方庫(kù))那默認(rèn)綁定規(guī)則把this綁定到全局導(dǎo)致后果會(huì)很?chē)?yán)重。

更安全的this

function foo(a, b) {

  console.log(`a:${a}, b:$`);

}

我們的DMZ空對(duì)象

var obj = Object.create(null);

把數(shù)組展開(kāi)成參數(shù)

foo.apply(obj, [2, 3]); // a: 2, b: 3

使用bind進(jìn)行柯里化

var bar = foo.bind(obj, 2);

bar(3); // a: 2,b :3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,613評(píng)論 0 13
  • 一、你不知道的JavaScript 1、作用域 作用域 LHS RHS RHS查詢(xún)與簡(jiǎn)單地查找某個(gè)變量的值別無(wú)二...
    頂兒響叮當(dāng)閱讀 407評(píng)論 0 0
  • 相關(guān)知識(shí)點(diǎn) 數(shù)據(jù)類(lèi)型、運(yùn)算、對(duì)象、function、繼承、閉包、作用域、原型鏈、事件、RegExp、JSON、Aj...
    sandisen閱讀 11,499評(píng)論 7 175
  • this在調(diào)用時(shí)綁定,完全取決于函數(shù)的調(diào)用位置。綁定規(guī)則 默認(rèn)綁定(獨(dú)立函數(shù)調(diào)用,綁定到全局對(duì)象,但嚴(yán)格模式下th...
    劉尐六閱讀 707評(píng)論 0 7
  • 我是個(gè)偽北漂,看著別人訴說(shuō)異地來(lái)到北京后的生活有多么艱難,住在地下室或者租住在一間十幾平米的房間里,早晚高峰擠...
    圍城不冷閱讀 238評(píng)論 0 0

友情鏈接更多精彩內(nèi)容