数组
数组在 JavaScript 中,可以容纳任意类型的值,可以是数字、字符、字符串、甚至是另一个数组。JavaScript 中也没有像很多静态语言中数组的限制。他会自动扩容,这更像是 Java 中的容器,因为已经定义好了,可以让我们不用像 Java 一样选择具体的存储方式,比如到底是使用数组还是链表。
var a = [];
a[0] = "a";
a; //["a"]
a.length; //1
a[2] = 1;
a[1]; //undefined
a; //["a", empty, 1]
a.length; //3
关于数组还有几点:
- 含有空白或空缺元素的数组,称为稀疏数组。
- 使用 delete 可以删除数组中的元素,但是数组的长度并不因此而有变化。它只是被置为空。
- 数组也是对象,它可以像对象一样扩充自己的属性,不过如果添加的属性时,使用的键值可以被转换为相应的十进制 索引 ,它会被当作数组的索引使用。
var a = [];
a[2] = "B";
a.length; //3
类数组
JavaScript 中有很多可以通过索引访问的对象,并且通常也都有 length 属性。不过他们并不是真的数组,它们无法使用 Array.prototype 上面的函数。比如 arguments 对象与很多 DOM 操作返回的 DOM 对象列表。我们可以使用 slice() 方法来创建一个真正的,并且拥有相应元素的数组,另外也可以使用 ES6 新增的 Array.From。
字符串
字符串在很多语言底层都是字符数组,JS中我们不知道它究竟是不是(不过我猜想应该是)。与数组不同,他被设计为不可变的,这和大部分基本类型一样。不过就算是它们的包装类型,也是不可变的。它不会允许你将值的内容改变,它只允许你重新生成并替换。
字符串也是伪数组,它可以使用方括号包装索引的方式来访问其中的元素。不过它依然是不可变的,如果你尝试着修改它,那么所有修改都会静默无效。它本身拥有一些与数组类似的方法。indexOf、concat 等...
var a = "abc";
a[0]; //"a"
a[0] = "b";
a; //"abc";
我们可以通过 Array.prototype 上的一些方法,来对其中的字符进行操作。如 map join 等... 这些操作的结果必须都是返回一个新的数组,而不会修改原来的数组。如果是在原数组上的操作,那么修改将会无效。如 reverse 操作,如果需要的可以使用 split ("") 来将其转换为对应的字符数组,有需要的话再通过 join("") 转换为对应的字符串 (如果不了解操作是否会影响原数组,最好直接使用这种方式)。
Array.prototype.map.call("abc", v => v.toUpperCase()); //["A", "B", "C"]
Array.prototype.join.call("abc", "-"); //"a-b-c"
"abc".split("").reverse().join(""); //"cba"
数值
JS 中只有 number 类型,并没有更细分的类型。它的数字类型是双精度浮点数,也就是一些强类型中的 double,它的字面量有几种写法
var a = 4.0; //4
var b = 4.; //4
var c = .4; //0.4
其中 4. 也是合法的操作,不过容易在一些其他问题上出现问题,像对数字字面量操作属性时
4.toFixed(2); //SyntaxError: Invalid or unexpected token
(4).toFixed(2); //"4.00"
4..toFixed(2); //"4.00"
4 .toFixed(2); //"4.00"
在第三个表达式 4..toFixed(2) 中第一的点被看作数字的字面量,而第二个点,才是对象的属性操作符。注意第四个表达式中间有一个空格。另外还有几张字母量的表达方式
var a = 2.43E2; //243
var b = 2.43e2; //243
var c = 0xf3 //243
var d = 0Xf3 //243
var e = 0363 //243
var f = 0o363 //243
var g = 0O363 //243
var h = 0b11110011 //243
var i = 0B11110011 //243
数字有几个常用的函数
- toFixed 用于让数字保持指定的小数位,它会进行四舍五入,并返回字符串。
- toExponential 用于将数字转换为的科学计数法。
- toPrecision 用于让数字保证指定的位数。
42.1.toFixed(2); //"42.10"
42.1.toFixed(0); //"42"
42.6.toFixed(0); //"43"
500000..toExponential(); //"5e+5"
42.59.toPrecision(1); //"4e+1"
42.59.toPrecision(2); //"43"
42.59.toPrecision(6); //"42.5900"
和大多数浮点数一样,浮点数的计算过程会有机器误差,
0.1 + 0.2 === 0.3; //false
理想中它们应该是相等,为什么会有这样的结果呢? 因为 0.1 + 0.2 的值 其实是 0.30000000000000004。所以条件判断的结果为 false。如果是这样的话,我们有没有什么方式可以判断结果是否相等呢?我们可以使用机器误差 它的值被定义在 Number.EPSILON 中。
function eq(n1, n2) {
return Math.abs(n1 - n2) < Number.EPSILON;
}
eq(0.1 + 0.2, 0.3); //true
另外还有一些常量
Number.MAX_VALUE (能够表达的最大数)
Number.MIN_VALUE (能够表达的最小数)
Number.MAX_SAFE_INTEGER (最大的安全整数)
Number.MIN_SAFE_INTEGER (最小的安全整数)
可以使用 Number.isInteger 来判断一个数是否是整数,Number.isSafeInteger 判断一个数是否是安全整数。
特殊的值
关于数字还有几个特殊的值.
NaN,它通常被译为 Not a Number 但是它其实也是 number 类型
typeof NaN; //"number"
它是一个不等于任何值的值(包括它自己)即自反性。
NaN !== NaN; //true
可以通过 Window.isNaN 来判断一个值是否为 NaN,但是这个函数也将非数字的值认为是 NaN,尽管它是一个 "Not a number."
但是它可能并不是一个 NaN。ES6 中新增的 Number.isNaN 对于其他类型则会认定为 NaN,你也可以使用 v !== v 的方式来判断一个值是否是真正的 NaN,因为只有 NaN 是不等于它自身的。
无穷数,在 JavaScript 中,对于除以 0 或者如果数的计算结果溢出,那么它们将会由两个值来替代。Infinity 与 -Infinity 它们分别被定义在 Number.POSITIVE_INFINITY 与 Number.NEGATIVE_INFINITY 它们分别对应于正无穷大与负无穷大。
计算结果一旦溢出为无穷数,就无法再得到有穷。另外还有几点
- Infinity / Infinity 的值为 NaN
- 有穷正数除以 Infinity 的值为 0
0 与 -0,在一些场景中可能会用到 -0,在乘除运算中可以得到 -0
0 / -3; //-0
0 * -3; //-0
因为 0 与 -0 是相等的如果需要判断是否是 -0。可以通过
function isNegZero(n) {
n = Number(n);
return (n == 0) && (1 / n == -Infinity);
}
isNegZero(0); //false
isNegZero(3 / -0); //true
ES6 中新增了 Object.is 方法来判断两个值是否相等,它可以处理 -0 与 0 以及 NaN 的情况
Object.is(NaN, NaN); //true
Object.is(0, -0); //false
undefined 与 null
undefined 类型只有一个值 undefined
null 类型也只有一个值 null
它们两者有一些细微的差别,一般 undefined 用来表示从未赋值。而 null 则代表赋值了,但现在是空值。null 是一个关键字,不允许被赋值。而undefined 则是一个标识符,可以被重新定义。
function foo() {
undefined = 2;
}
foo();
undefined; //undefined
非严格模式下,可以为全局的 undefined 赋值,好在这并不会影响 undefined 的值。不过如果在一个作用域中声明一个名为 undefined 的变量(这是合法的,纵使是在严格模式下)。那么根据词法作用域的访问规则,结果可能会吓你一跳。
(function(){
"use strict";
var undefined = true;
if ( undefined ) {
console.log("A"); //"A"
}
undefined; //true
})()
void 操作符
void 操作符可以让表达式的值返回为 undefined,无论后面的内容是什么,无论 void true、void 1 它们的结果都为 undefined。如果想让函数的返回值为 undefined 并且提前结束函数的时候会比较有用。
void true; //undefined
void 1; //undefined
值与引用
在其他的一些语言中,可以使用指向指针的指针,也就是引用传递,像 C# 中的 ref 等... 通过这个指针, 我们可以操作指针所指的内容,从而改变另一个变量所指向的内容。而在 JavaScript 中 我们传递的方法,总是值拷贝,只是拷贝的内容可能是一个引用。对于一个新变量的赋值,只是将这个变量所指向的内容给替换掉,并不会影响原来的内容。关于修改另一个变量的值的方式,可以理解为在对所指变量做了一次封装。从而可以改变另一变量的指向。这几乎又和引用类型与值类型的概念类似,只是存储内容的限制被放宽了。
JavaScript 除了 object, 其他内置类型都是值拷贝,而 object 是引用拷贝。注意函数、数组、以及那些自定义的类型。它们都是引用拷贝的方式传递,函数在运行的时候会对参数自动做一次这样的操作。
如果是引用类型,不希望自己被函数的副作用所改变。那么需要将所有引用的内容复制一遍。如果是值类型希望自己被改变,则可以将自己包装成引用类型。