初學Node.js的第一眼

寫這篇文章的原因是因為原本要研究React,第一件事就是遇上了Node.js,於是就打算撰寫一篇理解與認識Node.js的初學文章。

定義Node.js

綜合各種文獻定義,這裡做一些消化與整理。Node.js是一種有別於典型使用瀏覽器於網頁前端來運行Javascript的方式,讓Javascript語法可跳脫並運行於後端的網路應用。其使用Google優秀的V8 Javascript引擎作為基底,其特性為非同步事件驅動 (Asynchronous event-driven)的I/O處理,並具備非阻塞式(non-blocking) 特性,且有極佳擴充性的網路應用程式。

其中非同步I/O處理的部份,透過支援多平台的libuv函式庫來實現事件循環(event-loop)的機制,而libuv整合與改良了Linux上的libev與Windows的IOCP,以實現多平台的用法,有興趣的話可參考這篇這篇(不過中文教程翻的有點奇怪,英文好的可看原文版本)。

關於阻塞式(Blocking)與非阻塞式(Non-Blocking)呼叫的差異,於Node.js官網文件中有詳細的介紹。 簡言之,阻塞式呼叫裡,Node.js中的Javascript處理程序需等待非Javascript處理程序執行完後才可繼續運作,即所謂的「同步(Synchronous)」處理,不過尤其在處理檔案或網路資源時的效率遠遠低於CPU的處理速度,因此在阻塞式的I/O設計中,浪費了很多的時間在等待,因此對於追求高效能應用的一種阻礙。通常在其他的系統中,解決這種阻塞式的作法就是使用多執行緒(multi-thread)來解決。

不過在Node.js官方文章中,並沒有標準文件去定義「多執行緒」這樣的非同步處理作法。因此Node.js所謂的「非同步」作法,並非我們一般直覺理解的使用多執行緒的方式來達成,而是使用了非阻塞式的「單執行緒」來做到。但是這麼說在技術上而言,又不能說完全精準正確,因為Node.js採用了libuv來達成I/O機制,底層的事件循環運作,比上述的說法還要來得複雜一些,應該說以單執行緒為主,仍會透過執行緒池的方式達成不同作業系統下的非同步作業,並且可以接受callback的機制來實作回呼流程。

然而,在Node.js中,是沒有提供讓開發者使用多執行緒的開發方式,但並非Node.js的運作就只有單執行緒,因為如上述提到的libuv,在看不見的底層是運用了多執行緒來達成。

順帶一提的是,我們在說Node.js時,有時候會稱之為Node,把.js省略了,指的是相同的事物。

安裝Node.js

到Node.js的官網下載並安裝軟體主檔,安裝後可至Command Prompt打入指令確認安裝結果。透過以下指令可查詢Node.js與NPM版本。

node -v
npm -v

NPM是什麼?

當我們使用Node.js時,第一個會碰上且必須認識的,就是NPM。NPM為Node Package Manager的縮寫,從字面上來看應該可以猜的出是用來管理Node.js的各式各樣套件的管理工具,一般程式開發者也可以透過NPM發佈自己的Node.js套件,並分享給所有使用者安裝運用。若使用過Redhat Linux的朋友,應該有聽過或用過RPM,甚至是.Net體系中的NuGet,其概念與精神是差不多的。

NPM以Javascript語言開發完成,會在Node.js安裝完成時一併贈送!使用NPM安裝套件相當容易,也不少文章有完整的解說,在此不針對細節講解。簡單的語法如下。

#將目前所在目錄做初始化動作 (照著提示輸入相關資訊),執行完後會產生package.json檔
npm init

#安裝套件語法,預設不加參數為「本地安裝」(意即裝載在執行目錄下)。安裝後,會將各套件放置在./node_modules目錄中。
npm install <套件名稱>

#若其後帶入-g語法,即為Global「全域安裝」(安裝在系統目錄下)
npm install <套件名稱> -g

至於其他NPM功能,請參閱更多詳細的文章

而針對npm install的參數–save與–save-dev的差異,這有一篇文章寫的挺清的,可以參考一下。

再回到Node.js吧

若直接在Command Prompt中輸入node,即會直接進入node.js的執行模式,可以試著輸入console.log()指令進行個簡單的輸出。但一般我們不會這樣使用,而是先編寫.js檔,再進行叫用。

於執行目錄中,撰寫一個helloworld.js檔案。

console.log("Hello World");

來創造一個簡單的Web Server吧

透過引入Node.js中的http模組,我們可以簡單透過一段Javascript檔,就能在本機實現一個Web Server的服務。程式碼如下:

var http = require("http");

http.createServer(function(request, response){
	response.writeHead(200, {"content-type": "text/html; charset=utf-8"});
	response.write("這是Node.js架設的Web Server Page");
	response.end();
}).listen(8080);

console.log("Start Web Server...");

好了,如此就完成了我們Node.js的第一眼初學篇了!

Android Studio的Layout無法正確顯示問題 (看不見Hello World!)

此篇網誌,主要在講如何解決才興高彩烈裝好的android studio,立馬就發現Layout畫面顯示不出元件的問題的bug。

以目前我所安裝的android studio 3.1.3版本為例,安裝完後直接開啟一個空白專案,切換到activity_main.xml後,照理說要輕易的看見傳說中的”Hello World!”字樣才對。

可以看到在Component Tree中,有”Hello World!”字樣的TextView元件,但在圖上卻是一片白茫茫。仔細看看右上角,會有一個紅色驚嘆號,點開來看一下驚嘆的內容是什麼。

內容是“Failed to load AppCompat ActionBar with unknown error.”

(雷哥實在很難接受才剛灌好的全新的環境,就有Error的感覺呀!)

 

拿這個錯誤訊息網路上google一下,會發現在先前版本就已經發生了。

https://stackoverflow.com/questions/44449275/failed-to-load-appcompat-actionbar-with-unknown-error-in-android-studio

主要原因就是support library有點問題,解法就是碰碰運氣,換一個同版本但不同小版號的試試看。

展開左邊的Gradle Scripts 下第二項的build.gradle (Module:app)

找到dependencies區域,看到這一行 (未修改前)

implementation 'com.android.support:appcompat-v7:28.0.0-beta01'

我們把後面有問題的版號28.0.0-beta01改成28.0.0-alpha1試試

ps. 可到MVN Repository找一下appcompat-v7有release哪些版本了
https://mvnrepository.com/artifact/com.android.support/appcompat-v7?repo=google
由beta01往前推,有alpha3、alpha1,我選了alpha1,沒有原因,純粹高興。

碰碰運氣和驗證人品好壞,改完後右上方會出現”Sync now”的按鈕連結,點下去花點時間讓他載入這個版本的Library。

花個幾分花等待下載,答案即將就要揭曉!

小心翼翼的回到activity_main.xml的Layout畫面,發現了讓人精神為之一振、見證奇蹟的時刻,

讓人兩行淚可以奔放落下的”Hello World!”竟然在這翻碰運氣的修改之後出現了!

從此以後,就沒有理由說「我連Hello World!都看不到,叫我怎麼繼續開發下去呢?」了

Python初學重點 (07) – List

在Python中的List (中文有人翻作「串列」,但我傾向直接使用List這個英文就好,因為這中文翻譯不怎麼常用),是可以放入各種不同型別的一個有順序性集合,他的表現形態其實有點像其他語言的Array (陣列)。

以下是一個List的範例:

list1 = ['ant', 'bot', 'cat', 'dog']

print(list1)
## output:
# ['ant', 'bot', 'cat', 'dog']

print(list1[2])
## output:
# cat

到目前為止,應該都還ok,裡面的index從0開始算這點和絕大多數語言一樣。

放入異質型態的話,也可以,我們直接示範一個混合型,而且用雙層index方式取得子項目(也是一個List)的內容。

 
list2 = ['apple', 0.1415, 5, ['a', 'b', 'c']]

print(list2[1])
print(list2[3])
print(list2[3][1])
## output: 
# 0.1415
# ['a', 'b', 'c']
# b

接下來有幾個比較酷的index變化,第一個是使用負數的index,代表從後面取回來。-1是最後一個,以此類推。

 
list1 = ['ant', 'bot', 'cat', 'dog']

print(list1[-1])
## output: 
# dog

接下來介紹一個非常有趣的slice(切片)用法。這有一點像其他語言的range的觀念。
切片透過冒號來表示,list1[0:3],則代表從0開始,到3之前的內容,這裡slice後半邊的數值是「不包含」的index。
所以就是index為0、1、2的值。

如果是用list1[0:-1],那就是index為1、2、… 一直到最後一個(不含)的前一個index內容。
看一下範例比較清楚。

 
list1 = ['ant', 'bot', 'cat', 'dog']

print(list1[0:3])
print(list1[0:-1])
## Output:
# ['ant', 'bot', 'cat']
# ['ant', 'bot', 'cat']

上面這個例子,list1裡面有4個item,所以切片[0:3],只會拿到前三個。而切片[0:-1],會從第一個一路拿到倒數第二個(因為-1,最後一筆,並不包含)。
所以這兩個寫法,都恰好會拿到一樣的內容。

而切片更靈活的用法是可省略切片冒號前的值,或切片冒號後的值。
例如list1[1:],則代表index從1開始,省略後面就是指拿到最後一個為止。
而list1[:2],則是指index從頭開始,拿到index為2的前一個為止。
那麼切片前後都省略,只留冒號呢?像這樣,list1[:],省略前面是指從頭開始,省略後面是指到最後為止,所以這樣的寫法就等同於把整個List內容都印出來。
所以list1[:]其實等同於list1本身。可以用個list1 == list1[:]來做個驗證便知。

 
list1 = ['ant', 'bot', 'cat', 'dog']

print(list1[1:])
print(list1[:2])
print(list1[:])
print(list1)
print(list1 == list1[:])
## Output:
# ['bot', 'cat', 'dog']
# ['ant', 'bot']
# ['ant', 'bot', 'cat', 'dog']
# ['ant', 'bot', 'cat', 'dog']
# True

List的運算

把兩個List用加號運算子相加,可以得到串接的效果。而把List乘上一個整數值,則會將List內容複製成該整數倍的內容。不要以為是會將List的內容數值相乘喔。

 
list1 = ['ant', 'bot', 'cat', 'dog']
list2 = [10, 20, 30, 40]
list3 = list1 + list2
list4 = list2 * 2

print(list3)
print(list4)
## Output:
# ['ant', 'bot', 'cat', 'dog', 10, 20, 30, 40]
# [10, 20, 30, 40, 10, 20, 30, 40]

增加新的List item
使用append和insert,即可將新item附加在原本的List最末端(用append)或者插入在任意的index位置(用insert)

 
list1 = ['ant', 'bot', 'cat', 'dog']
list1.append('egg')
print(list1)
list1.insert(2, 'batman')
print(list1)
## Output:
# ['ant', 'bot', 'cat', 'dog', 'egg']
# ['ant', 'bot', 'batman', 'cat', 'dog', 'egg']

刪除List中的item內容

簡單使用「del」然後接List的項目index即可。而Python List中,也支援另一種刪除item的作法,使用remove。但remove中的參數並非使用index,而是直接寫入要刪除的「值」。不過要注意的是,如果想要remove的值不存在List中,則會出現ValueError錯誤。

 
list1 = ['ant', 'bot', 'cat', 'dog']
del list1[2]
print(list1)
list1.remove('dog')
print(list1)
## Output:
# ['ant', 'bot', 'dog']
# ['ant', 'bot']

Python初學重點 (06) – 例外處理

發生例外處理時怎麼辦,Python一樣有try/catch機制,只是在Python中是叫作try/except機制。

簡單的寫法如下:

try:
    1/0
except ValueError:
    print('Value Error')
except ZeroDivisionError:
    print('Predefined: ZeroDivisionError!!')
except Exception as e:
    print('Error message: ' + str(e))
else:
    print('Everything is ok.')
finally:
    print('Do it whatever...')

## Output:
# Predefined: ZeroDivisionError!!
# Do it whatever...

except的語法,可以放入Exception Type在後面,來判斷哪一類型的異常發生,要做特別的處理。如果要針對異常的內容再做處置,可以使用「as 異常變數」來處理其值。而else則是在「無任何異常時」才會觸發。

上面的程式碼,由於出現了除以0的錯誤,由於有定義ZeroDivisionError的異常Type,因此會被該行except處理掉。

倘若,沒有定義except ZeroDivisionError:,則會被萬用的except Exception as e:給收走。
記得except Exception as e:不能寫在比其他子異常類別還前面,否則明確定義的異常被「較一般化」的異常先收走後,就算是符合明確定義也不會被觸發(這在其他語言如Java也相同)。

try:
    1/0
except ValueError:
    print('Value Error')
except Exception as e:
    print('Error message: ' + str(e))
else:
    print('Everything is ok.')
finally:
    print('Do it whatever...')

## Output:
# Error message: division by zero
# Do it whatever...

而finally與其他語言差不多,都是「無論有沒有異常發生」都一定會保證觸發的區段。

完整Python3的異常類別,請參考Python文件

Python初學重點 (05) – global變數

在Python中,放在function中的變數,與function外的變數(global,全域變數)是不同的。在兩個區域的變數可以取相同的名字,當在function中有一個與全域變數名稱相同的變數時,在操作過程就會以區域變數為主,這個和其他語言差不多。

但由於Python的變數沒有「宣告」的階段,因此當你直接操作一個變數,給定值的過程,就可能「造出一個新的變數」。例如有一個全域變數叫var1 (原值是5),但是你在function中,想說要把這個全域變數直接改值,就用了var1 = 10,但沒料想到這行程式碼,Python會誤以為你宣告了一個區域變數var1,並給定了10的值。和你所想的可能就有不同的結果了。

def test():
    var1 = 10

var1 = 5    
test()
print(var1)

## output:
# 5

上面這段程式碼,var1 = 5這一行,並不是把全域變數重新給定值,而且做了一個新的區域變數,給定10,然後function結束後這個值就被丟棄了。

因此,如果你想要做的是在function中真的改到全域變數var1,要使用關鍵字「global」來告訴Python這個變數是全域變數。

def test():
    global var1
    var1 = 10

var1 = 5    
test()
print(var1)

## output:
# 10

但這裡還有一點需要注意,如果在function,去「使用」(例如印出)一個全域變數,還是可以直接使用到這個全域變數的。只有在做「設值」時,才會造出一個新的區域變數。

def test():
    print(var1)

var1 = 5    
test()

## output:
# 5

因此上述的例子,在print(var1)之前,不需要特別先加上global var1。

不過在Python中,是區域變數或者是全域變數,僅能「選邊站」,不能一下是區域變數一下又同時想要當全域變數,只要有模荾兩可的情況,就會出現錯誤。

def test():
    print(var1)
    var1 = 10

var1 = 5    
test()

## output:
# 錯誤: UnboundLocalError: local variable 'var1' referenced before assignment

意思是,我們照原本說的,直接使用print(var1)時,想要把var1當成是global來使用,但下面那一行var1 = 10時,會造出一個區域變數var1,但這樣和上面那一句就抵觸了,Python就會解讀成你造了一個var1區域變數之前,在沒宣告時就要使用。

如果你真正的意思是要使用那個global變數,則就乖乖的在前面加上global var1。
如果你真的要的是一個區域變數var1,那print(var1)就不應該擺在宣告之前,這明顯是語法錯誤。

Python初學重點 (04) – 聊聊print()

大家寫hello world一定會用到該程式的輸出打印的function,在Python就是print()。print()最基本的用法就是裡面放字串,除此之外還有一些小小妙用。

變更換行符號

預設print()印出字串後,會在文末補上換行符號,因此如果連續執行兩次print(),會看到兩句是換行的。如果你不想要印出print()後進行預設的換行,想要換成別的,可以使用關鍵參數「end」來變換。使用方法放在最右側,使用end='<你要的換行符號>’來達成。

print('Good')
print('Bad')
print('Ugly')

## Output:
# Good
# Bad
# Ugly

print('Good', end=' : ')
print('Bad', end=' : ')
print('Ugly')

## Output:
# Good : Bad : Ugly

變更連接分隔符號

如果print()裡放多個字串,預設會使用空白串成一行,如果使用「sep」關鍵參數來變更分隔符號,則可以改變成想要的樣子。
使用方法放在最右側,使用sep='<你要的分隔符號>’來達成。

print('Good', 'Bad', 'Ugly')

## Output:
# Good Bad Ugly

print('Good', 'Bad', 'Ugly', sep = '/')

## Output: 
# Good/Bad/Ugly

如果你同時要使用sep和end,則只要都將他們擺在一般參數的右側,則無順序之分。也就是寫成sep=’a’, end=’b’或是end=’b’, sep=’a’都一樣。

你關心的,如何做string format?

很多人使用了print,就會很想知道怎麼做string format。來吧,在Python其實有多種format的方式,且Python 2與3也略有不同,雷哥這邊推薦以下使用方法。

print('Hello, {:s}. You have {:d} messages.'.format('Rex', 999))

## Output: 
# Hello, Rex. You have 999 messages.

這裡的用法是,在字串裡面「挖洞」,放入你想要的變數,使用{:型別}來達成,想要放入字串變數則是{:s},整數是{:d},而浮點數則是{:f},常用的大概這些,其他請見下方列表。

為什麼要那個冒號呢?其實是有其他功能,冒號前可以自訂變數的index,如果你有要重覆使用那個變數時就可以用的到,例如{0:s}。但定義過的變數,要再使用時,請直接使用{變數index},後面的:型別就不要再加上了,因為已經宣告過了再宣告會有衝突,這理由應該很好理解。用法如下:

print('Hello, {0:s}. You have {1:d} messages. Good luck to you, {0}'.format('Rex', 999))

## Output: 
# Hello, Rex. You have 999 messages. Good luck to you, Rex

型別參考表

ConversionMeaning
dSigned integer decimal.
iSigned integer decimal.
oUnsigned octal.
uObsolete and equivalent to 'd', i.e. signed integer decimal.
xUnsigned hexadecimal (lowercase).
XUnsigned hexadecimal (uppercase).
eFloating point exponential format (lowercase).
EFloating point exponential format (uppercase).
fFloating point decimal format.
FFloating point decimal format.
gSame as "e" if exponent is greater than -4 or less than precision, "f" otherwise.
GSame as "E" if exponent is greater than -4 or less than precision, "F" otherwise.
cSingle character (accepts integer or single character string).
rString (converts any python object using repr()).
sString (converts any python object using str()).
%No argument is converted, results in a "%" character in the result.

延申閱讀:Formatted Output

機器學習從頭開始。

人工智慧當道,在2016~2017年可以算是人工智慧AI第三次熱潮最火的時間點,三天兩頭就有各種知名企業、學術研究機構、相關社群等的人工智慧論壇、研討會、課程、書藉,你要不聽到機器學習、深度學習、聊天機器人…等話題還真是困難。

AI雖然已經不是什麼新鮮事,不過總感謝一群默默投身在人工智慧領域的先驅者不離不棄這個議題與技術,幫這個技術做好墊基與建構穩固的應用工具,讓應用者可以用較少的力氣來接觸這門深入的學問和技術。

要踏入機器學習領域,除了工具的使用,軟體的選擇之外,其實仍然是要具備一些較底層的知識,才不會被工具框架牽著走,而不知其所以然。但要了解較為核心的知識,是難免會需要用到一些數學、統計或機率等的基礎,雖然這不是必要,但馬步與基本功能夠按步就班學好,後面才會具備獨立思考的能力。

要進入機器學習殿堂,當然推薦聽聽台大資工教授林軒田老師的線上課程《Machine Learning Foundations 機器學習基石》,這門課是「基石」,是為了通往更高深的學問的穩固基石課程,建議不要把Foundations翻成「基礎」,因為不得不說這門課其實不那麼容易,是為了讓有心要學習的人從最根本的數學驗證來證明機器學習的可行性以及機器學習學習過程要注意的事。

這堂課原本是在Coursera上 (有一定時間才能註冊學習),後來直接佛心的上到更開放的Youtube平台上供大家自由的觀看學習。這門課共有16講,在Youtube上切割成65個影片,需要有一點耐心或者和朋友一起組讀書會,較能夠堅持下來完成。

林軒田老師的課程講義在此,有興趣的讀者可以自行閱讀。

Coursera的課程

這堂課,在2017年9 月份時,分拆成上下兩堂課,分別是

1. 「機器學習基石上:數學篇 (Mathematical Foundations)」

2. 「機器學習基石下:方法篇 (Algorithmic Foundations)」

數學篇的課程大綱如下:

When Can Machines Learn? [何時可以使用機器學習]

  • 第一講:The Learning Problem [機器學習問題]
  • 第二講:Learning to Answer Yes/No [二元分類]
  • 第三講:Types of Learning [各式機器學習問題]
  • 第四講:Feasibility of Learning [機器學習的可行性] 

Why Can Machines Learn? [為什麼機器可以學習]

  • 第五講:Training versus Testing [訓練與測試]
  • 第六講:Theory of Generalization [舉一反三的一般化理論]
  • 第七講:The VC Dimension [VC 維度]
  • 第八講:Noise and Error [雜訊與錯誤]

JavsScript停看聽 (3): typeof與instanceof

typeof

用法:typeof 值或變數

回傳:代表某種類型的字串 {“boolean”, “number”, “string”, “object”, “undefined”, “function”}

簡單說,typeof用來檢查變數的型別。

> typeof true
"boolean"
> typeof 123
"number"
> typeof 1.5
"number"
> typeof "123"
"string"
> typeof {}
"object"
> typeof []
"object"
> typeof null
"object"
> typeof undefined
"undefined"
> typeof function(){return 0;}
"function"

從上面的例子中,應該很好理解。數字類型,無論整數或浮點數,都是回傳number。

而字串因為我們在前幾篇理解了,他是一個基本型別而非物件,因此不會回傳”object”。反而是回傳”string”這樣的基本型別。

而{}物件與陣列[]都回傳了”object”也是可以理解的。但null就需要注意一下,因為null是「沒有物件的非值」,因此他會回傳他原本預期該有值的型別,也就是物件。因此null會回傳的是”object”。

而undefined是一種未定義的內容,因此直接回傳的是”undefined”。

另外function類型也會直接回傳”function”來表示,這個也沒會有太大的問題。

 

instanceof

用法:值或變數 instanceof 建構器

instanceof的回傳值一律回傳boolean值。

instanceof用來判定前運算元是否由後面的建構器所建立出來。

> ({}) instanceof Object;
true
> [] instanceof Object;
true
> [] instanceof array;
true

JavsScript停看聽 (2): null和undefined的區別

JavaScript中用來表示「非值」除了大家所熟知的null之外,還有一個特有的undefined。以下來說明一下這兩者的差別。

null

表示一個物件類型的變數,他應該要有值而沒有值,則會以null來表示。如果你宣告了一個變數,並初始化其值為null,時,使用typeof()來觀察其型別,會發現他自動被視為物件類別。

但這是一個很詭異的事,因為他其實是想說明這個null所指的變數是一個沒有值的物件類型。不是說null是一個物件類型,因為null在前一篇提到他是「基本型別」。


var aNullVal = null;

console.log(aNullVal); // 印出null

console.log(typeof(aNullVal)); // 印出object

undefined

當宣告一個變數,沒有指定任何值時,他就會以undefined存在。或者預期該有參數而少傳(JavaScript是允許這樣的呼叫),對應不到的參數就會以undefined填滿。另外一個是直接使用不存在的屬性值,也會是undefined。


var x;
console.log(x); // 沒有給值的變數,印出undefined

function func(y){
    console.log(y);
}
func(); // 少傳了參數,可呼叫,但會印出undefined

var z = {};
console.log(z.name); // 使用不存在的物件屬性,印出undefined

 

JavsScript停看聽 (1): 變數的類型

JavaScript的變數可以分為兩大類,一是基本型別 (primitive),另一種則是物件 (object)。

基本型別 (primitive)

  • 布林值
  • 數字
  • 字串
  • null
  • undefined

物件 (object)

  • 只要是「非基本型別」,就全部都歸類在物件

基本型別中,包含了一個另人質疑的「字串」在裡面,相信大家寫過其他種程式語言的話,都知道字串一定是物件類型才對呀?!但在JavaScript中,不用懷疑,字串是基本型別。

那麼基本型別的重要特徵為何?

  1. 其特徵之一是進行比較的運算時,比較其值的內容來決定是否相等。
  2. 而第二個特徵是基本型別值無法變更其「屬性」。

聽到這裡應該又出現了第二種質疑,是吧?

既然是基本型別,那又哪來的屬性呢?屬性不是物件才有的嗎?字串在JavaScript中的確與其他語言的特性相當不同,其可以有屬性值,例如length,但是他卻不是物件。像這樣的屬性因其為基本型別而只能讀取無法更改。

因此這個例子應該可以理解

var name = 'Rex';
name.length = 5; // 字串為基本型別,其值無法被修改。 (但這行執行時,並不會報錯)
console.log(name.length);  //還是印出3

物件的重要特徵為何?

  1. 進行比較運算時,以其物件的參考 (reference) 來相比。
  2. 其屬性可進行變更。

這兩點就其他程式語言來說,應該不會有太多的問題。不過JavaScript的屬性是可以直接對該物件進行增減,所以物件除了可以異動已存在的屬性的值外,也可以擴充新屬性,或移除已存在屬性。

var emp = { name: 'Rex' };
emp.name = 'Eric';
emp.empNo = '12345';
console.log(emp);   //印出 Object {name: "Eric", empNo: "12345"}

其name可被修改,以及直接增加了empNo的屬性與值。

以上為JavaScript的兩大變數類型介紹。