জাভাস্ক্রিপ্ট: এর অর্থ কী?

this এর মান খুঁজে বের করা কঠিন হতে পারে, এটি কীভাবে করবেন তা এখানে…

জাভাস্ক্রিপ্টের this অনেক কৌতুকের বাট, এবং এর কারণ, ভাল, এটি বেশ জটিল। যাইহোক, আমি দেখেছি যে ডেভেলপাররা this সাথে মোকাবিলা এড়াতে আরও জটিল এবং ডোমেন-নির্দিষ্ট কিছু করে। আপনি যদি this সম্পর্কে অনিশ্চিত হন, আশা করি এটি সাহায্য করবে। এই আমার this গাইড.

আমি সবচেয়ে নির্দিষ্ট পরিস্থিতি দিয়ে শুরু করতে যাচ্ছি, এবং সর্বনিম্ন-নির্দিষ্ট দিয়ে শেষ করব। এই নিবন্ধটি অনেকটা বড় if (…) … else if () … else if (…) … , তাই আপনি সরাসরি প্রথম বিভাগে যেতে পারেন যা আপনি যে কোডটি দেখছেন তার সাথে মেলে।

  1. যদি ফাংশনটিকে একটি তীর ফাংশন হিসাবে সংজ্ঞায়িত করা হয়
  2. অন্যথায়, যদি ফাংশন/ক্লাসটিকে new দিয়ে কল করা হয়
  3. অন্যথায়, যদি ফাংশনের একটি 'বাউন্ড' this মান থাকে
  4. অন্যথায়, যদি this কল-টাইমে সেট করা থাকে
  5. অন্যথায়, যদি একটি প্যারেন্ট অবজেক্টের মাধ্যমে ফাংশনটি কল করা হয় ( parent.func() )
  6. অন্যথায়, যদি ফাংশন বা প্যারেন্ট স্কোপ কঠোর মোডে থাকে
  7. অন্যথায়

যদি ফাংশনটি একটি তীর ফাংশন হিসাবে সংজ্ঞায়িত করা হয়:

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

এই ক্ষেত্রে, this মান সর্বদা অভিভাবক সুযোগে this মতোই থাকে:

const outerThis = this;

const arrowFunction = () => {
  // Always logs `true`:
  console.log(this === outerThis);
};

তীর ফাংশনগুলি দুর্দান্ত কারণ this অভ্যন্তরীণ মান পরিবর্তন করা যায় না, এটি সর্বদা বাইরের this হিসাবে একই।

অন্যান্য উদাহরণ

তীর ফাংশন সহ, this মান bind দিয়ে পরিবর্তন করা যাবে না :

// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();

তীর ফাংশন সহ, call বা apply মাধ্যমে this মান পরিবর্তন করা যাবে না :

// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});

তীর ফাংশনের সাথে, ফাংশনটিকে অন্য বস্তুর সদস্য হিসাবে কল করে this মান পরিবর্তন করা যাবে না :

const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();

তীর ফাংশন সহ, ফাংশনটিকে কনস্ট্রাক্টর হিসাবে কল করে this মান পরিবর্তন করা যাবে না :

// TypeError: arrowFunction is not a constructor
new arrowFunction();

'বাউন্ড' উদাহরণ পদ্ধতি

উদাহরণ পদ্ধতির সাহায্যে, আপনি যদি নিশ্চিত করতে চান this সর্বদা ক্লাস ইনস্ট্যান্সকে বোঝায়, সর্বোত্তম উপায় হল তীর ফাংশন এবং ক্লাস ক্ষেত্রগুলি ব্যবহার করা:

class Whatever {
  someMethod = () => {
    // Always the instance of Whatever:
    console.log(this);
  };
}

উপাদানগুলিতে (যেমন প্রতিক্রিয়া উপাদান, বা ওয়েব উপাদান) ইভেন্ট শ্রোতা হিসাবে উদাহরণ পদ্ধতি ব্যবহার করার সময় এই প্যাটার্নটি সত্যিই কার্যকর।

উপরেরটি মনে হতে পারে যে এটি "অভিভাবক সুযোগে this this হবে" নিয়মটি ভঙ্গ করছে, তবে আপনি যদি কনস্ট্রাক্টরে জিনিসগুলি সেট করার জন্য শ্রেণী ক্ষেত্রগুলিকে সিনট্যাকটিক চিনি হিসাবে মনে করেন তবে এটি বোধগম্য হবে:

class Whatever {
  someMethod = (() => {
    const outerThis = this;
    return () => {
      // Always logs `true`:
      console.log(this === outerThis);
    };
  })();
}

// …is roughly equivalent to:

class Whatever {
  constructor() {
    const outerThis = this;
    this.someMethod = () => {
      // Always logs `true`:
      console.log(this === outerThis);
    };
  }
}

বিকল্প প্যাটেনগুলি কনস্ট্রাক্টরের মধ্যে একটি বিদ্যমান ফাংশন বাঁধাই বা কনস্ট্রাক্টরে ফাংশন বরাদ্দ করা জড়িত। আপনি যদি কোনো কারণে ক্লাস ক্ষেত্র ব্যবহার করতে না পারেন, তাহলে কনস্ট্রাক্টরে ফাংশন বরাদ্দ করা একটি যুক্তিসঙ্গত বিকল্প:

class Whatever {
  constructor() {
    this.someMethod = () => {
      // …
    };
  }
}

অন্যথায়, যদি ফাংশন/ক্লাসটিকে new দিয়ে কল করা হয়:

new Whatever();

উপরোক্ত Whatever কল করবে (অথবা এটির কন্সট্রাকটর ফাংশন যদি এটি একটি ক্লাস হয়) this সেটের সাথে Object.create(Whatever.prototype) এর ফলাফলে।

class MyClass {
  constructor() {
    console.log(
      this.constructor === Object.create(MyClass.prototype).constructor,
    );
  }
}

// Logs `true`:
new MyClass();

পুরানো শৈলী নির্মাণকারীদের জন্য একই সত্য:

function MyClass() {
  console.log(
    this.constructor === Object.create(MyClass.prototype).constructor,
  );
}

// Logs `true`:
new MyClass();

অন্যান্য উদাহরণ

যখন new দিয়ে ডাকা হয়, তখন this মান bind দিয়ে পরিবর্তন করা যাবে না :

const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();

যখন new এর সাথে কল করা হয়, তখন অন্য বস্তুর সদস্য হিসাবে ফাংশনটিকে কল করে this মান পরিবর্তন করা যায় না :

const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();

অন্যথায়, যদি ফাংশনের একটি 'বাউন্ড' থাকে this মান:

function someFunction() {
  return this;
}

const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);

যখনই boundFunction বলা হয়, তখন তার this মানটি হবে bind জন্য পাস করা বস্তুটি ( boundObject )।

// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);

অন্যান্য উদাহরণ

একটি আবদ্ধ ফাংশন কল করার সময়, this মান call বা apply সাথে পরিবর্তন করা যাবে না :

// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);

একটি আবদ্ধ ফাংশন কল করার সময়, অন্য বস্তুর সদস্য হিসাবে ফাংশন কল করে this মান পরিবর্তন করা যাবে না :

const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);

অন্যথায়, যদি this কল-টাইমে সেট করা থাকে:

function someFunction() {
  return this;
}

const someObject = {hello: 'world'};

// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);

this মান হল call / apply জন্য পাস করা বস্তু।

দুর্ভাগ্যবশত this DOM ইভেন্ট শ্রোতাদের মতো জিনিসগুলির দ্বারা কিছু অন্য মানতে সেট করা হয়েছে এবং এটি ব্যবহার করার ফলে কোড বোঝা কঠিন হতে পারে:

করবেন না
element.addEventListener('click', function (event) {
  // Logs `element`, since the DOM spec sets `this` to
  // the element the handler is attached to.
  console.log(this);
});

আমি উপরের মত ক্ষেত্রে this ব্যবহার করা এড়িয়ে চলুন, এবং পরিবর্তে:

করবেন
element.addEventListener('click', (event) => {
  // Ideally, grab it from a parent scope:
  console.log(element);
  // But if you can't do that, get it from the event object:
  console.log(event.currentTarget);
});

অন্যথায়, যদি ফাংশনটি একটি প্যারেন্ট অবজেক্টের মাধ্যমে কল করা হয় ( parent.func() ):

const obj = {
  someMethod() {
    return this;
  },
};

// Logs `true`:
console.log(obj.someMethod() === obj);

এই ক্ষেত্রে ফাংশনটিকে obj এর সদস্য বলা হয়, তাই this হবে obj । এটি কল-টাইমে ঘটে, তাই যদি ফাংশনটিকে তার মূল অবজেক্ট ছাড়া বা অন্য কোনও মূল বস্তুর সাথে কল করা হয় তবে লিঙ্কটি ভেঙে যায়:

const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);

const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);

someMethod() === obj মিথ্যা কারণ someMethod কে obj এর সদস্য বলা হয় না । এইরকম কিছু চেষ্টা করার সময় আপনি এই গোটচাটির সম্মুখীন হতে পারেন:

const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');

এটি ভেঙে যায় কারণ querySelector এর বাস্তবায়ন তার নিজস্ব this মানটিকে দেখে এবং এটি একটি DOM নোড হতে আশা করে এবং উপরের সংযোগটি ভেঙে দেয়। উপরেরটি সঠিকভাবে অর্জন করতে:

const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);

মজার ঘটনা: সমস্ত API অভ্যন্তরীণভাবে this ব্যবহার করে না। this রেফারেন্সগুলি এড়াতে console.log মতো কনসোল পদ্ধতিগুলি পরিবর্তন করা হয়েছে, তাই log console সাথে আবদ্ধ করার প্রয়োজন নেই৷

অন্যথায়, যদি ফাংশন বা পিতামাতার সুযোগ কঠোর মোডে থাকে:

function someFunction() {
  'use strict';
  return this;
}

// Logs `true`:
console.log(someFunction() === undefined);

এই ক্ষেত্রে, this মান অনির্ধারিত। যদি প্যারেন্ট স্কোপ কঠোর মোডে থাকে (এবং সমস্ত মডিউল কঠোর মোডে থাকে) তাহলে ফাংশনে 'use strict' প্রয়োজন হয় না।

অন্যথায়:

function someFunction() {
  return this;
}

// Logs `true`:
console.log(someFunction() === globalThis);

এই ক্ষেত্রে, this মান globalThis এর মতোই।

ফাউ!

আর এটাই! আমি this সম্পর্কে জানি সব. কোন প্রশ্ন? আমি মিস করেছি কিছু? নির্দ্বিধায় আমাকে টুইট করুন।

পর্যালোচনা করার জন্য Mathias Bynens , Ingvar Stepanyan , এবং Thomas Steiner- কে ধন্যবাদ।