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
และพารามิเตอร์ของผูกไว้กับพารามิเตอร์ที่กำหนดไว้fun
arg1, 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
ฟังก์ชันการจัดการเหตุการณ์ภายในโดยตรงหมายถึงองค์ประกอบที่เกี่ยวข้อง ที่ได้รับมอบหมายหน้าที่โดยตรงดังกล่าวสามารถทำได้โดยใช้วิธีการหรือผ่านวิธีการลงทะเบียนเหตุการณ์แบบดั้งเดิมเช่นaddeventListener
onclick
- ในทำนองเดียวกันเมื่อคุณใช้
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
จะทำตัวเหมือนตัวแปรทั่วไป: มันจะสืบทอดมาจากขอบเขตคำศัพท์ ฟังก์ชั่นที่ที่ฟังก์ชั่นที่ลูกศรที่กำหนดจะเป็นฟังก์ชั่นลูกศรthis
this
ดังนั้นนั่นเป็นพฤติกรรมเดียวกันกับ:
(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