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