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. vue权威指南笔记01——样式的设置

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. python的subprocess模块执行shell命令

    subprocess模块可以允许我们执行shell命令 一般来说,使用run()方法就可以满足大部分情况 使用run执行shell命令 In [5]: subprocess.run('echo &qu ...

  3. redis的一命令

    参考http://redisdoc.com/ 参考http://redis.io/commands 连接操作相关的命令 默认直接连接  远程连接-h 192.168.1.20 -p 6379 ping ...

  4. JS代码段:返回yyyy-mm-dd hh:mm:ss

    最近做项目的时候正好用到,本着能抄就抄的心态去百度搜索现成的代码, 没想到抄下来的好几个都是错的,要么getMonth没有加1,要么10以下的数字前面没有加0, 我真是日了狗了,这次把写好的正确的直接 ...

  5. Linux系统根据端口号来查看其进程并杀死进程

    1.首先是查看某个端口号,(以httpd服务为例) 2.查看此端口下进程以及进程号 3.我们使用awk命令过滤出第二列,即进程号 4.杀死进程 报错的这一行表示,要杀死的进程PID为3754,但是没有 ...

  6. 04LaTeX学习系列之---overleafz在线编辑平台的使用

    目录 目录 前言 (一)overleaf的认识 1.由来: 2.优点: 3.界面: (二)编译与查看 1.选择编译器 2.编译 (三)基本的设置与操作 1.基本设置 2.基本操作 目录 本系列是有关L ...

  7. February 6th, 2018 Week 6th Tuesday

    To be is to be perceived. 存在即被感知. How to interpret this quote? Maybe it means that everything in you ...

  8. SQLite基本操作-----IOS(如有雷同,纯属巧合)

    一.常用方法 sqlite3          *db, 数据库句柄,跟文件句柄FILE很类似 sqlite3_stmt      *stmt, 这个相当于ODBC的Command对象,用于保存编译好 ...

  9. 使用POI读写Word doc文件

    使用POI读写word doc文件 目录 1     读word doc文件 1.1     通过WordExtractor读文件 1.2     通过HWPFDocument读文件 2     写w ...

  10. dp 动态规划 之C - Apple Catching 简单基础

    终于开始写dp了,还很不熟练 It is a little known fact that cows love apples. Farmer John has two apple trees (whi ...