跟小組里一自稱小方方的賣萌90小青年聊天,IT男的壞習慣,聊着聊着就扯到技術上去了,小方方突然問
1、聲明一個數值類型的變量我看到三種,區別在哪:
var num = 123; //方式一 var num = Number(123); var num = new Number(123);
2、方式一明明是個數字字面量,為毛平常我們可以直接在上面調用各種方法,如下:
var num = 123; console.log(num.toString());
我嘴角微微一笑:少年你還嫩了點,哪止三種,我知道的至少有五種!!
笑着笑着嘴角開始抽搐,額角開始冒出了冷汗:至少有五種,沒錯,但是。。。區別在哪。。。
懷着老菜鳥特有的矜持和驕傲,我不屑地說:這都不知道,自己查資料去。。。轉過身,開始翻ECMAS - 262(第五版)
一、五種聲明數值類型變量的方式
//方式一:最常見的方式,通過數字字面量方式聲明
var num = 123;
//方式二:偶爾使用方式,大部分情況下是將字符串轉成數字 var num = Number(123);
//方式三:很少使用,各神書,包括犀牛書,都將其列入不推薦方式 var num = new Number(123);
//方式四:神方式,目前還沒見過人使用 var num = new Object(123);
//方式五:更離奇,更詭異 var num = Object(123);
可以看到,在上5種聲明方式種,方式一不用怎么說了,平常都是這樣用的;方式三 to 方式五屬於比較的使用,下文會分別說明:
- 五種聲明方式的區別?當你用顫巍巍的手指敲下代碼后,究竟發生了神馬?
- 方式一聲明的明明不是對象,但為什么平常我們可以在上面調用方法,如toString等?
二、各種聲明方式之間的區別
方式一:var num = 123;
EC5說明:
A numeric literal stands for a value of the Number type. This value is determined in two steps: first, a mathematical value (MV) is derived from the literal; second, this mathematical value is rounded as described below//.....
個人總結摘要:
- 解析變量的值,比如說取出整數部分、小數部分等,因為數字聲明方式還可以為num = .123,num = 123e4等形式
- 對解析出來的值取近似值,比如num = 123.33333333333333...3333333333333333333333333....,這個時候就要取近似值了,具體取近似則規則不展開
- 此種方式聲明的變量,只是個簡單的數字字面量,並不是對象(至於為什么可以在上面調用toString等方法,后文講解)
方式二:var num = Number(123);
EC5說明:
15.7.1 The Number Constructor Called as a Function
When Number is called as a function rather than as a constructor, it performs a type conversion. 15.7.1.1 Number ( [ value ] )
Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else returns +0.
個人總結摘要:
- 此處只是將Number當作一個普通的函數來調用,而不是構造方法,因此返回的不是對象,而是一個簡單的數值
- 本質與方式一相同;相對於方式一的區別在於,需要針對傳入參數的類型,執行不同的類型轉換過程,試圖將參數解析成對應的數值,具體規則如下:
方式三:var num = new Number(123);
15.7.2 The Number Constructor
When Number is called as part of a new expression it is a constructor: it initialises the newly created object. 15.7.2.1 new Number ( [ value ] )
The [[Prototype]] internal property of the newly constructed object is set to the original Number prototype object, the one that is the initial value of Number.prototype (15.7.3.1).
The [[Class]] internal property of the newly constructed object is set to "Number".
The [[PrimitiveValue]] internal property of the newly constructed object is set to ToNumber(value) if value wassupplied, else to +0.
The [[Extensible]] internal property of the newly constructed object is set to true.
- 此處將Number作用構造方法調用,返回的是Number類型的對象,該對象能夠訪問Number的原型屬性以及方法;這樣說可能有些迷惑,后面會說到
-
var num = new Number(123); console.log(typeof num); //輸出:object console.log(Object.prototype.toString.call(num)); //輸出:[object Number]
- 返回的Number類型對象內部的原始值( [[PrimitiveValue]]),為經過類型轉換后獲得的數字值,具體轉換規則與方式二提到的一致
方式四:var num = new Object(123);
15.2.2 The Object Constructor
When Object is called as part of a new expression, it is a constructor that may create an object.15.2.2.1 new Object ( [ value ] )
When the Object constructor is called with no arguments or with one argument value, the following steps are taken:
If value is supplied, then
a. If Type(value) is Object, then
If the value is a native ECMAScript object, do not create a new object but simply return value.
If the value is a host object, then actions are taken and a result is returned in an implementation-dependent manner that may depend on the host object.
b. If Type(value) is String, return ToObject(value).c. If Type(value) is Boolean, return ToObject(value).d. If Type(value) is Number, return ToObject(value).Assert: The argument value was not supplied or its type was Null or Undefined.
Let obj be a newly created native ECMAScript object.
Set the [[Prototype]] internal property of obj to the standard built-in Object prototype object (15.2.4).
Set the [[Class]] internal property of obj to "Object".
Set the [[Extensible]] internal property of obj to true.
Set all the internal methods of obj as specified in 8.12.
Return obj.
- 傳遞了參數,且參數是一個數字,則創建並返回一個Number類型的對象 —— 沒錯,其實等同於方式三
- 該Number對象的值等於傳入的參數,內部的[[prototype]]屬性指向Number.prototype
方式五:var num = Object(123);
EC5說明:
15.2.1 The Object Constructor Called as a Function
When Object is called as a function rather than as a constructor, it performs a type conversion.
15.2.1.1 Object ( [ value ] )
When the Object function is called with no arguments or with one argument value, the following steps are taken:
If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard built-in Object constructor had been called with the same arguments (15.2.2.1).
Return ToObject(value).
- 當傳入的參數為空、undefined或null時,等同於 new Object(param),param為用戶傳入的參數
- 否則,返回一個對象,至於具體轉換成的對象類型,可參見下表;具體到上面的例子,本質等同於new Number(123):
3. 簡單測試用例
var num = Object(123); console.log(typeof num); //輸出:object
console.log(Object.prototype.toString.call(num)); //輸出:[object Number]
三、var num = 123; 與 var num = new Number(123);
各位先賢哲們告誡我們最好用第一種方式,理由成分,擲地有聲:效率低,eval(num)的時候可能有意外的情況發生。。。巴拉巴拉
拋開上面的雜音,我們這里要關注的是,為什么下面的語句不會出錯:
var num = 123; console.log(num.toString(num)); //輸出:'123',竟然沒出錯
console.log(num.toString()); //輸出:'123',竟然沒出錯——面刪掉的例子在toString那誤加了個參數num,筆誤,感謝dino指出 :)
不是說字面量方式聲明的只是普通的數值類型,不是對象嗎?但不是對象哪來的toString方法調用,這不科學!
好吧,查了下犀牛書,找到了答案:
當用戶通過字面量方式聲明一個變量,並在該變量上調用如toString等方法,JS腳本引擎會偷偷地創建該變量對應的包裝對象,並在該對象上調用對應的方法;當調用結束,則銷毀該對象;這個過程對於用戶來說是不可見的,因此不少初學者會有這方面的困惑。
好吧,我承認上面這段話並不是原文內容,只是個人對犀牛書對應段落的理解,為了顯得更加專業權威故意加了引用標識。。。上面舉的那個例子,可以簡單看作下面過程:
var num = 123; var tmp = num; num = new Number(num); console.log(num.toString(num)); num = tmp;
(因為昨晚翻規范翻到快1點,實在困的不行,就偷懶了,相信犀牛書不會坑我)
四、寫在后面
Javascript的變量聲明方式、類型判斷等,一直都覺得無力吐槽,上面的內容對於初學者來說,無異於毀三觀;即使對於像本人這樣已經跟Javascript廝守了兩年多的老菜鳥,經常也被弄得稀里糊塗
簡單總結一下:
- 方式一、方式二本質相同
- 方式三、方式四、方式五本質相同
最后的最后:
文中示例如有錯漏,請指出;如覺得文章對您有用,可點擊“推薦” :)