《你不知道的JavaScript-中卷》第一部分-类型和语法-笔记-2-值

数组(array)、字符串(string)和数字(number)是一个程序最基本的组成部分

2.1 数组

在 JavaScript 中,数组可以容纳任何类型的值

js 复制代码
var a = [ 1, "2", [3] ];
a.length; // 3
a[0] === 1; // true
a[2][0] === 3; // true

对数组声明后即可向其中加入值,不需要预先设定大小。(js的数组,在其他语言中一般是动态数组或者list)

js 复制代码
var a = [ ];
a.length; // 0
a[0] = 1;
a[1] = "2";
a[2] = [ 3 ];
a.length; // 3

"稀疏"数组(sparse array,即含有空白或空缺单元的数组)时要特别注意。

js 复制代码
var a = [ ];

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

a[1]; // undefined
a.length; // 3

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

js 复制代码
var a = [ ];

a[0] = 1;
a["foobar"] = 2;

a.length; // 1

a["foobar"]; // 2
a.foobar; // 2

如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理

js 复制代码
var a = [ ];
a["13"] = 42;
a.length; // 14

类数组

有时需要将类数组(一组通过数字索引的值)转换为真正的数组

  • 工具函数 slice(..)
  • 工具函数 Array.from(..)
js 复制代码
function foo() {
    var arr = Array.prototype.slice.call(arguments);
    arr.push("bam");
    console.log(arr);
}
foo("bar", "baz"); // ["bar","baz","bam"]   

// 
function foo2() {
    var arr = Array.from(arguments);
    arr.push("bam");
    console.log(arr);
}
foo2("bar", "baz"); // ["bar","baz","bam"]

2.2 字符串

字符串和数组的确很相似,它们都是类数组

  • 都有 length 属性以及 indexOf(..)(从 ES5开始数组支持此方法)和 concat(..) 方法
js 复制代码
        var a = "foo";
        var b = ["f", "o", "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

        a; // "foo" 
        b; // ["f","o","o"]

字符串是不可变的,数组是可变的

  • 字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。
  • 数组的成员函数都是在其原始值上进行操作
js 复制代码
a[1] = "O";
b[1] = "O";
a; // "foo"
b; // ["f","O","o"]


c = a.toUpperCase();
a === c; // false

a; // "foo"
c; // "FOO"

b.push( "!" );
b; // ["f","O","o","!"]

"借用"数组的非变更方法来处理字符串

js 复制代码
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."

字符串的反转

  • 字符串是不可变的,需要拆分成数组后进行
js 复制代码
var c = a
        // 将a的值转换为字符数组
        .split( "" )
        // 将数组中的字符进行倒转
        .reverse()
        // 将数组中的字符拼接回字符串
        .join( "" );

c; // "oof"

2.3 数字

JavaScript 只有一种数值类型:number(数字),包括"整数"和带小数的十进制数

  • JavaScript 没有真正意义上的整数
  • JavaScript 中的"整数"就是没有小数的十进制数。所以 42.0 即等同于"整数"42
  • JavaScript 使用的是"双精度"格式(即 64 位二进制)

2.3.1 数字的语法

js 复制代码
// 数字常量一般用十进制表示
var a = 42;
var b = 42.3;

// 数字前面的 0 可以省略:
var a = 0.42;
var b = .42;

// 小数点后小数部分最后面的 0 也可以省略
var a = 42.0;
var 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 对象

  • tofixed(..) 方法可指定小数部分的显示位数
  • toPrecision(..) 方法用来指定有效数位的显示位数
js 复制代码
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"
console.log(typeof a.toFixed(0));//"string"

a.toPrecision(1); // "4e+1"
a.toPrecision(2); // "43"
a.toPrecision(3); // "42.6"
a.toPrecision(4); // "42.59"
a.toPrecision(5); // "42.590"
a.toPrecision(6); // "42.5900"

使用.操作符时,会被优先识别为数字常量的一部分,然后才是对象属性访问运算符

js 复制代码
// 无效语法:
42.toFixed(3); // SyntaxError

// 下面的语法都有效:
(42).toFixed(3); // "42.000"
0.42.toFixed(3); // "0.420"

//因为第一个 . 被视为 number 的一部分,第二个 . 是属性访问运算符。
42..toFixed(3); // "42.000" 

//请注意其中的空格
42 .toFixed(3); // "42.000"

指数形式

js 复制代码
var onethousand = 1E3; // 即 1 * 10^3
var onemilliononehundredthousand = 1.1E6; // 即 1.1 * 10^6

二进制、八进制和十六进制

less 复制代码
var a = 0xf3; // 243的十六进制
var a = 0Xf3; // 同上
var a = 0o363; // 243的八进制

var a = 0363; //从 ES6 开始 不再允许这样的八进制表示法

// ES6 支持以下新格式:
0o363; // 243的八进制
0O363; // 同上
0b11110011; // 243的二进制
0B11110011; // 同上

2.3.2 较小的数值

二进制浮点数最大的问题在于相等比较

js 复制代码
0.1 + 0.2 === 0.3; // false

二进制浮点数中的 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 中

js 复制代码
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

2.3.3 整数的安全范围

"整数"的安全值范围远远小于 Number.MAX_VALUE

  • 能够被"安全"呈现的最大整数是 2^53 - 1,即 9007199254740991,在 ES6 中被定义为Number.MAX_SAFE_INTEGER。
  • 最小整数是 -9007199254740991,在 ES6 中被定义为 Number.MIN_SAFE_INTEGER
  • 处理一些比较大的数字,使用字符串

2.3.4 整数检测

  • ES6 中的 Number.isInteger(..) 方法
  • 要检测一个值是否是安全的整数,可以使用 ES6 中的 Number.isSafeInteger(..) 方法
js 复制代码
Number.isInteger(42); // true
Number.isInteger(42.000); // true
Number.isInteger(42.3); // false

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

ES5中的方法

js 复制代码
if (!Number.isInteger) {
    Number.isInteger = function(num) {
        return typeof num == "number" && num % 1 == 0;
    };
}


if (!Number.isSafeInteger) {
    Number.isSafeInteger = function(num) {
        return Number.isInteger( num ) &&
        Math.abs( num ) <= Number.MAX_SAFE_INTEGER;
    };
}

2.3.5 32 位有符号整数

a | 0 可以将变量 a 中的数值转换为 32 位有符号整数

2.4 特殊数值

不是值的值

  • undefined 类型只有一个值,即 undefined
  • null 类型也只有一个值,即 null
  • null 指空值(empty value), 指曾赋过值,但是目前没有值
  • undefined 指没有值(missing value),指从未赋值
  • null 是一个特殊关键字,不是标识符,我们不能将其当作变量来使用和赋值
  • undefined 却是一个标识符,可以被当作变量来使用和赋值!!!

2.4.2 undefined

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

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

通过 void 运算符即可得到该值undefined

  • void 并不改变表达式的结果,只是让表达式不返回值
  • void 运算符在其他地方也能派上用场,比如不让表达式返回任何结果
js 复制代码
var a = 42;
console.log(void a, a); // undefined 42
js 复制代码
function doSomething() {
    // 注: APP.ready 由程序自己定义
    if (!APP.ready) {
        // 稍后再试
        return void setTimeout( doSomething,100 );
    }
    var result;
    // 其他
    return result;
}

// 现在可以了吗?
if (doSomething()) {
    // 立即执行下一个任务
}

//或者

if (!APP.ready) {
    // 稍后再试
    setTimeout( doSomething,100 );
    return;
}

2.4.3 特殊的数字

1. 不是数字的数字 NaN, 如果数学运算的操作数不是数字类型,就无法返回一个有效的数字

  • "不是数字的数字"仍然是数字类型
  • NaN 是一个"警戒值",用于指出数字类型中的错误情况
  • NaN 是一个特殊值,它和自身不相等,是唯一一个非自反的值,而 NaN != NaN 为 true
  • 使用内建的全局工具函数 isNaN(..) 来判断一个值是否是 NaN。
    • isNaN(..) 有一个严重的缺陷,它的检查方式过于死板,就是"检查参数是否不是 NaN,也不是数字"
js 复制代码
var a = 2 / "foo"; // NaN
typeof a === "number"; // true

a == NaN; // false
a === NaN; // false

isNaN( a ); // true

var b = "foo";
window.isNaN( b ); // true------晕!

2. 无穷数

  • 正无穷
  • 负无穷
js 复制代码
var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity

3. 零值

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

为什么需要负零

  • 有些应用程序中的数据需要以级数形式来表示(比如动画帧的移动速度),数字的符号位(sign)用来代表其他信息(比如移动的方向)
js 复制代码
// -0 除了可以用作常量以外,也可以是某些数学运算的返回值
var a = 0 / -3;
var b = 0 * -3;
console.log(a, b);//-0 -0

// 根据规范,对负零进行字符串化会返回 "0
console.log(a.toString());//0
console.log(a + "");//0
console.log(String(a));//0
console.log(JSON.stringify(a));//0

//如果反过来将其从字符串转换为数字,得到的结果是准确的
console.log(+"-0");//-0
console.log(Number("-0"));//-0
console.log(JSON.parse("-0"));//-0

// javascript 0和-0相等
var a = 0;
var b = 0 * -3;
console.log(a === b);//true
console.log(-0 === 0);//true

// 检测负零
function isNegZero(n) {
    n = Number(n);
    return n === 0 && 1 / n === -Infinity;
}
console.log(isNegZero(-0));//true
console.log(isNegZero(0 / -3));//true
console.log(isNegZero(0));//false

2.4.4 特殊等式

ES6 中新加入了一个工具方法 Object.is(..) 来判断两个值是否绝对相等

js 复制代码
var a = 2 / "foo";
var b = -3 * 0;
console.log(Object.is(a, NaN));// true
console.log(Object.is(b, -0)); // true
console.log(Object.is(b, 0)); // false

2.5 值和引用

JavaScript 对值和引用的赋值 / 传递在语法上没有区别,完全根据值的类型来决定

  • 简单值(即标量基本类型值,scalar primitive)总是通过值复制的方式来赋值 / 传递,包括null、undefined、字符串、数字、布尔和 ES6 中的 symbol。
  • 复合值(compound value)------对象(包括数组和封装对象)和函数,则总是通过引用复制的方式来赋值 / 传递。
相关推荐
斟的是酒中桃12 分钟前
基于Transformer的智能对话系统:FastAPI后端与Streamlit前端实现
前端·transformer·fastapi
烛阴30 分钟前
Fract - Grid
前端·webgl
JiaLin_Denny1 小时前
React 实现人员列表多选、全选与取消全选功能
前端·react.js·人员列表选择·人员选择·人员多选全选·通讯录人员选择
brzhang1 小时前
我见过了太多做智能音箱做成智障音箱的例子了,今天我就来说说如何做意图识别
前端·后端·架构
为什么名字不能重复呢?1 小时前
Day1||Vue指令学习
前端·vue.js·学习
eternalless2 小时前
【原创】中后台前端架构思路 - 组件库(1)
前端·react.js·架构
Moment2 小时前
基于 Tiptap + Yjs + Hocuspocus 的富文本协同项目,期待你的参与 😍😍😍
前端·javascript·react.js
Krorainas2 小时前
HTML 页面禁止缩放功能
前端·javascript·html
whhhhhhhhhw3 小时前
Vue3.6 无虚拟DOM模式
前端·javascript·vue.js
鱼樱前端3 小时前
rust基础(一)
前端·rust