Skip to content

Dynamic imports #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Nov 7, 2019
210 changes: 156 additions & 54 deletions 1-js/13-modules/03-modules-dynamic-imports/article.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,200 @@
# Dynamic imports

Export and import statements that we covered in previous chapters are called "static". The syntax is very simple and strict.
# পুরাতন "var"

First, we can't dynamically generate any parameters of `import`.
অধ্যায়ের প্রথম দিকে আমরা উল্লেখ করেছিলাম [variables](info:variables) কে তিন ভাবে ঘোষণা করা যায়।

The module path must be a primitive string, can't be a function call. This won't work:
১। `let`
২। `const`
৩। `var`

```js
import ... from *!*getModuleName()*/!*; // Error, only from "string" is allowed
```
লেক্সিকাল এনভায়রনমেন্টের ক্ষেত্রে `let` এবং `const` ঠিক একইভাবে আচরণ করে।

Second, we can't import conditionally or at run-time:
কিন্তু `var` সম্পূর্ণ ভিন্ন, যা খুব পুরানো কাল থেকেই উদ্ভূত হয়েছিল। এটি সাধারণত আধুনিক স্ক্রিপ্টগুলিতে ব্যবহৃত হয় না তবে এটি পুরানো স্ক্রিপ্টগুলিতে সচরাচর দেখা যাবে।

```js
if(...) {
import ...; // Error, not allowed!
আপনি যদি এই জাতীয় স্ক্রিপ্টগুলি সম্পর্কে জানার পরিকল্পনা না করেন তবে আপনি এই অধ্যায়টি এড়িয়ে যেতে পারেন, তবে তা আপনাকে পরে সমস্যায় ফেলতে পারে।

প্রথমদিকে দেখতে var ও let এর আচরণ একই রকম মনে হবে। সেটা হলো একটি ভেরিয়েবল ঘোষণা করাঃ

```js run
function sayHi() {
var phrase = "Hello"; // লোকাল ভেরিয়েবল, "let" এর পরিবর্তে "var"

alert(phrase); // Hello
}

{
import ...; // Error, we can't put import in any block
sayHi();

alert(phrase); // Error, phrase is not defined
```

...তবে এখানে পার্থক্য রয়েছে।

# "var" এর কোন ব্লকস্কোপ নেই।

যে সকল ভেরিয়েবল "var" দ্বারা ঘোষণা হয়, তারা হয় ফাংশন-ওয়াইড অথবা গ্লোবাল হয়ে থাকে।

এই ক্ষেত্রেঃ

```js run
if (true) {
var test = true; // "let" এর পরিবর্তে "var"
}

*!*
alert(test); // true, "if" ব্লকের বাইরেও এটি বিদ্যমান।
*/!*
```

That's because `import`/`export` aim to provide a backbone for the code structure. That's a good thing, as code structure can be analyzed, modules can be gathered and bundled into one file by special tools, unused exports can be removed ("tree-shaken"). That's possible only because the structure of imports/exports is simple and fixed.
`var` কোড ব্লকগুলিকে উপেক্ষা করার সাথে সাথে আমরা একটি গ্লোবাল `test` ভেরিয়েবল পেয়েছি.

But how can we import a module dynamically, on-demand?
যদি আমরা `var test` এর পরিবর্তে `let test` ব্যবহার করি, তবে ভেরিয়েবলটি কেবল `if` ব্লকের মধ্যে সীমাবদ্ধ থাকবেঃ

## The import() expression
```js run
if (true) {
let test = true; // "let" এর ব্যবহার
}

The `import(module)` expression loads the module and returns a promise that resolves into a module object that contains all its exports. It can be called from any place in the code.
*!*
alert(test); // এরর: test নির্ধারণ করা নেই
*/!*
```

We can use it dynamically in any place of the code, for instance:
লুপের ক্ষেত্রেও একই রকমঃ `var` লুপ অথবা ব্লকের লোকাল হতে পারে নাঃ

```js
let modulePath = prompt("Which module to load?");
for (var i = 0; i < 10; i++) {
// ...
}

import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, e.g. if no such module>)
*!*
alert(i); // 10, "i" লুপের পরেও বিদ্যমান, এটি একটি গ্লোবাল ভেরিয়েবল।
*/!*
```

Or, we could use `let module = await import(modulePath)` if inside an async function.
যদি কোন কোড ব্লক ফাংশনের ভিতরে থাকে, সেক্ষেত্রে `var` ফাংশন লেভেল ভেরিয়েবল হয়ে যায়।

For instance, if we have the following module `say.js`:
```js run
function sayHi() {
if (true) {
var phrase = "Hello";
}

```js
// 📁 say.js
export function hi() {
alert(`Hello`);
alert(phrase); // কাজ করবে
}

export function bye() {
alert(`Bye`);
}
sayHi();
alert(phrase); // এরর: phrase নির্ধারণ করা নেই (ডেভলপার কনসোল চেক করুন)
```

...Then dynamic import can be like this:
আমারা যেটা দেখলাম, `var` - `if`, `for` অথবা অন্য ব্লক ভেদ করে বাইরে আসতে পারে। তার কারন অনেক আগে জাভাস্ক্রিপ্টে কোন লেক্সিকাল এনভাইরমেন্ট ছিল না। এবং `var` তারই একটি অংশ।

```js
let {hi, bye} = await import('./say.js');
## "var" ফাংশনের শুরুতেই ঘোষিত হয়।

ফাংশনের শুরুতেই `var` ঘোষিত হয়ে যায়(অথবা স্ক্রিপ্ট গ্লোবালের জন্য শুরু হয়)

অন্যভাবে বলা যায়, `var` ভেরিয়্যবল গুলো ফাংশনের শুরুতেই ঘোষিত হয়, সেটাকে যেখানেই সংজ্ঞায়িত করা হোক না কেন(ধরে নিলাম এটি কোন নেস্টেড ফাংশনের মধ্যে নয়)।

তাহলেঃ

```js run
function sayHi() {
phrase = "Hello";

alert(phrase);

hi();
bye();
*!*
var phrase;
*/!*
}
sayHi();
```

Or, if `say.js` has the default export:
...টেকনিক্যালি এটির মতোই(`var phrase` উপরে স্থানান্তরিত করে দেয়)ঃ

```js
// 📁 say.js
export default function() {
alert("Module loaded (export default)!");
```js run
function sayHi() {
*!*
var phrase;
*/!*

phrase = "Hello";

alert(phrase);
}
sayHi();
```

...Then, in order to access it, we can use `default` property of the module object:
...অথবা এটির মতো(কোড ব্লকগুলি উপেক্ষা করা হয়েছে)

```js
let obj = await import('./say.js');
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');
```js run
function sayHi() {
phrase = "Hello"; // (*)

*!*
if (false) {
var phrase;
}
*/!*

say();
alert(phrase);
}
sayHi();
```

Here's the full example:
লোকেরা এ জাতীয় আচরণকে "hoisting" নামেও অভিহিত করে, কারণ সমস্ত var ফাংশনের শীর্ষে "hoisting" হয়।

[codetabs src="say" current="index.html"]
সুতরাং উপরের উদাহরণে, `if(false)` কখনও কার্যকর হয় না, কিন্তু এতে কোন সমস্যা নেই। ফাংশনের শুরুতে এর অভ্যন্তরের `var` প্রসেস হয়ে যায়, সুতরং `(*)` মুহূর্তে ভেরিয়েবলটি বিদ্যমান থাকে।

```smart
Dynamic imports work in regular scripts, they don't require `script type="module"`.
**ডিকলারেশন গুলো "hoisted" হলেও, কিন্তু "assignment" হয় না**

একটি উদাহরণ দিয়ে দিয়ে দেখা যাক, যেমনঃ

```js run
function sayHi() {
alert(phrase);

*!*
var phrase = "Hello";
*/!*
}

sayHi();
```

```smart
Although `import()` looks like a function call, it's a special syntax that just happens to use parentheses (similar to `super()`).
`var phrase` = "Hello" লাইনটির মধ্যে দুটি কাজ রয়েছেঃ

১। ভেরিয়েবল ঘোষণা `var`
২। ভেরিয়াবল আসাইনমেন্ট `=`।

So we can't copy `import` to a variable or use `call/apply` with it. That's not a function.
ফাংশন এক্সিকিউশনের শুরুতেই ডিক্লেয়ার করা হয়ে থাকে ("hoisted"),তবে অ্যাসাইনমেন্টটি সর্বদা যেখানে প্রদর্শিত হবে সেখানে কাজ করে। সুতরাং কোডটি মূলত এই ভাবে কাজ করে:

```js run
function sayHi() {
*!*
var phrase; // ভেরিয়েবল ডিক্লেয়ার শুরুতেই কাজ করে ...
*/!*

alert(phrase); // undefined

*!*
phrase = "Hello"; // ...অ্যাসাইনমেন্ট - যখন এক্সিকিউশন এখানে পৌঁছায়।
*/!*
}

sayHi();
```

কারন সকল var ফাংশনের শুরুতেই ডিক্লেয়ার করা হয়, আমরা ওই ফাংশন স্কোপের যে কোন জায়গায় থেকে ভেরিয়েবল সমূহ কে ব্যবহার করতে পারি। কিন্তু অ্যাসাইনমেন্টের আগ পর্যন্ত ভেরিয়েবল গুলো আনডিফাইন অবস্থায় থাকে।

উপরের দুটি উদাহরণে `alert` কোন এরর ছাড়াই চলে, কারন ভেরিয়েবল `phrase` বিদ্যমান রয়েছে। তবে এর মান এখনও নির্ধারিত হয়নি, সুতরাং এটি আনডিফাইন দেখায়।

## সারাংশ

এখানে দুটি প্রধান পার্থক্য রয়েছে `var` এবং `let/const` এর মধ্যেঃ

১। `var` ভেরিয়েবলের কোন ব্লক স্কোপ নেই, এগুলি সর্বনিম্ন ফাংশন লেভেল পর্যন্ত বিদ্যমান থাকে।
২। ফাংশনের শুরুতেই `var` ঘোষিত হয়ে যায়(স্ক্রিপ্ট গ্লোবালের জন্য শুরু হয়)।

গ্লোবাল অবজেক্টের সাথে সম্পর্কিত আরও একটি ছোটখাটো পার্থক্য রয়েছে, আমরা পরবর্তী অধ্যায়ে এটি আলোচনা করব।

এই পার্থক্যগুলি `var` কে বেশিরভাগ সময় `let` এর চেয়ে খারাপ করে তোলে। ব্লক-লেভেলের ভেরিয়েবলগুলি একটি দুর্দান্ত জিনিস। এই জন্য `let` এর স্ট্যান্ডার্ড চালু হয় অনেক আগে, এবং ভেরিয়েবল ঘোষণার জন্য এখন এটি একটি প্রধান উপায় (`const` সহ)।