1、数组

在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串、数字、对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的) 。----《你所不知道的JavaScript(中)》P11

看看下面的代码:

var a = [ 1, "2", [3] ];
a.length; //
a[0] === 1; // true
a[2][0] === 3; // true var b = [ ];
b.length; //
b[0] = 1;
b[1] = "2";
b[2] = [ 3 ];
b.length; // 3

对数组声明后即可向其中加入值,不需要预先设定大小 。有一点需要注意的是使用delete标识符删除数组元素的时候,数组的长度不变。

var a = [ 1, "2", [3] ];
delete a[0]; // true
a.length; //
a; // [empty, "2", Array(1)]

在创建“稀疏”数组(sparse array,即含有空白或空缺单元的数组)时也要特别注意:

var a = [ ];
a[0] = 1;
// 此处没有设置a[1]单元
a[2] = [ 3 ];
a[1]; // undefined
a.length; //

上面的代码可以正常运行,但其中的“空白单元”(empty slot)可能会导致出人意料的结果。 a[1] 的值为 undefined,但这与将其显式赋值为 undefined(a[1] = undefined)还是有所区别。 另外,还有这种情况,

var b = new Array(13);
b; // [empty × 13]
b.length; //

数组通过数字进行索引,但有趣的是它们也是对象,所以也可以包含字符串键值和属性(但这些并不计算在数组长度内):

var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; //
a["foobar"]; //
a.foobar; //

数组具有 length 属性,如果修改其 length 属性,会修改到数组的值,所以需要特别谨慎,避免修改到数组的 length 属性。

var c = [1,2,3,4,5];
c.length; //
c; // [1,2,3,4,5]; c.length = 3;
c; // [1,2,3] c.length = 6;
c; // [1, 2, 3, empty × 3]

这里有个问题需要特别注意,如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。

var a = [ ];
a["13"] = 13;
a.length; //

2、字符串

字符串和数组的确很相似,都有 length 属性以及 indexOf(..)(从 ES5开始数组支持此方法)和 concat(..) 方法:

var a = "foo";
var b = ["f","o","o"]; a[1]; // "o";
b[1]; // "o"; a.length; //
b.length; // a.indexOf( "o" ); //
b.indexOf( "o" ); // var c = a.concat( "bar" ); // "foobar"
var d = b.concat( ["b","a","r"] ); // ["f","o","o","b","a","r"] a === c; // false
b === d; // false

从上面看虽然字符串和数组的确有很多相似的地方,但这并不意味着它们都是“字符数组”。

JavaScript 中字符串是不可变的,而数组是可变的。并且 a[1] 在 JavaScript 中并非总是合法语法,在老版本的 IE 中就不被允许(现在可以了)。 正确的方法应该是 a.charAt(1)。

var a = "foo";
a.length; // a.length = 2; a; // "foo"
a.length; // a.charAt(0); // "f"

3、数字

JavaScript 只有一种数值类型: number(数字),包括“整数”和带小数的十进制数。此处“整数”之所以加引号是因为和其他语言不同, JavaScript 没有真正意义上的整数,这也是它一直以来为人诟病的地方。这种情况在将来或许会有所改观,但目前只有数字类型。  ----《你所不知道的JavaScript(中)》P15

JavaScript 中的“整数”就是没有小数的十进制数。所以 42.0 即等同于“整数” 42。

3.1 数字的语法

JavaScript 中的数字常量一般用十进制表示。例如:

var a = 42;
var b = 42.3;

数字前面的 0 可以省略,

var a = 0.42;
var b = .42;

小数点后小数部分最后面的 0 也可以省略,

var a = 42.0;
var b = 42.; //42. 这种写法没问题,只是不常见,但从代码的可读性考虑,不建议这样写。

默认情况下大部分数字都以十进制显示,小数部分最后面的 0 被省略,如:

var a = 42.300;
var b = 42.0;
a; // 42.3
b; //

由于数字值可以使用 Number 对象进行封装,因此数字值可以调用 Number.prototype 中的方法。例如, 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"

上面的方法不仅适用于数字变量,也适用于数字常量。不过对于 . 运算符需要给予特别注意,因为它是一个有效的数字字符,会被优先识别为数字常量的一部分,然后才是对象属性访问运算符。

// 无效语法:
42.toFixed( 3 ); // SyntaxError
// 下面的语法都有效:
(42).toFixed( 3 ); // "42.000"
0.42.toFixed( 3 ); // "0.420"
42..toFixed( 3 ); // "42.000
42 .toFixed(3); // "42.000" 注意.运算符前有空格

42.tofixed(3) 是无效语法,因为 . 被视为常量 42. 的一部分(如前所述),所以没有 . 属性访问运算符来调用 tofixed 方法。

42..tofixed(3) 则没有问题,因为第一个 . 被视为 number 的一部分,第二个 . 是属性访问运算符。只是这样看着奇怪,实际情况中也很少见。在基本类型值上直接调用的方法并不多见,不过这并不代表不好或不对。

3.2 较小的数值

0.1 + 0.2 === 0.3; // false

从数学角度来说,上面的条件判断应该为 true,可结果却是 false 。这个问题相信很多人在刚接触JavaScript的时候或多或少听过或者见过。原因是,二进制浮点数中的 0.1 和 0.2 并不是十分精确,它们相加的结果并非刚好等于0.3,而是一个比较接近的数字 0.30000000000000004,所以条件判断结果为 false。

那么应该怎样来判断 0.1 + 0.2 和 0.3 是否相等呢?

最常见的方法是设置一个误差范围值,通常称为“机器精度”(machine epsilon), 对JavaScript 的数字来说,这个值通常是 2^-52 (2.220446049250313e-16)。从 ES6 开始,该值定义在 Number.EPSILON 中,我们可以直接拿来用,也可以为 ES6 之前的版本写 polyfill:

if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}

可以使用 Number.EPSILON 来比较两个数字是否相等(在指定的误差范围内):

function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCloseEnoughToEqual( a, b ); // true
numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false

能够呈现的最大浮点数大约是 1.798e+308(这是一个相当大的数字),它定义在 Number.MAX_VALUE 中。最小浮点数定义在 Number.MIN_VALUE 中,大约是 5e-324,它不是负数,但无限接近于 0 !

3.3 整数的安全范围

数字的呈现方式决定了“整数”的安全值范围远远小于 Number.MAX_VALUE。能够被“安全”呈现的最大整数是 2^53 - 1,即 9007199254740991,在 ES6 中被定义为Number.MAX_SAFE_INTEGER。最小整数是 -9007199254740991,在 ES6 中被定义为 Number.MIN_SAFE_INTEGER。

Number.isSafeInteger( Number.MAX_SAFE_INTEGER ); // true
Number.isSafeInteger( Math.pow( 2, 53 ) ); // false
Number.isSafeInteger( Math.pow( 2, 53 ) - 1 ); // true

有时 JavaScript 程序需要处理一些比较大的数字,如数据库中的 64 位 ID 等。由于JavaScript 的数字类型无法精确呈现 64 位数值,所以必须将它们保存(转换)为字符串。

3.4 特殊数值

JavaScript 数据类型中有几个特殊的值需要开发人员特别注意和小心使用。

3.4.1 不是值的值

undefined 类型只有一个值,即 undefined。 null 类型也只有一个值,即 null。它们的名称既是类型也是值。

undefined 和 null 常被用来表示“空的”值或“不是值”的值。二者之间有一些细微的差别。例如:

• null 指空值(empty value)
• undefined 指没有值(missing value)
或者:
• undefined 指从未赋值
• null 指曾赋过值,但是目前没有值

null 是一个特殊关键字,不是标识符,我们不能将其当作变量来使用和赋值。然而undefined 却是一个标识符,可以被当作变量来使用和赋值。

3.4.2 undefined

在非严格模式下,我们可以为全局标识符 undefined 赋值:

function foo() {
undefined = 2; // 非常糟糕的做法!
}
foo();
function foo() {
"use strict";
undefined = 2; // TypeError!
}
foo();

在非严格和严格两种模式下,我们可以声明一个名为 undefined 的局部变量(强烈禁止此做法)。

function foo() {
"use strict";
var undefined = 2;
console.log( undefined ); //
}
foo();

注意:永远不要重新定义 undefined。

void 运算符

undefined 是一个内置标识符(除非被重新定义,见前面的介绍),它的值为 undefined,通过 void 运算符即可得到该值。表达式 void ___ 没有返回值,因此返回结果是 undefined。 void 并不改变表达式的结果,只是让表达式不返回值:

var a = 42;
console.log( void a, a ); // undefined 42

我们可以用 void 0 来获得 undefined,当然只用void true 或其他void 表达式也是可以的,void 0、 void 1 、void true 和 undefined 之间并没有实质上的区别。都是得到 undefined。

3.5 特殊的数字

数字类型中有几个特殊的值,下面将详细介绍。

3.5.1 不是数字的数字

如果数学运算的操作数不是数字类型(或者无法解析为常规的十进制或十六进制数字),就无法返回一个有效的数字,这种情况下返回值为 NaN。NaN 意指“不是一个数字”(not a number),这个名字容易引起误会,后面将会提到。将它理解为“无效数值”“失败数值”或者“坏数值”可能更准确些。 或者也可以把NaN理解为“不是数字的数字” 。

var a = 2 / "foo"; // NaN
typeof a === "number"; // true

NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。

NaN 是一个特殊值,它和自身不相等,是唯一一个非自反的值。即NaN==NaN为false,而 NaN != NaN 为 true,很奇怪吧? 那这样的话我们该如何比较和确定某个返回结果是否为NaN呢?

var a = 2 / "foo";
Number.isNaN(a); // true

实际上还有一个更简单的方法,即利用 NaN 不等于自身这个特点。 NaN 是 JavaScript 中唯一一个不等于自身的值。 那么就可以这样判断

isNaN = function(n) {
return n !== n;
};

3.5.2 无穷数

var a = 1 / 0;

上例的结果为 Infinity(即 Number.POSITIVE_INfiNITY)。同样:

var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity

如 果 除 法 运 算 中 的 一 个 操 作 数 为 负 数, 则 结 果 为 -Infinity( 即 Number.NEGATIVE_INfiNITY)。

和纯粹的数学运算不同, JavaScript 的运算结果有可能溢出,此时结果为Infinity 或者 -Infinity。 计算结果一旦溢出为无穷数(infinity)就无法再得到有穷数。换句话说,就是你可以从有穷走向无穷,但无法从无穷回到有穷。

有人也许会问:“那么无穷除以无穷会得到什么结果呢?”我们的第一反应可能会是“1”或者“无穷”,可惜都不是。因为从数学运算和 JavaScript 语言的角度来说, Infinity/Infinity 是一个未定义操作,结果为 NaN。

那么有穷正数除以 Infinity 呢?很简单,结果是 0。有穷负数除以 Infinity 呢?结果是 -0。

3.6 零值

JavaScript 有一个常规的 0(也叫作 +0)和一个 -0。

-0 除了可以用作常量以外,也可以是某些数学运算的返回值。例如:

var a = 0 / -3; // -0
var b = 0 * -3; // -0

负零在开发调试控制台中通常显示为 -0,但在一些老版本的浏览器中仍然会显示为 0。 注意:加法和减法运算不会得到负零(negative zero)。

根据规范,对负零进行字符串化会返回 "0":

var a = 0 / -3;
a; // -0 a.toString(); // "0"
a + ""; // "0"
String( a ); // "0" // JSON也如此
JSON.stringify( a ); // "0"

有意思的是,如果反过来将其从字符串转换为数字,得到的结果是准确的:

+"-0"; // -0
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0

负零转换为字符串的结果令人费解,它的比较操作也是如此:

var a = 0;
var b = 0 / -3;
a == b; // true
-0 == 0; // true
a === b; // true
-0 === 0; // true
0 > -0; // false
a > b; // false

-0===0?那这样我们该如何判断是 0 还是 -0?可以试试以下的方法:

function isNegZero(n) {
n = Number( n );
return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 ); // true
isNegZero( 0 / -3 ); // true
isNegZero( 0 ); // false

3.7 特殊等式

如前所述, NaN 和 -0 在相等比较时的表现有些特别。由于 NaN 和自身不相等,所以必须使用 ES6 中的 Number.isNaN(..)。而 -0 等于 0(对于 === 也是如此),因此我们必须使用 isNegZero(..) 这样的工具函数。好在ES6 中新加入了一个工具方法 Object.is(..) 来判断两个值是否绝对相等,可以用来处理上述所有的特殊情况:

var a = 2 / "foo";
var b = -3 * 0;
Object.is( a, NaN ); // true
Object.is( b, -0 ); // true
Object.is( b, 0 ); // false

上面的 Object.is( ) 我们大致可以这样理解

Object.is = function(v1, v2) {
// 判断是否是-0
if (v1 === 0 && v2 === 0) {
return 1 / v1 === 1 / v2;
}
// 判断是否是NaN
if (v1 !== v1) {
return v2 !== v2;
}
// 其他情况
return v1 === v2;
};

注意:能使用 == 和 === 时就尽量不要使用 Object.is(..),因为前者效率更高、更为通用。 Object.is(..) 主要用来处理那些特殊的相等比较。

js值----你所不知道的JavaScript系列(6)的更多相关文章

  1. js类型----你所不知道的JavaScript系列(5)

    ECMAScirpt 变量有两种不同的数据类型:基本类型,引用类型.也有其他的叫法,比如原始类型和对象类型等. 1.内置类型 JavaScript 有七种内置类型: • 空值(null) • 未定义( ...

  2. 闭包----你所不知道的JavaScript系列(4)

    一.闭包是什么? · 闭包就是可以使得函数外部的对象能够获取函数内部的信息. · 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. · 闭包就 ...

  3. 提升----你所不知道的JavaScript系列(3)

    很多编程语言在执行的时候都是自上而下执行,但实际上这种想法在JavaScript中并不完全正确, 有一种特殊情况会导致这个假设是错误的.来看看下面的代码, a = 2; var a; console. ...

  4. let和const----你所不知道的JavaScript系列(2)

    let 众所周知,在ES6之前,声明变量的关键字就只有var.var 声明变量要么是全局的,要么是函数级的,而无法是块级的. var a=1; console.log(a); console.log( ...

  5. LHS 和 RHS----你所不知道的JavaScript系列(1)

      变量的赋值操作会执行两个动作, 首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后在运行时引擎会在作用域中查找该变量, 如果能够找到就会对它赋值.----<你所不知道的Ja ...

  6. 你所不知道的JavaScript数组

    相信每一个 javascript 学习者,都会去了解 JS 的各种基本数据类型,数组就是数据的组合,这是一个很基本也十分简单的概念,他的内容没多少,学好它也不是件难事情.但是本文着重要介绍的并不是我们 ...

  7. 你所不知道的javascript数组特性

    工作中,我们经常使用js的数组,但是,下面的东西你见过吗? 1,文本下标: var a=[]; a[-1]=1; 你想过数组的下标为负数的情况吗?我们对数组的下标规定从0开始.但是上面那么写也还是可以 ...

  8. JavaScript中你所不知道的Object(二)--Function篇

    上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.Str ...

  9. 一些你所不知道的VS Code插件

    摘要: 你所不知道的系列. 原文:提高 JavaScript 开发效率的高级 VSCode 扩展之二! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 作为一名业余爱好者.专业人员,甚 ...

随机推荐

  1. JavaWeb:jsp

    本文内容: JSP的介绍 jsp的使用 EL表达式 JSTL的使用 首发日期:2018-06-18 JSP的介绍: JSP全称Java Server Pages. 与静态网页格式的html不同的是,j ...

  2. 13.2、进程的通信:Queue、Pipe、

    内容相关: 概念:进程的通信 Queue:创建与使用 Pipe:创建与使用 进程通信的概念 进程的资源空间是相互独立的,一般而言是不能相互访问的.但很多情况下进程间需要互相通信,来完成系统的某项功能. ...

  3. mysql中的utf8mb4、utf8mb4_unicode_ci、utf8mb4_general_ci

    1.utf8与utf8mb4(utf8 most bytes 4) MySQL 5.5.3之后增加了utfmb4字符编码 支持BMP(Basic Multilingual Plane,基本多文种平面) ...

  4. [20171225]没有备份数据文件的恢复.txt

    [20171225]没有备份数据文件的恢复.txt --//别人问的问题,增加了数据文件没有备份,如何恢复,实际上很简单,因为当前控制文件有记录建立时间只要从建立数据文件开始的--//归档日志都存在恢 ...

  5. [20171220]toad plsql显示整形的bug.txt

    [20171220]toad plsql显示整形的bug.txt --//下午有itpub网友反应,一个查询在sqlplus,pl/sql下不同.链接如下:--//http://www.itpub.n ...

  6. charles系列破解激活办法(最高charles4.2都可以激活)

    // Charles Proxy License // 适用于Charles任意版本的注册码,谁还会想要使用破解版呢. // Charles 4.2目前是最新版,可用.   Registered Na ...

  7. plsql备份表---只是表---不包含表数据

    写这个的同时还在备份,表的数据进度很慢,数据太大了. 用的工具是plsql 导出表:点击    tool工具  ---> export user object 导出用户目标  ----> ...

  8. 转:npm安装教程

    一.使用之前,我们先来掌握3个东西是用来干什么的. npm: Nodejs下的包管理器. webpack: 它主要的用途是通过CommonJS的语法把所有浏览器端需要发布的静态资源做相应的准备,比如资 ...

  9. sysfs文件系统的建立【转】

    http://blog.csdn.net/dndxhej/article/details/7434615 对sysfs和设备模型有了解的都会知道sysfs实际是为了将设备模型导出到用户空间的一个内存文 ...

  10. Mysql基础之 基础知识解释

    Mysql基础知识 RDBMS:关系型数据库管理系统.是将数据组织成相关的行和列的系统 存储过程:是存储在数据库中的一段声明性语句.触发器.java.php等都可以调用其存储过程.早期的mysql版本 ...