คำหลัก“ นี้” ทำงานอย่างไร


1309

ฉันสังเกตเห็นว่าไม่มีคำอธิบายที่ชัดเจนว่าthisคำหลักคืออะไรและมีการใช้งานอย่างถูกต้อง (และไม่ถูกต้อง) ใน JavaScript ในไซต์ Stack Overflow อย่างไร

ฉันได้เห็นพฤติกรรมแปลก ๆ บางอย่างกับมันและไม่เข้าใจว่าทำไมมันถึงเกิดขึ้น

มันthisทำงานอย่างไรและควรใช้เมื่อใด?


6
ฉันพบสิ่งนี้เมื่อฉัน googled "this" quirksmode.org/js/this.html
Wai Wong


2
Peter Michaux สนับสนุนการใช้this peter.michaux.ca/articles/javascript-widgets-without-this
Marcel Korpel

1
ภาพรวม MDN ไม่ได้ครึ่งไม่ดี ... developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/...
ดาด

2
คำอธิบายที่น่าสนใจเกี่ยวกับthisคำหลัก: rainsoft.io/gentle-explanation-of-this-in-javascript
Dmitri Pavlutin

คำตอบ:


1350

ฉันขอแนะนำให้อ่านขอบเขตบทความของMike West ใน JavaScript ( mirror ) ก่อน เป็นการแนะนำที่ยอดเยี่ยมและเป็นมิตรกับแนวคิดthisและขอบเขตขอบเขตใน JavaScript

เมื่อคุณเริ่มคุ้นเคยกับthisกฎจริงๆแล้วค่อนข้างง่าย ECMAScript 5.1 มาตรฐานกำหนดthis:

§11.1.1thisคำหลัก

thisประเมินคำหลักกับมูลค่าของ ThisBinding ของบริบทการดำเนินการปัจจุบัน

ThisBinding คือสิ่งที่ล่าม JavaScript เก็บรักษาเมื่อประเมินโค้ด JavaScript เช่นการลงทะเบียน CPU พิเศษซึ่งเก็บการอ้างอิงไปยังวัตถุ ล่ามจะอัพเดต ThisBinding ทุกครั้งที่สร้างบริบทการดำเนินการในหนึ่งในสามกรณีที่แตกต่างกัน:

1. บริบทการดำเนินการทั่วโลกเริ่มต้น

นี่เป็นกรณีของรหัส JavaScript ที่ประเมินที่ระดับบนสุดเช่นเมื่ออยู่ภายใน a <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>

เมื่อประเมินรหัสในบริบทการดำเนินการทั่วโลกเริ่มต้น ThisBinding ถูกตั้งค่าเป็นวัตถุทั่วโลกwindow( §10.4.1.1 )

ใส่รหัส eval

  • …โดยการโทรโดยตรงไปที่eval() ThisBinding จะไม่มีการเปลี่ยนแปลง มันเป็นค่าเดียวกันกับ ThisBinding ของบริบทการดำเนินการเรียก ( §10.4.2 (2) (a))

  • …ถ้าไม่ใช่โดยการเรียกโดยตรงไปยังeval()
    ThisBinding นี้จะถูกตั้งค่าเป็นวัตถุทั่วโลกราวกับว่ากำลังดำเนินการในบริบทการดำเนินการทั่วโลกเริ่มต้น ( §10.4.2 (1))

§15.1.2.1.1กำหนดสิ่งที่เรียกโดยตรงeval()คือ โดยทั่วไปeval(...)เป็นสายตรงในขณะที่สิ่งที่ต้องการ(0, eval)(...)หรือเป็นสายทางอ้อมvar indirectEval = eval; indirectEval(...); eval()ดูคำตอบของ chuckjต่อ(1, eval) ('this') vs eval ('this') ใน JavaScript? และรายละเอียด ECMA-262-5 ของ Dmitry Soshnikov บทที่ 2 โหมดเข้มงวด สำหรับเมื่อคุณอาจใช้eval()สายทางอ้อม

กำลังป้อนรหัสฟังก์ชัน

สิ่งนี้เกิดขึ้นเมื่อเรียกใช้ฟังก์ชัน ถ้ามีการเรียกใช้ฟังก์ชันบนวัตถุเช่นในobj.myMethod()หรือเทียบเท่าobj["myMethod"]()ดังนั้น ThisBinding จะถูกตั้งค่าเป็นวัตถุ ( objในตัวอย่าง§13.2.1 ) ในกรณีส่วนใหญ่ ThisBinding ถูกตั้งค่าเป็นวัตถุส่วนกลาง ( §10.4.3 )

เหตุผลในการเขียน "ในกรณีอื่น ๆ ส่วนใหญ่" เป็นเพราะมีแปดฟังก์ชั่นในตัว ECMAScript 5 ที่อนุญาตให้ระบุการเชื่อมโยงนี้ในรายการอาร์กิวเมนต์ ฟังก์ชั่นพิเศษเหล่านี้เรียกว่าthisArgซึ่งกลายเป็น ThisBinding เมื่อเรียกใช้ฟังก์ชัน (( 10.4.3 )

ฟังก์ชันในตัวพิเศษเหล่านี้คือ:

  • 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 ] )

ในกรณีของFunction.prototypeฟังก์ชั่นที่พวกเขาจะเรียกบนวัตถุฟังก์ชั่น แต่แทนที่จะตั้งค่า ThisBinding ไปยังวัตถุที่ฟังก์ชั่น ThisBinding thisArgถูกกำหนดให้เป็น

ในกรณีของArray.prototypeฟังก์ชั่นที่กำหนดcallbackfnจะถูกเรียกในบริบทการดำเนินการที่ ThisBinding ตั้งเป็นthisArgถ้าให้; มิฉะนั้นเพื่อวัตถุระดับโลก

เหล่านี้เป็นกฎสำหรับ JavaScript ธรรมดา เมื่อคุณเริ่มใช้ห้องสมุด JavaScript (เช่น jQuery) thisคุณอาจพบว่าการทำงานห้องสมุดบางจัดการค่าของ ผู้พัฒนาไลบรารี JavaScript เหล่านี้ทำเช่นนี้เพราะมันมีแนวโน้มที่จะรองรับกรณีการใช้งานที่พบบ่อยที่สุดและโดยทั่วไปผู้ใช้ของไลบรารีจะพบว่าพฤติกรรมนี้จะสะดวกกว่า เมื่อผ่านฟังก์ชั่นการโทรกลับที่อ้างอิงthisถึงฟังก์ชั่นห้องสมุดคุณควรดูเอกสารประกอบสำหรับการรับประกันใด ๆ เกี่ยวกับสิ่งที่มีค่าthisเมื่อฟังก์ชั่นที่เรียกว่า

หากคุณกำลังสงสัยว่า JavaScript ปรุงห้องสมุดค่าของห้องสมุดเป็นเพียงการใช้อย่างใดอย่างหนึ่งในตัวฟังก์ชันthis JavaScript ยอมรับ thisArgคุณสามารถเขียนฟังก์ชั่นของคุณเองโดยใช้ฟังก์ชั่นโทรกลับและthisArg:

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

มีกรณีพิเศษที่ฉันยังไม่ได้พูดถึง เมื่อสร้างวัตถุใหม่ผ่านตัวnewดำเนินการตัวแปลจาวาสคริปต์จะสร้างวัตถุใหม่ที่ว่างเปล่าตั้งค่าคุณสมบัติภายในบางอย่างแล้วเรียกใช้ฟังก์ชันตัวสร้างบนวัตถุใหม่ ดังนั้นเมื่อฟังก์ชั่นถูกเรียกในบริบทคอนสตรัคเตอร์ค่าของthisคือวัตถุใหม่ที่ล่ามสร้างขึ้น:

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);

ฟังก์ชั่นลูกศร

ลูกศรฟังก์ชั่น (แนะนำใน ECMA6) thisเปลี่ยนแปลงขอบเขตของ ดูคำถามที่ยอมรับในปัจจุบัน, ฟังก์ชั่นลูกศรเทียบกับการประกาศฟังก์ชั่น / การแสดงออก: พวกเขาเทียบเท่า / แลกเปลี่ยน สำหรับข้อมูลเพิ่มเติม. แต่ในระยะสั้น:

ฟังก์ชั่นลูกศรไม่มีthisผลผูกพัน .... ของตัวเอง แต่ตัวระบุเหล่านั้นจะได้รับการแก้ไขในขอบเขตศัพท์เช่นตัวแปรอื่น ๆ นั่นหมายความว่าภายในฟังก์ชั่นลูกศรthis... อ้างอิงถึงคุณค่าของthisสภาพแวดล้อมที่ฟังก์ชั่นลูกศรกำหนดไว้

เพื่อความสนุกทดสอบความเข้าใจของคุณด้วยตัวอย่าง

หากต้องการเปิดเผยคำตอบให้วางเมาส์เหนือช่องสีเทาอ่อน

  1. ค่าของthisเส้นที่ทำเครื่องหมายคืออะไร? ทำไม?

    window - บรรทัดที่ทำเครื่องหมายไว้จะถูกประเมินในบริบทการเรียกใช้งานโกลบอลเริ่มต้น

    if (true) {
        // What is `this` here?
    }
  2. มูลค่าของthisที่บรรทัดที่ทำเครื่องหมายเมื่อobj.staticFunction()ดำเนินการคืออะไร? ทำไม?

    obj - เมื่อเรียกใช้ฟังก์ชันบนวัตถุ ThisBinding ถูกตั้งค่าเป็นวัตถุ

    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. ค่าของthisเส้นที่ทำเครื่องหมายคืออะไร? ทำไม?

    window

    ในตัวอย่างนี้ล่าม JavaScript ป้อนรหัสฟังก์ชั่น แต่เป็นเพราะmyFun/ obj.myMethodไม่ได้เรียกว่าบนวัตถุ ThisBinding windowมีการตั้งค่า

    ซึ่งแตกต่างจากงูหลามซึ่งในการเข้าถึงวิธีการ ( obj.myMethod) สร้างวัตถุวิธีการที่ถูกผูกไว้

    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. ค่าของthisเส้นที่ทำเครื่องหมายคืออะไร? ทำไม?

    window

    อันนี้ช่างยุ่งยาก เมื่อประเมินรหัส EVAL, คือthis objอย่างไรก็ตามในรหัส eval myFunไม่ได้ถูกเรียกบนวัตถุดังนั้น ThisBinding ถูกตั้งค่าwindowสำหรับการโทร

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
  5. ค่าของthisเส้นที่ทำเครื่องหมายคืออะไร? ทำไม?

    obj

    บรรทัดmyFun.call(obj);กำลังเรียกใช้ฟังก์ชันในตัวพิเศษFunction.prototype.call()ซึ่งยอมรับthisArgว่าเป็นอาร์กิวเมนต์แรก

    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: พวกเขามีการอ้างอิงไปยังส่วนต่างๆภายในรุ่น 5.1 ของ ECMAScript มาตรฐานECMA-262 ฉันจัดเตรียมไว้เพื่อให้คุณสามารถอ่านมาตรฐานสำหรับรายละเอียดทางเทคนิคได้หากคุณต้องการ
Daniel Trebbien

1
ฉันคิดว่า @supertonsky ถูกต้องเกี่ยวกับ # 2 - ถ้า myFun () ถูกเรียกจากขอบเขตทั่วโลกและไม่ใช่วิธีการบนวัตถุ "นี่" จะเป็นวัตถุทั่วโลกดังนั้นการใช้คำพูดของคำถามจึงสำคัญ BTW - ฉันจริงๆชอบความคิดของการใช้การวางเมาส์เพื่อให้ได้คำตอบบางอย่างเช่นนี้
user655489

2
แต่jsfiddle.net/H4LYm/2แสดงให้เห็นว่าsetTimeoutตัวอย่างที่มีของthis window(global)
เควินเมเรดิ ธ

2
มาจากงูหลามอย่างใดอย่างหนึ่งจะจินตนาการถึงระดับของความยุ่งยากที่ฉันมีเมื่อฉันชนเข้ากับตัวอย่างที่ 3 .. smh
Marius Mucenicu

1
คำตอบนี้ควรได้รับการอัพเดตเพื่อสะท้อนความเป็นจริงของ ES2020 แม้ว่าการเปลี่ยนแปลงจะเป็นเพียงคำศัพท์
เบ็นแอสตัน

156

thisพฤติกรรมคำหลักที่แตกต่างกันใน JavaScript เมื่อเทียบกับภาษาอื่น ๆ ในภาษาเชิงวัตถุthisคำสำคัญหมายถึงอินสแตนซ์ปัจจุบันของชั้นเรียน ใน JavaScript ค่าของthisถูกกำหนดโดยบริบทการเรียกใช้ฟังก์ชัน ( context.function()) และตำแหน่งที่เรียกใช้

1. เมื่อใช้ในบริบทสากล

เมื่อคุณใช้thisในบริบทของโลกก็ถูกผูกไว้กับวัตถุทั่วโลก ( windowในเบราว์เซอร์)

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

เมื่อคุณใช้thisภายในฟังก์ชั่นที่กำหนดไว้ในบริบทโลกthisยังคงผูกพันกับวัตถุทั่วโลกเนื่องจากฟังก์ชั่นที่เกิดขึ้นจริงเป็นวิธีการของบริบทโลก

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

ข้างต้นf1ทำวิธีการของวัตถุทั่วโลก ดังนั้นเราสามารถเรียกมันบนwindowวัตถุดังนี้:

function f()
{
    return this;
}

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

2. เมื่อใช้วิธีการภายในวัตถุ

เมื่อคุณใช้thisคำสำคัญในวิธีการของวัตถุthisจะถูกผูกไว้กับวัตถุที่ล้อมรอบ "ทันที"

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

ด้านบนฉันได้ใส่คำลงในเครื่องหมายคำพูดคู่ทันที มันคือการทำให้จุดที่ถ้าคุณซ้อนวัตถุภายในวัตถุอื่นแล้วthisถูกผูกไว้กับพาเรนต์ทันที

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

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

แม้ว่าคุณจะเพิ่มฟังก์ชั่นลงในวัตถุอย่างชัดเจน แต่ก็ยังคงเป็นไปตามกฎข้างต้นซึ่งthisยังคงชี้ไปที่วัตถุหลักทันที

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. เมื่อเรียกใช้ฟังก์ชันที่ไม่ใช้บริบท

เมื่อคุณใช้thisฟังก์ชั่นภายในที่ถูกเรียกโดยไม่มีบริบทใด ๆ (เช่นไม่ได้อยู่ในวัตถุใด ๆ ) มันจะถูกผูกไว้กับวัตถุทั่วโลก ( windowในเบราว์เซอร์) (แม้ว่าฟังก์ชั่นจะถูกกำหนดภายในวัตถุ)

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 

ลองทุกฟังก์ชั่น

เราสามารถลองทำคะแนนเหนือฟังก์ชั่นได้เช่นกัน อย่างไรก็ตามมีความแตกต่างบางอย่าง

  • ด้านบนเราเพิ่มสมาชิกไปยังวัตถุโดยใช้สัญกรณ์ตัวอักษร thisเราสามารถเพิ่มสมาชิกในฟังก์ชั่นโดยใช้ เพื่อระบุ
  • สัญกรณ์ตัวอักษรวัตถุสร้างตัวอย่างของวัตถุที่เราสามารถใช้งานได้ทันที ด้วยฟังก์ชั่นเราอาจต้องสร้างอินสแตนซ์ของมันก่อนโดยใช้newโอเปอเรเตอร์
  • นอกจากนี้ในวิธีการตามตัวอักษรของวัตถุเราสามารถเพิ่มสมาชิกไปยังวัตถุที่กำหนดไว้แล้วอย่างชัดเจนโดยใช้ตัวดำเนินการจุด สิ่งนี้จะถูกเพิ่มไปยังอินสแตนซ์เฉพาะเท่านั้น อย่างไรก็ตามฉันได้เพิ่มตัวแปรลงในฟังก์ชั่นต้นแบบเพื่อที่จะได้รับการสะท้อนในทุกกรณีของฟังก์ชั่น

ด้านล่างฉันลองใช้ทุกสิ่งที่เราทำกับ Object และthisสูงกว่า แต่โดยการสร้างฟังก์ชันแรกแทนที่จะเขียนวัตถุโดยตรง

/********************************************************************* 
  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. เมื่อใช้ในฟังก์ชั่นคอนสตรัค

เมื่อฟังก์ชั่นถูกใช้เป็นตัวสร้าง (นั่นคือเมื่อมันถูกเรียกด้วยnewคำหลัก) thisภายในร่างกายของฟังก์ชั่นชี้ไปที่วัตถุใหม่ที่ถูกสร้างขึ้น

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. เมื่อใช้ภายในฟังก์ชั่นที่กำหนดไว้ในห่วงโซ่ต้นแบบ

หากวิธีการนี้อยู่บนโซ่ต้นแบบของวัตถุthisภายในวิธีดังกล่าวหมายถึงวัตถุวิธีการที่ถูกเรียกว่าราวกับว่าวิธีการที่กำหนดไว้ในวัตถุ

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. ฟังก์ชั่น Inside call (), Apply () และ bind ()

  • Function.prototypeวิธีการทั้งหมดเหล่านี้จะถูกกำหนดไว้ใน
  • วิธีการเหล่านี้ช่วยให้การเขียนฟังก์ชั่นหนึ่งครั้งและเรียกใช้ในบริบทที่แตกต่างกัน กล่าวอีกนัยหนึ่งพวกเขาอนุญาตให้ระบุค่าthisที่จะใช้ในขณะที่ฟังก์ชันกำลังดำเนินการ พวกเขายังใช้พารามิเตอร์ใด ๆ ที่จะส่งผ่านไปยังฟังก์ชั่นเดิมเมื่อมันถูกเรียก
  • fun.apply(obj1 [, argsArray])ตั้งค่าobj1เป็นthisด้านในfun()และเรียกการfun()ส่งผ่านองค์ประกอบของargsArrayมันเป็นข้อโต้แย้ง
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- ตั้งค่าobj1เป็นthisด้านในfun()และการโทรfun()ผ่านarg1, arg2, arg3, ...เป็นอาร์กิวเมนต์
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- ผลตอบแทนที่อ้างอิงถึงฟังก์ชั่นfunที่มีthisความสนุกสนานภายในผูกไว้กับobj1และพารามิเตอร์ของผูกไว้กับพารามิเตอร์ที่กำหนดไว้funarg1, arg2, arg3,...
  • โดยขณะนี้ความแตกต่างระหว่างapply, callและbindต้องได้กลายเป็นที่ชัดเจน applyช่วยให้การระบุข้อโต้แย้งในการทำงานเป็นวัตถุเช่นอาร์เรย์เช่นวัตถุที่มีlengthคุณสมบัติเป็นตัวเลขและคุณสมบัติจำนวนเต็มที่ไม่เป็นลบที่สอดคล้องกัน โดยที่callอนุญาตให้ระบุอาร์กิวเมนต์ของฟังก์ชันโดยตรง ทั้งสองapplyและcallเรียกใช้ฟังก์ชันในบริบทที่ระบุและด้วยอาร์กิวเมนต์ที่ระบุทันที ในทางกลับกันbindเพียงแค่ส่งคืนฟังก์ชั่นที่ถูกผูกไว้กับthisค่าที่ระบุและข้อโต้แย้ง เราสามารถรวบรวมการอ้างอิงถึงฟังก์ชันที่ส่งคืนโดยการกำหนดให้กับตัวแปรและหลังจากนั้นเราสามารถเรียกมันได้ตลอดเวลา
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. thisภายในตัวจัดการเหตุการณ์

  • เมื่อคุณกำหนดฟังก์ชั่นโดยตรงกับตัวจัดการเหตุการณ์ขององค์ประกอบการใช้thisฟังก์ชันการจัดการเหตุการณ์ภายในโดยตรงหมายถึงองค์ประกอบที่เกี่ยวข้อง ที่ได้รับมอบหมายหน้าที่โดยตรงดังกล่าวสามารถทำได้โดยใช้วิธีการหรือผ่านวิธีการลงทะเบียนเหตุการณ์แบบดั้งเดิมเช่นaddeventListeneronclick
  • ในทำนองเดียวกันเมื่อคุณใช้thisโดยตรงภายในคุณสมบัติเหตุการณ์ (ชอบ<button onclick="...this..." >) ขององค์ประกอบมันหมายถึงองค์ประกอบ
  • อย่างไรก็ตามการใช้thisทางอ้อมผ่านฟังก์ชั่นอื่น ๆ windowที่เรียกว่าภายในเหตุการณ์จัดการฟังก์ชั่นหรือคุณสมบัติเหตุการณ์แก้ไขไปยังวัตถุโลก
  • attachEventพฤติกรรมดังกล่าวจะประสบความสำเร็จเช่นเดียวกันเมื่อเราแนบฟังก์ชั่นในการจัดการเหตุการณ์โดยใช้วิธีการจัดกิจกรรมรูปแบบการลงทะเบียนไมโครซอฟท์ แทนที่จะกำหนดฟังก์ชั่นให้กับตัวจัดการเหตุการณ์ (และการสร้างวิธีการทำงานขององค์ประกอบ) มันจะเรียกฟังก์ชั่นในเหตุการณ์ (เรียกอย่างมีประสิทธิภาพในบริบททั่วโลก)

ผมขอแนะนำให้ดีขึ้นลองนี้ใน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. thisในฟังก์ชั่นลูกศร ES6

ในฟังก์ชั่นลูกศรthisจะทำตัวเหมือนตัวแปรทั่วไป: มันจะสืบทอดมาจากขอบเขตคำศัพท์ ฟังก์ชั่นที่ที่ฟังก์ชั่นที่ลูกศรที่กำหนดจะเป็นฟังก์ชั่นลูกศรthisthis

ดังนั้นนั่นเป็นพฤติกรรมเดียวกันกับ:

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

ดูรหัสต่อไปนี้:

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 

"เมื่อคุณใช้สิ่งนี้ภายในฟังก์ชั่นที่กำหนดไว้ในบริบทของโลกสิ่งนี้ยังคงผูกพันกับวัตถุทั่วโลกเนื่องจากฟังก์ชั่นนี้ทำขึ้นมาเป็นวิธีการของบริบทโลก" ไม่ถูกต้อง สิ่งนี้ถูกกำหนดโดยวิธีการเรียกใช้งานหรือการผูกไม่ใช่ตามที่มันถูกกำหนดไว้ การเรียกใช้ฟังก์ชันใด ๆ ที่ไม่มีการอ้างอิงพื้นฐาน ("บริบท") จะเริ่มต้นสิ่งนี้เป็นวัตถุทั่วโลกหรือยังคงไม่ได้กำหนดในโหมดเข้มงวด
RobG

@RobG hmm อาจเป็นไปได้ แต่ฉันพบสิ่งนี้ใน MDN : ในกรณีนี้ค่าของthisไม่ถูกตั้งค่าโดยการโทร เนื่องจากรหัสไม่ได้อยู่ในโหมดเข้มงวดค่าของthisต้องเป็นวัตถุเสมอจึงเป็นค่าเริ่มต้นไปยังวัตถุทั่วโลก และในความเป็นจริงนั่นคือสาเหตุที่ฉันคิดว่าเราสามารถโทรออกได้โดยตรงwindow.f1()ดังนั้นนั่นหมายถึงการf1()แนบกับwindowวัตถุแล้วฉันหมายถึงก่อนการเรียก ฉันเข้าใจผิดหรือเปล่า?
Mahesha999

ผมได้รับการแสดงความคิดเห็น (อาจจะไม่ได้อย่างชัดเจน) ในการเชื่อมโยงการตั้งค่าของคุณนี้ด้วย "ฟังก์ชั่นจะทำจริงวิธีการของบริบทโลก" ราวกับว่ามันเรียงลำดับของที่เรียกว่าwindow.fnซึ่งมันไม่ได้เป็น นี้เริ่มต้นที่วัตถุโลกเพราะไม่มีการอ้างอิงฐานที่ใช้ในการโทรออกไม่ได้เพราะจากที่ฟังก์ชั่นที่มีการกำหนดไว้ (ดังนั้นนี้จะยังคงกำหนดโดยวิธีการทำงานที่เรียกว่า) หากคุณอย่างชัดเจนเรียกว่าใช้window.fnแล้วคุณจะตั้งค่านี้จะหน้าต่าง ผลลัพธ์ที่เหมือนกันมีวิธีที่แตกต่างกัน :-)
RobG

"ด้านบนฉันได้ใส่คำทันที ... " ไม่คุณไม่ได้ คุณสามารถแก้ไขสิ่งนี้ได้ไหมเพื่อแก้ไขข้อผิดพลาด? ดูเหมือนความหมายของคำตอบและฉันไม่สามารถอ่านต่อได้จนกว่าข้อผิดพลาดจะได้รับการแก้ไขเนื่องจากกลัวว่าจะเรียนรู้สิ่งที่ไม่ถูกต้อง
TylerH

@TylerH ทำ Ctrl + F ในหน้านี้ในเบราว์เซอร์ของคุณเพื่อค้นหาสตริง "ทันที" (รวมถึงเครื่องหมายคำพูดคู่) ฉันคิดว่ามันอยู่ที่นั่นถ้าฉันเข้าใจผิดผิด
Mahesha999

64

จาวาสคริของ this

การเรียกฟังก์ชั่นที่เรียบง่าย

พิจารณาฟังก์ชั่นต่อไปนี้:

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

โปรดทราบว่าเรากำลังเรียกใช้สิ่งนี้ในโหมดปกตินั่นคือไม่ได้ใช้โหมดเข้มงวด

เมื่อทำงานในเบราว์เซอร์, ค่าของจะได้รับการบันทึกไว้เป็นthis windowนี่เป็นเพราะwindowตัวแปรทั่วโลกอยู่ในขอบเขตของเว็บเบราว์เซอร์

หากคุณรันโค้ดชิ้นเดียวกันนี้ในสภาพแวดล้อมเช่น node.js thisโปรดอ้างถึงตัวแปรกลางในแอป

ตอนนี้ถ้าเราเรียกใช้งานในโหมดเข้มงวดโดยการเพิ่มคำสั่ง"use strict";ไปที่จุดเริ่มต้นของการประกาศฟังก์ชั่นthisจะไม่อ้างถึงตัวแปรทั่วโลกในสภาพแวดล้อมอย่างใดอย่างหนึ่ง สิ่งนี้ทำเพื่อหลีกเลี่ยงความสับสนในโหมดเข้มงวด thisจะในกรณีนี้เพียงแค่บันทึกundefinedเพราะนั่นคือสิ่งที่มันเป็นมันไม่ได้กำหนด

thisในกรณีต่อไปนี้เราจะเห็นวิธีการจัดการค่าของ

การเรียกฟังก์ชันบนวัตถุ

มีหลายวิธีในการทำเช่นนี้ หากคุณได้เรียกวิธีการเนทีฟใน Javascript เช่นforEachและsliceคุณควรทราบแล้วว่าthisตัวแปรในกรณีนั้นอ้างอิงถึงสิ่งObjectที่คุณเรียกว่าฟังก์ชั่นนั้น (โปรดทราบว่าในจาวาสคริปต์ทุกอย่างเกี่ยวกับมันObjectรวมถึงArrays และFunctions) ยกตัวอย่างรหัสต่อไปนี้

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

หากObjectมีคุณสมบัติที่ถือเป็นFunctionคุณสมบัติที่เรียกว่าวิธีการ เมธอดนี้เมื่อถูกเรียกใช้จะมีการthisตั้งค่าตัวแปรให้Objectเป็นค่าที่สัมพันธ์กับมันเสมอ สิ่งนี้เป็นจริงสำหรับทั้งโหมดที่เข้มงวดและไม่เข้มงวด

โปรดทราบว่าหากวิธีการจัดเก็บ (หรือมากกว่าคัดลอก) ในตัวแปรอื่นการอ้างอิงถึงthisจะไม่ถูกเก็บรักษาไว้ในตัวแปรใหม่อีกต่อไป ตัวอย่างเช่น:

// continuing with the previous code snippet

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

พิจารณาสถานการณ์สมมติที่ใช้กันทั่วไปมากกว่า:

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

newคำหลัก

พิจารณาฟังก์ชั่นการสร้างใน 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`

มันทำงานอย่างไร เรามาดูกันว่าเกิดอะไรขึ้นเมื่อเราใช้newคำค้นหา

  1. การเรียกใช้ฟังก์ชันด้วยnewคีย์เวิร์ดจะเริ่มต้นObjectชนิดPersonทันที
  2. ตัวสร้างนี้มีชุดคอนสตรัคในการObject Personนอกจากนี้โปรดทราบว่าtypeof awalจะกลับมาObjectเท่านั้น
  3. ใหม่นี้จะได้รับการกำหนดต้นแบบของObject Person.prototypeซึ่งหมายความว่าวิธีใด ๆ หรือทรัพย์สินในPersonต้นแบบจะสามารถใช้ได้กับทุกกรณีรวมทั้งPersonawal
  4. Personตอนนี้ฟังก์ชั่นของตัวเองถูกเรียก; ถูกอ้างอิงถึงวัตถุที่สร้างขึ้นใหม่thisawal

ค่อนข้างตรงไปตรงมาใช่มั้ย

โปรดทราบว่าสเป็ค ECMAScript อย่างเป็นทางการไม่มีที่ระบุว่าฟังก์ชั่นดังกล่าวเป็นconstructorฟังก์ชั่นที่เกิดขึ้นจริง เป็นเพียงฟังก์ชั่นปกติและnewสามารถใช้กับฟังก์ชั่นใดก็ได้ มันเป็นเพียงแค่ว่าเราใช้พวกเขาเป็นเช่นนั้นและเราจึงเรียกพวกเขาว่าเป็นเช่นนั้นเท่านั้น

ฟังก์ชั่นการโทรในฟังก์ชั่น: callและapply

ดังนั้นใช่เนื่องจากfunctions ยังเป็นObjects(และตัวแปรชั้นหนึ่งในความเป็นจริงใน Javascript) แม้แต่ฟังก์ชั่นยังมีวิธีการที่ ... ดีฟังก์ชั่นของตัวเอง

ฟังก์ชั่นทั้งหมดสืบทอดมาจากทั่วโลกFunctionและมีสองวิธีที่หลากหลายcallและapplyและทั้งสองสามารถใช้เพื่อจัดการกับค่าของthisในฟังก์ชั่นที่พวกเขาถูกเรียก

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

นี่เป็นตัวอย่างการใช้งานcallทั่วไป มันเป็นพื้นใช้เวลาพารามิเตอร์แรกและชุดthisในฟังก์ชั่นที่อ้างอิงถึงfoo thisArgพารามิเตอร์อื่นทั้งหมดที่ผ่านไปcallถูกส่งไปยังฟังก์ชันfooเป็นอาร์กิวเมนต์
ดังนั้นรหัสด้านบนจะเข้าสู่{myObj: "is cool"}, [1, 2, 3]คอนโซล วิธีที่ดีในการเปลี่ยนค่าของthisฟังก์ชั่นใด ๆ

applyเกือบจะเหมือนกับที่callยอมรับว่าใช้พารามิเตอร์เพียงสองตัวเท่านั้นthisArgและอาร์เรย์ที่มีอาร์กิวเมนต์ที่จะถูกส่งผ่านไปยังฟังก์ชัน ดังนั้นการcallโทรด้านบนสามารถแปลเป็นapplyเช่นนี้:

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

โปรดทราบว่าcallและapplyสามารถแทนที่ค่าของการthisตั้งค่าโดยการเรียกใช้เมธอด dot ที่เรากล่าวถึงในหัวข้อย่อยที่สอง ง่ายพอ :)

กำลังนำเสนอ .... bind!

bindเป็นพี่ชายของและcall applyนอกจากนี้ยังเป็นวิธีที่สืบทอดโดยฟังก์ชั่นทั้งหมดจากFunctionนวกรรมิกระดับโลกใน Javascript ความแตกต่างระหว่างbindและcall/ applyคือคือทั้งจริงcallและapplyจะเรียกใช้ฟังก์ชัน bindในทางกลับกันจะส่งคืนฟังก์ชันใหม่ด้วยthisArgและที่argumentsตั้งไว้ล่วงหน้า ลองยกตัวอย่างเพื่อทำความเข้าใจสิ่งนี้ให้ดีขึ้น:

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]`

เห็นความแตกต่างระหว่างสาม? มันบอบบาง แต่ก็มีการใช้ต่างกัน ชอบcallและapply, bindนอกจากนี้ยังจะมากกว่าขี่ค่าของthisชุดโดยจุดวิธีการอุทธรณ์

นอกจากนี้โปรดทราบว่าทั้งสามฟังก์ชันนี้ไม่มีการเปลี่ยนแปลงใด ๆ กับฟังก์ชั่นดั้งเดิม callและapplyจะคืนค่าจากฟังก์ชันที่สร้างขึ้นใหม่ในขณะที่bindจะส่งคืนฟังก์ชันที่สร้างขึ้นใหม่เองพร้อมที่จะถูกเรียกใช้

สิ่งที่เพิ่มเติมคัดลอกสิ่งนี้

บางครั้งคุณไม่ชอบความจริงที่ว่าการthisเปลี่ยนแปลงด้วยขอบเขตโดยเฉพาะอย่างยิ่งขอบเขตที่ซ้อนกัน ลองดูตัวอย่างต่อไปนี้

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"
        });
    }
  };

ในโค้ดข้างต้นเราจะเห็นว่าค่าของการthisเปลี่ยนแปลงกับขอบเขตที่ซ้อนกัน แต่เราต้องการค่าthisจากขอบเขตเดิม ดังนั้นเรา 'คัดลอก' thisไปและใช้แทนการคัดลอกthat thisฉลาดใช่มั้ย

ดัชนี:

  1. สิ่งที่จะจัดขึ้นในthisโดยค่าเริ่มต้น?
  2. จะเกิดอะไรขึ้นถ้าเราเรียกใช้ฟังก์ชันเป็นเมธอดที่มีเครื่องหมายออบเจ็กต์จุด?
  3. ถ้าเราใช้newคำหลัก?
  4. เราจะจัดการthisกับcallและapplyอย่างไร
  5. bindการใช้
  6. การคัดลอกthisเพื่อแก้ปัญหาขอบเขตแบบซ้อน

47

"นี่" เป็นเรื่องเกี่ยวกับขอบเขต ทุกฟังก์ชั่นมีขอบเขตของตัวเองและเนื่องจากทุกอย่างใน JS เป็นวัตถุแม้แต่ฟังก์ชั่นก็สามารถเก็บค่าบางอย่างไว้ในตัวเองโดยใช้ "this" OOP 101 สอนว่า "นี่" ใช้ได้กับอินสแตนซ์ของวัตถุเท่านั้น ดังนั้นทุกครั้งที่มีการเรียกใช้งานฟังก์ชัน "อินสแตนซ์" ใหม่ของฟังก์ชันนั้นจะมีความหมายใหม่ของ "นี่"

คนส่วนใหญ่สับสนเมื่อพยายามใช้ "สิ่งนี้" ภายในฟังก์ชั่นปิดแบบไม่ระบุชื่อเช่น:

(ฟังก์ชั่น (ค่า) {
    this.value = value;
    $ ('. บางองค์ประกอบ). แต่ละ (ฟังก์ชั่น (ELT) {
        elt.innerHTML = this.value; // เอ่อโอ้!! อาจไม่ได้กำหนด
    });
}) (2);

ดังนั้นที่นี่ภายในแต่ละ () "นี่" ไม่ถือ "คุณค่า" ที่คุณคาดหวังไว้ (จาก

this.value = value;
เหนือมัน) ดังนั้นเพื่อแก้ไขปัญหานี้ (ไม่มีการเล่นสำนวน) ผู้พัฒนาสามารถ:

(ฟังก์ชั่น (ค่า) {
    var self = this; // การเปลี่ยนแปลงเล็กน้อย
    self.value = value;
    $ ('. บางองค์ประกอบ). แต่ละ (ฟังก์ชั่น (ELT) {
        elt.innerHTML = self.value; // วุ้ย !! == 2
    });
}) (2);

ลองดูสิ คุณจะเริ่มชอบรูปแบบการเขียนโปรแกรมนี้


6
"ทุกสิ่งใน JS เป็นวัตถุ" ไม่เป็นความจริง JavaScript ยังมีค่าดั้งเดิมดูbclary.com/2004/11/07/#a-4.3.2
Marcel Korpel

6
ค่าดั้งเดิมดูเหมือนจะมีวิธีการบางอย่างในตัวเองเช่น String # substring (), Number # toString () ฯลฯ ดังนั้นอาจจะไม่ได้มีระบบการตั้งชื่อเช่นเดียวกับบทความที่พวกเขาจริง ๆ ทำตัวราวกับว่าพวกเขาเป็นวัตถุ prototyped ทั้งหมดเช่น String # substring () เป็นจริง: String.prototype.substring = function () {... }) โปรดแก้ไขฉันหากฉันผิด
arunjitsingh

12
thisคำหลักมีอะไรจะทำอย่างไรกับขอบเขต นอกจากนี้ยังมีความหมายในฟังก์ชั่นที่ไม่ใช่คุณสมบัติของวัตถุ
Bergi

1
@ arunjitsingh— มีโรงเรียนแห่งความคิดสองแห่ง ฉันชอบสิ่งที่กล่าวว่า " ทุกอย่างเป็นวัตถุ แต่บางคนก็สามารถแสดงได้ด้วยวิธีดั้งเดิมเพื่อความสะดวก " ;-)
RobG

9
thisไม่ได้เกี่ยวกับขอบเขต มันคือทั้งหมดที่เกี่ยวกับบริบทการดำเนินการซึ่งไม่เหมือนกับขอบเขต JavaScript ถูกกำหนดขอบเขตโดย lexically (หมายถึงขอบเขตจะถูกกำหนดโดยตำแหน่งของโค้ด) แต่thisถูกกำหนดโดยวิธีการที่เรียกใช้ฟังก์ชันที่มีอยู่ซึ่งไม่ใช่ตำแหน่งที่ฟังก์ชันนั้นอยู่
Scott Marcus

16

ตั้งแต่หัวข้อนี้กระแทกขึ้นฉันได้รวบรวมคะแนนน้อยสำหรับผู้อ่านใหม่ในthisหัวข้อ

คุณค่าของการthisตัดสินใจเป็นอย่างไร?

เราใช้สิ่งนี้คล้ายกับวิธีที่เราใช้สรรพนามในภาษาธรรมชาติเช่นภาษาอังกฤษ:“ จอห์นวิ่งเร็วเพราะเขาพยายามขึ้นรถไฟ” แต่เราอาจเขียน“ … จอห์นพยายามขึ้นรถไฟ”

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 ไม่ได้กำหนดค่าไว้จนกว่าวัตถุจะเรียกใช้ฟังก์ชันตามที่กำหนดไว้ ในขอบเขตส่วนกลางตัวแปรและฟังก์ชันส่วนกลางทั้งหมดจะถูกกำหนดไว้บนwindowวัตถุ ดังนั้นthisในฟังก์ชั่นระดับโลกหมายถึง (และมีค่า) windowวัตถุระดับโลก

เมื่อuse strict, thisในระดับโลกและในฟังก์ชั่นที่ไม่ระบุชื่อที่ไม่ได้ผูกไว้กับวัตถุใด ๆ undefinedถือเป็นค่าของ

thisคำหลักคือเข้าใจผิดมากที่สุดเมื่อ: 1) เรายืมวิธีการที่ใช้this2) เรากำหนดวิธีการที่ใช้thisกับตัวแปร 3) ฟังก์ชั่นที่ใช้thisจะถูกส่งเป็นฟังก์ชั่นการโทรกลับและ 4) thisถูกนำมาใช้ภายในการปิด - ฟังก์ชั่นภายใน (2)

โต๊ะ

อนาคตคืออะไร

กำหนดในECMA Script 6ฟังก์ชันลูกศรใช้การthisเชื่อมโยงจากขอบเขตการล้อมรอบ (ฟังก์ชันหรือส่วนกลาง)

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!

ในขณะที่ฟังก์ชั่นลูกศรเป็นทางเลือกแทนการใช้bind()สิ่งสำคัญคือการปิดการใช้งานthisกลไกแบบเดิมๆ (1)


อ้างอิง:

  1. ต้นแบบ & ออบเจ็กต์นี้โดย Kyle Simpson © 2014 Getify Solutions
  2. javascriptissexy.com - http://goo.gl/pvl0GX
  3. Angus Croll - http://goo.gl/Z2RacU

16

thisใน JavaScript จะอ้างถึง 'เจ้าของ' ของฟังก์ชั่นที่จะถูกดำเนินการเสมอ

หากไม่มีการกำหนดเจ้าของอย่างชัดเจนดังนั้นจะมีการอ้างอิงเจ้าของหน้าต่างส่วนใหญ่บนสุด

ดังนั้นถ้าฉันทำ

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

element.onclick = someKindOfFunction;

thisจะอ้างถึงวัตถุองค์ประกอบ แต่ระวังผู้คนจำนวนมากทำผิดพลาดนี้

<element onclick="someKindOfFunction()">

ในกรณีหลังคุณเพียงแค่อ้างอิงฟังก์ชั่นไม่ใช่มอบให้กับองค์ประกอบ ดังนั้นthisจะอ้างถึงวัตถุหน้าต่าง


15

ทุกบริบทการดำเนินการใน javascript มีพารามิเตอร์นี้ที่กำหนดโดย:

  1. วิธีเรียกใช้ฟังก์ชัน (รวมถึงวิธีการเป็นวัตถุการใช้การโทรและการใช้การใช้งานใหม่ )
  2. การใช้งานของการผูก
  3. เล็ก ๆ สำหรับฟังก์ชั่นลูกศร (พวกเขานำสิ่งนี้มาจากบริบทการดำเนินการด้านนอก)
  4. ไม่ว่าจะเป็นรหัสที่อยู่ในโหมดเข้มงวดหรือไม่เข้มงวด
  5. ไม่ว่าจะเป็นรหัสถูกเรียกโดยใช้ eval

คุณสามารถตั้งค่าของนี้ใช้func.call, หรือfunc.applyfunc.bind

โดยค่าเริ่มต้นและสิ่งที่สร้างความสับสนให้ผู้เริ่มต้นส่วนใหญ่เมื่อเป็นผู้ฟังที่เรียกว่าหลังจากเหตุการณ์ที่จะเพิ่มขึ้นในองค์ประกอบ DOM ผู้นี้ค่าของฟังก์ชั่นเป็นองค์ประกอบ DOM

jQuery ทำให้สิ่งสำคัญนี้เปลี่ยนไปด้วย jQuery.proxy


9
มันถูกต้องกว่านี้เล็กน้อยที่จะบอกว่าการเรียกใช้ฟังก์ชันทุกครั้งมีขอบเขต กล่าวอีกนัยหนึ่งสิ่งที่ทำให้เกิดความสับสนthisใน Javascript ก็คือมันไม่ใช่คุณสมบัติที่แท้จริงของตัวฟังก์ชันเอง แต่เป็นสิ่งประดิษฐ์ของวิธีที่เรียกใช้ฟังก์ชัน
Pointy

@ pointy ขอบคุณ สิ่งที่ทำให้เกิดความสับสนมากที่สุดเกี่ยวกับเรื่องนี้ใน js คือความจริงที่ว่าในทุกภาษาที่ใช้ก่อนหน้านี้ (c #, c ++) - สิ่งนี้ไม่สามารถจัดการได้ n ชี้ไปยังอินสแตนซ์ของวัตถุเสมอในขณะที่ js มันขึ้นอยู่กับและสามารถเปลี่ยนแปลงได้ ฟังก์ชั่นการใช้func.callงานfunc.bindฯลฯ - Sushil
Sushil

2
thisไม่ได้อ้างอิงขอบเขตของฟังก์ชัน thisจะอ้างอิงวัตถุที่เฉพาะเจาะจง (หรืออาจจะundefined) ซึ่งเป็นคุณกล่าวว่าสามารถเปลี่ยนการใช้หรือ.call() ขอบเขต.apply()ของฟังก์ชั่นคือ (เป็นหลักเมื่อลดความซับซ้อน) ซึ่งตัวแปรนั้นมีการเข้าถึงและสิ่งนี้ขึ้นอยู่กับตำแหน่งที่ประกาศฟังก์ชั่นและไม่สามารถเปลี่ยนแปลงได้
nnnnnn

@Pointy: "มันถูกต้องอีกนิดที่จะบอกว่าการเรียกใช้ฟังก์ชันทุกครั้งมีขอบเขต" ที่ถูกต้องมากยิ่งขึ้นที่จะบอกว่าฟังก์ชั่น (และตอนนี้บล็อก) มีขอบเขต , ฟังก์ชั่นการโทรมีบริบท ขอบเขตกำหนดว่าตัวระบุคืออะไรที่สามารถใช้โดยรหัสในขอบเขตนั้น บริบทกำหนดสิ่งที่ตัวระบุเหล่านั้นถูกผูกไว้
TJ Crowder

1
"ไม่ว่าขอบเขตนั้นจะมีการอ้างอิงโดย" นี้ " ไม่thisและขอบเขตไม่มีอะไรเกี่ยวข้องกับสิ่งอื่นใน ES5 และก่อนหน้านี้ (เช่นเมื่อเขียนคำตอบนี้) ใน ES2015 (aka ES6) thisและขอบเขตที่เกี่ยวข้องหนึ่งวิธีที่น้อยที่สุดเป็นธรรม WRT ลูกศรฟังก์ชั่น (คนthisในฟังก์ชั่นลูกศรจะรับมาจากการปิดล้อมขอบเขตของมัน) แต่thisไม่เคยหมายถึงขอบเขต
TJ Crowder

10

ที่นี่เป็นแหล่งที่ดีของหนึ่งในthisJavaScript

นี่คือบทสรุป:

  • ทั่วโลกนี้

    ในเบราว์เซอร์ที่ขอบเขตทั่วโลกthisเป็นwindowวัตถุ

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

    ในการnodeใช้ repl thisเป็นเนมสเปซอันดับต้น globalคุณสามารถเรียกมันว่า

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

    ในnodeการดำเนินการจากสคริปต์thisที่ขอบเขตทั่วโลกเริ่มต้นเป็นวัตถุที่ว่างเปล่า มันไม่เหมือนกันglobal

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
  • ฟังก์ชั่นนี้

ยกเว้นในกรณีของตัวจัดการเหตุการณ์ DOM หรือเมื่อมีการthisArgให้ (ดูเพิ่มเติมลง) ทั้งในโหนดและในเบราว์เซอร์ที่ใช้thisในฟังก์ชั่นที่ไม่ได้เรียกว่ามีnewการอ้างอิงขอบเขตทั่วโลก ...

<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>

หากคุณใช้use strict;ซึ่งในกรณีนี้thisจะเป็น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>

ถ้าคุณเรียกใช้ฟังก์ชั่นที่มีจะเป็นบริบทใหม่ก็จะไม่อ้างอิงทั่วโลกnewthisthis

<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>
  • ต้นแบบนี้

ฟังก์ชั่นที่คุณสร้างกลายเป็นวัตถุฟังก์ชั่น พวกเขาจะได้รับprototypeคุณสมบัติพิเศษโดยอัตโนมัติซึ่งเป็นสิ่งที่คุณสามารถกำหนดค่าให้ เมื่อคุณสร้างอินสแตนซ์โดยการเรียกใช้ฟังก์ชันของnewคุณคุณจะสามารถเข้าถึงค่าที่คุณกำหนดให้กับprototypeคุณสมบัติได้ thisคุณสามารถเข้าถึงค่าเหล่านั้นใช้

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

Thing.prototype.foo = "bar";

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

มันมักจะเป็นความผิดพลาดในการกำหนดอาร์เรย์หรือวัตถุprototypeบน หากคุณต้องการให้อินสแตนซ์ของแต่ละคนมีอาร์เรย์ของตัวเองให้สร้างพวกมันในฟังก์ชั่นไม่ใช่ต้นแบบ

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 []
  • คัดค้านสิ่งนี้

คุณสามารถใช้thisในฟังก์ชั่นใด ๆ กับวัตถุเพื่ออ้างถึงคุณสมบัติอื่น ๆ บนวัตถุนั้น newนี้ไม่ได้เป็นเช่นเดียวกับอินสแตนซ์ที่สร้างขึ้นด้วย

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

obj.logFoo(); //logs "bar"
  • DOM เหตุการณ์นี้

ในตัวจัดการเหตุการณ์ HTML DOM thisนั้นเป็นการอ้างอิงถึงองค์ประกอบ DOM ที่เหตุการณ์นั้นเชื่อมต่อเสมอ

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();

นอกเสียจากคุณจะbindบริบท

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 สิ่งนี้

ภายในแอตทริบิวต์ HTML ที่คุณสามารถใส่ JavaScript thisเป็นการอ้างอิงถึงองค์ประกอบ

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
  • ทำสิ่งนี้

คุณสามารถใช้ในการเข้าถึงevalthis

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

var thing = new Thing();
thing.logFoo();
  • ด้วยสิ่งนี้

คุณสามารถใช้withเพื่อเพิ่มthisในขอบเขตปัจจุบันเพื่ออ่านและเขียนไปยังค่าบนthisโดยไม่อ้างอิงถึงthisอย่างชัดเจน

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 สิ่งนี้

jQuery จะอยู่ในหลาย ๆ ที่ที่thisอ้างถึงองค์ประกอบ 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

แดเนียลคำอธิบายที่ยอดเยี่ยม! สองสามคำในรายการนี้และรายการที่ดีของthisตัวชี้บริบทการดำเนินการในกรณีของตัวจัดการเหตุการณ์

ในสองคำ thisใน JavaScript จะชี้วัตถุที่ผู้ใช้ (หรือจากบริบทการดำเนินการ) ซึ่งทำงานอยู่ในปัจจุบันและเป็นแบบอ่านอย่างเดียวเสมอคุณไม่สามารถตั้งค่าได้ (ความพยายามดังกล่าวจะจบลงด้วย 'มือซ้ายไม่ถูกต้อง ด้านในข้อความ 'ที่ได้รับมอบหมาย

สำหรับตัวจัดการเหตุการณ์: ตัวจัดการเหตุการณ์แบบอินไลน์เช่น<element onclick="foo">แทนที่ตัวจัดการอื่น ๆ ที่แนบไว้ก่อนหน้านี้และก่อนหน้านี้ดังนั้นโปรดระมัดระวังและดีกว่าที่จะไม่ใช้การมอบหมายเหตุการณ์แบบอินไลน์เลย และต้องขอบคุณ Zara Alaverdyan ที่เป็นแรงบันดาลใจให้ฉันดูรายการตัวอย่างนี้ผ่านการถกเถียงที่ขัดแย้งกัน :)

  • 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

มีความสับสนมากมายเกี่ยวกับวิธีการตีความคำหลัก"นี้"ใน JavaScript หวังว่าบทความนี้จะทำให้ทุกคนได้พักผ่อนตลอดไป และอีกมากมาย โปรดอ่านบทความทั้งหมดอย่างละเอียด ได้รับการเตือนล่วงหน้าว่าบทความนี้มีความยาว

โดยไม่คำนึงถึงบริบทที่ใช้"สิ่งนี้"จะอ้างอิงถึง"วัตถุปัจจุบัน"ใน Javascript เสมอ แต่สิ่งที่เป็นวัตถุ "ปัจจุบัน"คือแตกต่างกันไปตามบริบท บริบทอาจจะตรง1 จาก 6ต่อไปนี้:

  1. ทั่วโลก (เช่นนอกฟังก์ชั่นทั้งหมด)
  2. การโทรภายใน "ฟังก์ชั่นที่ไม่ได้ผูกไว้" โดยตรง (เช่นฟังก์ชั่นที่ไม่ได้ผูกไว้กับการเรียกfunctionName.bind )
  3. ภายในอ้อม "ไม่ใช่ฟังก์ชั่นที่ถูกผูกไว้" โทรผ่านfunctionName.callและfunctionName.apply
  4. ภายในการโทร "ฟังก์ชั่นที่ถูกผูกไว้" (เช่นฟังก์ชันที่ถูกผูกไว้ด้วยการเรียกfunctionName.bind )
  5. ในขณะที่การสร้างวัตถุผ่าน "ใหม่"
  6. ตัวจัดการเหตุการณ์ Inside Inline DOM

ข้อมูลต่อไปนี้อธิบายถึงบริบทแต่ละข้อทีละคน:

  1. บริบททั่วโลก (เช่นนอกฟังก์ชั่นทั้งหมด):

    นอกฟังก์ชั่นทั้งหมด (เช่นในบริบทโลก) "วัตถุปัจจุบัน" (และด้วยเหตุนี้มูลค่าของ"นี้" ) มักจะเป็น วัตถุ"หน้าต่าง"สำหรับเบราว์เซอร์

  2. การโทรภายใน "Non Bound Function" โดยตรง :

    ภายในการเรียกโดยตรง "ฟังก์ชั่นที่ไม่ได้ผูกไว้" วัตถุที่เรียกใช้การเรียกฟังก์ชันจะกลายเป็น "วัตถุปัจจุบัน" (และด้วยเหตุนี้มูลค่าของ"นี่" ) หากฟังก์ชั่นที่เรียกว่าอย่างชัดเจนโดยไม่ต้องมีวัตถุปัจจุบันที่วัตถุปัจจุบันเป็นทั้ง"หน้าต่าง"วัตถุ (สำหรับโหมดที่ไม่ใช่เข้มงวด) หรือไม่ได้กำหนด (สำหรับโหมดเข้มงวด) ฟังก์ชั่นใด ๆ (หรือตัวแปร) ที่กำหนดใน บริบทสากลโดยอัตโนมัติกลายเป็นคุณสมบัติของวัตถุ"หน้าต่าง"สำหรับเช่นฟังก์ชั่นสมมติว่ามีการกำหนดไว้ในบริบทโลกเป็น

    function UserDefinedFunction(){
        alert(this)
        }

    มันจะกลายเป็นคุณสมบัติของวัตถุหน้าต่างราวกับว่าคุณได้กำหนดไว้เป็น

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

    ใน "โหมดที่ไม่เข้มงวด" การเรียก / เรียกใช้ฟังก์ชันนี้โดยตรงผ่าน "UserDefinedFunction ()"จะเรียก / เรียกใช้โดยอัตโนมัติในฐานะ"window.UserDefinedFunction ()"ทำให้"หน้าต่าง"เป็น "วัตถุปัจจุบัน" (และด้วยเหตุนี้ค่าของ" สิ่งนี้ " ) ภายใน" UserDefinedFunction "การเรียกฟังก์ชั่นนี้ใน" โหมดไม่เข้มงวด "จะส่งผลให้

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

    ใน "โหมดเข้มงวด" การเรียก / เรียกใช้ฟังก์ชันโดยตรงผ่าน "UserDefinedFunction ()"จะ"ไม่"เรียกโดยอัตโนมัติ / เรียกมันเป็น"window.UserDefinedFunction ()"ให้"วัตถุปัจจุบัน" (และค่าของ"this" ) ภายใน "UserDefinedFunction"จะต้องไม่ได้กำหนด การเรียกใช้ฟังก์ชั่นนี้ใน "โหมดเข้มงวด" จะส่งผลต่อไปนี้

    UserDefinedFunction() // displays undefined

    อย่างไรก็ตามการเรียกใช้อย่างชัดเจนโดยใช้วัตถุหน้าต่างจะส่งผลต่อไปนี้

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

    ให้เราดูตัวอย่างอื่น โปรดดูรหัสต่อไปนี้

     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

    ในตัวอย่างข้างต้นเราจะเห็นว่าเมื่อ"UserDefinedFunction"ถูกเรียกผ่านo1 , "นี้"ต้องใช้ค่าของO1และความคุ้มค่าของคุณสมบัติของ"A"และ"B"ได้แสดง ค่าของ"c"และ"d"ถูกแสดงเป็นไม่ได้กำหนดเนื่องจากo1ไม่ได้กำหนดคุณสมบัติเหล่านี้

    ในทำนองเดียวกันเมื่อ"UserDefinedFunction"ถูกเรียกผ่านo2 , "นี้"ต้องใช้ค่าของO2และความคุ้มค่าของคุณสมบัติของ"C"และ"D"รับค่าของ displayed.The "A"และ"B"ที่แสดงให้เห็นเป็นไม่ได้กำหนดเป็นo2ไม่ ไม่ได้กำหนดคุณสมบัติเหล่านี้

  3. ภายในอ้อม "ไม่ใช่ฟังก์ชั่นที่ถูกผูกไว้" โทรผ่านfunctionName.callและfunctionName.apply :

    เมื่อ"ฟังก์ชั่นที่ถูกผูกไว้ไม่ใช่"จะเรียกว่าผ่าน functionName.callหรือfunctionName.applyที่วัตถุ "ปัจจุบัน" (และด้วยเหตุนี้ค่าของ"นี้" ) ตั้งค่าของ "นี้"พารามิเตอร์ (พารามิเตอร์แรก) ส่งผ่านไปยังโทร / รหัสต่อไปนี้แสดงให้เห็นถึงสิ่งเดียวกัน

    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

    รหัสข้างต้นแสดงให้เห็นชัดเจนว่า "วันนี้" คุ้มค่าสำหรับการใด ๆ "ฟังก์ชั่นที่ถูกผูกไว้ไม่ใช่" สามารถเปลี่ยนแปลงผ่านโทร / ใช้ นอกจากนี้ถ้า "นี้"พารามิเตอร์ไม่ผ่านอย่างชัดเจนที่จะโทร / สมัคร , วัตถุ "ปัจจุบัน" (และด้วยเหตุนี้ค่าของ "นี้") จะถูกตั้งค่าเป็น"หน้าต่าง"ในโหมดที่ไม่ใช่ที่เข้มงวดและ"ไม่ได้กำหนด"ในโหมดที่เข้มงวด

  4. ภายในการโทร "ฟังก์ชั่นที่ถูกผูก" (เช่นฟังก์ชั่นที่ถูกผูกไว้ด้วยการเรียกfunctionName.bind ):

    ฟังก์ชั่นที่ถูกผูกไว้เป็นฟังก์ชั่นที่มีการแก้ไขค่า"this" รหัสต่อไปนี้แสดงให้เห็นถึงวิธีการ"นี้"ทำงานในกรณีที่ฟังก์ชั่นที่ถูกผูกไว้

    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

    ตามที่กำหนดในรหัสข้างต้น"นี้" ค่าสำหรับการใด ๆ "ฟังก์ชั่นที่ถูกผูกไว้" ไม่สามารถเปลี่ยนแปลงผ่านโทร นอกจากนี้หาก พารามิเตอร์"this"ไม่ได้ถูกส่งผ่านไปยังการเชื่อมโยงอย่างชัดเจน"วัตถุปัจจุบัน" (และด้วยเหตุนี้ค่าของ"this" ) นี้ถูกตั้งค่าเป็น"window"ในโหมด Non เข้มงวดและ"undefined"ในโหมดเข้มงวด อีกหนึ่งสิ่ง. ผูกฟังก์ชั่นที่ถูกผูกไว้แล้วไม่ได้เปลี่ยนค่าของ"นี้" มันยังคงตั้งเป็นค่าที่ตั้งค่าโดยฟังก์ชั่นการผูกแรก

  5. ในขณะที่การสร้างวัตถุผ่าน "ใหม่" :

    ภายในฟังก์ชั่นคอนสตรัคเตอร์"วัตถุปัจจุบัน" (และด้วยเหตุนี้ค่าของ "นี่" ) อ้างอิงวัตถุที่กำลังถูกสร้างผ่าน"ใหม่"โดยไม่คำนึงถึงสถานะการผูกของฟังก์ชั่น อย่างไรก็ตามถ้าตัวสร้างเป็นฟังก์ชันที่ถูกผูกไว้มันจะถูกเรียกด้วยชุดอาร์กิวเมนต์ที่กำหนดไว้ล่วงหน้าตามที่กำหนดไว้สำหรับฟังก์ชันที่ถูกผูกไว้

  6. ตัวจัดการเหตุการณ์ Inside Inline DOM :

    โปรดดูตัวอย่าง HTML ต่อไปนี้

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

    "นี้"ในตัวอย่างด้านบนอ้างถึงองค์ประกอบ "button" และองค์ประกอบ "div" ตามลำดับ

    ในตัวอย่างแรกสีของตัวอักษรของปุ่มจะถูกตั้งค่าเป็นสีขาวเมื่อมีการคลิก

    ในตัวอย่างที่สองเมื่อมีการคลิกองค์ประกอบ"div"มันจะเรียกใช้ฟังก์ชันOnDivClickด้วยพารามิเตอร์ที่สองที่อ้างอิงองค์ประกอบ div ที่คลิก อย่างไรก็ตามค่าของ"this" ภายใน OnDivClick SHALL ไม่ได้อ้างอิง องค์ประกอบdiv ที่ถูกคลิก ก็จะต้องถูกตั้งค่าเป็นหน้าต่าง "วัตถุ"หรือ "ไม่ได้กำหนด"ในองค์กรไม่เข้มงวดและโหมดเข้มงวดตามลำดับ (ถ้าOnDivClickเป็นฟังก์ชั่นไม่ได้ผูกไว้ ) หรือชุดเป็นค่าที่ถูกผูกไว้ที่กำหนดไว้ล่วงหน้า (ถ้าOnDivClickเป็นฟังก์ชั่นที่ถูกผูกไว้ )

ต่อไปนี้เป็นการสรุปบทความทั้งหมด

  1. ในบริบทสากล"นี่"มักจะหมายถึงวัตถุ"หน้าต่าง"

  2. เมื่อใดก็ตามที่มีการเรียกใช้ฟังก์ชันฟังก์ชันจะถูกเรียกใช้ในบริบทของวัตถุ ( "วัตถุปัจจุบัน" ) ถ้าวัตถุปัจจุบันไม่ได้ให้ชัดเจนที่วัตถุปัจจุบัน เป็นหน้าต่าง "วัตถุ"ในNON เข้มงวดโหมดและ"ไม่ได้กำหนด"ในโหมดเข้มงวดโดยค่าเริ่มต้น

  3. ค่าของ"this"ภายในฟังก์ชัน Non Bound เป็นการอ้างอิงไปยังวัตถุในบริบทที่ฟังก์ชันถูกเรียกใช้ ( "วัตถุปัจจุบัน" )

  4. ค่าของ"this"ภายในฟังก์ชัน Non Bound สามารถ overriden ได้โดยการ เรียกและใช้วิธีการของฟังก์ชัน

  5. ค่าของ"this"ได้รับการแก้ไขสำหรับฟังก์ชั่นที่ถูกผูกไว้และไม่สามารถแทนที่ได้ด้วยการเรียกและใช้วิธีการของฟังก์ชัน

  6. ฟังก์ชั่นการผูกและผูกพันแล้วจะไม่เปลี่ยนค่าของ "this" มันยังคงตั้งค่าเป็นค่าที่ตั้งค่าโดยฟังก์ชั่นการผูกแรก

  7. ค่าของ"นี่"ภายในตัวสร้างเป็นวัตถุที่จะถูกสร้างและเริ่มต้น

  8. ค่าของ"this"ภายในตัวจัดการเหตุการณ์ inline DOM อ้างอิงถึงองค์ประกอบที่กำหนดตัวจัดการเหตุการณ์


9

อาจเป็นบทความที่ละเอียดและครอบคลุมที่สุด thisคือ:

คำอธิบายที่อ่อนโยนของคำหลัก 'this' ใน JavaScript

แนวคิดที่อยู่เบื้องหลังthisคือการเข้าใจว่าประเภทการเรียกใช้ฟังก์ชันมีความสำคัญอย่างมากในการตั้งthisค่า


เมื่อมีปัญหาการระบุthis, ไม่ถามตัวเอง:

ไหนจะthisนำมาจาก ?

แต่ไม่ถามตัวเอง:

ฟังก์ชั่นนี้ถูกเรียกใช้อย่างไร?

สำหรับฟังก์ชั่นลูกศร (กรณีพิเศษของบริบทโปร่งใส) ถามตัวเอง:

สิ่งที่มีค่าได้thisที่ฟังก์ชั่นที่ลูกศรที่ถูกกำหนดไว้ ?

ความคิดนี้ถูกต้องเมื่อต้องรับมือthisและจะช่วยให้คุณไม่ปวดหัว


นอกเหนือจากการเชื่อมโยงไปยังบล็อกของคุณคุณอาจเจาะลึกลงไปอีกเล็กน้อยว่าการถามคำถามเหล่านั้นช่วยให้ใครบางคนเข้าใจthisคำหลักได้อย่างไร
Magnus Lind Oxlund

7

นี่คือคำอธิบายที่ดีที่สุดที่ฉันเคยเห็น: ทำความเข้าใจกับ JavaScripts นี้ด้วยความชัดเจน

การอ้างอิงนี้เสมอหมายถึง (และถือมูลค่า) วัตถุ - วัตถุเอกพจน์ - และมักจะใช้ภายในฟังก์ชั่นหรือวิธีการแม้ว่ามันจะสามารถนำมาใช้นอกฟังก์ชั่นในขอบเขตทั่วโลก โปรดทราบว่าเมื่อเราใช้โหมดเข้มงวดสิ่งนี้จะเก็บค่าไม่ได้กำหนดในฟังก์ชั่นทั่วโลกและในฟังก์ชั่นที่ไม่ระบุชื่อที่ไม่ได้ผูกไว้กับวัตถุใด ๆ

มีสี่สถานการณ์ที่มีความนี้อาจจะทำให้เกิดความสับสน:

  1. เมื่อเราผ่านเมธอด (ที่ใช้สิ่งนี้ ) เป็นอาร์กิวเมนต์ที่ใช้เป็นฟังก์ชันการโทรกลับ
  2. เมื่อเราใช้ฟังก์ชั่นภายใน (ปิด) เป็นสิ่งสำคัญที่จะต้องทราบว่าการปิดไม่สามารถเข้าถึงฟังก์ชั่นด้านนอกของสิ่งนี้ตัวแปรโดยใช้คำหลักนี้เพราะตัวแปรนี้สามารถเข้าถึงได้โดยฟังก์ชั่นของตัวเองเท่านั้นไม่ใช่โดยฟังก์ชั่นภายใน
  3. เมื่อวิธีการที่อาศัยนี้ถูกกำหนดให้กับตัวแปรข้ามบริบทซึ่งในกรณีนี้อ้างอิงวัตถุอื่นกว่าที่ตั้งใจไว้เดิม
  4. เมื่อใช้สิ่งนี้พร้อมกับการผูกใช้และวิธีการโทร

เขาให้ตัวอย่างโค้ดคำอธิบายและวิธีแก้ปัญหาซึ่งฉันคิดว่ามีประโยชน์มาก


6

ในแง่ pseudoclassical, วิธีการบรรยายจำนวนมากสอนคำหลัก 'นี้' เป็นวัตถุที่สร้างอินสแตนซ์โดยคลาสหรือวัตถุตัวสร้าง ทุกครั้งที่มีการสร้างวัตถุใหม่จากชั้นเรียนให้จินตนาการว่าภายใต้ประทุนนั้นจะมีการสร้างและส่งคืนวัตถุในท้องที่ภายใต้ฝากระโปรง ฉันจำได้ว่ามันสอนแบบนี้:

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

thisเป็นหนึ่งในแนวคิดที่เข้าใจผิดใน JavaScript เพราะมันทำงานแตกต่างกันเล็กน้อยในแต่ละที่ เพียงแค่thisอ้างถึง"เจ้าของ" ของฟังก์ชั่นที่เรากำลังดำเนินการอยู่

thisช่วยในการรับวัตถุปัจจุบัน (รู้จักในบริบทการดำเนินการ) ที่เราทำงานด้วย หากคุณเข้าใจว่าวัตถุใดที่ฟังก์ชั่นปัจจุบันถูกเรียกใช้งานคุณสามารถเข้าใจได้อย่างง่ายดายว่าปัจจุบันthisคืออะไร

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

ด้านบนเราสร้าง 3 ตัวแปรด้วยชื่อเดียวกัน 'วาล' หนึ่งในบริบทของโลกหนึ่งใน obj และอื่น ๆ ภายใน innerMethod ของ obj จาวาสคริปต์แก้ไขตัวระบุภายในบริบทที่เฉพาะเจาะจงโดยการขึ้นห่วงโซ่ขอบเขตจากท้องถิ่นไปทั่วโลก


สถานที่ไม่กี่แห่งที่thisสามารถสร้างความแตกต่างได้

การเรียกวิธีการของวัตถุ

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

เมื่อบรรทัดที่ 1 ถูกเรียกใช้จาวาสคริปต์จะสร้างบริบทการดำเนินการ (EC) สำหรับการเรียกใช้ฟังก์ชันโดยตั้งค่าthisเป็นวัตถุที่ถูกอ้างอิงโดยสิ่งที่มาก่อน "" . ดังนั้นในบรรทัดสุดท้ายที่คุณสามารถเข้าใจว่าa()ถูกดำเนินการในบริบทโลกซึ่งเป็นwindowกำลังดำเนินการในบริบทของโลกซึ่งเป็น

ด้วยตัวสร้าง

this สามารถใช้เพื่ออ้างถึงวัตถุที่กำลังสร้าง

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

เมื่อPerson()มีการดำเนินการใหม่วัตถุใหม่ทั้งหมดจะถูกสร้างขึ้น Personเรียกว่าและมันthisตั้งค่าเป็นวัตถุอ้างอิงใหม่

ฟังก์ชั่นการโทร

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 

หากเราพลาดnewคำwhatIsThisสำคัญให้อ้างอิงกับบริบทที่เป็นสากลที่สุดที่สามารถหาได้ (window )

ด้วยตัวจัดการเหตุการณ์

หากตัวจัดการเหตุการณ์เป็นแบบอินไลน์ให้thisอ้างถึงวัตถุทั่วโลก

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

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

เมื่อเพิ่มตัวจัดการเหตุการณ์ผ่าน JavaScript thisหมายถึงองค์ประกอบ DOM ที่สร้างเหตุการณ์



5

ค่าของ "this" ขึ้นอยู่กับ "บริบท" ซึ่งฟังก์ชันถูกใช้งาน บริบทสามารถเป็นวัตถุใด ๆ หรือวัตถุทั่วโลกเช่นหน้าต่าง

ดังนั้นความหมายของคำว่า "นี่" จึงแตกต่างจากภาษา OOP แบบดั้งเดิม และมันทำให้เกิดปัญหา: 1. เมื่อฟังก์ชั่นถูกส่งผ่านไปยังตัวแปรอื่น (ส่วนใหญ่เป็นโทรกลับ); และ 2. เมื่อมีการเรียกการปิดจากเมธอดสมาชิกของคลาส

ในทั้งสองกรณีนี้ถูกตั้งค่าเป็นหน้าต่าง


3

Whould นี้ความช่วยเหลือ? (ความสับสนส่วนใหญ่ของ 'this' ใน javascript นั้นมาจากข้อเท็จจริงที่ว่าโดยทั่วไปแล้วมันไม่ได้เชื่อมโยงกับวัตถุของคุณ แต่เป็นขอบเขตการดำเนินการปัจจุบัน - ซึ่งอาจไม่ตรงกับวิธีการทำงาน แต่มักจะรู้สึกอย่างนั้นกับฉัน - ดูบทความสำหรับคำอธิบายที่สมบูรณ์)


1
มันจะเป็นการดีกว่าถ้าบอกว่ามันเชื่อมโยง " กับบริบทการดำเนินการปัจจุบัน " ยกเว้นการเปลี่ยนแปลง ES6 (แบบร่าง) ที่มีฟังก์ชั่นลูกศรซึ่งจะสามารถแก้ไขได้ในบริบทการดำเนินการภายนอก
RobG

3

ข้อมูลเล็กน้อยเกี่ยวกับเรื่องนี้คำหลัก

มาบันทึกthisคำหลักเข้ากับคอนโซลในขอบเขตทั่วโลกโดยไม่ต้องใส่โค้ดเพิ่มอีก

console.log(this)

ในคำหลักไคลเอนต์ / เบราว์เซอร์ thisเป็นวัตถุทั่วโลกซึ่งเป็นwindow

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

และ

ในคีย์เวิร์ดรันไทม์ของเซิร์ฟเวอร์ / โหนด / Javascript thisก็เป็นวัตถุทั่วโลกเช่นกันmodule.exports

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

โปรดทราบว่าexportsเป็นเพียงการอ้างอิงถึงmodule.exports


1

การใช้งานสำหรับ Scope เช่นนี้

  <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>

ค่าของ txt1 และ txt เหมือนกันในตัวอย่างด้านบน $ (นี่) = $ ('# tbleName tbody tr') เหมือนกัน


1

ฉันมีสิ่งที่แตกต่างออกไป thisจากคำตอบอื่น ๆ ที่ฉันหวังว่าจะเป็นประโยชน์

วิธีหนึ่งที่จะดูที่ JavaScript เป็นที่จะเห็นว่ามีเพียงวิธีที่ 1 จะเรียกฟังก์ชั่น1 มันคือ

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

มีค่าบางค่าสำหรับ objectForThisมีเสมอค่าบางอย่างที่จัดมาให้

ทุกอย่างอื่นคือน้ำตาลสำหรับประโยค functionObject.call

ดังนั้นทุกอย่างสามารถอธิบายได้ด้วยวิธีที่มันแปลเป็น functionObject.callดังนั้นทุกสิ่งทุกอย่างที่สามารถอธิบายได้ด้วยวิธีการที่จะแปลเป็น

หากคุณเพิ่งเรียกฟังก์ชั่นก็thisคือ "วัตถุทั่วโลก" ซึ่งในเบราว์เซอร์คือหน้าต่าง

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

foo();  // this is the window object

ในคำอื่น ๆ

foo();

ถูกแปลอย่างมีประสิทธิภาพเป็น

foo.call(window);

โปรดทราบว่าถ้าคุณใช้โหมดเข้มงวดจากนั้นthisจะเป็นundefined

'use strict';

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

foo();  // this is the window object

ซึ่งหมายความว่า

ในคำอื่น ๆ

foo();

ถูกแปลอย่างมีประสิทธิภาพเป็น

foo.call(undefined);

ใน JavaScript มีตัวดำเนินการเช่น+และ-และ*และนอกจากนี้ยังมีตัวดำเนินการจุดซึ่งเป็น.

.ประกอบการเมื่อใช้ร่วมกับฟังก์ชั่นด้านขวาและวัตถุบนซ้ายได้อย่างมีประสิทธิภาพหมายถึง "ผ่านวัตถุthisฟังก์ชั่น

ตัวอย่าง

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

bar.foo();  // this is bar

ในคำอื่น ๆbar.foo()แปลเป็นconst temp = bar.foo; temp.call(bar);

โปรดทราบว่าไม่สำคัญว่าจะสร้างฟังก์ชันอย่างไร (ส่วนใหญ่ ... ) สิ่งเหล่านี้จะให้ผลลัพธ์ที่เหมือนกัน

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

ทั้งหมดนี้เป็นเพียงน้ำตาลสำหรับ

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

รอยย่นอีกอย่างหนึ่งคือโซ่ต้นแบบ เมื่อคุณใช้a.bJavaScript ลักษณะแรกบนวัตถุอ้างอิงโดยตรงสำหรับคุณสมบัติa bหากbไม่พบบนวัตถุนั้นจะมีลักษณะ JavaScript bในต้นแบบของวัตถุที่จะหา

มีหลายวิธีในการกำหนดต้นแบบของวัตถุโดยทั่วไปในปี 2562 เป็นclassคำสำคัญ สำหรับจุดประสงค์ของการthisแม้ว่ามันจะไม่สำคัญ สิ่งที่สำคัญคือเมื่อมองหาวัตถุaสำหรับคุณสมบัติbถ้าพบคุณสมบัติbบนวัตถุหรือเป็นลูกโซ่ต้นแบบหากbเป็นฟังก์ชันแล้วจะใช้กฎเดียวกันกับที่กล่าวไว้ข้างต้น การbอ้างอิงฟังก์ชั่นจะถูกเรียกโดยใช้callวิธีการและผ่านaเป็น objectForThis ดังแสดงด้านบนของคำตอบนี้

ตอนนี้ ลองจินตนาการว่าเราสร้างฟังก์ชั่นที่ชัดเจนthisก่อนที่จะเรียกฟังก์ชั่นอื่นแล้วเรียกมันด้วยตัวดำเนินการ.(dot)

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

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

const obj = {
  bar,
};

obj.bar();  

ต่อไปนี้การแปลเพื่อการใช้งานcall, กลายเป็นobj.bar() const temp = obj.bar; temp.call(obj);เมื่อเราเข้าสู่barฟังก์ชั่นที่เราเรียกfooแต่เราส่งผ่านอย่างชัดเจนในวัตถุอื่นสำหรับ objectForThis ดังนั้นเมื่อเราไปถึง foothisก็คือวัตถุภายใน

นี่คือสิ่งที่ทั้งสองbindและ=>ฟังก์ชันทำอย่างมีประสิทธิภาพ พวกมันเป็นน้ำตาลซินแทกติกมากกว่า พวกเขาสร้างฟังก์ชั่นที่มองไม่เห็นใหม่อย่างมีประสิทธิภาพเช่นเดียวกับbarข้างต้นที่กำหนดไว้อย่างชัดเจนthisก่อนที่จะเรียกฟังก์ชั่นที่ระบุ ในกรณีของการผูกถูกตั้งค่าเป็นสิ่งที่คุณส่งผ่านไปยังthisbind

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'});

โปรดทราบว่าหากfunctionObject.bindไม่มีอยู่เราก็สามารถทำสิ่งนี้ได้เช่นกัน

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

แล้วเราก็เรียกมันว่าอย่างนี้

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

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

ฟังก์ชั่นลูกศรตัว=>ดำเนินการคือน้ำตาลประโยคสำหรับผูก

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

เป็นเช่นเดียวกับ

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

เช่นเดียวbindกับฟังก์ชั่นใหม่ที่มองไม่เห็นถูกสร้างขึ้นที่เรียกใช้ฟังก์ชันที่กำหนดด้วยค่าที่ถูกผูกไว้objectForThisแต่ไม่เหมือนกับbindวัตถุที่ถูกผูกไว้ มันจะthisเกิดอะไรขึ้นเมื่อใช้=>ตัวดำเนินการ

ดังนั้นเช่นเดียวกับกฎข้างต้น

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()แปลว่าconst temp = obj.foo; temp.call(obj);ซึ่งหมายความว่าผู้ประกอบการที่อยู่ภายในลูกศรfooจะผูกกับฟังก์ชั่นที่มองไม่เห็นใหม่และกลับมองไม่เห็นว่าฟังก์ชั่นใหม่ที่ได้รับมอบหมายให้obj จะทำงานเหมือนที่เคยเป็นหรือเรียกฟังก์ชั่นใหม่ที่มองไม่เห็นที่สร้างขึ้น ฟังก์ชั่นที่มองไม่เห็นนั้นไม่สนใจการส่งผ่านเข้าไปและส่งผ่านเป็น objectForThis` ถึงฟังก์ชันลูกศรbb()b.call(window)b.call(undefined)foothisobj

โค้ดด้านบนแปลเป็น

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เป็นฟังก์ชันอื่นที่คล้ายคลึงกับcall

functionName.apply(objectForThis, arrayOfArgs);

แต่สำหรับ ES6 นั้นคุณสามารถแปลได้

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

0

thisJavascript สรุป:

  • ค่าของthisจะถูกกำหนดโดยวิธีการที่ฟังก์ชั่นไม่ได้ถูกเรียกใช้ซึ่งถูกสร้างขึ้น!
  • โดยปกติค่าของthisจะถูกกำหนดโดยวัตถุที่เหลือของจุด ( windowในพื้นที่โลก)
  • ในผู้ฟังเหตุการณ์ค่าของการthis อ้างอิงถึงองค์ประกอบ DOM ที่เหตุการณ์ถูกเรียก
  • เมื่ออยู่ในฟังก์ชั่นที่ถูกเรียกใช้พร้อมกับnewคีย์เวิร์ดค่าของการthisอ้างอิงไปยังวัตถุที่สร้างขึ้นใหม่
  • คุณสามารถจัดการกับค่าของthisที่มีฟังก์ชั่น: call, apply,bind

ตัวอย่าง:

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
              
             

ตัวอย่างผู้ฟังเหตุการณ์:

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>

ตัวสร้างตัวอย่าง:

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

เพื่อทำความเข้าใจ "สิ่งนี้" อย่างถูกต้องเราต้องเข้าใจบริบทและขอบเขตและความแตกต่างระหว่างสิ่งเหล่านั้น

ขอบเขต : ในขอบเขตจาวาสคริปต์เกี่ยวข้องกับการมองเห็นของตัวแปรขอบเขตทำได้โดยการใช้ฟังก์ชั่น (อ่านเพิ่มเติมเกี่ยวกับขอบเขต)

บริบท : บริบทเกี่ยวข้องกับวัตถุ มันหมายถึงวัตถุที่เป็นของฟังก์ชั่น เมื่อคุณใช้คีย์เวิร์ด JavaScript“ this” มันจะอ้างถึงวัตถุที่เป็นของฟังก์ชัน ตัวอย่างเช่นภายในฟังก์ชันเมื่อคุณพูดว่า:“ this.accoutNumber” คุณกำลังอ้างถึงคุณสมบัติ“ accoutNumber” ซึ่งเป็นของวัตถุที่เป็นของฟังก์ชันนั้น

หากวัตถุ“ myObj” มีวิธีการที่เรียกว่า“ getMyName” เมื่อใช้คีย์เวิร์ด JavaScript“ this” ใน“ getMyName” มันหมายถึง“ myObj” ถ้าฟังก์ชั่น“ getMyName” ถูกดำเนินการในขอบเขตทั่วโลกแล้ว“ นี่” หมายถึงวัตถุหน้าต่าง (ยกเว้นในโหมดเข้มงวด)

ตอนนี้เรามาดูตัวอย่าง:

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

เรียกใช้รหัส abobve ในผลลัพธ์ของเบราว์เซอร์จะ: ป้อนคำอธิบายรูปภาพที่นี่

ตามผลลัพธ์ที่คุณอยู่ในบริบทของวัตถุหน้าต่างจะเห็นได้ว่าต้นแบบหน้าต่างหมายถึงวัตถุ

ทีนี้ลองฟังก์ชั่นด้านใน:

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

เอาท์พุท:

ป้อนคำอธิบายรูปภาพที่นี่ ผลลัพธ์เหมือนกันเพราะเราบันทึกตัวแปร 'this' ในขอบเขตส่วนกลางและเราบันทึกไว้ในขอบเขตการทำงานเราไม่ได้เปลี่ยนบริบท ในบริบทกรณีที่ทั้งสองได้เหมือนกันที่เกี่ยวข้องกับวัตถุม่าย

ตอนนี้เรามาสร้างวัตถุของเราเอง ในจาวาสคริปต์คุณสามารถสร้างวัตถุได้หลายวิธี

 <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>

เอาท์พุท: ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นจากตัวอย่างข้างต้นเราพบว่าคำสำคัญ 'นี้' หมายถึงบริบทใหม่ที่เกี่ยวข้องกับ myObj และ myObject ยังมีต้นแบบลูกโซ่เป็นวัตถุ

ลองยกตัวอย่างอีก:

<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>

เอาท์พุท: ทำให้รู้สึกถูกต้องหรือไม่ (อ่านความคิดเห็น) ป้อนคำอธิบายรูปภาพที่นี่

หากคุณมีปัญหาในการทำความเข้าใจตัวอย่างข้างต้นให้ลองโทรกลับของเราเอง

<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>

เอาท์พุท: ป้อนคำอธิบายรูปภาพที่นี่

ตอนนี้เรามาทำความเข้าใจกับขอบเขตตนเอง IIFE และสิ่งนี้ว่าเป็นอย่างไร

       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> 

เอาต์พุตค่อนข้างเจ๋งใช่มั้ย ป้อนคำอธิบายรูปภาพที่นี่


-1

คำตอบง่ายๆ:

คำหลัก "นี้" ขึ้นอยู่กับบริบทของการเรียกใช้เสมอ พวกเขากล่าวถึงด้านล่าง

  1. ฟังก์ชั่นถูกเรียกด้วยคำใหม่

    หากฟังก์ชั่นถูกเรียกใช้พร้อมกับคีย์เวิร์ดใหม่ฟังก์ชันนี้จะถูกผูกไว้กับวัตถุที่สร้างขึ้นใหม่

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

    ในข้างต้นนี้จะถูกผูกไว้กับวัตถุ 'myCar'

  2. ฟังก์ชั่นถูกเรียกโดยใช้วิธีการโทรและใช้อย่างชัดเจน

    ในกรณีนี้สิ่งนี้จะถูกผูกไว้กับวัตถุที่ถูกส่งผ่านไปยังฟังก์ชันอย่างชัดเจน

    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. หากฟังก์ชั่นถูกเรียกด้วยวัตถุประสงค์โดยปริยายแล้วสิ่งนี้จะผูกพันกับวัตถุนั้น

    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. เมื่อฟังก์ชั่นถูกเรียกโดยไม่ต้องบริบทใด ๆ แล้วสิ่งนี้จะถูกผูกไว้กับวัตถุทั่วโลก

    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. ในโหมดที่เข้มงวดนี้จะไม่ได้รับการกำหนด

    function setName(name){
        "use strict"
        return this.name;
    }
    setName(); //WILL BE ERROR SAYING name IS UNDEFINED. 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.