跟小组里一自称小方方的卖萌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厮守了两年多的老菜鸟,经常也被弄得稀里糊涂
简单总结一下:
- 方式一、方式二本质相同
- 方式三、方式四、方式五本质相同
最后的最后:
文中示例如有错漏,请指出;如觉得文章对您有用,可点击“推荐” :)