zoukankan      html  css  js  c++  java
  • js值----你所不知道的JavaScript系列(6)

    1、数组

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

    看看下面的代码:

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

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

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

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

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

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

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

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

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

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

    var c = [1,2,3,4,5];
    c.length;  // 5
    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; // 14

    2、字符串

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

    var a = "foo";
    var b = ["f","o","o"];
    
    a[1]; // "o";
    b[1]; // "o";
    
    a.length; // 3
    b.length; // 3
    
    a.indexOf( "o" ); // 1
    b.indexOf( "o" ); // 1
    
    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;  // 3
    
    a.length = 2;
    
    a;  // "foo"
    a.length;  // 3
    
    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; // 42

    由于数字值可以使用 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 ); // 2
    }
    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 0void 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(..) 主要用来处理那些特殊的相等比较。 

  • 相关阅读:
    1105 Spiral Matrix (25分)(蛇形填数)
    1104 Sum of Number Segments (20分)(long double)
    1026 Table Tennis (30分)(模拟)
    1091 Acute Stroke (30分)(bfs,连通块个数统计)
    1095 Cars on Campus (30分)(排序)
    1098 Insertion or Heap Sort (25分)(堆排序和插入排序)
    堆以及堆排序详解
    1089 Insert or Merge (25分)
    1088 Rational Arithmetic (20分)(模拟)
    1086 Tree Traversals Again (25分)(树的重构与遍历)
  • 原文地址:https://www.cnblogs.com/slly/p/9330732.html
Copyright © 2011-2022 走看看