《JavaScript权威指南第7版》第3章 类型、值和变量

计算机程序通过操作值来工作,例如数字3.14或文本“Hello World”。可以在编程语言中表示和操作的这类值称为类型,而编程语言最基本的特征之一是它支持的类型集。当程序需要保留一个值以备将来使用时,它会将该值赋给(或“存储”该值到)变量中。变量有名称,它们允许在程序中使用这些名称来引用值。变量的工作方式是任何编程语言的另一个基本特征。本章介绍JavaScript中的类型、值和变量。它从概述和一些定义开始。

3.1 概述和定义

JavaScript类型可以分为两类:原始类型和对象类型。JavaScript的基本类型包括数字、文本字符串(称为字符串)和布尔真值(称为布尔值)。本章的大部分内容都致力于详细解释JavaScript中的数值(§3.2)和字符串(§3.3)类型。布尔值包含在§3.4中。

特殊的JavaScript值nullundefined是原始值,但它们不是数字、字符串或布尔值。每个值通常被认为是其自身特殊类型的唯一成员。§3.5有更多关于nullundefined的内容。ES6添加了一个新的特殊用途类型,即Symbol,它可以定义语言扩展而不损害向后兼容性。§3.6简要介绍了Symbol

任何不是数字、字符串、布尔值、符号、nullundefined的JavaScript值都是一个对象。对象是属性的集合,其中每个属性都有一个名称和一个值(原始类型或另一个对象)。第3.7节涵盖了一个非常特殊的对象,即全局对象,但第6章会有更详细的介绍。
普通JavaScript对象是命名值的无序集合。该语言还定义了一种特殊类型的对象,称为数组,它表示编号值的有序集合。JavaScript语言包含用于处理数组的特殊语法,数组有一些特殊的行为,这些行为将它们与普通对象区分开来。数组是第7章的主题。

除了基本对象和数组之外,JavaScript还定义了许多其他有用的对象类型。Set对象表示一组值。Map对象表示从键到值的映射。各种“类型化数组”类型有助于对字节数组和其他二进制数据进行操作。RegExp类型表示文本模式,并支持对字符串进行复杂的匹配、搜索和替换操作。Date类型表示日期和时间,并支持基本的日期算术。Error及其子类型表示执行JavaScript代码时可能出现的错误。所有这些类型都在第11章中介绍。
JavaScript与静态语言的不同之处在于,函数和类不仅仅是语言语法的一部分:它们本身就是可以由JavaScript程序操纵的值。与任何不是原始值的JavaScript值一样,函数和类是一种特殊的对象。在第8章和第9章中有详细介绍。

JavaScript解释器为内存管理执行自动垃圾收集。这意味着JavaScript程序员通常不需要担心对象或其他值的破坏或释放。当一个程序无法再引用某个值时,该值不再可访问时,解释器知道该值不能再使用,并自动回收它占用的内存。(JavaScript程序员有时确实需要注意确保值不会在无意中保持可访问性,从而导致长时间不可回收)
JavaScript支持面向对象的编程风格。简单地说,这意味着类型本身定义了处理值的方法,而不是使用全局定义的函数来操作各种类型的值。例如,要对数组a的元素进行排序,我们不将a传递给sort()函数。相反,我们调用的sort()方法是:

a.sort(); // 面向对象版本的 sort(a)。

方法定义见第9章。从技术上讲,只有JavaScript对象有方法。但是数字、字符串、布尔值和符号值的行为就像它们有方法一样。在JavaScript中,nullundefined是唯一不能调用方法的值。
JavaScript的对象类型是可变的,其原始类型是不可变的。可变类型的值可以更改:JavaScript程序可以更改对象属性和数组元素的值。数字、布尔值、符号、nullundefined都是不可变的,例如,讨论更改数字的值都没有意义。字符串可以看作是字符数组,您可能希望它们是可变的。然而,在JavaScript中,字符串是不可变的:您可以访问字符串的任何索引处的文本,但是JavaScript没有提供更改现有字符串文本的方法。可变值和不变值之间的差异将在§3.8中进一步探讨。

JavaScript可以自由地将值从一种类型转换为另一种类型。例如,如果一个程序需要一个字符串,而你给了它一个数字,它会自动将这个数字转换成字符串。如果在需要布尔值的地方使用非布尔值,JavaScript将相应地进行转换。类型转换规则在§3.9中进行了解释。JavaScript的自由类型转换规则影响其相等性的定义,== 相等运算符执行§3.9.1中描述的类型转换。(但是,在实践中,==相等运算符被弃用,取而代之的是严格的相等运算符===,后者不进行类型转换。有关两个操作符的更多信息,请参见§4.9.1。)

常量和变量允许您使用名称来引用程序中的值。常量用const声明,变量用let声明(在旧的JavaScript代码中用var声明)。JavaScript常量和变量是动态类型的:声明没有指定将分配什么类型的值。变量声明和赋值包含在§3.10中。

从这篇冗长的介绍中可以看出,这是一个内容广泛的章节,解释了JavaScript中如何表示和操作数据的许多基本细节。我们将从深入了解JavaScript数字和文本的细节开始。

3.2 数字

JavaScript的主要数字类型Number用于表示整数和近似实数。JavaScript使用IEEE754标准定义的64位浮点格式表示数字,这意味着它可以表示大到±1.7976931348623157×10308和小到±5×10−324的数字。

JavaScript数字格式允许您精确地表示−9007199254740992( −253 )和9007199254740992(253)之间的所有整数。如果使用大于此值的整数值,则可能会丢失后面数字的精度。但是请注意,JavaScript中的某些操作(如数组索引和第4章中描述的按位运算符)是用32位整数执行的。如果需要精确表示较大的整数,请参见§3.2.5

当一个数字直接出现在JavaScript程序中时,它被称为数字字面量。JavaScript支持多种格式的数字字面量,如下节所述。请注意,任何数字前面都可以加一个减号(-)使数字为负数。

3.2.1 数字字面值

在JavaScript程序中,以10为基数的整数被写成一系列数字。例如:

0
3 
10000000

除了以10为基数的整数文本外,JavaScript还可以识别十六进制(基数16)值。十六进制文本以0x0X开头,后跟一个十六进制数字字符串。十六进制数字是0到9或字母A(或A)到f(或f)中的一个,代表10到15的值。以下是十六进制整数文本的示例:

0xff // => 255: (15*16 + 15) 
0xBADCAFE // => 195939070

在ES6及更高版本中,还可以使用前缀0b0o(或0B0O)而不是0x以二进制(以2为基数)或八进制(以8为基)表示整数:

0b10101  // => 21:  (1*16 + 0*8 + 1*4 + 0*2 + 1*1)
0o377    // => 255: (3*64 + 7*8 + 7*1)

3.2.2 浮点字面值

浮点字面值可以有小数点;它们对实数使用传统语法。实值表示为数字的整数部分,后跟小数点和小数部分。

浮点字面值也可以用指数表示法表示:实数后跟字母e(或E),后跟可选的加号或减号,然后是整数指数。这个符号表示实数乘以10的指数幂。
更简洁地说,语法是:

[digits][.digits][(E|e)[(+|-)]digits]

例如:

3.14
2345.6789
.333333333333333333
6.02e23 // 6.02 × 10²³
1.4738223E-32 // 1.4738223 × 10⁻³²

数字文字中的分隔符
您可以在数字文本中使用下划线将长文本拆分为更易于阅读的块:

let billion = 1_000_000_000;//下划线为千位分隔符。
let bytes = 0x89_AB_CD_EF;//作为字节分隔符。
let bits = 0b0001_1101_0111;//作为半字节分隔符。
let fraction = 0.123_456_789;//也适用于小数部分。

在2020年初撰写本文时,数字文本中的下划线尚未作为JavaScript的一部分正式标准化。但它们处于标准化过程的高级阶段,并且由所有主流浏览器和 Node 实现。

3.2.3 JavaScript中的算术

JavaScript程序使用语言所提供的算术运算符处理数字。其中包括 + 表示加法,- 表示减法,* 表示乘法,/ 表示除,以及 % 表示模(除后的余数)。ES2016为指数增加了 **。有关这些和其他操作员的详细信息,请参阅第4章。

除了这些基本的算术运算符之外,JavaScript还通过一组定义为Math对象属性的函数和常量来支持更复杂的数学运算:

Math.pow(2,53)//=>9007199254740992:2到53的幂
Math.round(.6)//=>1.0:四舍五入到最接近的整数
Math.ceil(.6)//=>1.0:向上取整为整数
Math.floor(.6)//=>0.0:向下舍入为整数
Math.abs(-5)//=>5:绝对值
Math.max(x, y, z)//返回最大参数
Math.min(x, y, z)//返回最小参数
Math.random()//伪随机数x,其中0<=x<1.0
Math.PI //π:圆的周长/直径
Math.E // 自然对数的底
Math.sqrt(3) //=>3**0.5:3的平方根
Math.pow(3, 1/3)//=>3**(1/3):3的立方根
Math.sin(0)//三角法:同时Math.cos, Math.atan等等。
Math.log(10) //10的自然对数
Math.log(100)/Math.LN10 //100的以10为底的对数
Math.log(512)/Math.LN2 //以2为底的512对数
Math.exp(3//Math.E立方

ES6 在 Math 对象上定义了更多函数:

Math.cbrt(27)//=>3:立方根
Math.hypot(3, 4)//=>5:所有参数平方和的平方根
Math.log10(100)//=>2:以10为底的对数
Math.log2(1024)//=>10:以2为底的对数
Math.log1p(x) //自然对数(1+x);非常小的x精确
Math.expm1(x) //Math.exp(x) -1;与Math.log1p()相反
Math.sign(x) //-1、0或1表示参数<、==或>0
Math.imul(2,3)//=>6:32位整数的优化乘法
Math.clz32(0xf)//=>28:32位整数中前导零位的数目
Math.trunc(3.9)//=>3:通过截断小数部分转换为整数
Math.fround(x) //四舍五入到最接近的32位浮点数
Math.sinh(x) //双曲正弦。也Math.cosh(), Math.tanh()
Math.asinh(x) //双曲反正弦。也Math.acosh(), Math.atanh()

JavaScript中的算术在溢出、下溢或被零除的情况下不会引发错误。当数值运算的结果大于最大可表示数(溢出)时,结果是一个特殊的无穷大值,即Infinity。同样,当一个负值的绝对值大于最大可表示负数的绝对值时,结果是负无穷大,-Infinity。无穷大值 的行为与您预期的一样:加、减、乘或除以任何值都会得到一个无穷大的值(可能正负号会变反)。

当数值运算的结果比最小的可表示数字更接近于零时,就会发生下溢。在本例中,JavaScript返回0。如果下溢是由负数引起的,JavaScript会返回一个称为“负零”的特殊值,这个值与普通的零几乎没有区别,JavaScript程序员很少需要注意到它。

在JavaScript中被零除不是错误:它只返回无穷大或负无穷大。但是有一个例外:零除以零没有一个定义良好的值,这个操作的结果是一个特殊的非数字值NaN。如果试图用无穷大除以无穷大,取负数的平方根,或者使用算术运算符来处理无法转换为数字的非数字操作数,也会出现NaN

JavaScript预定义全局常量 InfinityNaN 来保存正无穷大和非数字值,这些值也可用作 Number 对象的属性:

Infinity // 正无穷大
Number.POSITIVE_INFINITY // 同样是正无穷大
1/0 // => Infinity
Number.MAX_VALUE * 2 // => Infinity; 溢出
-Infinity // 负无穷大
Number.NEGATIVE_INFINITY // 负无穷大
-1/0 // => -Infinity
-Number.MAX_VALUE * 2 // => -Infinity
NaN // 非数字值
Number.NaN // NaN的另一种写法
0/0 // => NaN
Infinity/Infinity // => NaN
Number.MIN_VALUE/2 // => 0: 下溢
-Number.MIN_VALUE/2 // => -0: 负零
-1/Infinity // -> -0: 负零
-0
//以下 Number 属性在ES6中定义
Number.parseInt() // 相当于全局的 parseInt() 函数
Number.parseFloat() // 相当于全局的 parseFloat() 函数
Number.isNaN(x) // x 是否是 NaN?
Number.isFinite(x) // x 是否是一个无穷大的数字?
Number.isInteger(x) // x 是否是个整数?
Number.isSafeInteger(x) // x 是否是个整数,且 -(2**53) < x < 2**53?
Number.MIN_SAFE_INTEGER // => -(2**53 - 1)
Number.MAX_SAFE_INTEGER // => 2**53 - 1
Number.EPSILON // => 2**-52: 数字之间的最小差异

NaN值在JavaScript中有一个不寻常的特性:它与任何其他值(包括它本身)相比都不相等。这意味着您不能编写x === NaN来确定变量x的值是否为NaN。相反,你必须写x != x 或者 Number.isNaN(x)。只有 x 确实是 NaN的时候,这些表达式的值才为真。

全局函数isNaN()类似于 Number.isNaN(). 如果其参数为NaN,或者该参数是无法转换为数字的非数字值,则返回true。相关函数 Number.isFinite() 如果其参数不是NaNInfinity-Infinity的数字,则返回true。全局isFinite() 函数的参数是或可以转换为有限数,则返回true

译者注,isNaN功能类似就不看了,看看isFinite有什么区别吧:

console.log(Number.isFinite('5')) // false  虽然'5'可以转换为数字,但是仍然返回false 
console.log(isFinite('5')) // true,  全局的isFinite,如果参数可以转换为数字,返回true 

负零值也有点不寻常。它与正零比较是相等的(甚至使用JavaScript的严格相等性测试 ===),这意味着这两个值几乎无法区分,除非用作除数:

let zero = 0; // 普通0
let negz = -0; // 负0
zero === negz // => true: 正0与负0相等
1/zero === 1/negz // => false: Infinity 和 -Infinity 不相等

3.2.4 二进制浮点和舍入错误

实数有无穷多,但只有有限个数(确切地说是18437736874454810627)可以用JavaScript浮点格式精确地表示。这意味着在JavaScript中使用实数时,数字的表示通常是实际数字的近似值。

JavaScript(以及几乎所有其他现代编程语言)使用的IEEE-754浮点表示是一种二进制表示,它可以精确地表示1/2、1/8和1/1024等分数。不幸的是,我们最常用的分数(尤其是在进行财务计算时)是小数:1/10、1/100,等等。二进制浮点表示法不能精确表示0.1这样简单的数字。

JavaScript数字有足够的精度,可以非常接近0.1。但是,这个数字不能准确地表示出来,这可能会导致问题。考虑以下代码:

let x = .3 - .2; // 30美分减去20美分
let y = .2 - .1; // 20美分减去10美分
x === y // => false: 这两个值不一样!
x === .1 // => false: .3-.2不等于.1
y === .1 // => true: .2-.1等于.1

由于舍入误差,.3和.2近似值之间的差异与.2和.1近似值之间的差异不完全相同。重要的是要理解这种问题不只是Javascript才有的。另外,请注意这里显示的代码中的值xy非常接近,并且接近正确的值。计算出的值对于几乎任何目的都是足够的;只有当我们试图比较相等性时才会出现问题。

如果这些浮点近似值不适合你的程序,请考虑使用放大后的整数。例如,您可以将货币值表示为整数美分,而不是分数美元。

3.2.5 具有任意精度的整数BigInt

JavaScript在ES2020中定义的最新特性之一是一种称为BigInt的新数值类型。到2020年初,它已经在Chrome、Firefox、Edge和Node中实现,Safari也在进行中。顾名思义,BigInt是一种数值类型,其值为整数。JavaScript中添加该类型主要是为了允许表示64位整数,这是与许多其他编程语言和api兼容所必需的。但是BigInt值可以有数千甚至数百万个数字,如果您需要处理那么大的数字。(但是请注意,BigInt实现不适合加密因为它们不试图阻止时序攻击。)

BigInt文本以数字字符串形式写入,后跟小写字母n。默认情况下,以10为基数,但可以使用0b0o0x前缀表示二进制、八进制和十六进制BigInt

1234n //一个不大的BigInt文本
0b111111n //二进制BigInt
0o7777n //八进制BigInt
0x80000000000000n //=>2n**63n:64位整数

可以将BigInt()用作将常规JavaScript数字或字符串转换为BigInt值的函数:

BigInt(Number.MAX_SAFE_INTEGER) // => 9007199254740991n
let string = "1" + "0".repeat(100); // 1 后面跟100个0.
BigInt(string) // => 10n**100n: 一个古戈尔

BigInt值的算术与使用常规JavaScript数字的算术类似,只是除法会删除任何余数并向下取整(接近零):

1000n + 2000n // => 3000n
3000n - 2000n // => 1000n
2000n * 3000n // => 6000000n
3000n / 997n // => 3n: 商是3
3000n % 997n // => 9n: 倒数 9
(2n ** 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值