تشير الكلمة الرئيسية this إلى قيمة العنصر المرتبط بالدالة
في وقت استدعائها، ما يعني أنّ قيمتها تختلف استنادًا
إلى ما إذا كان يتم استدعاء الدالة كطريقة أو دالة مستقلة أو
مُنشئ.
عند استدعاء دالة، يتم إنشاء مثيل للكلمة الرئيسية this بدون إعلامك كإشارة إلى العنصر الذي يحتوي على هذه الدالة، ما يتيح لك الوصول إلى السمات والطُرق المحدّدة بجانبها من داخل نطاقها.
يشبه العمل مع this في بعض النواحي العمل مع متغيّر تمّت تعريفه
باستخدام const. مثل الثابت، لا يمكن إزالة this ولا يمكن
إعادة تعيينه، ولكن يمكن تغيير طرق الكائن الذي تحتوي عليه الكلمة الرئيسيةthis.
الربط العام
خارج الدالة أو سياق كائن، يشير الرمز this إلى سمة
globalThis، وهي إشارة إلى الكائن العام في معظم
بيئات JavaScript. في سياق نص برمجي يتم تشغيله في متصفّح ويب،
الكائن العام هو كائن window:
this;
> Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, ...}
في Node.js، globalThis هو عنصر global:
$ node
Welcome to Node.js v20.10.0.
Type ".help" for more information.
> this
<ref *1> Object [global] {
...
}
خارج الوضع الصارم، يشير this أيضًا إلى الكائن العام داخل دالة مستقلة، لأنّ العنصر الرئيسي Window هو الكائن الذي "يملك"
هذه الدوال بشكل فعّال.
function myFunction() {
console.log( this );
}
myFunction();
> Window {...}
(function() {
console.log( this );
}());
> Window {...}
عند استخدام الوضع الصارم، تكون قيمة this هي undefined داخل دالة
مستقلة:
(function() {
"use strict";
console.log( this );
}());
> undefined
قبل طرح الوضع الصارم، كان يتم استبدال قيمة null أو undefined لعنصر this
بمرجع إلى العنصر الشامل. قد ترى أحيانًا
الربط الشامل يُشار إليه باسم "الربط التلقائي" بسبب هذا السلوك القديم.
الربط الضمني
عند استدعاء دالة كطريقة لكائن، يشير مثيل this داخل
هذه الطريقة إلى الكائن الذي يحتوي على الطريقة، ما يتيح الوصول إلى methods والسمات التي تصاحبها:
let myObject = {
myValue: "This is my string.",
myMethod() {
console.log( this.myValue );
}
};
myObject.myMethod();
> "This is my string."
قد يبدو أنّ قيمة this تعتمد على كيفية تعريف الدالة والعنصر
المُحيط بها. بدلاً من ذلك، يكون سياق قيمة this هو
سياق التنفيذ الحالي. في هذه الحالة، يكون سياق التنفيذ هو أنّ كائن
myObject يستدعي الطريقة myMethod، وبالتالي يكون myObject هو القيمة
لـ this. قد يبدو هذا الأمر تقنيًا في سياق المثالين السابقين، ولكن بالنسبة إلى الاستخدامات الأكثر تقدمًا لـ this، من المهم مراعاة هذا الاختلاف.
بشكل عام، استخدِم this بطرق لا تتوقع أن يكون للرمز المحيط بها
أي بنية معيّنة. الاستثناء من هذه القاعدة هو ES5
دوالّ الأسهم.
this في دوال الأسهم
في الدوالّ السهمية، يتم تحليلthis إلى عملية ربط في
بيئة تشمل السياق. وهذا يعني أنّه
this في دالة السهم تشير إلى قيمة this في
السياق المحيط الأقرب لهذه الدالة:
let myObject = {
myMethod() { console.log( this ); },
myArrowFunction: () => console.log( this ),
myEnclosingMethod: function () {
this.myArrowFunction = () => { console.log(this) };
}
};
myObject.myMethod();
> Object { myMethod: myMethod(), myArrowFunction: myArrowFunction() }
myObject.myArrowFunction();
> Window {...}
في المثال السابق، يسجِّل myObject.myMethod() العنصر myObject كجسم "يملك" هذه الطريقة، ولكن myObject.myArrowFunction() تعرِض globalThis
(أو undefined)، لأنّ مثيل this داخل دالة السهم
يشير بدلاً من ذلك إلى النطاق المحيط الأعلى.
في المثال التالي، تنشئ myEnclosingMethod دالة سهم على
العنصر الذي يحتوي عليها عند تنفيذها. تشير الآن نسخة this داخل دالة السهم إلى قيمة this داخل السياق المغلق
، وهو الطريقة التي تحتوي على دالة السهم هذه. بما أنّ
قيمة this داخل myEnclosingMethod تشير إلى myObject، بعد تحديد
دالة السهم، تشير this داخل دالة السهم أيضًا إلى
myObject:
let myObject = {
myMethod() { console.log( this ); },
myEnclosingMethod: function () {
this.myArrowFunction = () => { console.log(this) };
}
};
myObject.myEnclosingMethod();
myObject.myArrowFunction();
> Object { myMethod: myMethod(), myArrowFunction: myArrowFunction() }
الربط الصريح
يعالج الربط الضمني معظم حالات الاستخدام للعمل مع this. ومع ذلك،
قد تحتاج أحيانًا إلى قيمة this لتمثيل سياق تنفيذ
محدّد، بدلاً من السياق المُفترَض. أحد الأمثلة التوضيحية، وإن كان قديمًا بعض الشيء، هو استخدام this ضمن دالة ردّ الاتصال الخاصة بـ setTimeout،
لأنّ دالة ردّ الاتصال هذه لها سياق تنفيذ فريد:
var myObject = {
myString: "This is my string.",
myMethod() {
console.log( this.myString );
}
};
myObject.myMethod();
> "This is my string."
setTimeout( myObject.myMethod, 100 );
> undefined
على الرغم من أنّه تمّ حلّ هذا النقص المحدّد في setTimeout من خلال
ميزات أخرى، تمّ حلّ مشاكل مشابهة "لفقدان" this في السابق
من خلال إنشاء إشارة صريحة إلى قيمة this ضمن نطاق
السياق المقصود. قد ترى أحيانًا حالات يتم فيها منح this
لمتغيّر باستخدام معرّفات مثل that أو self أو _this في قواعد برمجية
قديمة. هذه هي اصطلاحات المعرّفات الشائعة للمتغيّرات التي تحتوي على قيمة this مُرسَلة.
عند استدعاء دالة باستخدام الطريقتَين call() أو bind() أو apply()،
this تشير صراحةً إلى الكائن الذي يتم استدعاؤه:
let myFunction = function() {
console.log( this.myValue );
}
let myObject = {
"myValue" : "This is my string."
};
myFunction.call( myObject );
> "This is my string."
var myObject = {
myString: "This is my string.",
myMethod() {
console.log( this.myString );
}
};
setTimeout( myObject.myMethod.bind( myObject ), 100 );
> "This is my string."
تلغي عملية الربط الصريح قيمة this المقدَّمة من عملية الربط الضمني.
let myObject = {
"myValue" : "This string sits alongside myMethod.",
myMethod() {
console.log( this.myValue );
}
};
let myOtherObject = {
"myValue" : "This is a string in another object entirely.",
};
myObject.myMethod.call( myOtherObject );
> "This is a string in another object entirely."
إذا تمّ استدعاء دالة بطريقة تؤدي إلى ضبط قيمة this على
undefined أو null، يتم استبدال هذه القيمة بglobalThis خارج
الوضع الصارم:
let myFunction = function() {
console.log( this );
}
myFunction.call( null );
> Window {...}
وبالمثل، إذا تمّ استدعاء دالة بطريقة تمنح this قيمة أساسية
، يتمّ استبدال هذه القيمة بعنصر التفاف القيمة الأساسية
خارج الوضع الصارم:
let myFunction = function() {
console.log( this );
}
let myNumber = 10;
myFunction.call( myNumber );
> Number { 10 }
في الوضع الصارم، لا يتم تحويل قيمة this المرسَلة إلى عنصر بأي شكل من الأشكال،
حتى إذا كانت قيمة أساسية أو null أو undefined:
"use strict";
let myFunction = function() {
console.log( this );
}
let myNumber = 10;
myFunction.call( myNumber );
> 10
myFunction.call( null );
> null
new ربط
عند استخدام فئة كمنشئ باستخدام الكلمة الرئيسية
new، يشير this إلى المثيل الذي تم إنشاؤه حديثًا:
class MyClass {
myString;
constructor() {
this.myString = "My string.";
}
logThis() {
console.log( this );
}
}
const thisClass = new MyClass();
thisClass.logThis();
> Object { myString: "My string." }
وبالمثل، تشير قيمة this داخل دالة باني يتم استدعاؤها باستخدام new
إلى العنصر الذي يتم إنشاؤه:
function MyFunction() {
this.myString = "My string.";
this.logThis = function() {
console.log( this );
}
}
const myObject = new MyFunction();
myObject.logThis();
> Object { myString: "My string.", logThis: logThis() }
ربط معالج الحدث
في سياق معالجات الأحداث، تشير قيمة this إلى العنصر الذي
يُستخدَم لتشغيلها. داخل دالة ردّ الاتصال لمعالج الأحداث، يعني ذلك أنّ this
يشير إلى العنصر المرتبط بالمعالج:
let button = document.querySelector( "button" );
button.addEventListener( "click", function( event ) { console.log( this ); } );
عندما يتفاعل مستخدم مع button في المقتطف السابق، تكون النتيجة هو
عنصر العنصر الذي يحتوي على <button> نفسه:
> Button {}
عند استخدام دالة سهم كدالّة ردّ اتصال لمُستمع الأحداث، يتمّ توفير قيمة
this مرة أخرى من خلال أقرب سياق تنفيذ مُغلق. في المستوى العلوي، يعني ذلك أنّ this داخل دالة ردّ اتصال معالِج الحدث هو
globalThis:
let button = document.querySelector( "button" );
button.addEventListener( "click", ( event ) => { console.log( this ); } );
> undefined
كما هو الحال مع أي عنصر آخر، عند استخدام الطريقة call() أو bind() أو apply()
للإشارة إلى دالة ردّ الاتصال الخاصة بمراقب الحدث، تشير this
إلى العنصر صراحةً:
let button = document.querySelector( "button" );
let myObject = {
"myValue" : true
};
function handleClick() {
console.log( this );
}
button.addEventListener( "click", handleClick.bind( myObject ) );
> Object { myValue: true }
التحقّق من فهمك
بالنسبة إلى نص برمجي يتم تشغيله في متصفّح ويب، ما هو العنصر الشامل
الذي يشير إليه this عند استخدامه خارج دالة أو
سياق عنصر؟
windowbrowserundefined