Làm thế nào để từ khóa này làm việc này?


1309

Tôi đã nhận thấy rằng dường như không có một lời giải thích rõ ràng về những gì this từ khóa là gì và cách nó được sử dụng chính xác (và không chính xác) trong JavaScript trên trang web Stack Overflow.

Tôi đã chứng kiến ​​một số hành vi rất kỳ lạ với nó và đã không hiểu tại sao nó lại xảy ra.

Làm thế nào để thislàm việc và khi nào nó nên được sử dụng?


6
Tôi đã tìm thấy điều này khi tôi googled "này" quirksmode.org/js/this.html
Wai Wong


2
Peter Michaux ủng hộ việc sử dụng this peter.michaux.ca/articles/javascript-widgets-without-this
Marcel Korpel

1
Tổng quan về MDN không phải là một nửa xấu ... developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/
dat

2
Một lời giải thích thú vị về thistừ khóa: rainsoft.io/gentle-explanation-of-this-in-javascript
Dmitri Pavlutin

Câu trả lời:


1350

Tôi khuyên bạn nên đọc Phạm vi bài viết của Mike West trong JavaScript ( gương ) trước. Đây là một giới thiệu tuyệt vời, thân thiện với các khái niệm thisvà chuỗi phạm vi trong JavaScript.

Khi bạn bắt đầu quen với this , các quy tắc thực sự khá đơn giản. Các ECMAScript 5.1 Tiêu chuẩn định nghĩa this:

§11.1.1 Các thistừ khóa

Các thisđánh giá lại từ khóa để giá trị của ThisBinding của bối cảnh thực hiện

ThisBinding là thứ mà trình thông dịch JavaScript duy trì khi nó đánh giá mã JavaScript, giống như một thanh ghi CPU đặc biệt chứa tham chiếu đến một đối tượng. Trình thông dịch cập nhật ThisBinding bất cứ khi nào thiết lập bối cảnh thực thi trong một trong ba trường hợp khác nhau:

1. Bối cảnh thực hiện toàn cầu ban đầu

Đây là trường hợp mã JavaScript được đánh giá ở cấp cao nhất, ví dụ: khi trực tiếp bên trong <script>:

<script>
  alert("I'm evaluated in the initial global execution context!");

  setTimeout(function () {
      alert("I'm NOT evaluated in the initial global execution context.");
  }, 1);
</script>

Khi đánh giá mã trong bối cảnh thực thi toàn cầu ban đầu, ThisBinding được đặt thành đối tượng toàn cục, window( §10.4.1.1 ).

Nhập mã eval

  • Bằng cách gọi trực tiếp tới eval() ThisBinding không thay đổi; nó có cùng giá trị với ThisBinding của bối cảnh thực hiện cuộc gọi ( §10.4.2 (2) (a)).

  • Nếu không phải bằng một cuộc gọi trực tiếp tới eval()
    ThisBinding được đặt thành đối tượng toàn cầu như thể thực thi trong bối cảnh thực thi toàn cầu ban đầu ( §10.4.2 (1)).

§15.1.2.1.1 định nghĩa cuộc gọi trực tiếp eval()là gì. Về cơ bản, eval(...)là một cuộc gọi trực tiếp trong khi một cái gì đó như (0, eval)(...)hoặc var indirectEval = eval; indirectEval(...);là một cuộc gọi gián tiếp đến eval(). Xem câu trả lời của chuckj cho (1, eval) ('this') so với eval ('this') trong JavaScript? ECMA-262-5 của DmitryITEnikov một cách chi tiết. Chương 2. Chế độ nghiêm ngặt. khi bạn có thể sử dụng một eval()cuộc gọi gián tiếp .

Nhập mã chức năng

Điều này xảy ra khi gọi một chức năng. Nếu một hàm được gọi trên một đối tượng, chẳng hạn như trong obj.myMethod()hoặc tương đương obj["myMethod"](), thì ThisBinding được đặt thành đối tượng ( objtrong ví dụ; §13.2.1 ). Trong hầu hết các trường hợp khác, ThisBinding được đặt thành đối tượng toàn cục ( §10.4.3 ).

Lý do để viết "trong hầu hết các trường hợp khác" là vì có tám hàm dựng sẵn ECMAScript 5 cho phép ThisBinding được chỉ định trong danh sách đối số. Các hàm đặc biệt này có cái gọi là thisArgThisBinding khi gọi hàm ( §10.4.3 ).

Các chức năng tích hợp đặc biệt này là:

  • Function.prototype.apply( thisArg, argArray )
  • Function.prototype.call( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Function.prototype.bind( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Array.prototype.every( callbackfn [ , thisArg ] )
  • Array.prototype.some( callbackfn [ , thisArg ] )
  • Array.prototype.forEach( callbackfn [ , thisArg ] )
  • Array.prototype.map( callbackfn [ , thisArg ] )
  • Array.prototype.filter( callbackfn [ , thisArg ] )

Trong trường hợp của các Function.prototypehàm, chúng được gọi trên một đối tượng hàm, nhưng thay vì đặt ThisBinding cho đối tượng hàm, ThisBinding được đặt thành thisArg.

Trong trường hợp của các Array.prototypehàm, cái đã cho callbackfnđược gọi trong ngữ cảnh thực thi trong đó ThisBinding được đặt thành thisArgnếu được cung cấp; mặt khác, đối tượng toàn cầu.

Đó là những quy tắc cho JavaScript đơn giản. Khi bạn bắt đầu sử dụng các thư viện JavaScript (ví dụ: jQuery), bạn có thể thấy rằng các hàm thư viện nhất định thao túng giá trị của this. Các nhà phát triển của các thư viện JavaScript đó làm điều này bởi vì nó có xu hướng hỗ trợ các trường hợp sử dụng phổ biến nhất và người dùng của thư viện thường thấy hành vi này thuận tiện hơn. Khi chuyển các hàm gọi lại tham chiếu thisđến các hàm thư viện, bạn nên tham khảo tài liệu cho bất kỳ đảm bảo nào về giá trị của thiskhi hàm được gọi.

Nếu bạn đang tự hỏi làm thế nào một thư viện JavaScript thao túng giá trị của this, thì thư viện chỉ đơn giản là sử dụng một trong các hàm JavaScript tích hợp chấp nhận a thisArg. Bạn cũng có thể viết hàm riêng của mình bằng hàm gọi lại và thisArg:

function doWork(callbackfn, thisArg) {
    //...
    if (callbackfn != null) callbackfn.call(thisArg);
}

Có một trường hợp đặc biệt tôi chưa đề cập. Khi xây dựng một đối tượng mới thông qua newtoán tử, trình thông dịch JavaScript tạo một đối tượng mới, trống, đặt một số thuộc tính bên trong và sau đó gọi hàm xây dựng trên đối tượng mới. Do đó, khi một hàm được gọi trong ngữ cảnh của hàm tạo, giá trị của thislà đối tượng mới mà trình thông dịch tạo ra:

function MyType() {
    this.someData = "a string";
}

var instance = new MyType();
// Kind of like the following, but there are more steps involved:
// var instance = {};
// MyType.call(instance);

Chức năng mũi tên

Các hàm mũi tên (được giới thiệu trong ECMA6) thay đổi phạm vi this. Xem câu hỏi chính tắc hiện có, hàm Mũi tên so với khai báo / biểu thức hàm: Chúng có tương đương / có thể trao đổi không? để biết thêm thông tin. Nhưng tóm lại:

Các hàm mũi tên không có this.... ràng buộc riêng . Thay vào đó, những định danh được giải quyết trong phạm vi từ vựng như bất kỳ biến nào khác. Điều đó có nghĩa là bên trong một hàm mũi tên, this... tham chiếu đến các giá trị thistrong môi trường mà hàm mũi tên được định nghĩa trong.

Chỉ để cho vui, kiểm tra sự hiểu biết của bạn với một số ví dụ

Để tiết lộ câu trả lời, di chuột qua các hộp màu xám nhạt.

  1. Giá trị của thistại dòng được đánh dấu là gì? Tại sao?

    window - Dòng được đánh dấu được đánh giá trong bối cảnh thực hiện toàn cầu ban đầu.

    if (true) {
        // What is `this` here?
    }
  2. Giá trị của this tại dòng được đánh dấu khi obj.staticFunction()được thực hiện là gì? Tại sao?

    obj - Khi gọi một hàm trên một đối tượng, ThisBinding được đặt thành đối tượng.

    var obj = {
        someData: "a string"
    };
    
    function myFun() {
        return this // What is `this` here?
    }
    
    obj.staticFunction = myFun;
    
    console.log("this is window:", obj.staticFunction() == window);
    console.log("this is obj:", obj.staticFunction() == obj);
      

  3. Giá trị của thistại dòng được đánh dấu là gì? Tại sao?

    window

    Trong ví dụ này, trình thông dịch JavaScript nhập mã chức năng, nhưng vì myFun / obj.myMethodkhông được gọi trên một đối tượng, ThisBinding được đặt thành window.

    Điều này khác với Python, trong đó việc truy cập một phương thức ( obj.myMethod) tạo ra một đối tượng phương thức bị ràng buộc .

    var obj = {
        myMethod: function () {
            return this; // What is `this` here?
        }
    };
    var myFun = obj.myMethod;
    console.log("this is window:", myFun() == window);
    console.log("this is obj:", myFun() == obj);
      

  4. Giá trị của thistại dòng được đánh dấu là gì? Tại sao?

    window

    Điều này là khó khăn. Khi đánh giá mã eval, thisobj. Tuy nhiên, trong mã eval, myFunkhông được gọi trên một đối tượng, vì vậy ThisBinding được đặt thành windowcho cuộc gọi.

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
  5. Giá trị của thistại dòng được đánh dấu là gì? Tại sao?

    obj

    Dòng myFun.call(obj);này đang gọi hàm tích hợp đặc biệt Function.prototype.call(), chấp nhận thisArglàm đối số đầu tiên.

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        someData: "a string"
    };
    console.log("this is window:", myFun.call(obj) == window);
    console.log("this is obj:", myFun.call(obj) == obj);
      


6
@Ali: Chúng là các tham chiếu đến các phần trong phiên bản 5.1 của Tiêu chuẩn ECMAScript, ECMA-262 . Tôi cung cấp cho họ để bạn có thể đọc Tiêu chuẩn cho các chi tiết kỹ thuật nếu bạn muốn.
Daniel Trebbien

1
Tôi nghĩ rằng @supertonsky đúng về số 2 - nếu myFun () được gọi từ phạm vi toàn cầu, và không phải là một phương thức trên đối tượng, "điều này" sẽ là đối tượng toàn cầu, vì vậy việc đặt câu hỏi là vấn đề. btw - Tôi thực sự thích ý tưởng sử dụng mouseover để có câu trả lời cho một cái gì đó như thế này.
dùng655361

2
Nhưng, jsfiddle.net/H4LYm/2 cho thấy setTimeoutví dụ có thiscủa window(global).
Kevin Meredith

2
đến từ Python người ta sẽ tưởng tượng mức độ thất vọng mà tôi có khi tôi va vào ví dụ thứ 3 ..
smh

1
Câu trả lời này có lẽ nên được cập nhật để phản ánh thực tế ES2020, ngay cả khi những thay đổi chỉ là thuật ngữ.
Ben Aston

156

Các thiscư xử từ khóa khác nhau trong JavaScript so với các ngôn ngữ khác. Trong các ngôn ngữ hướng đối tượng, thistừ khóa đề cập đến thể hiện hiện tại của lớp. Trong JavaScript, giá trị của thisđược xác định bởi ngữ cảnh gọi của hàm ( context.function()) và nơi nó được gọi.

1. Khi được sử dụng trong bối cảnh toàn cầu

Khi bạn sử dụng thistrong bối cảnh toàn cầu, nó bị ràng buộc với đối tượng toàn cầu ( windowtrong trình duyệt)

document.write(this);  //[object Window]

Khi bạn sử dụng thisbên trong một hàm được xác định trong bối cảnh toàn cầu, thisvẫn bị ràng buộc với đối tượng toàn cầu vì hàm này thực sự được tạo thành một phương thức của bối cảnh toàn cầu.

function f1()
{
   return this;
}
document.write(f1());  //[object Window]

Trên đây f1là một phương pháp của đối tượng toàn cầu. Do đó chúng ta cũng có thể gọi nó trên windowđối tượng như sau:

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2. Khi được sử dụng bên trong phương thức đối tượng

Khi bạn sử dụng thistừ khóa bên trong một phương thức đối tượng, thisbị ràng buộc với đối tượng kèm theo "ngay lập tức".

var obj = {
    name: "obj",
    f: function () {
        return this + ":" + this.name;
    }
};
document.write(obj.f());  //[object Object]:obj

Ở trên tôi đã đặt từ ngay lập tức trong dấu ngoặc kép. Đó là để xác định rằng nếu bạn lồng đối tượng bên trong một đối tượng khác, thì thisbị ràng buộc với cha mẹ ngay lập tức.

var obj = {
    name: "obj1",
    nestedobj: {
        name:"nestedobj",
        f: function () {
            return this + ":" + this.name;
        }
    }            
}

document.write(obj.nestedobj.f()); //[object Object]:nestedobj

Ngay cả khi bạn thêm hàm rõ ràng vào đối tượng như một phương thức, nó vẫn tuân theo các quy tắc trên, đó thisvẫn là trỏ đến đối tượng cha ngay lập tức.

var obj1 = {
    name: "obj1",
}

function returnName() {
    return this + ":" + this.name;
}

obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1

3. Khi gọi hàm không ngữ cảnh

Khi bạn sử dụng thishàm bên trong được gọi mà không có bất kỳ ngữ cảnh nào (tức là không phải trên bất kỳ đối tượng nào), nó bị ràng buộc với đối tượng toàn cầu ( windowtrong trình duyệt) (ngay cả khi chức năng được xác định bên trong đối tượng).

var context = "global";

var obj = {  
    context: "object",
    method: function () {                  
        function f() {
            var context = "function";
            return this + ":" +this.context; 
        };
        return f(); //invoked without context
    }
};

document.write(obj.method()); //[object Window]:global 

Thử tất cả với các chức năng

Chúng ta có thể thử các điểm trên với các chức năng quá. Tuy nhiên có một số khác biệt.

  • Ở trên chúng tôi đã thêm thành viên vào các đối tượng bằng cách sử dụng ký hiệu nghĩa đen. Chúng tôi có thể thêm thành viên vào các chức năng bằng cách sử dụngthis . để chỉ định chúng.
  • Ký hiệu đối tượng tạo ra một thể hiện của đối tượng mà chúng ta có thể sử dụng ngay lập tức. Với chức năng, trước tiên chúng ta có thể cần tạo cá thể của nó bằng cách sử dụngnew toán tử.
  • Ngoài ra, trong một cách tiếp cận theo nghĩa đen của đối tượng, chúng ta có thể thêm các thành viên vào đối tượng đã được xác định rõ ràng bằng cách sử dụng toán tử dấu chấm. Điều này chỉ được thêm vào ví dụ cụ thể. Tuy nhiên tôi đã thêm biến vào nguyên mẫu hàm để nó được phản ánh trong tất cả các phiên bản của hàm.

Bên dưới tôi đã thử tất cả những điều chúng tôi đã làm với Object thistrở lên, nhưng bằng cách tạo hàm đầu tiên thay vì trực tiếp viết một đối tượng.

/********************************************************************* 
  1. When you add variable to the function using this keyword, it 
     gets added to the function prototype, thus allowing all function 
     instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
    this.name = "ObjDefinition";
    this.getName = function(){                
        return this+":"+this.name;
    }
}        

obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition   

/********************************************************************* 
   2. Members explicitly added to the function protorype also behave 
      as above: all function instances have their own copy of the 
      variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
    return "v"+this.version; //see how this.version refers to the
                             //version variable added through 
                             //prototype
}
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   3. Illustrating that the function variables added by both above 
      ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
    this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1

obj2.incrementVersion();      //incrementing version in obj2
                              //does not affect obj1 version

document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj', 
                                    getName1 : function(){
                                        return this+":"+this.name;
                                    }                            
                                  };

document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj

/********************************************************************* 
   5. If the method is on an object's prototype chain, `this` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

4. Khi được sử dụng bên trong hàm constructor .

Khi hàm được sử dụng như một hàm tạo (đó là khi nó được gọi bằng newtừ khóa), thisbên trong thân hàm trỏ đến đối tượng mới đang được xây dựng.

var myname = "global context";
function SimpleFun()
{
    this.myname = "simple function";
}

var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
//   object being constructed thus adding any member
//   created inside SimipleFun() using this.membername to the
//   object being constructed
//2. And by default `new` makes function to return newly 
//   constructed object if no explicit return value is specified

document.write(obj1.myname); //simple function

5. Khi được sử dụng bên trong chức năng được xác định trên chuỗi nguyên mẫu

Nếu phương thức nằm trên chuỗi nguyên mẫu của một đối tượng, thisbên trong phương thức đó đề cập đến đối tượng mà phương thức được gọi, như thể phương thức được định nghĩa trên đối tượng.

var ProtoObj = {
    fun: function () {
        return this.a;
    }
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun() 
//to be the method on its prototype chain

var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999

//Notice that fun() is defined on obj3's prototype but 
//`this.a` inside fun() retrieves obj3.a   

6. Hàm bên trong call (), áp dụng () và bind ()

  • Tất cả các phương pháp này được định nghĩa trên Function.prototype .
  • Các phương thức này cho phép viết một hàm một lần và gọi nó trong các ngữ cảnh khác nhau. Nói cách khác, chúng cho phép chỉ định giá trị thissẽ được sử dụng trong khi hàm đang được thực thi. Họ cũng lấy bất kỳ tham số nào được truyền cho hàm ban đầu khi nó được gọi.
  • fun.apply(obj1 [, argsArray])Đặt obj1làm giá trị thisbên trong fun()và gọi fun()các phần tử chuyển qua argsArraylàm đối số của nó.
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- Đặt obj1làm giá trị thisbên trong fun()và các cuộc gọi fun()chuyển qua arg1, arg2, arg3, ...làm đối số của nó.
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- Trả về tham chiếu cho hàm funvới thisniềm vui bên trong bị ràng buộc obj1và các tham số của funràng buộc với các tham số được chỉ định arg1, arg2, arg3,....
  • Bây giờ sự khác biệt giữa apply, callbindphải trở nên rõ ràng. applycho phép chỉ định các đối số để hoạt động như một đối tượng giống như mảng, tức là một đối tượng có lengththuộc tính số và các thuộc tính số nguyên không âm tương ứng. Trong khi đó callcho phép xác định trực tiếp các đối số cho hàm. Cả hai applycallngay lập tức gọi hàm trong ngữ cảnh đã chỉ định và với các đối số đã chỉ định. Mặt khác, bindchỉ cần trả về hàm bị ràng buộc với thisgiá trị được chỉ định và các đối số. Chúng ta có thể nắm bắt tham chiếu đến hàm trả về này bằng cách gán nó cho một biến và sau đó chúng ta có thể gọi nó bất cứ lúc nào.
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
      //above add.call(o,5,6) sets `this` inside
      //add() to `o` and calls add() resulting:
      // this.a + inc1 + inc2 = 
      // `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
      // `o.a` i.e. 4 + 5 + 6 = 15

var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />");    //15

var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
      // 4 + 5 + 6 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. thisbên trong xử lý sự kiện

  • Khi bạn gán hàm trực tiếp cho các trình xử lý sự kiện của một phần tử, việc sử dụng thishàm xử lý sự kiện trực tiếp bên trong tham chiếu đến phần tử tương ứng. Việc gán chức năng trực tiếp như vậy có thể được thực hiện bằng addeventListenerphương pháp hoặc thông qua các phương thức đăng ký sự kiện truyền thống nhưonclick .
  • Tương tự, khi bạn sử dụng thistrực tiếp bên trong thuộc tính sự kiện (như<button onclick="...this..." > ) của phần tử, nó đề cập đến phần tử.
  • Tuy nhiên, việc sử dụng thisgián tiếp thông qua chức năng khác được gọi bên trong chức năng xử lý sự kiện hoặc thuộc tính sự kiện sẽ phân giải thành đối tượng toàn cụcwindow .
  • Hành vi tương tự ở trên đạt được khi chúng tôi đính kèm chức năng vào trình xử lý sự kiện bằng phương pháp mô hình Đăng ký sự kiện của Microsoft attachEvent. Thay vì gán hàm cho trình xử lý sự kiện (và do đó tạo ra phương thức hàm của phần tử), nó gọi hàm trên sự kiện (gọi nó một cách hiệu quả trong bối cảnh toàn cầu).

Tôi khuyên bạn nên thử tốt hơn trong JSFiddle .

<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>

8. thistrong chức năng mũi tên ES6

Trong một hàm mũi tên, thissẽ hành xử giống như các biến phổ biến: nó sẽ được kế thừa từ phạm vi từ vựng của nó. Hàm this, trong đó hàm mũi tên được xác định, sẽ là hàm mũi tênthis .

Vì vậy, đó là hành vi tương tự như:

(function(){}).bind(this)

Xem mã sau đây:

const globalArrowFunction = () => {
  return this;
};

console.log(globalArrowFunction()); //window

const contextObject = {
  method1: () => {return this},
  method2: function(){
    return () => {return this};
  }
};

console.log(contextObject.method1()); //window

const contextLessFunction = contextObject.method1;

console.log(contextLessFunction()); //window

console.log(contextObject.method2()()) //contextObject

const innerArrowFunction = contextObject.method2();

console.log(innerArrowFunction()); //contextObject 

"Khi bạn sử dụng điều này bên trong một chức năng được xác định trong bối cảnh toàn cầu, điều này vẫn bị ràng buộc với đối tượng toàn cầu vì chức năng này thực sự là một phương thức của bối cảnh toàn cầu." không chính xác điều này được thiết lập bằng cách một hàm được gọi hoặc bằng liên kết , không phải bởi nơi nó được định nghĩa. Gọi bất kỳ chức năng nào mà không có tham chiếu cơ sở ("bối cảnh") sẽ mặc định điều này cho đối tượng toàn cầu hoặc không được xác định trong chế độ nghiêm ngặt.
RobG

@RobG hmm có thể, nhưng tôi đã tìm thấy điều này trên MDN : Trong trường hợp này, giá trị của thiskhông được đặt bởi cuộc gọi. Vì mã không ở chế độ nghiêm ngặt, giá trị của thisphải luôn là một đối tượng để nó mặc định cho đối tượng toàn cục. Và trên thực tế đó là lý do tại sao tôi nghĩ rằng chúng ta có thể trực tiếp thực hiện cuộc gọi window.f1(), vì vậy điều đó có nghĩa f1()là đã được gắn vào windowđối tượng, ý tôi là trước khi gọi. Có phải tôi đã hiểu sai?
Mahesha999

Tôi đã bình luận (có lẽ không rõ ràng) về việc bạn liên kết cài đặt này với "chức năng thực sự được tạo thành một phương thức của bối cảnh toàn cầu", như thể nó được gọi là window.fn, mà nó không được gọi. mặc định này cho đối tượng toàn cục vì không có tham chiếu cơ sở nào được sử dụng trong cuộc gọi, không phải do vị trí của hàm được xác định (vì vậy điều này vẫn được đặt theo cách gọi hàm). Nếu bạn gọi nó một cách rõ ràng bằng cách sử dụng window.fn, thì bạn đang đặt cái này thành cửa sổ . Cùng một kết quả, cách khác nhau về nó. :-)
RobG

"Ở trên tôi đã đặt từ ngay lập tức ..." không bạn đã không. Bạn có thể vui lòng sửa lại để sửa lỗi không? Có vẻ như ngữ nghĩa đối với câu trả lời và do đó tôi không thể tiếp tục đọc cho đến khi lỗi được sửa vì sợ học sai.
TylerH

@TylerH làm Ctrl + F trên trang này trong trình duyệt của bạn để tìm chuỗi "ngay lập tức" (bao gồm cả dấu ngoặc kép) Tôi nghĩ rằng nó ở đó nếu tôi hiểu bạn sai
Mahesha999

64

Javascript this

Gọi hàm đơn giản

Hãy xem xét các chức năng sau:

function foo() {
    console.log("bar");
    console.log(this);
}
foo(); // calling the function

Lưu ý rằng chúng tôi đang chạy này trong chế độ bình thường, tức là chế độ nghiêm ngặt không được sử dụng.

Khi chạy trong trình duyệt, giá trị của thissẽ được ghi lại dưới dạng window. Đây là vìwindow biến toàn cục trong phạm vi của trình duyệt web.

Nếu bạn chạy cùng một đoạn mã này trong một môi trường như node.js, this sẽ đề cập đến biến toàn cục trong ứng dụng của bạn.

Bây giờ nếu chúng ta chạy nó trong chế độ nghiêm ngặt bằng cách thêm câu lệnh "use strict";vào đầu khai báo hàm, thissẽ không còn đề cập đến biến toàn cục trong một trong hai môi trường. Điều này được thực hiện để tránh nhầm lẫn trong chế độ nghiêm ngặt. thistrong trường hợp này chỉ cần đăng nhậpundefined , bởi vì đó là những gì nó được, nó không được xác định.

Trong các trường hợp sau đây, chúng ta sẽ thấy cách thao tác giá trị của this .

Gọi một hàm trên một đối tượng

Có nhiều cách khác nhau để làm điều này. Nếu bạn đã gọi các phương thức gốc trong Javascript như forEachslice, bạn sẽ biết rằng thisbiến trong trường hợp đó đề cập đến hàm Objectmà bạn đã gọi hàm đó (Lưu ý rằng trong javascript, mọi thứ đều là một Object, bao gồm cả Arrays và Functions). Lấy mã sau đây làm ví dụ.

var myObj = {key: "Obj"};
myObj.logThis = function () {
    // I am a method
    console.log(this);
}
myObj.logThis(); // myObj is logged

Nếu một thuộc tính Objectchứa một thuộc tính Function, thuộc tính đó được gọi là một phương thức. Phương thức này, khi được gọi, sẽ luôn có thisbiến được đặt thànhObject được liên kết với nó. Điều này đúng cho cả chế độ nghiêm ngặt và không nghiêm ngặt.

Lưu ý rằng nếu một phương thức được lưu trữ (hoặc đúng hơn, được sao chép) trong một biến khác, thì tham chiếu đến thiskhông còn được bảo tồn trong biến mới. Ví dụ:

// continuing with the previous code snippet

var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation

Xem xét một kịch bản phổ biến hơn thực tế:

var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself

các newtừ khóa

Hãy xem xét một hàm tạo trong Javascript:

function Person (name) {
    this.name = name;
    this.sayHello = function () {
        console.log ("Hello", this);
    }
}

var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`

Cái này hoạt động ra sao? Chà, hãy xem điều gì xảy ra khi chúng ta sử dụng newtừ khóa.

  1. Gọi hàm bằng newtừ khóa sẽ ngay lập tức khởi tạo một ObjectkiểuPerson .
  2. Hàm tạo của cái này Objectcó hàm tạo của nó được đặt thành Person. Ngoài ra, lưu ý rằng typeof awalsẽ trở lạiObject chỉ .
  3. Điều này mới Objectsẽ được chỉ định nguyên mẫu của Person.prototype. Điều này có nghĩa là mọi phương thức hoặc thuộc tính trong Personnguyên mẫu sẽ có sẵn cho tất cả các trường hợp Person, bao gồmawal .
  4. Các chức năng Personchính nó bây giờ được gọi; thislà một tài liệu tham khảo cho các đối tượng mới được xây dựng awal.

Khá đơn giản nhỉ?

Lưu ý rằng đặc tả ECMAScript chính thức không nêu rõ rằng các loại hàm đó là các constructorhàm thực tế . Chúng chỉ là các chức năng bình thường, và newcó thể được sử dụng trên bất kỳ chức năng nào. Chỉ là chúng tôi sử dụng chúng như vậy, và vì vậy chúng tôi gọi chúng là như vậy mà thôi.

Các hàm gọi trên Hàm: callapply

Vì vậy, vâng, vì functioncũng là Objects(và trên thực tế là các biến hạng nhất trong Javascript), ngay cả các hàm cũng có các phương thức ... tốt, chính chúng là các hàm.

Tất cả các chức năng kế thừa từ toàn cầu Function, và hai trong số nhiều phương pháp của nó là callapply, và cả hai có thể được sử dụng để thao tác các giá trị thistrong hàm mà chúng được gọi.

function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);

Đây là một ví dụ điển hình của việc sử dụng call. Về cơ bản, nó lấy tham số đầu tiên và đặt thistrong hàm foolàm tham chiếu thisArg. Tất cả các tham số khác được truyền vào callđược truyền cho hàm foodưới dạng đối số.
Vì vậy, đoạn mã trên sẽ đăng nhập {myObj: "is cool"}, [1, 2, 3]vào giao diện điều khiển. Cách khá hay để thay đổi giá trị của thisbất kỳ chức năng nào.

applygần giống như callchấp nhận rằng nó chỉ cần hai tham số: thisArgvà một mảng chứa các đối số được truyền cho hàm. Vì vậy, callcuộc gọi trên có thể được dịch thành applynhư thế này:

foo.apply(thisArg, [1,2,3])

Lưu ý rằng callapplycó thể ghi đè giá trị củathis tập hợp bằng cách gọi phương thức dấu chấm mà chúng ta đã thảo luận trong dấu đầu dòng thứ hai. Đủ đơn giản :)

Trình bày .... bind!

bindlà anh trai của callapply. Nó cũng là một phương thức được kế thừa bởi tất cả các hàm từ hàm tạo toàn cục Functiontrong Javascript. Sự khác biệt giữa bindcall/ applylà cả hai callapplysẽ thực sự gọi hàm. bindmặt khác, trả về một hàm mới với thisArgvà được argumentsđặt trước. Hãy lấy một ví dụ để hiểu rõ hơn về điều này:

function foo (a, b) {
    console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */

bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`

Thấy sự khác biệt giữa ba? Nó là tinh tế, nhưng chúng được sử dụng khác nhau. Thích callapply, bindcũng sẽ vượt quá giá trị củathis tập hợp bằng cách gọi phương thức dot.

Cũng lưu ý rằng cả hai chức năng này đều không thay đổi chức năng ban đầu. callapplysẽ trả về giá trị từ các hàm được xây dựng mới trong khibind sẽ trả về chính hàm được xây dựng mới, sẵn sàng để được gọi.

Thêm công cụ, sao chép này

Đôi khi, bạn không thích thực tế là thisthay đổi theo phạm vi, đặc biệt là phạm vi lồng nhau. Hãy xem ví dụ sau đây.

var myObj = {
    hello: function () {
        return "world"
        },
    myMethod: function () {
        // copy this, variable names are case-sensitive
        var that = this;
        // callbacks ftw \o/
        foo.bar("args", function () {
            // I want to call `hello` here
            this.hello(); // error
            // but `this` references to `foo` damn!
            // oh wait we have a backup \o/
            that.hello(); // "world"
        });
    }
  };

Trong đoạn mã trên, chúng ta thấy rằng giá trị thisthay đổi với phạm vi lồng nhau, nhưng chúng ta muốn giá trị thistừ phạm vi ban đầu. Vì vậy, chúng tôi 'sao chép' thissang thatvà sử dụng bản sao thay vì this. Khéo léo hả?

Mục lục:

  1. Những gì được tổ chức thistheo mặc định?
  2. Điều gì xảy ra nếu chúng ta gọi hàm là một phương thức với ký hiệu Object-dot?
  3. Nếu chúng ta sử dụng new từ khóa thì sao?
  4. Làm thế nào để chúng ta thao tác thisvới callapply?
  5. Sử dụng bind.
  6. Sao chép thisđể giải quyết các vấn đề phạm vi lồng nhau.

47

"Đây" là tất cả về phạm vi. Mỗi hàm đều có phạm vi riêng và vì mọi thứ trong JS là một đối tượng, thậm chí một hàm có thể lưu trữ một số giá trị vào chính nó bằng cách sử dụng "this". OOP 101 dạy rằng "điều này" chỉ áp dụng cho các trường hợp của một đối tượng. Do đó, mỗi khi một hàm thực thi, một "thể hiện" mới của hàm đó có một ý nghĩa mới là "cái này".

Hầu hết mọi người bị lẫn lộn khi họ cố gắng sử dụng "cái này" bên trong các hàm đóng ẩn danh như:

(hàm (giá trị) {
    this .value = value;
    $ ('. một số phần tử'). mỗi (hàm (elt) {
        elt.innerHTML = this.value; // ồ ồ !! có thể không xác định
    });
}) (2);

Vì vậy, ở đây, bên trong mỗi (), "này" không giữ "giá trị" mà bạn mong đợi (từ

this .value = value;
ở trên nó). Vì vậy, để khắc phục vấn đề này (không có ý định chơi chữ), nhà phát triển có thể:

(hàm (giá trị) {
    var tự = cái này; // thay đổi nhỏ
    tự.value = giá trị;
    $ ('. một số phần tử'). mỗi (hàm (elt) {
        elt.innerHTML = self.value; // phew !! == 2
    });
}) (2);

Hãy thử nó; bạn sẽ bắt đầu thích mô hình lập trình này



6
Các giá trị nguyên thủy dường như có một số phương thức trên chính chúng, như Chuỗi # chuỗi con (), Số # toString (), v.v. Vì vậy, có thể không có cùng danh pháp như bài viết đó, chúng thực sự hành xử như thể chúng là các đối tượng (chúng là tất cả các nguyên mẫu, ví dụ: Chuỗi # chuỗi con () thực sự là: String.prototype.subopes = function () {...}). Vui long sửa cho tôi nêu tôi sai.
arunjitsingh

12
Các thistừ khóa không có gì để làm với phạm vi. Ngoài ra, nó cũng có ý nghĩa trong các hàm không phải là thuộc tính của các đối tượng.
Bergi

1
@ arunjitsingh Có hai trường phái suy nghĩ về điều đó. Tôi thích câu nói " mọi thứ đều là đối tượng, nhưng một số có thể được đại diện bởi người nguyên thủy để thuận tiện ". ;-)
RobG

9
thiskhông phải là TẤT CẢ về phạm vi. Đó là TẤT CẢ về bối cảnh thực hiện, không giống với phạm vi. JavaScript nằm trong phạm vi từ vựng (nghĩa là phạm vi được xác định bởi vị trí của mã), nhưng thisđược xác định bằng cách hàm được chứa nó - không phải là hàm đó ở đâu.
Scott Marcus

16

Vì chủ đề này đã bị xáo trộn, tôi đã tổng hợp một vài điểm cho độc giả mới vào thischủ đề.

Giá trị của thisxác định như thế nào?

Chúng tôi sử dụng điều này tương tự như cách chúng tôi sử dụng đại từ trong các ngôn ngữ tự nhiên như tiếng Anh: Mạnh John đang chạy rất nhanh vì anh ấy đang cố bắt tàu. Thay vào đó, chúng tôi có thể đã viết ra những bản vá của John .

var person = {    
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {

    // We use "this" just as in the sentence above:
       console.log(this.firstName + " " + this.lastName);

    // We could have also written:
       console.log(person.firstName + " " + person.lastName);
    }
}

this không được gán một giá trị cho đến khi một đối tượng gọi hàm trong đó nó được xác định. Trong phạm vi toàn cầu, tất cả các biến và hàm toàn cục được xác định trên windowđối tượng. Do đó, thistrong một hàm toàn cục đề cập đến (và có giá trị) của windowđối tượng toàn cầu .

Khi use strict, thistrong các hàm toàn cầu và ẩn danh không bị ràng buộc với bất kỳ đối tượng nào có giá trị là undefined.

Các thistừ khóa là hiểu lầm nhất khi: 1), chúng tôi mượn một phương pháp mà sử dụng this, 2) chúng ta gán một phương pháp mà sử dụng thischo một biến, 3) một hàm sử dụng thisđược thông qua như là một chức năng gọi lại, và 4)this được sử dụng bên trong một đóng cửa - một chức năng bên trong. (2)

bàn

Cái gì nắm giữ tương lai

Được xác định trong ECMA Script 6 , các hàm mũi tên thông quathis liên kết từ phạm vi kèm theo (hàm hoặc toàn cục).

function foo() {
     // return an arrow function
     return (a) => {
     // `this` here is lexically inherited from `foo()`
     console.log(this.a);
  };
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };

var bar = foo.call(obj1);
bar.call( obj2 ); // 2, not 3!

Mặc dù các hàm mũi tên cung cấp một giải pháp thay thế cho việc sử dụng bind(), nhưng điều quan trọng cần lưu ý là về cơ bản chúng đang vô hiệu hóa thiscơ chế truyền thống có lợi cho phạm vi từ vựng được hiểu rộng rãi hơn. (1)


Người giới thiệu:

  1. Nguyên mẫu & đối tượng này , bởi Kyle Simpson. © 2014 Giải pháp Getify.
  2. javascriptissexy.com - http://goo.gl/pvl0GX
  3. Angus Croll - http://goo.gl/Z2RacU

16

thistrong JavaScript luôn đề cập đến 'chủ sở hữu' của chức năng đang được thực thi .

Nếu không có chủ sở hữu rõ ràng được xác định, thì chủ sở hữu trên cùng, đối tượng cửa sổ, được tham chiếu.

Vì vậy, nếu tôi đã làm

function someKindOfFunction() {
   this.style = 'foo';
}

element.onclick = someKindOfFunction;

thissẽ đề cập đến các đối tượng yếu tố. Nhưng hãy cẩn thận, rất nhiều người mắc lỗi này.

<element onclick="someKindOfFunction()">

Trong trường hợp sau, bạn chỉ tham chiếu hàm, không bàn giao nó cho phần tử. Do đó, thissẽ đề cập đến các đối tượng cửa sổ.


15

Mỗi bối cảnh thực hiện trong javascript có này tham số được thiết lập bằng cách:

  1. Cách hàm được gọi (bao gồm như một phương thức đối tượng, sử dụng lệnh gọiáp dụng , sử dụng mới )
  2. Sử dụng liên kết
  3. Về mặt cơ bản cho các hàm mũi tên (họ chấp nhận điều này trong bối cảnh thực hiện bên ngoài của họ)
  4. Mã ở chế độ nghiêm ngặt hay không nghiêm ngặt
  5. Liệu mã được gọi bằng cách sử dụng eval

Bạn có thể đặt giá trị này bằng cách sử dụng func.call, func.applyhoặc func.bind.

Theo mặc định và điều gây nhầm lẫn cho hầu hết người mới bắt đầu, khi người nghe được gọi sau khi một sự kiện được nêu lên trên một phần tử DOM, giá trị này của hàm là phần tử DOM.

jQuery làm cho điều này trở nên tầm thường để thay đổi với jQuery.proxy.


9
Sẽ đúng hơn một chút khi nói rằng mọi lệnh gọi hàm đều có phạm vi. Nói cách khác, điều khó hiểu thistrong Javascript là nó không phải là một thuộc tính nội tại của chính chức năng, mà là một sự giả tạo về cách thức chức năng được gọi.
Pointy

@ cảm ơn. Điều gây ra sự nhầm lẫn nhất về điều này trong js là trong tất cả các ngôn ngữ được sử dụng trước đó (c #, c ++), - điều này không thể được xử lý n luôn luôn trỏ đến đối tượng trong khi trong js nó phụ thuộc và có thể thay đổi khi gọi các chức năng sử dụng func.call, func.bindv.v. - Sushil
Sushil

2
thiskhông không tham khảo phạm vi của một chức năng. thissẽ tham chiếu một đối tượng cụ thể (hoặc có thể undefined), như bạn đã nói có thể được thay đổi bằng cách sử dụng .call()hoặc .apply(). Phạm vi của hàm là (về cơ bản, khi được đơn giản hóa) biến nào nó có quyền truy cập và điều này phụ thuộc hoàn toàn vào nơi hàm được khai báo và không thể thay đổi.
nnnnnn

@Pointy: "Sẽ đúng hơn một chút khi nói rằng mọi lệnh gọi hàm đều có phạm vi." Thậm chí chính xác hơn để nói rằng các hàm (và bây giờ là các khối) có phạm vi , các lệnh gọi hàm có ngữ cảnh . Phạm vi xác định những gì các định danh có thể được sử dụng bởi mã trong phạm vi đó. Bối cảnh xác định những gì các định danh bị ràng buộc.
TJ Crowder

1
"Bất kể phạm vi đó là gì, được tham chiếu bởi" này "." Không, thisvà phạm vi không có gì để làm với nhau trong ES5 và trước đó (ví dụ: khi câu trả lời này được viết). Trong ES2015 (còn gọi là ES6) thisvà phạm vi có liên quan đến một chức năng mũi tên wrt khá tối thiểu (chức năng thistrong mũi tên được kế thừa từ phạm vi kèm theo của nó), nhưng thiskhông bao giờ đề cập đến phạm vi.
TJ Crowder

10

Đây là một nguồn tốt thistrong JavaScript.

Dưới đây là tóm tắt:

  • toàn cầu này

    Trong một trình duyệt, ở phạm vi toàn cầu, thiswindowđối tượng

    <script type="text/javascript">
      console.log(this === window); // true
      var foo = "bar";
      console.log(this.foo); // "bar"
      console.log(window.foo); // "bar"

    Trong nodeviệc sử dụng thay thế, thislà không gian tên hàng đầu. Bạn có thể tham khảo nó như global.

    >this
      { ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
        Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
        ...
    >global === this
     true

    Khi nodethực hiện từ một tập lệnh, thisở phạm vi toàn cục bắt đầu như một đối tượng trống. Nó không giống nhưglobal

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
  • chức năng này

Ngoại trừ trong trường hợp trình xử lý sự kiện DOM hoặc khi a thisArgđược cung cấp (xem thêm xuống), cả trong nút và trong trình duyệt sử dụng thischức năng không được gọi với newtham chiếu phạm vi toàn cầu.

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

Nếu bạn sử dụng use strict;, trong trường hợp đó thissẽ làundefined

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      "use strict";
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

Nếu bạn gọi một chức năng với newcác thissẽ là một bối cảnh mới, nó sẽ không tham chiếu toàn cầu this.

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    new testThis();
    console.log(this.foo); //logs "bar"

    console.log(new testThis().foo); //logs "foo"
</script>
  • nguyên mẫu này

Hàm bạn tạo trở thành đối tượng hàm. Họ tự động nhận được một thuộc tính đặc biệt prototype, đó là thứ bạn có thể gán giá trị cho. Khi bạn tạo một cá thể bằng cách gọi hàm của bạn với newbạn sẽ có quyền truy cập vào các giá trị bạn đã gán cho thuộc prototypetính. Bạn truy cập những giá trị đó bằng cách sử dụng this.

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

Thing.prototype.foo = "bar";

var thing = new Thing(); //logs "bar"
console.log(thing.foo);  //logs "bar"

Nó thường là một sai lầm khi gán mảng hoặc đối tượng trên prototype. Nếu bạn muốn các cá thể có từng mảng riêng, hãy tạo chúng trong hàm chứ không phải nguyên mẫu.

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
  • phản đối điều này

Bạn có thể sử dụng thistrong bất kỳ chức năng nào trên một đối tượng để chỉ các thuộc tính khác trên đối tượng đó. Điều này không giống như một ví dụ được tạo ra new.

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"
  • Sự kiện DOM này

Trong trình xử lý sự kiện HTML DOM, thisluôn có tham chiếu đến phần tử DOM, sự kiện được đính kèm

function Listener() {
    document.getElementById("foo").addEventListener("click",
       this.handleClick);
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs "<div id="foo"></div>"
}

var listener = new Listener();
document.getElementById("foo").click();

Trừ khi bạn bindbối cảnh

function Listener() {
    document.getElementById("foo").addEventListener("click", 
        this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs Listener {handleClick: function}
}

var listener = new Listener();
document.getElementById("foo").click();
  • HTML này

Bên trong các thuộc tính HTML mà bạn có thể đặt JavaScript, thislà một tham chiếu đến phần tử.

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
  • đánh giá này

Bạn có thể sử dụng evalđể truy cập this.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();
  • Với cái này

Bạn có thể sử dụng withđể thêm thisvào phạm vi hiện tại để đọc và ghi vào các giá trị thismà không cần tham khảo thisrõ ràng.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
  • jQuery cái này

jQuery ở nhiều nơi sẽ thisđề cập đến một phần tử DOM.

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>

9

Daniel, lời giải thích tuyệt vời! Một vài từ về điều này và danh sách tốt về thiscon trỏ ngữ cảnh thực thi trong trường hợp xử lý sự kiện.

Nói cách khác, thistrong JavaScript chỉ ra đối tượng từ ai (hoặc từ bối cảnh thực thi) chức năng hiện tại đã được chạy và nó luôn ở chế độ chỉ đọc, bạn không thể đặt nó bằng mọi cách (một nỗ lực như vậy sẽ kết thúc bằng 'Tay trái không hợp lệ bên trong tin nhắn chuyển nhượng.

Đối với các trình xử lý sự kiện: các trình xử lý sự kiện nội tuyến, chẳng hạn như <element onclick="foo">, ghi đè bất kỳ trình xử lý nào khác được đính kèm trước đó và trước đó, vì vậy hãy cẩn thận và tốt hơn hết là không nên ủy thác sự kiện nội tuyến. Và cảm ơn Zara Alaverdyan, người đã truyền cảm hứng cho tôi vào danh sách các ví dụ này thông qua một cuộc tranh luận không đồng tình :)

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick = function() {doSomething();} // In the doSomething - Window
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onclick, function () { // this }') // window, all the compliance to IE :)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <button onclick="foo"> // In the foo - window, but you can <button onclick="foo(this)">

9

Có rất nhiều nhầm lẫn về cách từ khóa "này" được diễn giải trong JavaScript. Hy vọng bài viết này sẽ đặt tất cả những người nghỉ ngơi một lần và mãi mãi. Và nhiều hơn nữa. Xin vui lòng đọc toàn bộ bài viết một cách cẩn thận. Được báo trước rằng bài viết này là dài.

Bất kể bối cảnh sử dụng nó là gì, "cái này" luôn tham chiếu đến "đối tượng hiện tại" trong Javascript. Tuy nhiên, những gì "đối tượng hiện tại" là khác nhau theo bối cảnh . Các bối cảnh có thể được chính xác 1 trong tổng số 6 sau:

  1. Toàn cầu (tức là bên ngoài tất cả các chức năng)
  2. Bên Direct "Non Chức năng Bound" Gọi (tức là một chức năng mà đã không được ràng buộc bằng cách gọi functionName.bind )
  3. Bên trong "Hàm không giới hạn" gián tiếp Gọi qua functionName.callfunctionName.apply
  4. Bên trong "Chức năng Bound" Gọi (tức là một chức năng mà đã được ràng buộc bằng cách gọi functionName.bind )
  5. Trong khi Tạo đối tượng thông qua "mới"
  6. Bên trong xử lý sự kiện Inline DOM

Phần sau đây mô tả từng bối cảnh này:

  1. Bối cảnh toàn cầu (tức là bên ngoài tất cả các chức năng):

    Bên ngoài tất cả các chức năng (tức là trong bối cảnh toàn cầu), "đối tượng hiện tại" (và do đó giá trị của "này" ) luôn là đối tượng "cửa sổ" cho các trình duyệt.

  2. Bên trong cuộc gọi "Chức năng không ràng buộc" trực tiếp :

    Bên trong một lệnh gọi "Hàm không giới hạn" trực tiếp, đối tượng đã gọi lệnh gọi hàm trở thành "đối tượng hiện tại" (và do đó là giá trị của "this" ). Nếu một hàm được gọi mà không có đối tượng hiện tại rõ ràng , thì đối tượng hiện tạiđối tượng "cửa sổ" (Đối với Chế độ không nghiêm ngặt) hoặc không xác định (Đối với Chế độ nghiêm ngặt). Bất kỳ hàm (hoặc biến) nào được xác định trong Ngữ cảnh toàn cầu sẽ tự động trở thành một thuộc tính của đối tượng "cửa sổ". Ví dụ: Hàm giả sử được xác định trong Ngữ cảnh toàn cầu như

    function UserDefinedFunction(){
        alert(this)
        }

    nó trở thành thuộc tính của đối tượng cửa sổ, như thể bạn đã định nghĩa nó là

    window.UserDefinedFunction=function(){
      alert(this)
    }  

    Trong "Chế độ không nghiêm ngặt", Gọi / Gọi hàm này trực tiếp thông qua "UserDefinedFunction ()" sẽ tự động gọi / gọi nó dưới dạng "window.UserDefinedFunction ()" tạo "window" làm "đối tượng hiện tại" (và do đó là giá trị của " this " ) trong " UserDefinedFunction ". Nhập chức năng này vào" Chế độ không nghiêm ngặt "sẽ dẫn đến kết quả như sau

    UserDefinedFunction() // displays [object Window]  as it automatically gets invoked as window.UserDefinedFunction()

    Trong "Chế độ nghiêm ngặt", Gọi / gọi hàm trực tiếp thông qua "UserDefinedFunction ()" sẽ "KHÔNG" tự động gọi / gọi nó dưới dạng "window.UserDefinedFunction ()" .Hãy sử dụng "đối tượng hiện tại" (và giá trị của "this" ) trong "UserDefinedFunction" sẽ không được xác định . Gọi chức năng này trong "Chế độ nghiêm ngặt" sẽ dẫn đến kết quả như sau

    UserDefinedFunction() // displays undefined

    Tuy nhiên, việc gọi nó một cách rõ ràng bằng cách sử dụng đối tượng cửa sổ sẽ dẫn đến kết quả như sau

    window.UserDefinedFunction() // "always displays [object Window]   irrespective of mode."

    Chúng ta hãy xem xét một ví dụ khác. Vui lòng xem đoạn mã sau

     function UserDefinedFunction()
        {
            alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
        }
    
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
          }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    o1.f() // Shall display 1,2,undefined,undefined
    o2.f() // Shall display undefined,undefined,3,4

    Trong ví dụ trên, chúng ta thấy rằng khi "UserDefinedFunction" được gọi qua o1 , "this" sẽ nhận giá trị của o1 và giá trị của các thuộc tính "a""b" được hiển thị. Giá trị của "c""d" được hiển thị là không xác địnho1 không xác định các thuộc tính này

    Tương tự như vậy khi "UserDefinedFunction" đã được gọi thông qua o2 , "này" mất giá trị của O2 và giá trị của thuộc tính của nó "c""d" có được displayed.The giá trị của "a""b" được hiển thị như là không xác định như o2 làm không xác định các tính chất này.

  3. Bên trong "Hàm không giới hạn" gián tiếp Gọi qua functionName.callfunctionName.apply :

    Khi "Hàm không giới hạn" được gọi thông qua functionName.call hoặc functionName.apply , "đối tượng hiện tại" (và do đó giá trị của "this" ) được đặt thành giá trị của tham số "this" (tham số đầu tiên) được truyền cho cuộc gọi / áp dụng . Các mã sau đây chứng minh tương tự.

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
           }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    UserDefinedFunction.call(o1) // Shall display 1,2,undefined,undefined
    UserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
    
    UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4
    UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
    
    o1.f.call(o2) // Shall display undefined,undefined,3,4
    o1.f.apply(o2) // Shall display undefined,undefined,3,4
    
    o2.f.call(o1) // Shall display 1,2,undefined,undefined
    o2.f.apply(o1) // Shall display 1,2,undefined,undefined

    Đoạn mã trên cho thấy rõ rằng giá trị "này" cho bất kỳ "Hàm giới hạn NON" nào có thể được thay đổi thông qua cuộc gọi / áp dụng . Ngoài ra, nếu tham số "này" không được truyền rõ ràng để gọi / áp dụng , "đối tượng hiện tại" (và do đó giá trị của "này") được đặt thành "cửa sổ" ở chế độ Không nghiêm ngặt và "không xác định" ở chế độ nghiêm ngặt.

  4. Bên trong "Chức năng Bound" Gọi (tức là một chức năng đã được ràng buộc bằng cách gọi functionName.bind ):

    Hàm bị ràng buộc là một hàm có giá trị "này" đã được sửa. Đoạn mã sau đã trình bày cách "này" hoạt động trong trường hợp hàm bị ràng buộc

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
              a:1,
              b:2,
              f:UserDefinedFunction,
              bf:null
           }
    var o2={
               c:3,
               d:4,
               f:UserDefinedFunction,
               bf:null
            }
    
    var bound1=UserDefinedFunction.bind(o1); // permanantly fixes "this" value of function "bound1" to Object o1
    bound1() // Shall display 1,2,undefined,undefined
    
    var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2
    bound2() // Shall display undefined,undefined,3,4
    
    var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2
    bound3() // Shall display undefined,undefined,3,4
    
    var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1
    bound4() // Shall display 1,2,undefined,undefined
    
    o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2
    o1.bf() // Shall display undefined,undefined,3,4
    
    o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1
    o2.bf() // Shall display 1,2,undefined,undefined
    
    bound1.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    
    bound1.apply(o2) // Shall still display 1,2,undefined,undefined. "apply" cannot alter the value of "this" for bound function
    
    o2.bf.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    o2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function

    Như được đưa ra trong đoạn mã trên, giá trị "này" cho bất kỳ "Hàm giới hạn" nào KHÔNG THỂ được thay đổi thông qua cuộc gọi / áp dụng . Ngoài ra, nếu tham số "this" không được truyền rõ ràng để liên kết, "đối tượng hiện tại" (và do đó giá trị của "this" ) được đặt thành "window" ở chế độ Nonrict và "không xác định" ở chế độ nghiêm ngặt. Một điều nữa. Liên kết một chức năng đã bị ràng buộc không thay đổi giá trị của "này" . Nó vẫn được đặt làm giá trị được thiết lập bởi chức năng liên kết đầu tiên.

  5. Trong khi Tạo đối tượng thông qua "mới" :

    Bên trong hàm constructor, "đối tượng hiện tại" (và do đó giá trị của "this" ) tham chiếu đến đối tượng hiện đang được tạo thông qua "new" bất kể trạng thái liên kết của hàm. Tuy nhiên, nếu hàm tạo là một hàm bị ràng buộc, nó sẽ được gọi với tập đối số được xác định trước như được đặt cho hàm bị ràng buộc.

  6. Bên trong xử lý sự kiện Inline DOM :

    Vui lòng xem Đoạn mã HTML sau

    <button onclick='this.style.color=white'>Hello World</button>
    <div style='width:100px;height:100px;' onclick='OnDivClick(event,this)'>Hello World</div>

    Phần tử "này" trong các ví dụ trên đề cập đến phần tử "nút" và phần tử "div" tương ứng.

    Trong ví dụ đầu tiên, màu phông chữ của nút sẽ được đặt thành màu trắng khi nhấp vào.

    Trong ví dụ thứ hai khi phần tử "div" được nhấp, nó sẽ gọi hàm OnDivClick với tham số thứ hai tham chiếu phần tử div được nhấp. Tuy nhiên, giá trị của "cái này" trong OnDivClick SHALL KHÔNG tham chiếu phần tử div đã nhấp . Nó sẽ được đặt là "đối tượng cửa sổ" hoặc "không xác định" trong Chế độ không nghiêm ngặtChế độ nghiêm ngặt tương ứng (nếu OnDivClick là một hàm không liên kết ) hoặc được đặt thành giá trị Bound được xác định trước (nếu OnDivClick là một hàm bị ràng buộc )

Sau đây tóm tắt toàn bộ bài viết

  1. Trong bối cảnh toàn cầu "cái này" luôn đề cập đến đối tượng "cửa sổ"

  2. Bất cứ khi nào một chức năng được gọi, nó được gọi trong ngữ cảnh của một đối tượng ( "đối tượng hiện tại" ). Nếu đối tượng hiện tại không được cung cấp rõ ràng, thì đối tượng hiện tại"đối tượng cửa sổ" trong Chế độ nghiêm ngặt NON"không xác định" trong Chế độ nghiêm ngặt theo mặc định.

  3. Giá trị của "this" trong hàm Non Bound là tham chiếu đến đối tượng trong bối cảnh hàm được gọi ( "đối tượng hiện tại" )

  4. Giá trị của "this" trong hàm Non Bound có thể được ghi đè bằng cách gọiáp dụng các phương thức của hàm.

  5. Giá trị của "this" được cố định cho hàm Bound và không thể bị ghi đè bằng cách gọiáp dụng các phương thức của hàm.

  6. Hàm ràng buộc và đã ràng buộc không thay đổi giá trị của "này". Nó vẫn được đặt làm giá trị được thiết lập bởi chức năng liên kết đầu tiên.

  7. Giá trị của "cái này" trong một hàm tạo là đối tượng đang được tạo và khởi tạo

  8. Giá trị của "này" trong trình xử lý sự kiện DOM nội tuyến được tham chiếu đến phần tử mà trình xử lý sự kiện được cung cấp.


9

Có lẽ bài viết chi tiết và toàn diện nhất thislà:

Giải thích nhẹ nhàng về từ khóa 'this' trong JavaScript

Ý tưởng đằng sau thislà để hiểu rằng các kiểu gọi hàm có tầm quan trọng đáng kể trong việc thiết lập thisgiá trị.


Khi gặp khó khăn trong việc xác định this, đừng tự hỏi:

Trong trường hợp được thislấy từ ?

nhưng làm tự hỏi mình:

Làm thế nào là chức năng được gọi ?

Đối với chức năng mũi tên (trường hợp đặc biệt trong suốt ngữ cảnh), hãy tự hỏi:

Giá trị nào có thischức năng mũi tên xác định ?

Suy nghĩ này là chính xác khi giải quyết thisvà sẽ cứu bạn khỏi đau đầu.


Ngoài việc liên kết với blog của bạn, có lẽ bạn có thể tìm hiểu sâu hơn một chút về cách hỏi những câu hỏi đó giúp ai đó hiểu thistừ khóa?
Magnus Lind Oxlund

7

Đây là lời giải thích tốt nhất tôi từng thấy: Hiểu về JavaScripts điều này với Clarity

Các này tham khảo LUÔN đề cập đến (và giữ giá trị của) một đối tượng một số ít đối tượng và nó thường được sử dụng bên trong một hàm hoặc một phương pháp, mặc dù nó có thể được sử dụng bên ngoài một hàm trong phạm vi toàn cầu. Lưu ý rằng khi chúng tôi sử dụng chế độ nghiêm ngặt, giá trị này sẽ giữ giá trị không xác định trong các hàm toàn cục và trong các hàm ẩn danh không bị ràng buộc với bất kỳ đối tượng nào.

Có bốn trường hợp nơi này có thể gây nhầm lẫn:

  1. Khi chúng ta truyền một phương thức (sử dụng phương thức này ) làm đối số được sử dụng làm hàm gọi lại.
  2. Khi chúng ta sử dụng một chức năng bên trong (một bao đóng). Điều quan trọng cần lưu ý là việc đóng cửa không thể truy cập vào chức năng bên ngoài này biến bằng cách sử dụng từ khóa này bởi vì biến này chỉ có thể truy cập được bởi chính hàm đó, không phải bởi các hàm bên trong.
  3. Khi một phương thức dựa vào điều này được gán cho một biến trên các bối cảnh, trong trường hợp này, phương thức này tham chiếu đến một đối tượng khác so với dự định ban đầu.
  4. Khi sử dụng điều này cùng với các phương thức liên kết, áp dụng và gọi.

Ông đưa ra các ví dụ mã, giải thích và giải pháp, mà tôi nghĩ là rất hữu ích.


6

Theo thuật ngữ giả cổ điển, cách nhiều bài giảng dạy từ khóa 'cái này' là một đối tượng được khởi tạo bởi một lớp hoặc đối tượng xây dựng đối tượng. Mỗi khi một đối tượng mới được xây dựng từ một lớp, hãy tưởng tượng rằng dưới mui xe, một thể hiện cục bộ của một đối tượng 'này' được tạo và trả về. Tôi nhớ nó đã dạy như thế này:

function Car(make, model, year) {
var this = {}; // under the hood, so to speak
this.make = make;
this.model = model;
this.year = year;
return this; // under the hood
}

var mycar = new Car('Eagle', 'Talon TSi', 1993);
// ========= under the hood
var this = {};
this.make = 'Eagle';
this.model = 'Talon TSi';
this.year = 1993;
return this;

5

thislà một trong những khái niệm bị hiểu sai trong JavaScript bởi vì nó hành xử hơi khác nhau từ nơi này đến nơi khác. Đơn giản, thisđề cập đến "chủ sở hữu" của chức năng chúng tôi hiện đang thực hiện .

thisgiúp để có được đối tượng hiện tại (còn gọi là bối cảnh thực thi) mà chúng ta làm việc với. Nếu bạn hiểu đối tượng nào thì hàm hiện tại đang được thực thi, bạn có thể dễ dàng hiểu hiện tại thislà gì

var val = "window.val"

var obj = {
    val: "obj.val",
    innerMethod: function () {
        var val = "obj.val.inner",
            func = function () {
                var self = this;
                return self.val;
            };

        return func;
    },
    outerMethod: function(){
        return this.val;
    }
};

//This actually gets executed inside window object 
console.log(obj.innerMethod()()); //returns window.val

//Breakdown in to 2 lines explains this in detail
var _inn = obj.innerMethod();
console.log(_inn()); //returns window.val

console.log(obj.outerMethod()); //returns obj.val

Ở trên, chúng tôi tạo 3 biến có cùng tên 'val'. Một trong bối cảnh toàn cầu, một bên trong obj và bên trong bên trong Nội bộ của obj. JavaScript giải quyết các định danh trong một bối cảnh cụ thể bằng cách đi lên chuỗi phạm vi từ địa phương đi toàn cầu.


Vài nơi thiscó thể được phân biệt

Gọi một phương thức của một đối tượng

var status = 1;
var helper = {
    status : 2,
    getStatus: function () {
        return this.status;
    }
};

var theStatus1 = helper.getStatus(); //line1
console.log(theStatus1); //2

var theStatus2 = helper.getStatus;
console.log(theStatus2()); //1

Khi dòng 1 được thực thi, JavaScript sẽ thiết lập bối cảnh thực thi (EC) cho lệnh gọi hàm, thiết lập thischo đối tượng được tham chiếu bởi bất kỳ thứ gì xuất hiện trước dấu ".". Vì vậy, trong dòng cuối cùng bạn có thể hiểu rằng a()đã được thực hiện trong bối cảnh toàn cầu đó làwindow .

Với người xây dựng

this có thể được sử dụng để chỉ đối tượng được tạo

function Person(name){
    this.personName = name;
    this.sayHello = function(){
        return "Hello " + this.personName;
    }
}

var person1 = new Person('Scott');
console.log(person1.sayHello()); //Hello Scott

var person2 = new Person('Hugh');
var sayHelloP2 = person2.sayHello;
console.log(sayHelloP2()); //Hello undefined

Khi mới Person()được thực thi, một đối tượng hoàn toàn mới được tạo ra.Personđược gọi và của nóthis được thiết lập để tham chiếu đối tượng mới đó.

Chức năng gọi

function testFunc() {
    this.name = "Name";
    this.myCustomAttribute = "Custom Attribute";
    return this;
}

var whatIsThis = testFunc();
console.log(whatIsThis); //window

var whatIsThis2 = new testFunc();
console.log(whatIsThis2);  //testFunc() / object

console.log(window.myCustomAttribute); //Custom Attribute 

Nếu chúng ta bỏ lỡ newtừ khóa,whatIsThis tham khảo bối cảnh toàn cầu nhất mà nó có thể tìm thấy (window )

Với xử lý sự kiện

Nếu xử lý sự kiện là nội tuyến, thisđề cập đến đối tượng toàn cầu

<script type="application/javascript">
    function click_handler() {
        alert(this); // alerts the window object
    }
</script>

<button id='thebutton' onclick='click_handler()'>Click me!</button>

Khi thêm trình xử lý sự kiện thông qua JavaScript, hãy thistham chiếu đến phần tử DOM đã tạo sự kiện.



5

Giá trị của "này" phụ thuộc vào "bối cảnh" trong đó hàm được thực thi. Bối cảnh có thể là bất kỳ đối tượng hoặc đối tượng toàn cầu, tức là cửa sổ.

Vì vậy, Semantic của "this" khác với các ngôn ngữ OOP truyền thống. Và nó gây ra vấn đề: 1. khi một chức năng được chuyển sang một biến khác (rất có thể, một cuộc gọi lại); và 2. khi một bao đóng được gọi từ một phương thức thành viên của một lớp.

Trong cả hai trường hợp, điều này được đặt thành cửa sổ.


3

Whould này giúp đỡ? . xem bài viết để được giải thích đầy đủ)


1
Sẽ tốt hơn nếu nói nó được liên kết " với bối cảnh thực hiện hiện tại ". Ngoại trừ ES6 (bản nháp) thay đổi với các hàm mũi tên, trong đó điều này được giải quyết trên bối cảnh thực thi bên ngoài.
RobG

3

Một chút thông tin về từ khóa này

Hãy đăng nhập thistừ khóa vào bảng điều khiển trong phạm vi toàn cầu mà không cần thêm mã nào ngoài

console.log(this)

Trong Client / Trình duyệt this từ khóa là một đối tượng toàn cầu đó làwindow

console.log(this === window) // true

Trong Máy chủ / Node / Javascript runtime this từ khóa cũng là một đối tượng toàn cầu đó làmodule.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

Hãy ghi nhớ exportschỉ là một tài liệu tham khảomodule.exports


1

sử dụng cho Phạm vi như thế này

  <script type="text/javascript" language="javascript">
$('#tbleName tbody tr').each(function{
var txt='';
txt += $(this).find("td").eq(0).text();
\\same as above but synatx different
var txt1='';
 txt1+=$('#tbleName tbody tr').eq(0).text();
alert(txt1)
});
</script>

giá trị của txt1 và txt giống nhau trong ví dụ trên $ (this) = $ ('# tbleName tbody tr') là giống nhau


1

Tôi có một cách khác this với các câu trả lời khác mà tôi hy vọng là hữu ích.

Một cách để xem JavaScript là thấy rằng chỉ có 1 cách để gọi hàm 1 . Nó là

functionObject.call(objectForThis, arg0, arg1, arg2, ...);

Luôn luôn có một số giá trị được cung cấp cho objectForThis.

Mọi thứ khác là cú pháp đường cho functionObject.call

Vì vậy, mọi thứ khác có thể được mô tả bằng cách dịch nó thành functionObject.call.

Nếu bạn chỉ gọi một chức năng thì đó thislà "đối tượng toàn cầu" mà trong trình duyệt là cửa sổ

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

foo();  // this is the window object

Nói cách khác,

foo();

đã được dịch thành

foo.call(window);

Lưu ý rằng nếu bạn sử dụng chế độ nghiêm ngặt thì thissẽundefined

'use strict';

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

foo();  // this is the window object

nghĩa là

Nói cách khác,

foo();

đã được dịch thành

foo.call(undefined);

Trong JavaScript có nhà khai thác như +-*. Ngoài ra còn có toán tử dấu chấm.

Các .nhà điều hành khi được sử dụng với một chức năng trên bên phải và một đối tượng ở bên trái có hiệu quả có nghĩa là "đối tượng vượt qua nhưthis hoạt động.

Thí dụ

const bar = {
  name: 'bar',
  foo() { 
    console.log(this); 
  },
};

bar.foo();  // this is bar

Nói cách khác, bar.foo()dịch thànhconst temp = bar.foo; temp.call(bar);

Lưu ý rằng chức năng được tạo ra không quan trọng (chủ yếu là ...). Tất cả những điều này sẽ tạo ra kết quả tương tự

const bar = {
  name: 'bar',
  fn1() { console.log(this); },
  fn2: function() { console.log(this); },
  fn3: otherFunction,
};

function otherFunction() { console.log(this) };

bar.fn1();  // this is bar
bar.fn2();  // this is bar
bar.fn3();  // this is bar

Một lần nữa tất cả chỉ là cú pháp đường cho

{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }

Một nếp nhăn khác là chuỗi nguyên mẫu. Khi bạn sử dụng a.bJavaScript, trước tiên, hãy nhìn vào đối tượng được tham chiếu trực tiếp bởi athuộc tính b. Nếu bkhông tìm thấy trên đối tượng thì JavaScript sẽ tìm trong nguyên mẫu của đối tượng cần tìm b.

Có nhiều cách khác nhau để xác định nguyên mẫu của một đối tượng, phổ biến nhất trong năm 2019 là classtừ khóa. Đối với các mục đích thismặc dù nó không quan trọng. Điều quan trọng là vì nó tìm trong đối tượng acho thuộc tính bnếu nó tìm thấy thuộc tính btrên đối tượng hoặc trong chuỗi nguyên mẫu của nó nếu bkết thúc là một hàm thì áp dụng quy tắc tương tự như trên. Các btham chiếu hàm sẽ được gọi bằng cách sử dụng callphương thức và truyền adưới dạng objectFor This như thể hiện phần trên của câu trả lời này.

Hiện nay. Hãy tưởng tượng chúng ta tạo một hàm đặt rõ ràng thistrước khi gọi một hàm khác và sau đó gọi nó bằng .toán tử (dấu chấm)

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

function bar() {
  const objectForThis = {name: 'moo'}
  foo.call(objectForThis);  // explicitly passing objectForThis
}

const obj = {
  bar,
};

obj.bar();  

Theo bản dịch để sử dụng call, obj.bar()trở thành const temp = obj.bar; temp.call(obj);. Khi chúng ta nhập barhàm chúng ta gọi foonhưng chúng ta chuyển một cách rõ ràng vào một đối tượng khác cho objectFor This vì vậy khi chúng ta đến foo thislà đối tượng bên trong đó.

Đây là những gì cả hai bind=>chức năng có hiệu quả làm. Họ là cú pháp đường hơn. Họ xây dựng một cách hiệu quả một chức năng vô hình mới chính xác như barở trên mà thiết lập rõ ràng thistrước khi nó gọi bất kỳ chức năng nào được chỉ định. Trong trường hợp liên kết thisđược đặt thành bất cứ điều gì bạn vượt qua bind.

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

const bar = foo.bind({name: 'moo'});

// bind created a new invisible function that calls foo with the bound object.

bar();  

// the objectForThis we are passing to bar here is ignored because
// the invisible function that bind created will call foo with with
// the object we bound above

bar.call({name: 'other'});

Lưu ý rằng nếu functionObject.bindkhông tồn tại, chúng ta có thể làm cho riêng mình như thế này

function bind(fn, objectForThis) {
  return function(...args) {
    return fn.call(objectForthis, ...args);
  };
}

và sau đó chúng ta có thể gọi nó như thế này

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

const bar = bind(foo, {name:'abc'});

Hàm mũi tên, =>toán tử là đường cú pháp cho liên kết

const a = () => {console.log(this)};

giống như

const tempFn = function() {console.log(this)}; 
const a = tempFn.bind(this);

Giống như bind, một hàm vô hình mới được tạo ra gọi hàm đã cho có giá trị ràng buộc objectForThisnhưng không giống như bindđối tượng bị ràng buộc là ẩn. Đó là bất cứ điều gì thisxảy ra khi người =>vận hành được sử dụng.

Vì vậy, giống như các quy tắc trên

const a = () => { console.log(this); }  // this is the global object
'use strict';
const a = () => { console.log(this); }  // this is undefined
function foo() {
  return () => { console.log(this); }
}

const obj = {
  foo,
};
const b = obj.foo();
b();

obj.foo()dịch const temp = obj.foo; temp.call(obj);nghĩa là toán tử mũi tên bên trong foosẽ liên kết objvới một hàm vô hình mới và trả về hàm vô hình mới được gán cho b. b()sẽ hoạt động như nó luôn có b.call(window)hoặc b.call(undefined)gọi hàm vô hình mới foođược tạo. Hàm vô hình đó bỏ qua việc thistruyền vào nó và chuyển objdưới dạng objectFor This` cho hàm mũi tên.

Đoạn mã trên dịch thành

function foo() {
  function tempFn() {
    console.log(this);
  }
  return tempFn.bind(this);
}

const obj = {
  foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);

1apply là một chức năng khác tương tự nhưcall

functionName.apply(objectForThis, arrayOfArgs);

Nhưng về mặt khái niệm ES6, bạn thậm chí có thể dịch nó thành

functionName.call(objectForThis, ...arrayOfArgs);

0

Tóm tắt thisJavascript:

  • Giá trị của thisđược xác định bằng cách hàm được gọi không, nơi nó được tạo!
  • Thông thường giá trị của thisđược xác định bởi Object nằm bên trái dấu chấm. ( windowtrong không gian toàn cầu)
  • Trong các trình lắng nghe sự kiện, giá trị của this tham chiếu đến phần tử DOM mà sự kiện được gọi.
  • Khi trong hàm được gọi với newtừ khóa, giá trị của thistham chiếu đến đối tượng mới được tạo
  • Bạn có thể thao tác các giá trị thisvới các chức năng: call, apply,bind

Thí dụ:

let object = {
  prop1: function () {console.log(this);}
}

object.prop1();   // object is left of the dot, thus this is object

const myFunction = object.prop1 // We store the function in the variable myFunction

myFunction(); // Here we are in the global space
              // myFunction is a property on the global object
              // Therefore it logs the window object
              
             

Ví dụ người nghe sự kiện:

document.querySelector('.foo').addEventListener('click', function () {
  console.log(this);   // This refers to the DOM element the eventListener was invoked from
})


document.querySelector('.foo').addEventListener('click', () => {
  console.log(this);  // Tip, es6 arrow function don't have their own binding to the this v
})                    // Therefore this will log the global object
.foo:hover {
  color: red;
  cursor: pointer;
}
<div class="foo">click me</div>

Ví dụ xây dựng:

function Person (name) {
  this.name = name;
}

const me = new Person('Willem');
// When using the new keyword the this in the constructor function will refer to the newly created object

console.log(me.name); 
// Therefore, the name property was placed on the object created with new keyword.


0

Để hiểu "điều này" đúng, người ta phải hiểu bối cảnh và phạm vi và sự khác biệt giữa chúng.

Phạm vi : Trong phạm vi javascript có liên quan đến khả năng hiển thị của các biến, phạm vi đạt được thông qua việc sử dụng hàm. (Đọc thêm về phạm vi)

Bối cảnh : Bối cảnh có liên quan đến các đối tượng. Nó đề cập đến đối tượng mà một chức năng thuộc về. Khi bạn sử dụng từ khóa JavaScript này, từ khóa này, nó đề cập đến đối tượng thuộc về chức năng nào. Ví dụ: bên trong một hàm, khi bạn nói: về điều này.accoutNumber, bạn đang đề cập đến thuộc tính, accoutNumber, thuộc về đối tượng mà hàm đó thuộc về.

Nếu đối tượng Cameron myObj, có một phương thức gọi là Nhận getMyName, khi từ khóa JavaScript, thì điều này được sử dụng bên trong của Get getMyName, thì nó đề cập đến Chuyện myObj. Nếu chức năng mà get getNameName được thực thi trong phạm vi toàn cầu, thì thì điều này có nghĩa là đối tượng cửa sổ này (ngoại trừ trong chế độ nghiêm ngặt).

Bây giờ hãy xem một số ví dụ:

    <script>
        console.log('What is this: '+this);
        console.log(this);
    </script>

Mã Runnig abobve trong đầu ra trình duyệt sẽ: nhập mô tả hình ảnh ở đây

Theo đầu ra bạn đang ở trong bối cảnh của đối tượng cửa sổ, cũng có thể thấy rằng nguyên mẫu cửa sổ đề cập đến Đối tượng.

Bây giờ chúng ta hãy thử bên trong một chức năng:

    <script>
        function myFunc(){
            console.log('What is this: '+this);
            console.log(this);
        }
        myFunc();
    </script>

Đầu ra:

nhập mô tả hình ảnh ở đây Đầu ra giống nhau vì chúng tôi đã đăng nhập biến 'this' trong phạm vi toàn cầu và chúng tôi đã đăng nhập nó trong phạm vi chức năng, chúng tôi đã không thay đổi bối cảnh. Trong cả hai trường hợp bối cảnh là như nhau, liên quan đến đối tượng góa phụ .

Bây giờ hãy tạo đối tượng của chúng ta. Trong javascript, bạn có thể tạo một đối tượng theo nhiều cách.

 <script>
        var firstName = "Nora";
        var lastName = "Zaman";
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printNameGetContext:function(){
                console.log(firstName + " "+lastName);
                console.log(this.firstName +" "+this.lastName);
                return this;
            }
        }

      var context = myObj.printNameGetContext();
      console.log(context);
    </script>

Đầu ra: nhập mô tả hình ảnh ở đây

Vì vậy, từ ví dụ trên, chúng tôi thấy rằng từ khóa 'này' đang đề cập đến một bối cảnh mới có liên quan đến myObj và myObject cũng có chuỗi nguyên mẫu cho Object.

Hãy đi ném một ví dụ khác:

<body>
    <button class="btn">Click Me</button>
    <script>
        function printMe(){
            //Terminal2: this function declared inside window context so this function belongs to the window object.
            console.log(this);
        }
        document.querySelector('.btn').addEventListener('click', function(){
            //Terminal1: button context, this callback function belongs to DOM element 
            console.log(this);
            printMe();
        })
    </script>
</body>

đầu ra: Có ý nghĩa phải không? (đọc bình luận) nhập mô tả hình ảnh ở đây

Nếu bạn gặp khó khăn để hiểu ví dụ trên, hãy thử với cuộc gọi lại của chúng tôi;

<script>
        var myObj = {
            firstName:"Lord",
            lastName:'Baron',
            printName:function(callback1, callback2){
                //Attaching callback1 with this myObj context
                this.callback1 = callback1;
                this.callback1(this.firstName +" "+this.lastName)
                //We did not attached callback2 with myObj so, it's reamin with window context by default
                callback2();
                /*
                 //test bellow codes
                 this.callback2 = callback2;
                 this.callback2();
                */
            }
        }
        var callback2 = function (){
            console.log(this);
        }
        myObj.printName(function(data){
            console.log(data);
            console.log(this);
        }, callback2);
    </script>

đầu ra: nhập mô tả hình ảnh ở đây

Bây giờ hãy hiểu Phạm vi, Tự, IIFE và NÀY ứng xử như thế nào

       var color = 'red'; // property of window
       var obj = {
           color:'blue', // property of window
           printColor: function(){ // property of obj, attached with obj
               var self = this;
               console.log('In printColor -- this.color: '+this.color);
               console.log('In printColor -- self.color: '+self.color);
               (function(){ // decleard inside of printColor but not property of object, it will executed on window context.
                    console.log(this)
                    console.log('In IIFE -- this.color: '+this.color);
                    console.log('In IIFE -- self.color: '+self.color); 
               })();

               function nestedFunc(){// decleard inside of printColor but not property of object, it will executed on window context.
                    console.log('nested fun -- this.color: '+this.color);
                    console.log('nested fun -- self.color: '+self.color);
               }

               nestedFunc(); // executed on window context
               return nestedFunc;
           }
       };

       obj.printColor()(); // returned function executed on window context
   </script> 

Đầu ra khá tuyệt vời phải không? nhập mô tả hình ảnh ở đây


-1

Câu trả lời đơn giản:

Từ khóa "này" luôn phụ thuộc vào ngữ cảnh của lệnh gọi. Chúng được đề cập dưới đây.

  1. CHỨC NĂNG ĐƯỢC GỌI VỚI TỪ KHÓA MỚI

    Nếu hàm được gọi với từ khóa MỚI thì NÀY sẽ bị ràng buộc với đối tượng mới được tạo.

    function Car(){
      this.name="BMW";
    }
    const myCar=new Car();
    myCar.name; // output "BMW"

    Ở trên, điều này sẽ được ràng buộc với đối tượng 'myCar'

  2. CHỨC NĂNG ĐƯỢC GỌI TUYỆT VỜI SỬ DỤNG CÁC PHƯƠNG PHÁP GỌI VÀ ÁP DỤNG.

    Trong trường hợp này, NÀY sẽ bị ràng buộc với đối tượng được truyền rõ ràng cho hàm.

    var obj1={"name":"bond"};
    function printMessage(msg){
        return msg+" "+this.name;
    }
    const message=printMessage.call(obj1,"my name is ");
    console.log(message); //HERE THIS WILL BE BOUND TO obj1 WHICH WE PASSED EXPLICITLY. SAME FOR APPLY METHOD ALSO.
  3. NẾU CHỨC NĂNG ĐƯỢC GỌI VỚI ĐỐI TƯỢNG NGAY LẬP TỨC NÀY SILL ĐƯỢC TRỞ THÀNH ĐỐI TƯỢNG NÀY

    var obj1={
       "name":"bond",
        getName: function () {
                    return this.name;
                 }
    };
    const newname=obj1.getName();
    console.log(newname); //HERE THIS WILL BE BOUND TO obj1(WHITCHEVER OBJECT IS MENTIONED BEFORE THE DOT THIS WILL BE BOUND TO THAT)
  4. KHI CHỨC NĂNG ĐƯỢC GỌI MÀ KHÔNG CÓ BẤT K CONT TIẾP THEO NÀY NÀO S B TRỞ THÀNH ĐỐI TƯỢNG TOÀN CẦU

    const util = {
       name: 'Utility',
       getName: function () {
                    return this.name;
    };
    
    const getName=util.getName;
    const newName=getName();
    console.log(newName); // IF THIS EXECUTED IN BROWSER THIS WILL BE  BOUND TO WINDOW OBJECT. IF THIS EXECUTED IN SERVER THIS WILL BE BOUND TO GLOBAL OBJECT
  5. TRONG CHẾ ĐỘ CHIẾN LƯỢC NÀY S UND ĐƯỢC HIỂU

    function setName(name){
        "use strict"
        return this.name;
    }
    setName(); //WILL BE ERROR SAYING name IS UNDEFINED. 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.