আরো একভাবে ফাংশন ডিক্লেয়ার করা যায়। যদিও এটির ব্যবহার খুব কম, তবে অনেকসময় এটি ব্যবহার করা ছাড়া উপায় থাকে না।
ফাংশন ডিক্লেয়ার করার সিনট্যাক্সটি হল:
let func = new Function ([arg1, arg2, ...argN], functionBody);
ফাংশনটিতে আর্গুমেন্ট arg1...argN
এবং functionBody
থাকে।
উদাহরণের মাধ্যমে সিনট্যাক্সটি সহজে বুঝতে পারব, এখানে একটি ফাংশন ডিক্লেয়ার করেছি যার দুটি আর্গুমেন্ট আছে:
let sum = new Function('a', 'b', 'return a + b');
alert( sum(1, 2) ); // 3
এবং নিচের ফাংশনটিতে কোন আর্গুমেন্ট নেই, শুধুমাত্র ফাংশনের বডি আছে:
let sayHi = new Function('alert("Hello")');
sayHi(); // Hello
অন্যান্য ফাংশনের সাথে এটির মূল পার্থক্যটি হল, ফাংশনটি তৈরি হয় স্ট্রিং হতে, যা রানটাইমে পাস করা যায়।
অ্যারো বা রেগুলার ফাংশনগুলোর ক্ষেত্রে আমরা দেখেছি, ফাংশনের বডি বা আর্গুমেন্ট স্ক্রিপ্টে আগে থেকেই ডিক্লেয়ার করতে হত।
তবে, new Function
এর মাধ্যমে আমরা কোন স্ট্রিংকে ফাংশনে পরিবর্তন করতে পারি। যেমন, আমরা সার্ভার কোন একটি ফাংশন পাঠাতে পারি এবং স্ক্রিপ্টে এটি এক্সিকিউট করতে পারি:
let str = ...সার্ভার হতে ডায়নামিক্যালি ফাংশনের বিস্তারিত তথ্য নিয়ে আসবে ...
let func = new Function(str);
func();
এটি কিছু নির্দিষ্ট ক্ষেত্রে ব্যবহার করা হয়, যেমন আমরা সার্ভার হতে কোন কোড বা টেমপ্লেট হতে ডায়নামিক্যালি কোন একটি ফাংশন কে কম্পাইল করতে চাই, এক্ষেত্রে এটি ব্যবহার করতে হবে।
সাধারণত একটি ফাংশন কোথায় ডিক্লেয়ার করা হয়েছে তার বিস্তারিত [[Environment]]
প্রপার্টিতে সংরক্ষণ করে। এটি তার লেক্সিকাল এনভায়রনমেন্টকে রেফারেন্স করে (যা এই অধ্যায়ে আলোচনা করেছি info:closure)।
কিন্তু যখন একটি ফাংশন new Function
দ্বারা তৈরি হয়, এটি কারেন্ট লেক্সিকাল এনভায়রনমেন্টকে [[Environment]]
এ সেট করে না, তবে গ্লোবাল স্কোপে অ্যাক্সেস থাকে।
সুতরাং, এই ধরণের ফাংশন আউটার ভ্যারিয়েবলকে অ্যাক্সেস করতে পারে না, শুধুমাত্র গ্লোবাল স্কোপকে অ্যাক্সেস করতে পারে।
function getFunc() {
let value = "test";
*!*
let func = new Function('alert(value)');
*/!*
return func;
}
getFunc()(); // error: value is not defined
রেগুলার ফাংশনে দেখুন:
function getFunc() {
let value = "test";
*!*
let func = function() { alert(value); };
*/!*
return func;
}
getFunc()(); // *!*"test"*/!*, from the Lexical Environment of getFunc
যদিওবা new Function
দেখতে কিছুটা ভিন্ন, তবে এটি কিছু ক্ষেত্রে অনেক কাজে আসে।
মনে করুন আমরা স্ট্রিং হতে একটি ফাংশন তৈরি করব। কিন্তু ফাংশনটির কোড কি হবে বা এটির কাজ কি হবে তা আমরা জানিনা (এজন্য আমরা এগুলার ফাংশন ব্যবহার করব না), তবে এক্সিকিউশনের সময় কোড কি হবে তা জানতে পারি, হতে পারে কোডটি পাব অন্য কোন রিসোর্স বা সার্ভার থেকে।
আমাদের নতুন ফাংশনটিকে মূল স্ক্রিপ্টের সাথে ইন্টারঅ্যাক্ট করা দরকার।
যদি এটি আউটার ভ্যারিয়েবলকে অ্যাক্সেস করতে পারে তাহলে কি হবে?
সমস্যাটি দেখা দেয় যখন আমরা প্রোডাকশনের জন্য জাভাস্ক্রিপ্টকে minifier দ্বারা কম্প্রেসড করি, ফলে আমাদের কোডের অতিরিক্ত কমেন্ট, স্পেসগুলো রিমুভ হয়ে যায়, এছাড়াও লোকাল ভ্যারিয়েবলগুলোর নাম সংক্ষিপ্ত হয়ে যায়।
যেমন, যদি কোন ফাংশনে একটি ভ্যারিয়েবল থাকে let userName
, তাহলে মিনিফাই হওয়ার সময় এটি হতে পারে let a
(অথবা অন্য কোন নাম বা ক্যারেক্টার), এবং এটি ঐ লোকাল স্কোপের সব জায়গায় হয়। এবং এটির জন্য কোন সমস্যা হয় না, কেননা ভ্যারিয়েবলটি লোকাল, ফলে এটি অন্য কোন স্কোপ হতে অ্যাক্সেস হবে না। এবং মিনিফাই এর সময় ঐ ফাংশনের সকল জায়গায় ভ্যারিয়েবলটি প্রতিস্থাপিত হয়। minifier যথেষ্ট স্মার্ট, এরা কোড অ্যানালাইজ করে এসব করে, ফলে কোডে কোন ব্রেক হয় না।
সুতরাং যদি new Function
এর আউটার স্কোপে অ্যাক্সেস থাকে, তাহলে userName
ভ্যারিয়েবলের নাম পরিবর্তনের ফলে অ্যাক্সেস করতে পারবে না, এবং এরর তৈরি হতে পারে।
যদি new Function
এর আউটার স্কোপে অ্যাক্সেস থাকে, তাহলে মিনিফাই হওয়ার পর সমস্যা হতে পারে
এছাড়াও, এই ধরণের কোড আর্কিটেকচার ভঙ্গুর প্রকৃতির হয়।
আউটার স্কোপের কোন কিছু new Function
এ পাস করার জন্য আমাদের আর্গুমেন্ট ব্যবহার করতে হবে।
সিনট্যাক্স:
let func = new Function ([arg1, arg2, ...argN], functionBody);
আমরা আর্গুমেন্টকে কমা সেপারেটেড স্ট্রিং হিসেবে পাঠাতে পারি।
যেমন:
new Function('a', 'b', 'return a + b'); // basic syntax
new Function('a,b', 'return a + b'); // comma-separated
new Function('a , b', 'return a + b'); // comma-separated with spaces
new Function
তৈরি হওয়ার সময় [[Environment]]
এ শুধুমাত্র গ্লোবাল ল্যাক্সিকাল এনভায়রনমেন্ট কে রেফার করে। এরা আউটার স্কোপকে অ্যাক্সেস করতে পারে না। তবে এটি ভালো, কেননা এক্ষেত্রে আমরা সহজে এরর নির্নয় করতে পারি। আউটার স্কোপের ভ্যারিয়েবল অ্যাক্সেসের জন্য ভ্যারিয়েবলকে আর্গুমেন্ট হিসেবে পাঠাতে পারি।