- Published on
連續記錄挑戰Day35-Javascript-變數
- Authors
- Name
- Penghua Chen(PH)
JavaScript的變數、範疇與hoisting
變數
關於變數這個部分會提到的是:
- 變數是什麼?
- 變數的型別與介紹
- ES6新的變數宣告方法:
let
與const
什麼是變數 ?
「變數」,可以想成是用來存放資料的箱子,透過這個箱子中儲存的值,才使程式在執行過程中不斷存取資料的行為得以執行。
變數的宣告方式在ES6之前只能透過var
宣告,宣告方式如下:
var 變數名稱 = 初始值;
var number = 5;
如果你對於同一個變數宣告了兩次,則第二次的宣告值會覆蓋第一次的值
var number = 10;
var number = 15;
//會得到值為15
console.log(number);
那也許你會想問,是不是可以不要給予初始值呢?
答案是:當然可以。 所以下列這個寫法也是沒問題的:
var 變數名稱;
var number;
那又或許你會想問:那不宣告直接指定值可以嗎,如下:
msg = "Hello World!"
在Javascript中,如果沒有先宣告就指定值給變數,Javascript就會自動幫你宣告該變數。
但這樣可能會讓自己遇上些麻煩,所以請務必養成良好的習慣,使用變數前一定要記得宣告!
變數的命名規則
關於變數命名也是有其規則存在,必須按照這個規則設定才不會有問題
規則 | Example |
---|---|
開頭必須是英文字母、底線、錢字號 | name 、_name 、$name |
需要區分大小寫 | Name 、name |
第二個字母後可以用數字、底線 | msg1 、msg_1 |
不能使用保留字 | 保留字一覽表 |
變數的型別與介紹
變數分成兩大型別: 基本型別(primitive types)與物件型別(object types)
- 基本型別 (primitive types)
- 數字(Number)
- 字串(String)
- 布林值(Boolean)
- null
- undefined
基本型別:數字(Number)
JavaScript中的數字都是以浮點數來表示,所有的實數在JavaSript都是以浮點數格式精確的表示。
JavaScript使用IEEE-754浮點表示法,為二元表示法。意即可以精確的表示1/2、1/8、1/1024這類小數,但對於0.1這類卻無法精確表示。
var x= .3 - .2;
var y = .2 - .1;
console.log(x == y); // false,在JS中兩者不會得到相同答案
而且當運算結果如果發生下列狀況,並不會有錯誤訊息。
運算結果 | 值 |
---|---|
overflow(溢位) | Infinity(運算後的正值大於可表示的數字) |
overflow(溢位) | -Infinity(運算後的負值大於可表示的數字) |
underflow(下溢) | 0 (運算後的值比最小可以表示的數字還要接近0時) |
0/0 | NaN(特殊的非數值) |
Infinity/Infinity | NaN(特殊的非數值) |
-Infinity/-Infinity | NaN(特殊的非數值) |
NaN是什麼?
NaN是Not-A-Number的意思,代表不是數值的數值。
夠抽象吧,什麼是不是數值的數值?我想也許只能透過例子來了解了。
首先,先確認一下NaN在JavaScript中是什麼型別?
console.log(typeof NaN); //型別為Number
再來,我們測試看看在什麼情況會獲得NaN的值。
console.log(Number(1)); // 值為1,型別為Number
console.log(Number('1')); // 值為1,型別為Number
console.log(Number('abc')); // 值為NaN,型別為Number
console.log(Number(undefined)); // 值為NaN,型別為Number
console.log(Number(0/0)); // 值為NaN,型別為Number
關於上面的測試結果,在MDN有這一敘述:
NaN值的來源:NaN 會在算術運算(arithmetic operations)出現 undefined 或是 unrepresentable 值的結果時產生。這些值不一定是溢出條件。NaN 亦為試圖給毫無可用數字的原始值、予以強制運算之結果。 NaN 值的來源
題外話,那有沒有辦法判斷輸入的值會不會得到NaN呢?
可以的,透過 Number.isNaN() 可以判斷,若回傳true,則表示值為NaN,否則為false。
將上面的測試例子透過Number.isNaN()檢視看看:
console.log(Number.isNaN(Number(1))); // false
console.log(Number.isNaN.(Number('1'))); // false
console.log(Number.isNaN(Number('abc'))); // true
console.log(Number.isNaN(Number(undefined))); // true
console.log(Number.isNaN(Number(0/0))); // true
終於到了最後一個關於NaN的特性,那就是NaN:與任何值都不相等,包含他自己本身。
//因為NaN與任何值都不相等,包含自己本身,所以當x!=x時,才會是true。
var x = NaN;
console.log(x==NaN); // false
console.log(x!=x); // true
基本型別:文字(String)
文字型別其實蠻好理解的,我們可以透過單引號''
或者雙引號""
的方式讓值以字串格式呈現。
var str = 'Hello World';
var str = "Hello World";
至於什麼時候該用單引號或雙引號,則可以依照字串的狀況做選擇。
好比說字串的內容中已經有單引號,那就應該用雙引號包住字串
var str = "He\'s hero!";
而且在上面的這個例子中有一個很明顯的 \
之所以會用到的原因在於字串型別中有一些有意義的特殊字,如果不透過這樣子處理會有問題。
而透過 \
+字串的方式稱為跳脫序列
基本型別:布林值(Boolean)
- 只有兩個值: true / false
- JavaScript中,任何值都可以轉為布林值。
基本型別:空值(null)
用在表示沒有相對應的值,而有趣的點是,透過typeof(null)會得到它的錯誤型別為物件。
因為null才是正確型別,但許多網頁已經在這樣子的基礎上建立,所以如果修復的話會造成更多的bug。
基本型別:未定義(undefined)
用來表示某參數的值尚未被定義,有以下幾種情況:
- 表示值不存在,但變數已經被宣告。
var a;
console.log(a);
- 使用物件未定義的屬性
var obj = {
name:'Bill',
}
console.log(obj.hobbies);
- 函式沒有回傳值
var ary = [1,2,3];
var result = function(){
ary.forEach(value=>value*value);
}
console.log(result(ary));
ES6新的變數宣告方法: let 與 const
最後要提到的是ES6的新方法 let 與const,這兩個方法的出現,是為了更有效的控制變數的存取範圍。
而let、const的宣告方式與var無異,如下:
let number = 5;
const number2 =10;
但是這兩者都規定不能重複宣告變數,否則會有Identidier 'XXX' has already been declared的提示字樣
而const
的用途則又稍微有些不同,是用在常數的設定。
意思是,透過const宣告的值通常都有一個特殊意義的值,且這個值不會被更動。
關於變數的介紹就到這邊為止囉!明天見~
區塊範疇(block scope)、函式範疇(function scope)
在了解什麼是區塊範疇(block scope)以及函式範疇(function scope)之前,要先知道什麼是範疇。
範疇(scope):
當我們在程式中引進變數時,我們必須給予這些變數一個安置它們的空間,如此一來我們才能在需要的時候將它們找出來使用。而範疇就是用來訂定安置變數的空間的規則
區域範疇
區域範疇的定義是變數在 {...}
以外的地方為不可見的,只有在 {...}
裡面才可以使用到那些被宣告的變數。
函式範疇
函式範疇的定義是變數在 function(){...}
以外的地方為不可見,只有在函式中才可以使用那些被宣告的變數。
JavaScript在ES6之前是透過 var
宣告變數,所以會建立以函式為範疇的變數。 但在ES6之後可以透過 let
/ const
兩種新的變數宣告方法,建立以區塊為範疇的變數。
Ex1. 函式範疇
function scope(){
var a =1;
//a=1
console.log(a);
let b=2;
if(b>1){
//a = 1
console.log(a);
}
}
scope();
//a is not defined
console.log(a);
Ex2. 函式範疇2
function scope(){
let b=2;
if(b>1){
var a = 2;
let c = 3;
}
//a=2,因為var的宣告方式,讓變數使用的範疇規則為函式範疇,所以即使寫在if條件句中,也一樣可得到值。
console.log(a);
//c is not defined。 let的宣告方式讓c只在if條件句中可見,所以在這裡得不到值。
console.log(c);
}
scope();
Ex3. let
/ const
: 只在本身定義的區塊 {...}
中有效
var foo = true;
if(foo){
//bar只在{...}的區塊有效,所以bar只在if條件句中可見
let bar = foo*2;
bar = something(bar);
console.log(bar);
}
//ReferenceError
console.log(bar);
提升(hoisting)
- JavaScript環境建立時,會先將所有的變數、函數先存入記憶體位址中(但這過程並不包含指定給它們的值,其值會在原地等待被執行),而這個會將宣告的變數、函式從原本撰寫的位置提升到程式碼最上端的行為也稱為hoisting。
Ex. 變數的hoisting
/*透過console獲得a的值會得到 'undefined' 而非 'a is not defined' */
/*這裡就是因為hoisting的關係,變數a在一開始就被存入記憶體(但不包含值),所以會是得到undefined(表示值不存在,但變數已經被宣告)。*/
/*
執行過程等同:
var a;
console.log(a);
a = 3;
*/
console.log(a);
var a = 3;
Ex2. 函式的hoisting
/*觀念同於變數,會先將函式名稱存入記憶體中,所以當console時可以得到值。*/
console.log(test());
function test() {
return 2;
}
Ex3. 函式裡面的變數
/*函式中的變數會hoisting到函式區塊的最上層(因為JS函式範疇的規範,所以不會跑出function scope)*/
function test() {
console.log(a);
var a = 3;
}
test();