第一章 类型
js中的类型
js语言中的类型分为基本类型和对象类型
类型有:(除对象外,其他统称为基本类型)
- 空值(null)
- 未定义(undefined)
- 布尔值(boolean)
- 数字(number)
- 字符串(string)
- 对象(object)
- 符号(symbol,ES6中新增)
如何判别值的类型
typeof运算符可以查看值的类型,他返回的是类型的字符串值,但是这七种值和他们的字符串值并不一一对应
typeof undefined === "undefined"; // true
typeof true === "boolean"; // true
typeof 42 === "number"; // true
typeof "42" === "string"; // true
typeof { life: 42 } === "object"; // true
// ES6中新加入的类型
typeof Symbol() === "symbol"; // true
以上六种判别均正常,但是null例外
typeof null === "object"; // true
我们需要使用复合条件来检测 null 值的类型
var a = null;
(!a && typeof a === "object"); // true
还有一种情况:
typeof function a(){ /* .. */ } === "function"; // true
typeof [1,2,3] === "object"; // true
undefined 和 undeclared
变量在未持有值的时候为 undefined。此时 typeof 返回 "undefined
var a;
typeof a; // "undefined"
var b = 42;
var c;
// later
b = c;
typeof b; // "undefined"
typeof c; // "undefined"
大多数开发者倾向于将 undefined 等同于 undeclared(未声明),但在 JavaScript 中它们完全
是两回事。
已在作用域中声明但还没有赋值的变量,是 undefined 的。
相反,还没有在作用域中声明过的变量,是 undeclared 的。
var a;
a; // undefined
b; // ReferenceError: b is not defined
更让人抓狂的是 typeof 处理 undeclared 变量的方式。例如:
var a;
typeof a; // "undefined"
typeof b; // "undefined"
对于 undeclared(或者 not defined)变量,typeof 照样返回 "undefined"。
请注意虽然 b 是
一个 undeclared 变量,但 typeof b 并没有报错。这是因为 typeof 有一个特殊的安全防范机制。
typeof Undeclared
该安全防范机制对在浏览器中运行的 JavaScript 代码来说还是很有帮助的,
因为多个脚本文件会在共享的全局命名空间中加载变量。
// 这样会抛出错误
if (DEBUG) {
console.log( "Debugging is starting" );
}
// 这样是安全的
if (typeof DEBUG !== "undefined") {
console.log( "Debugging is starting" );
}
//这不仅对用户定义的变量(比如 DEBUG)有用,对内建的 API 也有帮助:
if (typeof atob === "undefined") {
atob = function() { /*..*/ };
}
还有一种不用通过 typeof 的安全防范机制的方法,就是检查所有全局变量是否是全局对象
的属性,浏览器中的全局对象是 window。所以前面的例子也可以这样来实现:
if (window.DEBUG) {
// ..
}
if (!window.atob) {
// ..
}
与 undeclared 变量不同,访问不存在的对象属性(甚至是在全局对象 window 上)不会产生
ReferenceError 错误。
一些开发人员不喜欢通过 window 来访问全局对象,尤其当代码需要运行在多种 JavaScript
环境中时(不仅仅是浏览器,还有服务器端,如 node.js 等),因为此时全局对象并非总是
window。
小结
JavaScript 有 七 种 内 置 类 型:null、undefined、boolean、number、string、object 和
symbol,可以使用 typeof 运算符来查看。
变量没有类型,但它们持有的值有类型。类型定义了值的行为特征。
很多开发人员将 undefined 和 undeclared 混为一谈,但在 JavaScript 中它们是两码事。
undefined 是值的一种。undeclared 则表示变量还没有被声明过。
遗憾的是,JavaScript 却将它们混为一谈,在我们试图访问 "undeclared" 变量时这样报错:ReferenceError: a is not defined,并且 typeof 对 undefined 和 undeclared 变量都返回
"undefined"。
然而,通过 typeof 的安全防范机制(阻止报错)来检查 undeclared 变量,有时是个不错的
办法。
第二章 值
数组
和其他强类型语言不同,在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串、
数字、对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的)
使用 delete 运算符可以将单元从数组中删除,但是请注意,单元删除后,数
组的 length 属性并不会发生变化。第 5 章将详细介绍 delete 运算符。
数组通过数字进行索引,但有趣的是它们也是对象,所以也可以包含字符串键值和属性
(但这些并不计算在数组长度内)
var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; // 1
a["foobar"]; // 2
a.foobar; // 2
这里有个问题需要特别注意,如果字符串键值能够被强制类型转换为十进制数字的话,它
就会被当作数字索引来处理。
var a = [ ];
a["13"] = 42;
a.length; // 14
在数组中加入字符串键值 / 属性并不是一个好主意。建议使用对象来存放键值 / 属性值,
用数组来存放数字索引值。
类数组
有时需要将类数组(一组通过数字索引的值)转换为真正的数组,这一般通过数组工具函数(如 indexOf(..)、concat(..)、forEach(..) 等)来实现。
例如,一些 DOM 查询操作会返回 DOM 元素列表,它们并非真正意义上的数组,但十分
类似。另一个例子是通过 arguments 对象(类数组)将函数的参数当作列表来访问(从
ES6 开始已废止)。
工具函数 slice(..) 经常被用于这类转换:
function foo() {
var arr = Array.prototype.slice.call( arguments );
arr.push( "bam" );
console.log( arr );
}
foo( "bar", "baz" ); // ["bar","baz","bam"]
用 ES6 中的内置工具函数 Array.from(..) 也能实现同样的功能:
...
var arr = Array.from( arguments );
...
字符串
许多数组函数用来处理字符串很方便。虽然字符串没有这些函数,但可以通过“借用”数
组的非变更方法来处理字符串:
var a = "foo";
var b = ["f","o","o"];
a.join; // undefined
a.map; // undefined
var c = Array.prototype.join.call( a, "-" );
var d = Array.prototype.map.call( a, function(v){
return v.toUpperCase() + ".";
} ).join( "" );
c; // "f-o-o"
d; // "F.O.O."
另一个不同点在于字符串反转(JavaScript 面试常见问题)。数组有一个字符串没有的可变更成员函数 reverse():
a.reverse; // undefined
b.reverse(); // ["!","o","O","f"]
b; // ["f","O","o","!"]
可惜我们无法“借用”数组的可变更成员函数,因为字符串是不可变的:
Array.prototype.reverse.call( a );
// 返回值仍然是字符串"foo"的一个封装对象(参见第3章):(
// 在浏览器中会报以下错误
一个变通(破解)的办法是先将字符串转换为数组,待处理完后再将结果转换回字符串:
这种方法的确简单粗暴,但对简单的字符串却完全适用
var c = a
// 将a的值转换为字符数组
.split( "" )
// 将数组中的字符进行倒转
.reverse()
// 将数组中的字符拼接回字符串
.join( "" );
c; // "oof"
数字
数字的语法
var a = 42;
var b = 42.3;
小数点前面的 0 可以省略:
var a = 42.0;
var b = 42.;
默认情况下大部分数字都以十进制显示,小数部分最后面的 0 被省略,如:
var a = 42.300;
var b = 42.0;
a; // 42.3
b; // 42
特别大和特别小的数字默认用指数格式显示,与 toExponential() 函数的输出结果相同。
例如:
var a = 5E10;
a; // 50000000000
a.toExponential(); // "5e+10"
var b = a * a;
b; // 2.5e+21
var c = 1 / a;
c; // 2e-11
由于数字值可以使用 Number 对象进行封装(参见第 3 章),因此数字值可以调用 Number.prototype 中的方法(参见第 3 章)。例如,tofixed(..) 方法可指定小数部分的显示位数:
var a = 42.59;
a.toFixed( 0 ); // "43"
a.toFixed( 1 ); // "42.6"
a.toFixed( 2 ); // "42.59"
a.toFixed( 3 ); // "42.590"
a.toFixed( 4 ); // "42.5900"
请注意,上例中的输出结果实际上是给定数字的字符串形式,如果指定的小数部分的显示
位数多于实际位数就用 0 补齐。