this

Bản dịch này chưa hoàn thành. Xin hãy giúp dịch bài viết này từ tiếng Anh

Từ khoá this của hàm  trong JavaScript hơi khác so với các ngôn ngữ khác. Nó cũng có  một vài điểm khác nhau giữa 2 chế độ strict mode và non-strict mode.

Trong hầu hết các trường hợp, giá trị của this được xác định bởi cách gọi hàm (runtime binding). Nó không thể được thiết lập bằng cách gán trong khi thực thi, và nó có thể khác nhau mỗi lần hàm được gọi. ES5 giới thiệu phương thức bind() để thiết lập giá trị của this bất kể hàm được gọi thế nào, và ES2015 giới thiệu arrow functions mà không cung cấp ràng buộc với this của chúng (Nó sẽ giữ giá trị this của lexical context kèm theo).

Cú pháp

this

Giá trị

Một thuộc tính của bối cảnh thực thi (global, function or eval), trong non–strict mode, luôn luôn tham chiếu tới một đối tượng và trong strict mode có thể là bất kỳ giá trị nào.

Global context

Trong global context (bên ngoài các hàm), this tham chiếu tới global object cho dù trong strict mode hoặc không.

// Trong trình duyệt, đối tượng window ?là global object:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"

Note: Bạn có thể dễ dàng lấy được  global object bằng cách sử dụng thuộc tính toàn cầu globalThis, bất kể bối cảnh hiện tại mà mã của bạn đang chạy.

Function context

Bên trong một hàm, giá trị của this phụ thuộc vào cách gọi hàm.

Simple call

Vì đoạn mã sau không ở chế độ strict mode, và vì  giá trị của this không được thiết lập khi gọi, this mặc định là global objecct, đó là window trong trình duyệt. 

function f1() {
  return this;
}

// In a browser:
f1() === window; // true 

// In Node:
f1() === global; // true

Tuy nhiên, trong chế độ strict mode, nếu giá trị của this không được thiết lập khi vào bối cảnh thực thi, nó sẽ giữ giá trị undefined, như ví dụ sau:

function f2() {
  'use strict'; // see strict mode
  return this;
}

f2() === undefined; // true
 Trong ví dụ thứ 2, this nên là undefined, bởi vì f2 được gọi 1 cách trực tiếp và không phải là một phương thức hoặc thuộc tính của một đối tượng(ví dụ window.f2()). ?Tính năng này sẽ không được triển khai trong một số trình duyệt khi chúng lần đầu hỗ trợ chế độ strict mode. Như kết quả trên, chúng không trả về đối tượng window.

Để thiết lập giá trị cụ thể của this khi gọi hàm, sử dụng call(), hoặc apply() như các ví dụ dưới đây.

Example 1

// Một đối tượng có thể truyền vào như là tham số đầu tiên của call hoặc apply và this sẽ được ràng buộc với nó..
var obj = {a: 'Custom'};

// Thuộc tính này được thiết lập trên global object
var a = 'Global';

function whatsThis() {
  return this.a;  // Giá trị của this phụ thuộc vào cách hàm được gọi.
}

whatsThis();          // 'Global'
whatsThis.call(obj);  // 'Custom'
whatsThis.apply(obj); // 'Custom'

Example 2

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {a: 1, b: 3};

// Tham số đầu tiên là đối tượng sử dụng như là
// 'this', tham số tiếp theo được truyền vào là 
// đối số trong hàm gọi
add.call(o, 5, 7); // 16

// Tham số đầu tiên là đối tượng sử dụng như là
// 'this', tham số thứ 2 là 1 mảng
// các phần tử được sử dụng làm đối số trong lệnh gọi hàm
add.apply(o, [10, 20]); // 34

 Chú ý trong chế độ  non–strict mode, với call và apply, nếu giá trị được truyền vào this không phải là đối tượng, một nỗ lực sẽ được thực hiện để chuyển đổi nó thành đối tượng bằng cách sử dụng ToObject. Vì thế nếu bạn truyền vào giá trị primitive như 7 hoặc 'foo', nó sẽ được chuyển đổi thành Object bằng cách sử dụng các constructor liên quan, do đó 7 sẽ được chuyển đổi thành đối tượng như tạo bằng new Number(7) và string 'foo' cũng được chuyển đổi thành đối tượng như tạo bằng new String('foo'), ví dụ:

function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7);     // [object Number]
bar.call('foo'); // [object String]

The bind method

ECMAScript 5 giới thiệu Function.prototype.bind(). Gọi f.bind(someObject) tạo ra một hàm mới với cùng thân hàm và phạm vi như hàm f, nhưng this chỉ xảy ra trong hàm ban đầu, trong những hàm mới nó bị ràng buộc vĩnh viễn với đối số đầu tiên của bind, bất kể các hàm được sử dụng thế nào..

function f() {
  return this.a;
}

var g = f.bind({a: 'azerty'});
console.log(g()); // azerty

var h = g.bind({a: 'yoo'}); // bind only works once!
console.log(h()); // azerty

var o = {a: 37, f: f, g: g, h: h};
console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty

Arrow functions

Trong arrow functions, this giữ giá trị ?this của lexical context kèm theo. Trong đoạn mã toàn cục, nó sẽ được thiết lập là global object.

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

 Lưu ý: nếu đối số  this được truyền vào call, bind, hoặc apply ?trong việc gọi một arrow function nó sẽ bị bỏ qua. Bạn vẫn có thể thêm các đối số cho việc gọi hàm nhưng đối số đầu tiên (thisArg) nên được đặt thành null.

// Call as a method of an object
var obj = {func: foo};
console.log(obj.func() === globalObject); // true

// Attempt to set this using call
console.log(foo.call(obj) === globalObject); // true

// Attempt to set this using bind
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

Không có vấn đề gì ở đây, this của foo vẫn giữ nguyên giá trị khi nó được tạo  (trong ví dụ trên, nó là global object). Điều tương tự cũng được áp dụng cho những arrow function được tạo bên trong hàm khác: this của chúng giữ giá trị this của lexical context kèm theo.

// Tạo 1 đối tượng với phương thức bar trả về 1 hàm, hàm này sẽ
// trả về this của nó. Hàm trả về là arrow function, 
// vì thế this của nó được ràng buộc vĩnh viễn với this của hàm kèm theo.
// Giá trị của bar có thể được thiết lập trong khi gọi hàm,
// lần lượt đặt giá của hàm trả về.
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

// Gọi phương thức bar của obj, thiết lập this là obj.
// Gán một tham chiếu tới hàm trả về là fn
var fn = obj.bar();

// Gọi hàm fn mà không thiết lập 'this',
// thông thường sẽ mặc định cho global object hoặc undefined trong strict mode
console.log(fn() === obj); // true

// Nhưng hãy cẩn thận nếu bạn tham chiếu phương thức của đối tượng mà không gọi nó
var fn2 = obj.bar;
// Gọi hàm arrow function bên trong phương thức bar()
// nó sẽ trả về window, bởi vì nó theo 'this' từ fn2.
console.log(fn2()() == window); // true

Trong ví dụ trên, hàm (gọi nó là hàm ẩn danh A) gán cho obj.bar trả về một hàm khác (gọi là hàm ẩn danh B) mà nó là một arrow function. Kết quả là, this của hàm B được thiết lập vĩnh viễn là this của obj.bar (hàm A) khi được gọi. Khi hàm trả về (hàm B)  được gọi, this của nó sẽ luôn là  những gì được thiết lập ban đầu. Trong đoạn mã trên, this của hàm B được thiết lập theo this của hàm A đó là obj, vì thế nó vẫn được thiết lập là obj ngay cả khi được gọi  theo cách thông thường thiết lập this thành undefined hoặc global object (hoặc bất kỳ phương thức nào khác như trong ví dụ trên được thực thi trong bối cảnh toàn cầu).

As an object method

Khi một hàm được gọi như là một phương thức của đối tượng,this được đặt thành đối tượng mà có phương thức được gọi trên.

Trong ví dụ dưới đây, khi o.f() được gọi, this bên trong hàm sẽ liên kết với đối tượng o.

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

Lưu ý hành vi này hoàn toàn không bị ảnh hưởng bởi cách thức hoặc nơi chức năng được khai báo. Trong ví dụ ở trên, chúng ta khai báo hàm f bên trong đối tượng o. Tuy nhiên, chúng ta có thể dễ dàng khai báo hàm trước và đính kèm nó vào o.f. Làm như vậy sẽ có kết quả tương tự:

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // 37

Điều này chứng tỏ rằng vấn đề chỉ là việc gọi hàm f của o.

Tương tự, ràng buộc với this chỉ bị ảnh hưởng bởi tham chiếu trực tiếp nhất. Trong ví dụ dưới, khi chúng ta gọi hàm, chúng ta gọi nó như là một phương thức g của đối tượng o.b. Khi thực thi, this bên trong hàm sẽ tham chiếu tới o.b. Thực tế đối tượng này là một thành viên của o không ảnh hưởng; tham chiếu trực tiếp nhất mới là quan trọng nhất.

var o = {prop: 37}; 
function independent() { return this.prop; } 
o.f = independent; 
console.log(o.f()); // 37 bởi vì tham chiếu trực tiếp nhất là o
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42 bởi vì tham chiếu trực tiếp nhất là o.b

this on the object's prototype chain

The same notion holds true for methods defined somewhere on the object's prototype chain. If the method is on an object's prototype chain, this refers to the object the method was called on, as if the method were on the object.

var o = {f: function() { return this.a + this.b; }};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

In this example, the object assigned to the variable p doesn't have its own f property, it inherits it from its prototype. But it doesn't matter that the lookup for f eventually finds a member with that name on o; the lookup began as a reference to p.f, so this inside the function takes the value of the object referred to as p. That is, since f is called as a method of p, its this refers to p. This is an interesting feature of JavaScript's prototype inheritance.

this with a getter or setter

Again, the same notion holds true when a function is invoked from a getter or a setter. A function used as getter or setter has its this bound to the object from which the property is being set or gotten.

function sum() {
  return this.a + this.b + this.c;
}

var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  }
};

Object.defineProperty(o, 'sum', {
    get: sum, enumerable: true, configurable: true});

console.log(o.average, o.sum); // 2, 6

As a constructor

When a function is used as a constructor (with the new keyword), its this is bound to the new object being constructed.

While the default for a constructor is to return the object referenced by this, it can instead return some other object (if the return value isn't an object, then the this object is returned).

/*
 * Constructors work like this:
 *
 * function MyConstructor(){
 *   // Actual function body code goes here.  
 *   // Create properties on |this| as
 *   // desired by assigning to them.  E.g.,
 *   this.fum = "nom";
 *   // et cetera...
 *
 *   // If the function has a return statement that
 *   // returns an object, that object will be the
 *   // result of the |new| expression.  Otherwise,
 *   // the result of the expression is the object
 *   // currently bound to |this|
 *   // (i.e., the common case most usually seen).
 * }
 */

function C() {
  this.a = 37;
}

var o = new C();
console.log(o.a); // 37


function C2() {
  this.a = 37;
  return {a: 38};
}

o = new C2();
console.log(o.a); // 38

In the last example (C2), because an object was returned during construction, the new object that this was bound to simply gets discarded. (This essentially makes the statement "this.a = 37;" dead code. It's not exactly dead because it gets executed, but it can be eliminated with no outside effects.)

As a DOM event handler

When a function is used as an event handler, its this is set to the element on which the listener is placed (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener()).

// When called as a listener, turns the related element blue
function bluify(e) {
  // Always true
  console.log(this === e.currentTarget);
  // true when currentTarget and target are the same object
  console.log(this === e.target);
  this.style.backgroundColor = '#A5D9F3';
}

// Get a list of every element in the document
var elements = document.getElementsByTagName('*');

// Add bluify as a click listener so when the
// element is clicked on, it turns blue
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', bluify, false);
}

In an inline event handler

When the code is called from an inline on-event handler, its this is set to the DOM element on which the listener is placed:

<button onclick="alert(this.tagName.toLowerCase());">
  Show this
</button>

The above alert shows button. Note however that only the outer code has its this set this way:

<button onclick="alert((function() { return this; })());">
  Show inner this
</button>

In this case, the inner function's this isn't set so it returns the global/window object (i.e. the default object in non–strict mode where this isn't set by the call).

Specifications

Specification
ECMAScript (ECMA-262)
The definition of 'The this keyword' in that specification.

Browser compatibility

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
thisChrome Full support 1Edge Full support 12Firefox Full support 1IE Full support 4Opera Full support 9.5Safari Full support 1WebView Android Full support 1Chrome Android Full support 18Firefox Android Full support 4Opera Android Full support 10.1Safari iOS Full support 1Samsung Internet Android Full support 1.0nodejs Full support 0.1.100

Legend

Full support  
Full support

See also