ES2015 প্রক্সি উপস্থাপন করা হচ্ছে

আদ্দি ওসমানী
Addy Osmani

ES2015 প্রক্সিগুলি ( Chrome 49 এবং পরবর্তীতে) জাভাস্ক্রিপ্টকে একটি মধ্যস্থতা API প্রদান করে, যা আমাদেরকে একটি টার্গেট অবজেক্টে সমস্ত ক্রিয়াকলাপ আটকাতে বা বাধা দিতে এবং এই লক্ষ্য কীভাবে কাজ করে তা সংশোধন করতে সক্ষম করে৷

প্রক্সিগুলির প্রচুর সংখ্যক ব্যবহার রয়েছে, যার মধ্যে রয়েছে:

  • আটক
  • বস্তু ভার্চুয়ালাইজেশন
  • সম্পদ ব্যবস্থাপনা
  • ডিবাগিংয়ের জন্য প্রোফাইলিং বা লগিং
  • নিরাপত্তা এবং অ্যাক্সেস নিয়ন্ত্রণ
  • বস্তু ব্যবহারের জন্য চুক্তি

প্রক্সি এপিআই-এ একটি প্রক্সি কনস্ট্রাক্টর থাকে যা একটি নির্দিষ্ট লক্ষ্য বস্তু এবং একটি হ্যান্ডলার অবজেক্ট নেয়।

var target = { /* some properties */ };
var handler = { /* trap functions */ };
var proxy = new Proxy(target, handler);

একটি প্রক্সির আচরণ হ্যান্ডলার দ্বারা নিয়ন্ত্রিত হয়, যা লক্ষ্য বস্তুর মূল আচরণকে বেশ কয়েকটি কার্যকর উপায়ে পরিবর্তন করতে পারে। হ্যান্ডলারে ঐচ্ছিক ফাঁদ পদ্ধতি রয়েছে (যেমন .get() , .set() , .apply() ) যা প্রক্সিতে সংশ্লিষ্ট অপারেশন সঞ্চালিত হলে বলা হয়।

আটক

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

var target = {};

var superhero = new Proxy(target, {
    get: function(target, name, receiver) {
        console.log('get was called for:', name);
        return target[name];
    }
});

superhero.power = 'Flight';
console.log(superhero.power);

Chrome 49 এ উপরের কোডটি চালালে আমরা নিম্নলিখিতগুলি পাই:

get was called for: power  
"Flight"

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

ফাঁদ ফাংশন, যদি এটি বেছে নেয়, ইচ্ছামত একটি অপারেশন বাস্তবায়ন করতে পারে (যেমন লক্ষ্য বস্তুতে অপারেশন ফরওয়ার্ড করা)। একটি ফাঁদ সুনির্দিষ্ট না হলে ডিফল্টরূপে এটিই ঘটে। উদাহরণস্বরূপ, এখানে একটি নো-অপ ফরওয়ার্ডিং প্রক্সি রয়েছে যা এটি করে:

var target = {};

var proxy = new Proxy(target, {});
    // operation forwarded to the target
proxy.paul = 'irish';
// 'irish'. The operation has been  forwarded
console.log(target.paul);

আমরা শুধু প্লেইন অবজেক্টের প্রক্সি করার দিকে তাকিয়েছি, কিন্তু আমরা ঠিক তত সহজে একটা ফাংশন অবজেক্টকে প্রক্সি করতে পারি, যেখানে একটা ফাংশন আমাদের টার্গেট। এইবার আমরা handler.apply() ফাঁদ ব্যবহার করব:

// Proxying a function object
function sum(a, b) {
    return a + b;
}

var handler = {
    apply: function(target, thisArg, argumentsList) {
        console.log(`Calculate sum: ${argumentsList}`);
        return target.apply(thisArg, argumentsList);
    }
};

var proxy = new Proxy(sum, handler);
proxy(1, 2);
// Calculate sum: 1, 2
// 3

প্রক্সি সনাক্তকরণ

জাভাস্ক্রিপ্ট সমতা অপারেটর ( == এবং === ) ব্যবহার করে একটি প্রক্সির পরিচয় পর্যবেক্ষণ করা যেতে পারে। আমরা জানি, যখন দুটি বস্তুতে প্রয়োগ করা হয় তখন এই অপারেটরগুলি বস্তুর পরিচয় তুলনা করে। পরবর্তী উদাহরণ এই আচরণ প্রদর্শন করে. অন্তর্নিহিত লক্ষ্যগুলি একই হওয়া সত্ত্বেও দুটি স্বতন্ত্র প্রক্সির তুলনা মিথ্যা ফেরত দেয়। অনুরূপ শিরায়, লক্ষ্যবস্তুটি তার যেকোনো প্রক্সি থেকে আলাদা:

// Continuing previous example

var proxy2 = new Proxy (sum, handler);
(proxy==proxy2); // false
(proxy==sum); // false

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

ব্যবহারের ক্ষেত্রে

উল্লিখিত হিসাবে, প্রক্সিগুলির ব্যবহারের ক্ষেত্রে বিস্তৃত অ্যারে রয়েছে। উপরের অনেকগুলি, যেমন অ্যাক্সেস কন্ট্রোল এবং প্রোফাইলিং জেনেরিক র‍্যাপারের অধীনে পড়ে: প্রক্সি যা একই ঠিকানা "স্পেস"-এ অন্যান্য বস্তুকে মোড়ানো। ভার্চুয়ালাইজেশনও উল্লেখ করা হয়েছিল। ভার্চুয়াল অবজেক্ট হল প্রক্সি যেগুলি একই অ্যাড্রেস স্পেসে থাকা প্রয়োজন ছাড়াই অন্যান্য অবজেক্টকে অনুকরণ করে। উদাহরণগুলির মধ্যে রয়েছে দূরবর্তী বস্তু (যেগুলি অন্যান্য স্থানগুলিতে অবজেক্টগুলিকে অনুকরণ করে) এবং স্বচ্ছ ফিউচার (এমুলেটিং ফলাফল যা এখনও গণনা করা হয়নি)।

হ্যান্ডলার হিসাবে প্রক্সি

প্রক্সি হ্যান্ডলারদের জন্য একটি খুব সাধারণ ব্যবহারের ক্ষেত্রে একটি মোড়ানো বস্তুর উপর একটি অপারেশন সম্পাদন করার আগে বৈধতা বা অ্যাক্সেস নিয়ন্ত্রণ পরীক্ষা করা। চেক সফল হলেই অপারেশনটি এগিয়ে যায়। নীচের বৈধতা উদাহরণ এটি প্রদর্শন করে:

var validator = {
    set: function(obj, prop, value) {
    if (prop === 'yearOfBirth') {
        if (!Number.isInteger(value)) {
        throw new TypeError('The yearOfBirth is not an integer');
        }

        if (value > 3000) {
        throw new RangeError('The yearOfBirth seems invalid');
        }
    }

    // The default behavior to store the value
    obj[prop] = value;
    }
};

var person = new Proxy({}, validator);

person.yearOfBirth = 1986;
console.log(person.yearOfBirth); // 1986
person.yearOfBirth = 'eighties'; // Throws an exception
person.yearOfBirth = 3030; // Throws an exception

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

এটি সহজে বিমূর্ত করা কঠিন হতে পারে, প্রতিটি অপশনকে ভিন্নভাবে ফরোয়ার্ড করা হতে পারে। একটি নিখুঁত পরিস্থিতিতে, যদি সমস্ত ক্রিয়াকলাপগুলি শুধুমাত্র একটি ফাঁদের মাধ্যমে সমানভাবে ফানেল করা যায়, হ্যান্ডলারকে শুধুমাত্র একবার একক ফাঁদে বৈধতা পরীক্ষা করতে হবে। আপনি প্রক্সি হ্যান্ডলারকে প্রক্সি হিসাবে প্রয়োগ করে এটি করতে পারেন। এটি দুর্ভাগ্যবশত এই নিবন্ধের সুযোগের বাইরে।

অবজেক্ট এক্সটেনশন

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

function extend(sup,base) {

    var descriptor = Object.getOwnPropertyDescriptor(base.prototype,"constructor");

    base.prototype = Object.create(sup.prototype);

    var handler = {
    construct: function(target, args) {
        var obj = Object.create(base.prototype);
        this.apply(target,obj, args);
        return obj;
    },

    apply: function(target, that, args) {
        sup.apply(that,args);
        base.apply(that,args);
    }
    };

    var proxy = new Proxy(base, handler);
    descriptor.value = proxy;
    Object.defineProperty(base.prototype, "constructor", descriptor);
    return proxy;
}

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

var Car = extend(Vehicle, function(name, year) {
    this.year = year;
});

Car.prototype.style = "Saloon";

var Tesla = new Car("Model S", 2016);

console.log(Tesla.style); // "Saloon"
console.log(Tesla.name); // "Model S"
console.log(Tesla.year);  // 2016

প্রবেশাধিকার নিয়ন্ত্রণ

অ্যাক্সেস কন্ট্রোল হল প্রক্সিগুলির জন্য আরেকটি ভাল ব্যবহারের ক্ষেত্রে। অবিশ্বস্ত কোডের একটি অংশে একটি টার্গেট অবজেক্ট পাস করার পরিবর্তে, কেউ তার প্রক্সিটি এক ধরণের প্রতিরক্ষামূলক ঝিল্লিতে মোড়ানো যেতে পারে। একবার অ্যাপটি মনে করে যে অবিশ্বস্ত কোডটি একটি নির্দিষ্ট কাজ সম্পন্ন করেছে, এটি রেফারেন্স প্রত্যাহার করতে পারে যা প্রক্সিটিকে তার লক্ষ্য থেকে বিচ্ছিন্ন করে। মেমব্রেন এই বিচ্ছিন্নতাকে পুনরাবৃত্তভাবে প্রসারিত করবে সমস্ত বস্তুর কাছে যা সংজ্ঞায়িত করা হয়েছিল মূল লক্ষ্য থেকে পৌঁছানো যায়।

প্রক্সির সাথে প্রতিফলন ব্যবহার করে

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

Python বা C# এর মতো স্ট্যাটিকালি টাইপ করা ভাষাগুলি দীর্ঘদিন ধরে একটি প্রতিফলন API অফার করেছে, কিন্তু জাভাস্ক্রিপ্টের একটি গতিশীল ভাষা হওয়ার প্রয়োজন নেই। কেউ যুক্তি দিতে পারে ES5 এর ইতিমধ্যে বেশ কয়েকটি প্রতিফলন বৈশিষ্ট্য রয়েছে, যেমন Array.isArray() বা Object.getOwnPropertyDescriptor() যা অন্যান্য ভাষায় প্রতিফলন হিসাবে বিবেচিত হবে। ES2015 একটি প্রতিফলন API প্রবর্তন করেছে যা এই বিভাগের জন্য ভবিষ্যতের পদ্ধতিগুলিকে রাখবে, তাদের সম্পর্কে যুক্তি করা সহজ করে তুলবে৷ এটি বোধগম্য হয় কারণ অবজেক্টকে প্রতিফলন পদ্ধতির জন্য একটি বালতির পরিবর্তে একটি বেস প্রোটোটাইপ বোঝানো হয়।

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

// Field interception with Proxy and the Reflect API

var pioneer = new Proxy({}, {
    get: function(target, name, receiver) {
        console.log(`get called for field: ${name}`);
        return Reflect.get(target, name, receiver);
    },

    set: function(target, name, value, receiver) {
        console.log(`set called for field: ${name} and value: ${value}`);
        return Reflect.set(target, name, value, receiver);
    }
});

pioneer.firstName = 'Grace';
pioneer.secondName = 'Hopper';
// Grace
pioneer.firstName

কোন আউটপুট:

set called for field: firstName and value: Grace
set called for field: secondName and value: Hopper
get called for field: firstName

আরেকটি উদাহরণ হল যেখানে কেউ চাইলে:

  • প্রতিবার যখন আমরা নির্দিষ্ট যুক্তির সাথে কাজ করতে চাই তখন ম্যানুয়ালি একটি নতুন প্রক্সি তৈরি করা এড়াতে একটি কাস্টম কনস্ট্রাক্টরের মধ্যে একটি প্রক্সি সংজ্ঞা মোড়ানো।

  • পরিবর্তনগুলি 'সংরক্ষণ' করার ক্ষমতা যোগ করুন, কিন্তু শুধুমাত্র যদি ডেটা আসলে পরিবর্তন করা হয় (অনুমানিকভাবে সংরক্ষণ অপারেশনটি অত্যন্ত ব্যয়বহুল হওয়ার কারণে)।

function Customer() {

    var proxy = new Proxy({
    save: function(){
        if (!this.dirty){
        return console.log('Not saving, object still clean');
        }
        console.log('Trying an expensive saving operation: ', this.changedProperties);
    },

    }, {

    set: function(target, name, value, receiver) {
        target.dirty = true;
        target.changedProperties = target.changedProperties || [];

        if(target.changedProperties.indexOf(name) == -1){
        target.changedProperties.push(name);
        }
        return Reflect.set(target, name, value, receiver);
    }

    });

    return proxy;
}


var customer = new Customer();

customer.name = 'seth';
customer.surname = 'thompson';
// Trying an expensive saving operation:  ["name", "surname"]
customer.save();

আরও প্রতিফলিত API উদাহরণের জন্য, Tagtree দ্বারা ES6 প্রক্সি দেখুন।

Polyfilling Object.observe()

যদিও আমরা Object.observe() কে বিদায় জানাচ্ছি, এখন ES2015 প্রক্সি ব্যবহার করে তাদের পলিফিল করা সম্ভব। সাইমন ব্ল্যাকওয়েল সম্প্রতি একটি প্রক্সি-ভিত্তিক Object.observe() শিম লিখেছেন যা পরীক্ষা করার মতো। এরিক আরভিডসনও 2012 সালে একটি মোটামুটি নির্দিষ্ট সম্পূর্ণ সংস্করণ লিখেছিলেন।

ব্রাউজার সমর্থন

ES2015 প্রক্সিগুলি Chrome 49, Opera, Microsoft Edge এবং Firefox-এ সমর্থিত। সাফারির বৈশিষ্ট্যটির প্রতি মিশ্র পাবলিক সংকেত রয়েছে তবে আমরা আশাবাদী। প্রতিফলনটি ক্রোম, অপেরা এবং ফায়ারফক্সে রয়েছে এবং এটি মাইক্রোসফ্ট এজ-এর জন্য উন্নয়নশীল।

Google প্রক্সির জন্য একটি সীমিত পলিফিল প্রকাশ করেছে। এটি শুধুমাত্র জেনেরিক র‍্যাপারের জন্য ব্যবহার করা যেতে পারে, কারণ এটি শুধুমাত্র একটি প্রক্সি তৈরির সময় পরিচিত প্রক্সি বৈশিষ্ট্যগুলিই করতে পারে৷

আরও পড়া