Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 106
The old "var"#161
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
Uh oh!
There was an error while loading. Please reload this page.
Merged
The old "var" #161
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
1d59c6c
Replace whole article with English version
mahdiHash f36050a
Translate a part of article
mahdiHash 38ff899
Translate a part of article
mahdiHash dd64bde
Translate a part of article
mahdiHash a6bbd2a
Change a word for better understanding
mahdiHash d07c051
Translate a part of article
mahdiHash 4682926
Translate article
mahdiHash 8419d8c
Apply suggestions from code review
mahdiHash 1710d2b
Fix a typo and change a word
mahdiHash File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,75 +1,62 @@ | ||
| # The old "var" | ||
| # کلمهی "var" قدیمی | ||
| ```smart header="This article is for understanding old scripts" | ||
| The information in this article is useful for understanding old scripts. | ||
| ```smart header="این مقاله برای فهمیدن اسکریپتهای قدیمی است" | ||
| اطلاعات این مقاله برای فهمیدن اسکریپتهای قدیمی مفید است. | ||
| That's not how we write new code. | ||
| ما کدهای جدید را اینگونه نمینویسیم. | ||
| ``` | ||
| In the very first chapter about [variables](info:variables), we mentioned three ways of variable declaration: | ||
| دقیقا در فصل اول درباره [متغیرها](info:variables)، ما سه راه تعریف متغیر را معرفی کردیم: | ||
| 1. `let` | ||
| 2. `const` | ||
| 3. `var` | ||
| `let` and `const` behave exactly the same way in terms of Lexical Environments. | ||
| But `var` is a very different beast, that originates from very old times. It's generally not used in modern scripts, but still lurks in the old ones. | ||
| If you don't plan meeting such scripts you may even skip this chapter or postpone it, but then there's a chance that it bites you later. | ||
| From the first sight, `var` behaves similar to `let`. That is, declares a variable: | ||
| تعریف متغیر توسط `var` مانند `let` است. اکثر اوقات ما میتوانیم `let` را با `var` جایگزین کنیم یا برعکس و توقع داشته باشیم که دستورات کار کنند: | ||
| ```js run | ||
| var message = "Hi"; | ||
| alert(message); // Hi | ||
| var message = "سلام"; | ||
| alert(message); // سلام | ||
| ``` | ||
| But internally `var` is a very different beast, that originates from very old times. It's generally not used in modern scripts, but still lurks in the old ones. | ||
| اما از درون، `var` گونهای بسیار متفاوت است که از زمانهای قدیم وجود دارد. به طور کلی در اسکریپتهای جدید استفاده نمیشود ولی هنوز هم در اسکریپتهای قدیمی کمین کرده است. | ||
| If you don't plan on meeting such scripts you may even skip this chapter or postpone it. | ||
| اگر شما قرار نیست چنین اسکریپتهایی را ببینید میتوانید این فصل را رد کنید یا مطالعه آن را عقب بیاندازید. | ||
| On the other hand, it's important to understand differences when migrating old scripts from `var` to `let`, to avoid odd errors. | ||
| از سویی دیگر، زمانی که اسکریپتهای قدیمی را از `var` به `let` کوچ میدهیم، دانستن تفاوتها برای جلوگیری از ارورهای عجیب مهم است. | ||
| ## "var" has no block scope | ||
| ## کلمهی "var" محدوده بلوک ندارد | ||
| Variables, declared with `var`, are either function-scoped or global-scoped. They are visible through blocks. | ||
| متغیرهایی که با `var` تعریف شدهاند، یا محدوده تابع دارند یا محدوده گلوبال. آنها در این بلوکها قابل رویت هستند. | ||
| For instance: | ||
| برای مثال: | ||
| ```js | ||
| ```js run | ||
| if (true) { | ||
| var test = true; // use "var" instead of "let" | ||
| var test = true; // استفاده کنید "var" از "let" به جای | ||
| } | ||
| *!* | ||
| alert(test); // true, the variable lives after if | ||
| alert(test); // true ،هم باقی میماند if متغیر بعد از | ||
| */!* | ||
| ``` | ||
| @@@needs translation@@@ | ||
| @@@old part@@@ | ||
| If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. But `var` ignores code blocks, so we've got a global `test`. | ||
| @@@old part@@@ | ||
| @@@new part@@@ | ||
| As `var` ignores code blocks, we've got a global variable `test`. | ||
| چون `var` بلوکهای کد را نادیده میگیرد، ما یک متغیر گلوبال `test` خواهیم داشت. | ||
| If we used `let test` instead of `var test`, then the variable would only be visible inside `if`: | ||
| اگر ما به جای `var test` از `let test` استفاده میکردیم، سپس متغیر فقط درون `if` قابل رویت بود: | ||
| ```js run | ||
| if (true) { | ||
| let test = true; // use "let" | ||
| let test = true; // "let" استفاده از | ||
| } | ||
| *!* | ||
| alert(test); // ReferenceError: test is not defined | ||
| */!* | ||
| ``` | ||
| @@@new part@@@ | ||
| @@@needs translation@@@ | ||
| The same thing for loops: `var` cannot be block- or loop-local: | ||
| همین مورد برای حلقهها هم صدق میکند: `var` نمیتواند در محدوده بلوک حلقه باشد: | ||
| ```js | ||
| for (var i = 0; i < 10; i++) { | ||
| @@ -78,87 +65,89 @@ for (var i = 0; i < 10; i++) { | ||
| } | ||
| *!* | ||
| alert(i); // 10, "i" is visible after loop, it's a global variable | ||
| alert(one); // 1, "one" is visible after loop, it's a global variable | ||
| alert(i); // 10 ،بعد از حلقه قابل رویت است چون یک متغیر گلوبال است "i" | ||
| alert(one); // 1 ،بعد از حلقه قابل رویت است چون یک متغیر گلوبال است "one" | ||
| */!* | ||
| ``` | ||
| If a code block is inside a function, then `var` becomes a function-level variable: | ||
| اگر یک بلوک کد درون تابع باشد، سپس `var` یک متغیر در سطح تابع میشود: | ||
| ```js | ||
| ```js run | ||
mahdyar marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| function sayHi() { | ||
| if (true) { | ||
| var phrase = "Hello"; | ||
| var phrase = "سلام"; | ||
| } | ||
| alert(phrase); // works | ||
| alert(phrase); // کار میکند | ||
| } | ||
| sayHi(); | ||
| alert(phrase); // ReferenceError: phrase is not defined | ||
| ``` | ||
| As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript, blocks had no Lexical Environments, and `var` is a remnant of that. | ||
| همانطور که میبینیم، `var` از درون `if`، `for` یا بقیه بلوکهای کد بیرون میآید. به این دلیل که در زمانهای قدیم، بلوکها در جاوااسکریپت محیطهای لغوی نداشتند و `var` یک باقیمانده از آن است. | ||
| ## "var" tolerates redeclarations | ||
| ## کلمه "var" تعریفهای دوباره را قبول میکند | ||
| If we declare the same variable with `let` twice in the same scope, that's an error: | ||
| اگر ما با `let` متغیری یکسان را دوبار در محدوده بلوک یکسان تعریف کنیم، یک ارور ایجاد میشود: | ||
| ```js run | ||
| let user; | ||
| let user; // SyntaxError: 'user' has already been declared | ||
| ``` | ||
| With `var`, we can redeclare a variable any number of times. If we use `var` with an already-declared variable, it's just ignored: | ||
| با `var` ما میتوانیم یک متغیر را هر چند بار که بخواهیم دوباره تعریف کنیم. اگر ما از `var` همراه با یک متغیر از قبل تعریف شده استفاده کنیم، نادیده گرفته میشود: | ||
| ```js run | ||
| var user = "Pete"; | ||
| var user = "John"; // this "var" does nothing (already declared) | ||
| // ...it doesn't trigger an error | ||
| var user = "John"; // کاری انجام نمیدهد (از قبل تعریف شده) "var" این | ||
| // ...اروری ایجاد نمیکند | ||
| alert(user); // John | ||
| ``` | ||
| ## "var" variables can be declared below their use | ||
| ## متغیرهای "var" میتوانند پایین محل استفادهشان تعریف شوند | ||
| `var` declarations are processed when the function starts (or script starts for globals). | ||
| متغیرهای تعریف شده با `var` زمانی که اجرای تابع شروع میشود (یا برای متغیرهای گلوبال زمانی که اسکریپت شروع میشود) پردازش میشوند. | ||
| In other words, `var` variables are defined from the beginning of the function, no matter where the definition is (assuming that the definition is not in the nested function). | ||
| به عبارتی دیگر، متغیرهای `var` بدون توجه به محل تعریف آنها، از زمانی که اجرای تابع شروع میشود تعریف میشوند (با فرض اینکه تعریف کردن درون تابع تودرتو نیست). | ||
| So this code: | ||
| پس این کد: | ||
| ```js | ||
| ```js run | ||
| function sayHi() { | ||
| phrase = "Hello"; | ||
| phrase = "سلام"; | ||
| alert(phrase); | ||
| *!* | ||
| var phrase; | ||
| */!* | ||
| } | ||
| sayHi(); | ||
| ``` | ||
| ...Is technically the same as this (moved `var phrase` above): | ||
| ...از لحاظ فنی با این کد برابر است (عبارت `var phrase` را بالا بردیم): | ||
| ```js | ||
| ```js run | ||
| function sayHi() { | ||
| *!* | ||
| var phrase; | ||
| */!* | ||
| phrase = "Hello"; | ||
| phrase = "سلام"; | ||
| alert(phrase); | ||
| } | ||
| sayHi(); | ||
| ``` | ||
| ...Or even as this (remember, code blocks are ignored): | ||
| ...حتی با این هم برابر است (به یاد داشته باشید که بلوکهای کد نادیده گرفته میشوند): | ||
| ```js | ||
| ```js run | ||
| function sayHi() { | ||
| phrase = "Hello"; // (*) | ||
| phrase = "سلام"; // (*) | ||
| *!* | ||
| if (false) { | ||
| @@ -168,130 +157,131 @@ function sayHi() { | ||
| alert(phrase); | ||
| } | ||
| sayHi(); | ||
| ``` | ||
| People also call such behavior "hoisting" (raising), because all `var` are "hoisted" (raised) to the top of the function. | ||
| افراد به آن «بالا بردن» هم میگویند چون تمام `var`ها به بالای تابع «سعود میکنند». | ||
| So in the example above, `if (false)` branch never executes, but that doesn't matter. The `var` inside it is processed in the beginning of the function, so at the moment of `(*)` the variable exists. | ||
| پس در مثال بالا، شاخه `if (false)` هیچوقت اجرا نمیشود اما اصلا مهم نیست. `var` که درون آن است در ابتدای اجرای تابع پردازش میشود پس هنگام اجرای `(*)` متغیر وجود دارد. | ||
| **Declarations are hoisted, but assignments are not.** | ||
| **تعریف متغیر بالا میرود اما مقداردهیها نه.** | ||
| That's best demonstrated with an example: | ||
| این موضوع یک مثال بهتر نمایش داده میشود: | ||
| ```js run | ||
| function sayHi() { | ||
| alert(phrase); | ||
| *!* | ||
| var phrase = "Hello"; | ||
| var phrase = "سلام"; | ||
| */!* | ||
| } | ||
| sayHi(); | ||
| ``` | ||
| The line `var phrase = "Hello"` has two actions in it: | ||
| خط `var phrase = "سلام"` در خودش دو کار انجام میدهد: | ||
| 1. Variable declaration `var` | ||
| 2. Variable assignment `=`. | ||
| 1. تعریف متغیر با `var`. | ||
| 2. مقداردهی متغیر با `=`. | ||
| The declaration is processed at the start of function execution ("hoisted"), but the assignment always works at the place where it appears. So the code works essentially like this: | ||
| تعریف متغیر در ابتدای اجرای تابع پردازش میشود («بالا میرود») اما مقداردهی همیشه در جایی که وجود دارد انجام میشود. پس کد بالا اساسا مانند کد پایین کار میکند: | ||
| ```js run | ||
| function sayHi() { | ||
| *!* | ||
| var phrase; // declaration works at the start... | ||
| var phrase; // ...تعریف متغیر در ابتدا انجام میشود | ||
| */!* | ||
| alert(phrase); // undefined | ||
| *!* | ||
| phrase = "Hello"; // ...assignment - when the execution reaches it. | ||
| phrase = "Hello"; // ...مقداردهی - زمانی که اجرا به آن میرسد | ||
| */!* | ||
| } | ||
| sayHi(); | ||
| ``` | ||
| Because all `var` declarations are processed at the function start, we can reference them at any place. But variables are undefined until the assignments. | ||
| چون تمام تعریف متغیرهای `var` در ابتدای تابع پردازش میشوند، ما میتوانیم به آنها در هر زمانی رجوع کنیم. اما متغیرها تا زمان مقداردهی برابر با undefined هستند. | ||
| In both examples above, `alert` runs without an error, because the variable `phrase` exists. But its value is not yet assigned, so it shows `undefined`. | ||
| در هر دو مثال بالا، `alert` بدون هیچ اروری اجرا میشود چون متغیر `phrase` وجود دارد. اما مقدار آن هنوز تخصیص داده نشده است پس `undefined` را نشان میشود. | ||
| ## IIFE | ||
| ## روش IIFE | ||
| In the past, as there was only `var`, and it has no block-level visibility, programmers invented a way to emulate it. What they did was called "immediately-invoked function expressions" (abbreviated as IIFE). | ||
| در گذشته، چون فقط `var` وجود داشت و قابلیت رویت در بلوک کد را ندارد، برنامهنویسان برای تقلید آن راهی ایجاد کردند. کاری کردند را «فراخوانی بلافاصلهی function expressionها (immediately-invoked function expressions)» است (خلاصه شده به عنوان IIFE). | ||
| That's not something we should use nowadays, but you can find them in old scripts. | ||
| امروزه از این روش نباید استفاده کنیم اما میتوانید آنها را در اسکریپتهای قدیمی پیدا کنید. | ||
| An IIFE looks like this: | ||
| یک IIFE اینگونه به نظر میرسد: | ||
| ```js run | ||
| (function() { | ||
| var message = "Hello"; | ||
| var message = "سلام"; | ||
| alert(message); // Hello | ||
| alert(message); // سلام | ||
| })(); | ||
| ``` | ||
| Here, a Function Expression is created and immediately called. So the code executes right away and has its own private variables. | ||
| اینجا، یک Function Expression ساخته شده و بلافاصله فراخوانی شده است. پس کد هر چه سریعتر اجرا میشود و متغیرهای مخصصوص خودش را دارد. | ||
| The Function Expression is wrapped with parenthesis `(function {...})`, because when JavaScript engine encounters `"function"` in the main code, it understands it as the start of a Function Declaration. But a Function Declaration must have a name, so this kind of code will give an error: | ||
| Function Expression درون پرانتز قرار گرفته است `(function {...})` چون زمانی که موتور جاوااسکریپت در کد اصلی با `"function"` مواجه میشود، آن را به عنوان ابتدای یک Function Declaration فرض میکند. اما یک Function Declaration باید اسم داشته باشد پس کدی به این شکل ارور ایجاد میکند: | ||
| ```js run | ||
| // Tries to declare and immediately call a function | ||
| // سعی میکنیم یک تابع را تعریف و بلافاصله فراخوانی کنیم | ||
| function() { // <-- SyntaxError: Function statements require a function name | ||
| var message = "Hello"; | ||
| var message = "سلام"; | ||
| alert(message); // Hello | ||
| alert(message); // سلام | ||
| }(); | ||
| ``` | ||
| Even if we say: "okay, let's add a name", that won't work, as JavaScript does not allow Function Declarations to be called immediately: | ||
| حتی اگر بگوییم: «مشکلی نیست، بیایید یک اسم اضافه کنیم» باز هم کار نمیکند چون جاوااسکریپت اجازه نمیدهد که Function Declarationها بلافاصله فراخوانی شوند: | ||
| ```js run | ||
| // syntax error because of parentheses below | ||
| // به دلیل وجود پرانتزهای پایین ارور سینتکس دریافت میکنیم | ||
| function go() { | ||
| }(); // <-- can't call Function Declaration immediately | ||
| }(); // <-- را بلافاصله فراخوانی کرد Function Declaration نمیتوان | ||
| ``` | ||
| So, the parentheses around the function is a trick to show JavaScript that the function is created in the context of another expression, and hence it's a Function Expression: it needs no name and can be called immediately. | ||
| پس پرانتزهای دور تابع یک ترفند است تا به جاوااسکریپت نشان دهیم که تابع در زمینهی عبارتی دیگر ساخته شده و از این رو یک Function Expression است: به اسم نیازی ندارد و میتواند بلافاصله فراخوانی شود. | ||
| There exist other ways besides parentheses to tell JavaScript that we mean a Function Expression: | ||
| برای اینکه به جاوااسکریپت بگوییم که منظورمان یک Function Expression است راههای دیگری در کنار پرانتزها وجود دارد: | ||
| ```js run | ||
| // Ways to create IIFE | ||
| // IIFE راههایی برای ایجاد | ||
| (function() { | ||
| alert("Parentheses around the function"); | ||
| alert("پرانتزهای دور تابع"); | ||
| }*!*)*/!*(); | ||
| (function() { | ||
| alert("Parentheses around the whole thing"); | ||
| alert("پرانتزهای دور تمام عبارت"); | ||
| }()*!*)*/!*; | ||
| *!*!*/!*function() { | ||
| alert("Bitwise NOT operator starts the expression"); | ||
| alert("عملگر بیتی NOT عبارت را آغاز میکند"); | ||
| }(); | ||
| *!*+*/!*function() { | ||
| alert("Unary plus starts the expression"); | ||
| alert("عملگر مثبت یگانه عبارت را آغاز میکند"); | ||
| }(); | ||
| ``` | ||
| In all the above cases we declare a Function Expression and run it immediately. Let's note again: nowadays there's no reason to write such code. | ||
| در تمام موارد بالا ما یک Function Expression تعریف میکنیم و آن را بلافاصله فراخوانی میکنیم. بیایید دوباره به این موضوع توجه کنیم: امروزه هیج دلیلی برای نوشتن چنین کدی وجود ندارد. | ||
| ## Summary | ||
| ## خلاصه | ||
| There are two main differences of `var`: | ||
| بین `var` و `let/const` دو تفاوت اصلی وجود دارد: | ||
mahdyar marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 1. `var` variables have no block scope, their visibility is scoped to current function, or global, if declared outside function. | ||
| 2. `var` declarations are processed at function start (script start for globals). | ||
| 1. متغیرهای `var` محدودیت بلوک ندارند، قابلیت رویت آنها یا محدود به تابع کنونی است یا اگر بیرون از تابع تعریف شده باشند محدود به گلوبال است. | ||
| 2. تعریف متغیر با `var` در ابتدای اجرای تابع پردازش میشود (یا برای متغیرهای گلوبال در ابتدای اسکریپت). | ||
| There's one more very minor difference related to the global object, that we'll cover in the next chapter. | ||
| یک تفاوت جزئی دیگر در رابطه با شیء گلوبال وجود دارد که در فصل بعدی آن را بیان میکنیم. | ||
| These differences are actually a bad thing most of the time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely. | ||
| این تفاوتهای در اکثر اوقات `var` را نسبت به `let` بدتر جلوه میدهند. متغیرهای سطح بلوک چیز خیلی خوبی هستند. به این دلیل است که خیلی قبلتر `let` در استاندارد معرفی شد و حالا برای تعریف متغیر روش اصلی است (در کنار `const`). | ||
mahdyar marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.