টাইপস্ক্রিপ্ট "এই" স্কোপিংয়ের সমস্যা যখন jquery কলব্যাকে ডাকা হয়


107

আমি টাইপস্ক্রিপ্টে "এটি" এর স্কোপিং পরিচালনা করার জন্য সর্বোত্তম পদ্ধতির বিষয়ে নিশ্চিত নই।

আমি কোডকে টাইপস্ক্রিপ্টে রূপান্তর করছি এমন একটি সাধারণ প্যাটার্নের উদাহরণ এখানে:

class DemonstrateScopingProblems {
    private status = "blah";
    public run() {
        alert(this.status);
    }
}

var thisTest = new DemonstrateScopingProblems();
// works as expected, displays "blah":
thisTest.run(); 
// doesn't work; this is scoped to be the document so this.status is undefined:
$(document).ready(thisTest.run); 

এখন, আমি কলটিতে পরিবর্তন করতে পারি ...

$(document).ready(thisTest.run.bind(thisTest));

... যা কাজ করে। তবে এটা ভয়াবহ। এর অর্থ হ'ল কোডগুলি কিছু কিছু পরিস্থিতিতে সংকলন করতে এবং সূক্ষ্মভাবে কাজ করতে পারে তবে আমরা যদি সুযোগটি বেঁধে রাখতে ভুলে যাই তবে এটি ভেঙে যাবে।

আমি ক্লাসের মধ্যে এটি করার একটি উপায় চাই, যাতে ক্লাসটি ব্যবহার করার সময় আমাদের "এই" কী থেকে যায় তা নিয়ে চিন্তা করার দরকার নেই।

কোনও পরামর্শ?

হালনাগাদ

আর একটি পদ্ধতি যা কাজ করে তা হ'ল চর্বিযুক্ত তীর:

class DemonstrateScopingProblems {
    private status = "blah";

    public run = () => {
        alert(this.status);
    }
}

এটি কি বৈধ পন্থা?


2
এটি সহায়ক হবে: youtube.com/watch?v=tvocUcbCupA
বাসারাত

দ্রষ্টব্য: রায়ান তার উত্তরটি টাইপস্ক্রিপ্ট উইকিতে অনুলিপি করেছেন ।
ফ্র্যাঙ্কলিন ইউ

টাইপস্ক্রিপ্ট 2+ সমাধানের জন্য এখানে দেখুন ।
দেিলান

উত্তর:


166

আপনার এখানে কয়েকটি বিকল্প রয়েছে যার প্রত্যেকটির নিজস্ব ট্রেড অফ রয়েছে। দুর্ভাগ্যক্রমে কোনও সুস্পষ্ট সেরা সমাধান নেই এবং এটি সত্যই প্রয়োগের উপর নির্ভর করবে।


আপনার প্রশ্নে প্রদর্শিত হিসাবে স্বয়ংক্রিয় ক্লাস বাইন্ডিং :

class DemonstrateScopingProblems {
    private status = "blah";

    public run = () => {
        alert(this.status);
    }
}
  • ভাল / খারাপ: এটি আপনার ক্লাসের উদাহরণ অনুসারে প্রতি পদ্ধতিতে অতিরিক্ত ক্লোজার তৈরি করে। যদি এই পদ্ধতিটি কেবলমাত্র নিয়মিত পদ্ধতি কলগুলিতে ব্যবহৃত হয় তবে এটি ওভারকিল। যাইহোক, যদি এটি কলব্যাক পজিশনে প্রচুর ব্যবহৃত হয় তবে thisপ্রতিটি কল সাইট পরিবর্তে নতুন ক্লোজার তৈরি করার পরিবর্তে শ্রেণীর উদাহরণের জন্য প্রসঙ্গটি ক্যাপচার করা আরও দক্ষ ।
  • ভাল: বাহ্যিক কলারদের thisপ্রসঙ্গ পরিচালনা করতে ভুলে যাওয়া অসম্ভব
  • ভাল: টাইপস্ক্রিপ্টে টাইপসেফ
  • ভাল: যদি ফাংশনটির প্যারামিটার থাকে তবে কোনও অতিরিক্ত কাজ হবে না
  • খারাপ: উত্পন্ন ক্লাসগুলি এইভাবে লিখিত বেস ক্লাসের পদ্ধতিগুলি কল করতে পারে না super.
  • খারাপ: কোন পদ্ধতিগুলি "প্রাক-সীমাবদ্ধ" এবং কোনটি আপনার শ্রেণি এবং এর গ্রাহকদের মধ্যে অতিরিক্ত নন-টাইপসেফ চুক্তি তৈরি করছে না তার সঠিক শব্দার্থক।

Function.bind
এছাড়াও প্রদর্শিত হিসাবে:

$(document).ready(thisTest.run.bind(thisTest));
  • ভাল / খারাপ: প্রথম পদ্ধতির তুলনায় মেমরি / পারফরম্যান্সের বিপরীতে
  • ভাল: যদি ফাংশনটির প্যারামিটার থাকে তবে কোনও অতিরিক্ত কাজ হবে না
  • খারাপ: টাইপস্ক্রিপ্টে, বর্তমানে এটির কোনও ধরণের সুরক্ষা নেই
  • খারাপ: আপনার কাছে যদি তা বিবেচিত হয় তবে কেবল ECMAScript 5 এ উপলব্ধ
  • খারাপ: আপনাকে উদাহরণের নামটি দু'বার টাইপ করতে হবে


টাইপস্ক্রিপ্টে চর্বিযুক্ত তীর (ব্যাখ্যামূলক কারণে কিছু ডামি পরামিতি সহ এখানে দেখানো হয়েছে):

$(document).ready((n, m) => thisTest.run(n, m));
  • ভাল / খারাপ: প্রথম পদ্ধতির তুলনায় মেমরি / পারফরম্যান্সের বিপরীতে
  • ভাল: টাইপস্ক্রিপ্টে, এটিতে 100% প্রকারের সুরক্ষা রয়েছে
  • ভাল: ECMAScript 3 এ কাজ করে
  • ভাল: আপনাকে কেবল একবার নাম টাইপ করতে হবে
  • খারাপ: আপনাকে প্যারামিটারগুলি দু'বার টাইপ করতে হবে
  • খারাপ: বৈকল্পিক পরামিতিগুলির সাথে কাজ করে না

1
+1 দুর্দান্ত উত্তর রায়ান, ভাল এবং কনস এর ভাঙ্গন পছন্দ, ধন্যবাদ!
জোনাথন মোফ্যাট

- আপনার ফাংশন.বাইন্ডে, ইভেন্টটি সংযুক্ত করার জন্য আপনি প্রতিটি সময় একটি নতুন বন্ধন তৈরি করেন।
131

1
চর্বিযুক্ত তীরটি ঠিক তা করেছে !! : ডি: ডি = () => আপনাকে অনেক ধন্যবাদ! : ডি
ক্রিস্টোফার স্টক

@ রায়ান-কাভানফ কীভাবে ভাল-মন্দ সম্পর্কে অবগত হবে যখন অবজেক্টটি মুক্তি পাবে? > 30 মিনিটের জন্য সক্রিয় এমন এসপিএর উদাহরণ হিসাবে, উপরের কোনটি জেএস আবর্জনা সংগ্রহকারীদের পক্ষে পরিচালনা করা সবচেয়ে ভাল?
abbaf33f

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

16

আর একটি সমাধান যা কিছু প্রাথমিক সেটআপের প্রয়োজন হয় তবে তার অদম্য আলো দিয়ে অর্থ প্রদান করে, আক্ষরিকভাবে এক-শব্দের সিনট্যাক্স ব্যবহারকারীদের মাধ্যমে জেআইটি-বাইন্ড পদ্ধতিতে মেথড ডেকোরেটর ব্যবহার করে ।

আমি এই ধারণার বাস্তবায়ন প্রদর্শনের জন্য গিটহাবের উপর একটি রেপো তৈরি করেছি (মন্তব্যগুলির সাথে কোডটির 40 টি লাইন দিয়ে একটি উত্তরের সাথে এটি ফিট করা কিছুটা দীর্ঘ) , যা আপনি কেবল যেমনটি ব্যবহার করবেন:

class DemonstrateScopingProblems {
    private status = "blah";

    @bound public run() {
        alert(this.status);
    }
}

আমি এখনও এই উল্লেখ কোথাও দেখিনি, তবে এটি নির্বিঘ্নে কাজ করে। এছাড়াও, এই পদ্ধতির কোনও উল্লেখযোগ্য খারাপ দিক নেই: রানটাইম টাইপ-সুরক্ষার জন্য কিছু টাইপ-চেকিং সহ এই ডেকোরেটরটির বাস্তবায়ন তুচ্ছ এবং সোজা, এবং প্রাথমিক পদ্ধতি কলের পরে মূলত শূন্য ওভারহেড সহ আসে।

অপরিহার্য অংশটি ক্লাস প্রোটোটাইপে নিম্নলিখিত প্রাপ্তকারীকে সংজ্ঞায়িত করছে, যা প্রথম কলের আগেই কার্যকর করা হয় :

get: function () {
    // Create bound override on object instance. This will hide the original method on the prototype, and instead yield a bound version from the
    // instance itself. The original method will no longer be accessible. Inside a getter, 'this' will refer to the instance.
    var instance = this;

    Object.defineProperty(instance, propKey.toString(), {
        value: function () {
            // This is effectively a lightweight bind() that skips many (here unnecessary) checks found in native implementations.
            return originalMethod.apply(instance, arguments);
        }
    });

    // The first invocation (per instance) will return the bound method from here. Subsequent calls will never reach this point, due to the way
    // JavaScript runtimes look up properties on objects; the bound method, defined on the instance, will effectively hide it.
    return instance[propKey];
}

সম্পূর্ণ উত্স


পরিবর্তে ক্লাস ডেকরেটারে এটি করে, পদ্ধতিগুলির উপর পুনরাবৃত্তি করে এবং তাদের প্রত্যেকের জন্য একটি পাসে উপরের সম্পত্তি বর্ণনাকারী সংজ্ঞা দিয়ে ধারণাটি আরও একধাপ এগিয়ে নেওয়া যেতে পারে।


ঠিক যেটা আমার দরকার ছিল!
মার্সেল ভ্যান ডের ড্রিফট

14

Necromancing।
একটি স্পষ্ট সহজ সমাধান রয়েছে যার জন্য তীর-ফাংশনগুলির প্রয়োজন হবে না (তীর-ফাংশনগুলি 30% ধীরে ধীরে), বা জিআইটি-পদ্ধতিগুলি গেটসগুলির মাধ্যমে through
এই সমাধানটি কনস্ট্রাক্টরের মধ্যে এই প্রসঙ্গটি আবদ্ধ করা।

class DemonstrateScopingProblems 
{
    constructor()
    {
        this.run = this.run.bind(this);
    }


    private status = "blah";
    public run() {
        alert(this.status);
    }
}

শ্রেণীর নির্মাতার সমস্ত ফাংশন স্বয়ংক্রিয়ভাবে আবদ্ধ করতে আপনি একটি অটোবাইন্ড পদ্ধতি লিখতে পারেন:

class DemonstrateScopingProblems 
{

    constructor()
    { 
        this.autoBind(this);
    }
    [...]
}


export function autoBind(self)
{
    for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
    {
        const val = self[key];

        if (key !== 'constructor' && typeof val === 'function')
        {
            // console.log(key);
            self[key] = val.bind(self);
        } // End if (key !== 'constructor' && typeof val === 'function') 

    } // Next key 

    return self;
} // End Function autoBind

মনে রাখবেন যে আপনি যদি অটোবাইন্ড-ফাংশনটিকে সদস্য ফাংশন হিসাবে একই শ্রেণিতে না রাখেন তবে এটি ঠিক autoBind(this);এবং নাthis.autoBind(this);

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

এটার মত:

export function autoBind(self)
{
    for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
    {

        if (key !== 'constructor')
        {
            // console.log(key);

            let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);

            if (desc != null)
            {
                let g = desc.get != null;
                let s = desc.set != null;

                if (g || s)
                {
                    if (g)
                        desc.get = desc.get.bind(self);

                    if (s)
                        desc.set = desc.set.bind(self);

                    Object.defineProperty(self.constructor.prototype, key, desc);
                    continue; // if it's a property, it can't be a function 
                } // End if (g || s) 

            } // End if (desc != null) 

            if (typeof (self[key]) === 'function')
            {
                let val = self[key];
                self[key] = val.bind(self);
            } // End if (typeof (self[key]) === 'function') 

        } // End if (key !== 'constructor') 

    } // Next key 

    return self;
} // End Function autoBind

আমাকে "অটোবাইন্ড (এটি)" না "এটি" অটোবাইন্ড (এটি) "ব্যবহার করতে হয়েছিল
জনঅপিনিকার

@ জনঅপিনিকার: হ্যাঁ, এটি.আউটোবাইন্ড (এটি) ধরে নিয়েছে যে অটোবাইন্ড শ্রেণীর ভিতরে রয়েছে, আলাদা রফতানি হিসাবে নয়।
স্টিফান স্টেইগার

আমি এখন বুঝতে পেরেছি. আপনি পদ্ধতিটি একই ক্লাসে রেখেছেন। আমি এটিকে "ইউটিলিটি" মডিউলটিতে রেখেছি।
জনঅপিনিকার

2

আপনার কোডটিতে, আপনি কি নীচের মত শেষ পংক্তিকে পরিবর্তন করার চেষ্টা করেছেন?

$(document).ready(() => thisTest.run());
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.