Con trỏ “this” trong Javascript trong hàm lồng nhau


93

Tôi có một câu hỏi liên quan đến cách con trỏ "this" được xử lý trong một kịch bản hàm lồng nhau.

Giả sử tôi chèn mã mẫu sau đây vào một trang web. Tôi gặp lỗi khi gọi hàm lồng nhau "doSomeEffects ()". Tôi đã kiểm tra trong Firebug và nó chỉ ra rằng khi tôi ở trong hàm lồng nhau đó, con trỏ "this" thực sự đang trỏ đến đối tượng "cửa sổ" chung - điều mà tôi không mong đợi. Chắc hẳn tôi đang không hiểu điều gì đó một cách chính xác bởi vì tôi nghĩ rằng kể từ khi tôi khai báo hàm lồng nhau trong một hàm của đối tượng, nó phải có phạm vi "cục bộ" liên quan đến hàm (nghĩa là con trỏ "this" sẽ tham chiếu đến chính đối tượng như nó như thế nào trong câu lệnh "if" đầu tiên của tôi).

Bất kỳ con trỏ nào (không có ý định chơi chữ) sẽ được đánh giá cao.

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();

Chính xác câu hỏi của bạn là gì?
Sarfraz

2
Khi được sử dụng bên trong một hàm, thistham chiếu đến đối tượng mà hàm được gọi.
approxiblue

8
Những gì bạn có thể làm trong phạm vi bên ngoài là một cái gì đó giống như var self = this;và sau đó tham chiếu đến selftrong hàm bên trong thông qua bao đóng.
Kai

1
doSomeEffectskhông liên quan cụ thể với bất kỳ đối tượng nào, vì vậy thisđược giả định là cửa sổ, mẹ của tất cả các yếu tố.
approxiblue

3
@JoJoeDad Làm cách nào để nói điều này một cách ngoại giao? Nhưng câu trả lời được đưa ra bởi chuckj dưới đây cho đến nay là câu trả lời cho câu hỏi của bạn. Để thực sự hiểu những gì đang xảy ra, bạn nên đọc ngữ cảnh thực thi , chuỗi phạm vitừ khóa này . Và từ cái nhìn của một số câu trả lời ở đây, những người khác cũng nên đọc chúng. Tôi cố gắng truyền bá javascript tốt. Đó là lý do tại sao tôi dành thời gian để đưa ra những liên kết này.
onefootswill

Câu trả lời:


120

Trong JavaScript, thisđối tượng thực sự dựa trên cách bạn thực hiện các lệnh gọi hàm của mình.

Nói chung, có ba cách để thiết lập thisđối tượng:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

Trong tất cả các ví dụ trên, thisđối tượng sẽ là someThing. Gọi một hàm mà không có đối tượng cha đứng đầu thường sẽ giúp bạn có được đối tượng toàn cục mà trong hầu hết các trình duyệt có nghĩa là windowđối tượng.


Tôi đã thay đổi mã thành this.doSomeEffects();dựa trên câu trả lời của bạn nhưng nó vẫn không hoạt động. Tại sao?
Arashsoft

1
@Arashsoft thistrong this.doSomeEffects()điểm đến std_obj. Như đã giải thích trong câu trả lời ở trên nếu một hàm không có tham chiếu đối tượng thì nó phải thislà một đối tượng cửa sổ.
Shivam

38

Vì đây dường như là một trong những câu hỏi được ủng hộ nhiều nhất thuộc loại này, hãy để tôi nói thêm, sau ngần ấy năm, giải pháp ES6 sử dụng các hàm mũi tên:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

5
Bây giờ đó là sự tiến bộ!
Joshua Pinter

Giải pháp tuyệt vời, đơn giản.
Joshua Schlichting

32

thiskhông phải là một phần của phạm vi đóng, nó có thể được coi như một tham số bổ sung cho hàm được ràng buộc tại trang web cuộc gọi. Nếu phương thức không được gọi là một phương thức thì đối tượng toàn cục được chuyển dưới dạng this. Trong trình duyệt, đối tượng toàn cục giống hệt window. Ví dụ: hãy xem xét funciton sau,

function someFunction() {
}

và đối tượng sau,

var obj = { someFunction: someFunction };

Nếu bạn gọi hàm bằng cú pháp phương thức, chẳng hạn như,

obj.someFunciton();

sau đó thisbị ràng buộc với obj.

Nếu bạn gọi trực tiếp someFunction (), chẳng hạn như,

someFunction();

sau đó thisđược liên kết với đối tượng toàn cục, đó làwindow .

Công việc phổ biến nhất xung quanh là nắm bắt điều này vào kết thúc chẳng hạn như,

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      

that = cái này thật hoàn hảo
Nather Webber

10

Có sự khác biệt giữa các biến bao vây và "this". "this" thực sự được xác định bởi người gọi của hàm, trong khi các biến rõ ràng vẫn còn nguyên bên trong khối khai báo hàm được gọi là bao vây. Xem ví dụ bên dưới:

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

bạn có thể dùng thử tại đây: http://jsfiddle.net/kSTBy/

Điều đang xảy ra trong hàm của bạn là "doSomeEffects ()", được gọi một cách rõ ràng, điều này có nghĩa là ngữ cảnh hoặc "this" của hàm là cửa sổ. nếu "doSomeEffects" là một phương thức nguyên mẫu, ví dụ this.doSomeEffects khi nói "myObject", thì myObject.doSomeEffects () sẽ khiến "this" là "myObject".


4
cho lười biếng và thiếu kiên nhẫn, bản demo này các bản ghi:_this.name = myFirstObject this.name = mySecondObject
ptim

9

Để hiểu câu hỏi này, hãy thử lấy đầu ra cho đoạn mã sau

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

Đoạn mã trên sẽ xuất như sau vào bảng điều khiển:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

Trong hàm bên ngoài, cả this và self đều tham chiếu đến myObject và do đó cả hai đều có thể tham chiếu và truy cập foo đúng cách.

Tuy nhiên, trong hàm bên trong, điều này không còn đề cập đến myObject nữa. Do đó, this.foo không được xác định trong hàm bên trong, trong khi tham chiếu đến biến cục bộ vẫn nằm trong phạm vi và có thể truy cập ở đó. (Trước ECMA 5, điều này trong hàm bên trong sẽ tham chiếu đến đối tượng cửa sổ toàn cầu; trong khi đối với ECMA 5, đối tượng này trong hàm bên trong sẽ không được xác định.)


1
Trong hàm bên trong, thisđề cập đến đối tượng cửa sổ (trong môi trường trình duyệt) hoặc đối tượng TOÀN CẦU (trong môi trường node.js)
raneshu

2
Tại sao điều đó xảy ra?
setu

@raneshu "sử dụng nghiêm ngặt" và thiskhông còn đề cập đến cửa sổ
rofrol

3

Theo giải thích của Kyle, bạn có thể sử dụng callhoặc applyđể chỉ định thistrong hàm:

Đây là khái niệm được áp dụng cho mã của bạn:

var std_obj = {
    options: {
        rows: 0,
        cols: 0
    },
    activeEffect: "none",
    displayMe: function() {

        // the 'this' pointer is referring to the std_obj
        if (this.activeEffect == "fade") {}

        var doSomeEffects = function() {
            // the 'this' pointer is referring to the window obj, why?
            if (this.activeEffect == "fade") {}
        }

        doSomeEffects.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle


0

Tôi cũng nhận được cảnh báo " Quyền truy cập tham chiếu có thể không hợp lệ vào trường lớp thông qua trường này "

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class

0

Vì nó không được đề cập nên tôi sẽ đề cập rằng sử dụng .bind()là một giải pháp -


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

Đây là một ví dụ đơn giản hơn -

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

Đúng là sử dụng một hàm mũi tên =>trông có vẻ bóng bẩy và dễ dàng cho người lập trình. Tuy nhiên, cần lưu ý rằng phạm vi từ vựng có thể yêu cầu nhiều công việc hơn về mặt xử lý và bộ nhớ để thiết lập và duy trì phạm vi từ vựng đó, so với việc chỉ liên kết một hàm thisvới một con trỏ thông qua .bind().

Một phần lợi ích của việc phát triển các lớp trong JS là cung cấp một phương pháp để thishiện diện và sẵn có một cách đáng tin cậy hơn, xoay chuyển khỏi phạm vi lập trình hàm và từ vựng, và do đó giảm chi phí.

Từ MDN

Cân nhắc về hiệu suất Sẽ không khôn ngoan nếu tạo các hàm trong các hàm khác một cách không cần thiết nếu các lệnh đóng không cần thiết cho một tác vụ cụ thể, vì nó sẽ ảnh hưởng tiêu cực đến hiệu suất tập lệnh cả về tốc độ xử lý và mức tiêu thụ bộ nhớ.

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.