我喜欢JavaScript.它是一门集强大与灵活于一身的语言,当然前提是你得知道如何去正确的使用它.一旦你真正掌握了JavaScript,你几乎可以用它来做任何事情,而且能做的既快又好.
如果你认为JavaScript太简单或者太低级, 那么你已经掉入了一个陷阱. 并且你会发现有很多人已经掉入了这样的陷阱中了.这些所谓的JavaScript开发者也许会告诉你,一些其他的语言 “X” 更好.
他们甚至会说,如果有一个将能将X语言转换为JavaScript的系统,那就太好了.想要逃出这个陷阱一直到真正的掌握JavaScript需要付出很多的努力和贡献.相信我,因为从1997年开始,我就已经在学习JavaScript了.
我是通过学习官方标准文档掌握了JavaScript的所有高级知识,所以你也可以通过这种方法来掌握完整的语言知识.如果你的职称中包含了“JavaScript开发者”,那么你应该这样做.
在本篇博客中,我将会给出一个短小的JavaScript代码片段,然后让你给出这段代码的正确输出.如果你是一个JavaScript开发者,你会发现这样的题目真是太简单了.如果你仍然处在学习这门语言的过程中,你可能会遇到一些困难,不过你可以好好读一下代码下面的解释部分.
下面的JavaScript代码显示了一个弹出框.弹出框中显示什么?
var five = 5;
five.three = 3;
alert(five + five.three);
跳到本篇文章的最后查看正确的答案.接下来是为什么会有这样的结果的解释.
在JavaScript中,一共有六种数据类型: Object, Number, String, Boolean, Null, 以及 Undefined.
对象(Objects)类型包含了数组(arrays), 函数(functions),以及其他的一般对象.数字(Numbers)类型可以是整型(integers)或者浮点数(floating point)类型以及特殊值NaN和Infinity. 字符串(Strings)类型包含了空字符串, ”". 布尔值(Booleans)类型只有两个值:true和false.最后两个基本数据类型有点特殊:Null类型只有一个值:null, Undefined类型只有一个值:undefined.所有Object除外的类型都称之为“原始值(primitive)”.JavaScript变量的类型不是在定义的时候明确指定的,还是在脚本运行的时候自动推断出来的.在上面的代码中,名为five的变量是Number类型的,因为它被赋值了一个Number字面量5.
和其他的计算机编程语言类似,JavaScript也会隐式的将某个值的类型转换成适合对该值进行操作的运算符的类型.和其他语言不同的是, JavaScript会非常积极地做这些转换.比如表达式”5″ – “3″的运算结果是数字2,因为减号运算符会把两边的操作数全部转换为数字再进行运算.如果一个操作数不能被转换为数字,则会使用NaN (“Not a Number”)来替代.例如表达式”5″ – “Fred”会被隐式的转换为5 – NaN,则运算结果也会是NaN.
一套完整的隐式转换规则并不是十分复杂,你只需要知道每个操作符需要什么类型的操作数.
任意值转换为原始值遵循这样的规则“任意值转换为原始值”. 其中对象到原始值的转换比较复杂:如果操作数的类型必须是Number类型,这就意味着JavaScript引擎会调用对象的valueOf()方法,如果返回的结果仍然不是一个原始值,则会调用对象的toString()方法转换成一个String类型的值.如果操作数的类型必须是String类型,则会首先调用对象的toString()方法,如果返回的结果不是一个原始值,再调用对象的valueOf()方法.不管那种情况,如果最终的转换结果仍然不是一个原始值,则抛出异常.
如果操作数的类型必须是一个数字,但该操作数的实际类型是:
Object:
该值首先被转换成原始值,如果转换的结果不是一个数字,则会进入下面的转换分支.
String:
字符串类型的值会按照通常的JavaScript规则转换成数字类型
Boolean:
如果值为true,转换为1,否则转换为0
Null:
0
Undefined:
NaN
如果操作数的类型必须是一个字符串,但该操作数的实际类型是:
Object:
该值首先被转换成原始值,如果转换的结果不是一个字符串,则会进入下面的转换分支.
Number:
数字直接转为字符串, 比如 ”123″ or ”12.34″
Boolean:
转为”true”或”false”
Null:
“null”
Undefined:
“undefined”
如果操作数的类型必须是一个布尔值,但该操作数的实际类型是:
Object:
true
Number:
如果值为0,则转为false,否则转为true (译者注:NaN也会转为false)
String:
如果值为一个空字符串”",则转为false,否则转为true
Null:
false
Undefined:
false
如果操作数的类型必须是一个对象值,但该操作数的实际类型是:
Number:
使用包装对象类型Number来包装原始值,new Number(value)
String:
使用包装对象类型String来包装原始值,new String(value)
Boolean:
使用包装对象类型Boolean来包装原始值,new Boolean(value)
Null:
抛出异常
Undefined:
抛出异常
现在所有的转换规则已经很清晰了,让我们回到最初的例子中.
var five = 5;
five.three = 3;
alert(five + five.three);
正如我们前面提到的,第一行代码创建了一个名为five,类型为Number的变量.
当用属性访问符作用在变量five上时,它的值会转换为Object类型.这种操作称之为“包装”,并且依赖于Number构造函数,该构造函数会产生一个对象,而不是一个原始值.第二行代码实际上等同于下面的代码:
(new Number(five)).three=3;
正如你看到的,我们并没有将新生成的Number对象的引用保存到一个变量中.在该表达式运行过后,被添加three属性的这个对象会被丢弃.而five变量的值没有任何变化.
第三行代码中的表达式five.three会再一次创建一个Number对象.这个新的对象并没有three属性,所以返回了特殊值undefined.结果等同于:
alert(5+undefined);
加法运算符会把两边的操作数全部转换为数字.在本例中,undefined会被转换为NaN,也就成了:
alert(5+NaN);
这也就解释了为什么最后的弹出框中仅仅显示了一个NaN.
(译者注:还差最后一步隐式转换,在进行alert()的时候,NaN会被自动转换为”NaN”)
评论:
jerone:
你这篇文章所讲的内容只是JavaScript的一部分,确切的说只包含了ECMAScript.除了这些,JavaScript还包含了DOM,也就是说还有Node, Element, HTMLElement等更多的数据类型.
作者回复:
Node, Element 以及 HTMLElement 都不是数据类型,就像Array, Date 和 RegExp一样,他们都属于对象(Object)类型.
Marcus Pope:
我认为应该指出正确的编写方式,下面的代码才能够正常运行
var five = new Number(5);
alert(five); //5
five.three = 3;
alert(five + five.three); //8
我还认为最好应该用“number”来指代数字原始值,而用“Number”来指代数字类型的包装对象,这样才能减少读者的困惑.这也是在JavaScript中使用typeof操作符来查看这些值的内部类属性时显示的字符串.(译者注:这里指的是typeof 5 === “number”)
ACRESTAN:
是啊,这篇文章很让人困惑,就是因为作者使用了大写字母开头的类型字符串来描述原始值…
比如这句 “the first line creates a variable called five whose type is Number”,这是不对的!因为你的代码就证明了这一点.
metadings:
还有第七种数据类型,JavaScript中最重要的类型:函数(Function).在JavaScript中,使用函数可以创建新的对象,所以原始值类型和函数类型还是有区别的.
var Project = function () { };
// Project instanceof Function === true
// Project instanceof Object === true
// (typeof Project === 'function') === true
var o = new Project();
// o instanceof Project === true
// o instanceof Object === true
// (typeof Project === 'object') === true
请仔细阅读这个链接http://metadea.de/V/,看看我是怎么写出真正的JavaScript类函数的.(译者注:还有一句没翻译,看不明白,觉的这个人在扯蛋.)
作者回复:
很显然函数在JavaScript中非常重要,值得用单独的文章来讲解它,但函数不是另外一种数据类型.函数只是一种特殊的对象.虽然特殊,但仍是对象.
由许许多多不同的构造函数创建的对象们都继承了同一个类型:Object.这很容易检测到:如果Object(a) === a,则a是一个对象类型,而不是其他原始值类型.
ulu:
这篇文章正好进一步证明了JavaScript不只是一个最强大的语言,同时也是最令人困惑的语言.应该不惜一切代价避免使用它.总有一天,会有一个更人性化的语言集成在所有的主流浏览器中,我希望在我有生之年能够赶上.
作者回复:
当你手中拿着一把锤子时,所有事物看上去都像钉子.当你认为JavaScript很让人迷惑时,任何特性都能成为你的证据.
Dave Chapman :
这种类型的编码错误正是我们为什么需要一个更好的能够在我们码字的同时,在运行代码之前进行语法检查的IDE的原因.比如.在closure compiler中运行例子中的代码会产生如下的警告信息:
“JSC_INEXISTENT_PROPERTY: Property three never defined on Number at line 4 character 13:
alert(five + five.three);”
(译者注:closure compiler是谷歌的代码压缩器或者叫编译器.https://developers.google.com/closure/compiler/)
simonleung :
一个数字可以是“object”类型的,也可以是“number”类型的.
当用在条件语句中或者其他需要转换为布尔值的地方,这两种类型的数字会产生不同的结果,例如.
Number(0)会得到true,因为Number(0)是一个对象,而0显然会得到false.
(译者注:这里他说错了.Number作为函数只是一个类型转换函数,返回的仍然是原始值,只有用new Number(0),Number才算是一个构造函数,返回的才是对象)
另外一个,在null上进行typeof操作,返回”object”,但它不是真正的对象,null是假值,会被转换为false.