JavaScript之this

本文共 1065 字,讀完只需 4 分鐘javascript

概述

在 JAVA 中,this 的概念很透徹,就是指向當前對象(方法和屬性的持有者),在編譯的時候就能肯定 this 指代,而因爲 JavaScript 中 this 是動態綁定,或稱爲運行期綁定的,在絕大多數狀況下,函數的調用方式決定了 this 的值,因此在 JS 中不能在定義時決定地定義 this 是哪一個上下文對象。java

this 的指向只和函數的調用位置(方式)有關,和函數聲明的位置無關。node

本文就從 this 應用場景調用方式的角度,解析 this 的用法。瀏覽器

首先,要知道,this 的做用是什麼?閉包

函數體內部,指代函數當前的運行上下文。app

1、全局上下文

在全局上下文中,this 指向全局對象,在瀏覽器中指向 window,在 NodeJs 中指向 global。函數

var a = "全局";
console.log(this)  // 瀏覽器:window
console.log(this)  // nodejs:global
console.log(this.a)  // "全局"
複製代碼

這是 JS 中的默認綁定,在全局上下文中,this 會默認綁定到 全局對象。post

2、函數聲明

在非嚴格模式下,未加關鍵字 var 聲明的變量,會成爲全局對象下的屬性,因此,此時 在嚴格模式下,this 將保持他進入執行上下文時的值,因此下面的 this 將會默認爲 undefined。ui

function foo() {
 "use strict";
    console.log(this);
}

foo();  // undefined 嚴格模式
複製代碼

上述也是 JS 中 this 的默認綁定,在函數定義中,函數中的 this 會默認綁定到全局對象或者 undefined。this

當函數做爲對象裏的方法被調用時,函數中 this 是調用該函數的對象。

function foo() {
    console.log("foo");
}

var obj = {
    this.bar = "bar";
    this.foo = foo;
}

obj.foo(); // "foo"
複製代碼

在調用位置,是 obj 對象調用了 foo 函數,此時 this 就指向了 obj 對象。

function foo() {
    console.log(this.bar);
}

var obj2 =  {
    bar: "2",
    foo: foo
}

var obj1 = {
    bar: "1",
    obj2: obj2
}

obj1.obj2.foo()  // "2";
複製代碼

這是 JS 中 this 的隱式綁定,this 會隱式綁定到調用的最近一層上下文對象。

3、call & apply

其實在函數中,函數正確的調用應該是用 call, apply 函數的形式。

function foo() {
    console.log(this.bar);
}

let obj = {
    bar: "1",
    foo: foo
}

obj.foo();  // `.` 點調用實際上是語法糖

foo.call(obj)  // 1 正確的姿式
foo.apply(obj) // 1
複製代碼

這是 JS 中 this 的顯式綁定,函數經過從 call 和 apply 函數能夠顯式指定函數的調用對象。

ES5 提供了一個 bind()方法:

let obj2 = {
    a: 2
}

let foo = function () {
    console.log(this.a);
}

let bar = foo.bind(obj2);
bar() // 2;
複製代碼

就是 JS 中 this 的硬綁定,bind() 函數會返回一個指定了調用對象的函數,返回的函數的被指定了 this 且 this 沒法改變。

4、構造函數

當函數加上關鍵字 new 後,函數會變成一個構造函數,此時構造函數中的 this 指向即將建立的對象實例,同時對象實例會關聯到構造函數的原型對象 prototype 上。

function Foo() {
    this.a = 3;
}

var obj = new Foo();

obj.a // 3
複製代碼

5、箭頭函數

ES6 中,提供了函數定義的語法糖,讓定義函數變得更簡潔(沒有 arguments, 沒有原型)。同時上面四條綁定規則在箭頭函數中不適用,使得 this 的查找更可控。

// 通常函數:
var a = 1;
var obj = {
  a: 2,
  say: function() {
    console.log(this.a)
  }
}
obj.say();  // console.log 打印值爲 2

// 箭頭函數
var a = 1;
var obj = {
  a: 2,
  say: () => {
    console.log(this.a)
  }
}
obj.say(); // 1 !!!
複製代碼

因爲箭頭函數定義時, obj {} 不是執行上下文,say 變量引用的箭頭函數,其 this 是父級執行上下文,也就是全局上下文,因此 this.a 就是 全局變量 a: 1;

在箭頭函數中,this 與封閉詞法上下文的 this 保持一致,this 被永久綁定到了它最近一層非箭頭函數的 this,而與函數的調用位置無關。

總結

在絕大多數狀況下,函數的調用方式決定了 this 的值,但最終都指向了調用了它的那個上下文對象。

歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。

掘金專欄 JavaScript 系列文章

  1. JavaScript之變量及做用域
  2. JavaScript之聲明提高
  3. JavaScript之執行上下文
  4. JavaScript之變量對象
  5. JavaScript原型與原型鏈
  6. JavaScript之做用域鏈
  7. JavaScript之閉包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值傳遞
  11. JavaScript之例題中完全理解this
  12. JavaScript專題之模擬實現call和apply
相關文章
相關標籤/搜索