iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 2
2
Software Development

為什麼世界需要Typescript系列 第 2

奇異的變數宣告 - 02

再論變數宣告

Javascript 宣告變數方式是

var name = "flash";

但是如果你不小心這樣寫

var name = "flash";
var name = "abc";

在任何程式碼編輯器編輯這段程式碼的時候, 不會提示你重複宣告. Javascript 執行時期(runtime) 也不會爆出任何錯誤.

Typescript 提供了let 關鍵字, 你可以動手試試看在VSCode 中, 將上述程式碼改成

let name = "flash";
let name = "abc";

VSCode 會提示你錯誤

Cannot redeclare block-scoped variable 'name'.

還有一種情況, 在Javascript 中這樣寫

for (var i = 0; i < 10 ; i++) {
   setTimeout(function() {
      console.log(i); 
   }, 100);
}

想要測試Javascript 執行結果, 你可以打開任何瀏覽器, 打開開發人員工具控制台, 將Javascript 程式碼內容複製貼上, 並執行看看.

猜猜看上面的Javascript 程式碼會輸出什麼樣的結果? 大多數人都會直覺的回答

0 1 2 3 4 5 6 7 8 9

但是實際上跑出來的結果卻是

10 10 10 10 10 10 10 10 10 10

因為var 有個獲取(Capturing) 問題, 而且 setTimeout 會在 for 迴圈之後才執行, 而 for 迴圈跑完之後i 變數就變成 10, 便輸出的同一個變數 i (10)

Typescript 可以解決這樣的問題, 用 let 就解決了

for (let i = 0; i < 10 ; i++) {
   setTimeout(function() {
      console.log(i); 
   }, 100);
}

還有一種Javascript 怪異的現象, 假設你輸出不存在的變數

console.log(a);

打開瀏覽器的開發人員工具控制台, 貼上上段程式碼, 你應該看到

Uncaught ReferenceError: a is not defined

這結果很合理, 但是你改成下面程式碼, 再一次貼到開發人員工具控制台

console.log(a);
var a = 10;

結果卻是

undefined

我剛入門Javascript 的時候, 看到這就傻眼了....

JavaScript 是一種直譯解釋性程式語言.

剛接觸到Javascript 的人都有這一句介紹話: "JavaScript 是一種直譯解釋性程式語言"

但是實際上, 這句話只對一半, Javascript 執行的時候透過V8 (Google 開發的Javascript 引擎)編譯成中間機器碼, 然後由瀏覽器逐行加載解釋執行.

所以呢, 我貼上去的Javascript 原始碼在瀏覽器並不是 "直接" 直譯執行, 而是先被轉換為中間機器碼, 再直譯中間機器碼一行一行逐行加載解釋執行.

但就單論程式碼來看, 至少應該給我輸出 "Uncaught ReferenceError: a is not defined" 這才合理阿,
那為什麼Javascript 要改變這種不合理的行為?

從小學習C/C++ 的人都知道, 我們一定要先宣告變數才可以使用, 但在Javascript 世界裡可以不用宣告變數就使用. 例如以下

i = 123;
console.log(i);

從小學習C/C++ 的人也知道, 我們也一定要宣告函式才可以使用(就是 .h .hpp 定義檔), 但在Javascript 世界裡, Javascript 也希望我們不用宣告任何函式就可以使用.

甚麼!? 看看下面的例子

test();
function test() {
   console.log("hello world");
}

你會說, 這很簡單, 把 function test 宣告移到上面, 不就好了嗎? 這很合情合理, 宣告函式之後就可以使用, 這很合理阿! 為什麼要 "不用宣告" 這東西?

function test() {
   console.log("hello world");
}
test();

再舉個下面的例子

function walk(n) {
   if( n>10 ) {
      logEvent(n);
   }
}

function logEvent(msg) {
   console.log(msg);
   walk(1);
}

假如不想要類似 .h .hpp 宣告檔案, 又要有 "一定要宣告函式" 這件事情, 那麼直譯walk() 裡面程式碼的時候會發現到 logEvent() 因為沒有在前面定義宣告, 會跑出直譯錯誤.

但假如我們把 logEvent() 在原始碼檔案中順序位置移到 walk() 上面, 但直譯logEvent() 的時候又會發現walk() 沒有在前面定義宣告, 又再次跑出直譯錯誤.

你會發現這很像遞迴問題....

故Javascript 存在著 var 提升(hoisting) 的問題

而且如果使用var 的時候不小心這樣寫的話

x = 5; 
console.log(x);
... 中間隔了程式碼
var x; 

你的Javascript 就會出現不可預期的錯誤.

為了說明Typescript 他的好, 首先我們先從let 方式開始吧! Typescript 可以讓 let 不允許你重複宣告同樣的變數名稱, 也不允許你未經定義宣告就使用變數.

Typescript 也提供了 const 關鍵字

const name = "flash";

const 的功能和 let 一樣是宣告變數, 但不同的地方是 const 如同其名是不允許變數內容被改變的.

Typescript 宣告變數基本類型有

let name: string = "";
let id: number = 123;
let birth: Date = new Date();
let myArr: number[] = [];
let myBool: boolean = false;

Typescript 沒有直接提供字典(Dictionary) 關鍵字讓我們直接使用字典, 不過我們可以限制其約束.

例如下面是宣告字典的範例

class Student {
   name: string;
   id: number;
}

let dict: {[id: number]: Student} = {};

宣告字典的地方是以 大括號開頭和結束 包起來的, 裡面寫

[id: number]

表示這是字典的Key, Key 的名稱是id , Key 的型態是 number.

使用字典的時候, 可以這樣使用

let student = new Student();
dict[student.id] = student;

假如你誤用以下程式碼

dict[student.id] = 123;

Typescript 就會提示你錯誤

Type '123' is not assignable to type 'Student'.

上一篇
楔子 - 01
下一篇
定義物件 - 03
系列文
為什麼世界需要Typescript30

1 則留言

0
huli
iT邦新手 5 級 ‧ 2019-09-09 23:07:46

let 跟 const 應該是 JavaScript(ES6) 本身就有提供的東西,而不是 typescript 所提供的喔。就算沒有使用 typescript 也可以用 let 跟 const

想當年我是在2012年接觸Typescript, 也就是六年前沒有ES6 ...

huli iT邦新手 5 級 ‧ 2019-09-10 21:25:22 檢舉

雖然說 TypeScript 是 2012 年出現的沒錯,但 let 跟 const 是在 2015 年的 1 月的 TypeScript 1.4 版本才支援的喔。裡面也有提到 let 與 const 是 ES6 的東西。

相關連結:

  1. Announcing TypeScript 1.4
  2. TypeScript wiki
  3. GitHub release

我要留言

立即登入留言