{ PH_Dev }

Published on

連續記錄挑戰Day35-Javascript-變數

Authors
  • avatar
    Name
    Penghua Chen(PH)
    Twitter

JavaScript的變數、範疇與hoisting

變數

關於變數這個部分會提到的是:

  1. 變數是什麼?
  2. 變數的型別與介紹
  3. ES6新的變數宣告方法: letconst

什麼是變數 ?

「變數」,可以想成是用來存放資料的箱子,透過這個箱子中儲存的值,才使程式在執行過程中不斷存取資料的行為得以執行。

變數的宣告方式在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
需要區分大小寫Namename
第二個字母後可以用數字、底線msg1msg_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/0NaN(特殊的非數值)
Infinity/InfinityNaN(特殊的非數值)
-Infinity/-InfinityNaN(特殊的非數值)

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();