سلام دوستان! یکی از مهمترین موضوعات جاوااسکریپت، کلمه‌کلیدی this هست که توی شرایط مختلف مقدارهای متفاوتی داره. توی این قسمت می‌خوایم با این کلمه‌کلیدی آشنا بشیم.

توی این قسمت موارد زیر رو یاد می‌گیریم:

 

this چیه؟ 🤔

کلمه‌کلیدی this که معمولاً توی توابع استفاده میشه، به قسمتی از کد اشاره می‌کنه که داره تابع رو اجرا می‌کنه. مقدار this یک آبجکت هست.

فرض کنیم یک تابع توی جاوااسکریپت داره اجرا میشه. دونستن اینکه یک تابع چه جوری نوشته شده و یا توی کدوم قسمت از کد تعریف شده، روی مقدار this تاثیر نداره. فقط نحوه‌ی فراخوانی اون تابع هست که مقدار this رو تعیین می‌کنه. برای اینکه مقدار this رو متوجه بشیم، کافیه فقط بدونیم این تابع به چه صورت تعریف و فراخوانی شده. امروز می‌خوایم بدونیم this توی شرایط مختلف چه مقدارهایی داره.

 

this توی توابع چه مقداری داره؟

مقدار this توی توابع به قسمتی از برنامه اشاره می‌کنه که تابع داره توی اون اجرا میشه. کد زیر رو در نظر بگیرید:

function f() {
  return this.name;
}

ما به هیچ وجه نمی‌تونیم تشخیص بدیم که this توی این تابع به کدوم آبجکت اشاره می‌کنه. چون هنوز مشخص نیست این تابع کجا داره اجرا میشه. پس باید ببینیم این تابع کجا داره اجرا میشه.

توی کد زیر، ما تابع foo رو داریم توی بیرونی‌ترین اسکوپ که اون رو با عنوان آبجکت سراسری (Global Object و توی مرورگر window) می‌شناسیم صدا می‌زنیم:

function foo() {
  alert(this);
}

foo(); // [object Window]

پس اینطوری می‌تونیم به پراپرتی‌های آبجکت window که همون متغیرهای گلوبال اسکوپ هستن دسترسی داشته باشیم:

>> var name = "David";

function getName() {
  alert(this.name);
}

>> getName();

توی این کد، اجراکنندهٔ تابع getName که توی خط آخر صدا زده شده، آبجکت سراسری هست. پس this توی این تابع به آبجکت سراسری اشاره می‌کنه. و بنابراین this.name توی خط ۴ به متغیری اشاره می‌کنه توی آبجکت سراسری تعریف شده که مقدار اون David هست.

خب بیاین یه جور دیگه تابع getName رو صدا بزنیم جوری که صاحبش عوض بشه:

var name = "David";

function getName() {
  alert(this.name);
}

const obj = {
  name: "John",
  <<print: getName>>
}

obj.print();

آبجکت obj یک پراپرتی داره به اسم print که مقدار اون برابر با تابع getName اول کد هست. وقتی توی خط آخر پراپرتی print رو مثل یک تابع صدا می‌زنیم، در اصل تابع getName داره صدا زده میشه، با این تفاوت که اجرا کنندهٔ این تابع، آبجکت obj هست و بنابراین مقدار this، آبجکت obj در نظر گرفته میشه. چون تابع getName توسط obj و از داخل اون داره فراخوانی میشه.

همونطور که گفته شد، مهم نیست یک تابع کجا تعریف شده. مهم اینه که کجا داره ازش استفاده میشه. بنابراین خروجی خط ۱۲ برابر با John هست که توی obj تعریف شده.

 

حالا کد زیر رو در نظر بگیرید:

var name = "David";

var person = {
  name: "John",
  getName() {
    alert(this.name);
  }
}

const myGetName = person.getName;

myGetName();      // David
person.getName(); // John

۱. توی خط ۱۰ مقدار متد getName از آبجکت person رو ریختیم توی متغیری به اسم myGetName. دقت کنین که هنوز این متد رو صدا نزدیم (توی خط ۱۰ پرانتزهای تابع رو حذف کردیم که اجرا نشه). پس اون رو می‌تونیم مثل یک تابع هر جای برنامه صدا بزنیم.

۲. توی خط ۱۲ که صاحب این قسمت همون آبجکت سراسری هست، تابع myGetName رو فراخونی می‌کنیم. پس خروجی، مقدار متغیر name از آبجکت سراسری (خط ۱) هست.

۳. اما خروجی خط ۱۳ چطور؟ اینجا getName داره از درون آبجکت ‌person صدا زده میشه. پس this به nameی اشاره می‌کنه که توی آبجکت person هست یعنی John.

 

مقدار this توی توابع با use strict

توی توابع با حالت سخت‌گیرانه، مقدار this دیگه آبجکت سراسری نیست. بلکه undefined خواهد بود:

function edge() {
  "use strict";

  return this;
}

alert(edge()); // undefined

 

مقدار this بیرون از توابع

مقدار this وقتی بیرون از توابع استفاده بشه، به آبجکت سراسری یا Global Object اشاره می‌کنه:

alert(this); // [object Window] in browsers

 

مقدار this در Arrow Functions

یکی از نکات مهمی که باید در نظر داشته باشیم اینه که مقدار this توی توابع معمولی با مقدار اون توی Arrow Function فرق می‌کنه. همونطور که گفتیم مقدار this توی توابع معمولی به آبجکتی اشاره می‌کنه که داره اون تابع رو اجرا می‌کنه. اما توی Arrow Function ها مقدار this به صاحب (اسکوپ) آبجکتی اشاره می‌کنه که ‌‌Arrow Function توی اون تعریف شده. بدون در نظر گرفتن اینکه تابع به چه صورت داره استفاده میشه:

var name = "John";

const person = {
  name: 'David',

  printName: () => {
    alert(this.name);
  }
}

person.printName();

توی مثال بالا printName توی آبجکت person تعریف شده. آبجکت ‌person کجا تعریف شده؟ آبجکت سراسری. پس this.name توی متد printName به مقدار name خط ۱ (John) که یک متغیر سراسری هست اشاره می‌کنه.

در واقع وقتی توی این توابع this رو صدا می‌زنیم، انگار اون this توی یک تابع نیست. پس باید ببینیم در صورت وجود نداشتن این تابع، this چه مقداری داشت.
 

مقدار this در توابع Constructor

کد زیر رو در نظر بگیرید. تابع Person یک Constructor Function هست که از اون با کلمه‌کلیدی new یک آبجکت ساختیم:

function Person(name) {
  this.name = name;
}

const mario = new Person("Mario");
const emily = new Person("Emily");

alert(mario.name); // Mario
alert(emily.name); // Emily

هنگام ساخته شدن نمونه از تابع سازنده:

۱. ابتدا یک آبجکت خالی ساخته میشه به اسم this

۲. درون این آبجکت، با this.name یک پراپرتی به اسم name با مقدار ورودی که به تابع پاس داده شده قراره می‌گیره

۳. نهایتاً آبجکت this برای ما return میشه.

برای مثال چیزهایی شبیه به این اتفاق می‌افته:

function Person(name) {
  var this = {}
  this.name = name;

  return this;
}

پس نمونه‌هایی که از یک تابع سازنده ساخته میشن، دارای مقدار this جداگونه و منحصر به فرد خودشون هستن و همیشه توی هر شرایطی به خود آبجکتِ نمونه اشاره می‌کنن.

 

چطوری یک this ثابت و پایدار داشته باشیم؟

گفتیم که تا قبل از اجرای کد، نمی‌تونیم مقدار this رو تشخیص بدیم. تابع زیر رو در نظر بگیرید:

function getName() {
  return this.name;
}

اینجا this خط ۲ مقدارش قابل تشخیص نیست. چون هنوز نمی‌دونیم توسط کدوم قسمت می‌خواد اجرا بشه. اما شرایطی هست که دلمون می‌خواد صراحتاً مشخص کنیم this به کجا اشاره کنه و دیگه دغدغهٔ این رو نداشته باشیم که این تابع کجا و به چه صورت داره صدا زده میشه.

ما با استفاده از متدهای call و apply می‌تونیم صراحتاً مشخص کنیم که مقدار this توی توابع به چه آبجکتی اشاره کنه

همه توابع توی خودشون متدهای call و apply رو دارن. اولین ورودی که این متدها می‌گیرن همون آبجکتی هست که می‌خوایم this به اون اشاره کنه:

funcName.call(obj);
funcName.apply(obj);

کد زیر رو درنظر بگیرید:

function getName() {
  alert(this.name);
}

const person = {
  name: "Mario",
}

>> getName.apply(person);
>> getName.call(person);

وقتی تابع getName رو مثل خط‌های ۹ و ۱۰ فراخونی می‌کنیم، این تابع در شرایطی اجرا میشه که this توی اون داره به آبجکت person اشاره می‌کنه.

برای آشنایی بیشتر با این متدها، سوال ۲۷ و ۲۸ سوالات مصاحبه جاوااسکریپت رو ببینید:

همچنین یک متد دیگه داریم به اسم bind که کار مشابهی انجام میده که از پست زیر با اون می‌تونین به صورت مفصل آشنا بشین:

 

خب دوستان با مهمترین نکته‌ها از کلمه‌کلیدی this آشنا شدیم. در شرایطی که خیلی‌ها اون رو یک افسانه و یک چیز پر رمز و راز می‌دونن، دیدیم که با در نظر داشتن چند قانون، چقدر راحت می‌تونیم مقدار اون رو تشخیص بدیم.

امیدوارم از این قسمت استفاده کرده باشید روزتون خوش 😉✌️

 

codeburst.io/all-about-this-and-new-keywords-in-javascript-38039f71780c

https://www.w3schools.com/js/js_this.asp

https://towardsdatascience.com/javascript-context-this-keyword-9a78a19d5786

https://www.codementor.io/@dariogarciamoya/understanding-this-in-javascript-with-arrow-functions-gcpjwfyuc

https://www.w3schools.com/js/js_arrow_function.asp

https://stackoverflow.com/questions/1986896/what-is-the-difference-between-call-and-apply

https://javascript.info/constructor-new

https://www.w3schools.com/js/js_function_call.asp