首页 > 编程语言 >写给不耐烦程序员的 JavaScript 指南(二)

写给不耐烦程序员的 JavaScript 指南(二)

时间:2024-01-23 14:55:07浏览次数:26  
标签:运算符 JavaScript number 整数 写给 程序员 true Math

第四部分:原始值

原文:exploringjs.com/impatient-js/pt_primitive-values.html

译者:飞龙

协议:CC BY-NC-SA 4.0

下一步:14 非值 undefinednull

十四、非值的 undefined 和 null

原文:exploringjs.com/impatient-js/ch_undefined-null.html

译者:飞龙

协议:CC BY-NC-SA 4.0


  • 14.1 undefined vs. null

  • 14.2 Occurrences of undefined and null

    • 14.2.1 Occurrences of undefined

    • 14.2.2 Occurrences of null

  • 14.3 Checking for undefined or null

  • 14.4 The nullish coalescing operator (??) for default values [ES2020]

    • 14.4.1 Example: counting matches

    • 14.4.2 Example: specifying a default value for a property

    • 14.4.3 Using destructuring for default values

    • 14.4.4 Legacy approach: using logical Or (||) for default values

    • 14.4.5 空值合并赋值运算符 (??=) [ES2021]

  • 14.5 undefined and null don’t have properties

  • 14.6 The history of undefined and null


许多编程语言都有一个叫做 null 的“非值”。它表示变量当前没有指向对象 - 例如,当它尚未初始化时。

相反,JavaScript 有两个:undefinednull

14.1 undefined vs. null

这两个值非常相似,经常可以互换使用。它们的区别因此很微妙。语言本身做出以下区分:

  • undefined 意味着“未初始化”(例如变量)或“不存在”(例如对象的属性)。

  • null 意味着“有意的缺少任何对象值”(引用自语言规范)。

程序员可能会做出以下区分:

  • undefined 是语言使用的非值(当某些东西未初始化时等)。

  • null 意味着“明确关闭”。也就是说,它有助于实现一个包括有意义值和代表“没有有意义值”的元值的类型。这种类型在函数式编程中称为option typemaybe type

14.2 Occurrences of undefined and null

下面的小节描述了语言中 undefinednull 出现的地方。我们将遇到几种稍后在本书中更详细解释的机制。

14.2.1 Occurrences of undefined

未初始化的变量 myVar

let myVar;
assert.equal(myVar, undefined);

参数 x 未提供:

function func(x) {
 return x;
}
assert.equal(func(), undefined);

属性 .unknownProp 丢失:

const obj = {};
assert.equal(obj.unknownProp, undefined);

如果我们没有通过 return 语句明确指定函数的结果,JavaScript 会为我们返回 undefined

function func() {}
assert.equal(func(), undefined);

14.2.2 Occurrences of null

对象的原型要么是对象,要么在原型链的末端是 nullObject.prototype 没有原型:

> Object.getPrototypeOf(Object.prototype)
null

如果我们对字符串(例如 'x')进行正则表达式匹配(例如 /a/),我们要么得到一个具有匹配数据的对象(如果匹配成功),要么得到 null(如果匹配失败):

> /a/.exec('x')
null

JSON 数据格式 不支持 undefined,只支持 null

> JSON.stringify({a: undefined, b: null})
'{"b":null}'

14.3 Checking for undefined or null

检查任何一个:

if (x === null) ···
if (x === undefined) ···

x 有值吗?

if (x !== undefined && x !== null) {
 // ···
}
if (x) { // truthy?
 // x is neither: undefined, null, false, 0, NaN, ''
}

xundefined 还是 null

if (x === undefined || x === null) {
 // ···
}
if (!x) { // falsy?
 // x is: undefined, null, false, 0, NaN, ''
}

Truthy 意味着“如果强制转换为布尔值,则为 true”。Falsy 意味着“如果强制转换为布尔值,则为 false”。这两个概念在§15.2 “Falsy and truthy values”中得到了适当的解释。

14.4 The nullish coalescing operator (??) for default values [ES2020]

有时我们会收到一个值,只想在它既不是null也不是undefined时使用它。否则,我们希望使用一个默认值作为后备。我们可以通过空值合并运算符(??)来实现这一点:

const valueToUse = receivedValue ?? defaultValue;

以下两个表达式是等价的:

a ?? b
a !== undefined && a !== null ? a : b

14.4.1 示例:计算匹配项

以下代码显示了一个现实世界的例子:

function countMatches(regex, str) {
 const matchResult = str.match(regex); // null or Array
 return (matchResult ?? []).length;
}

assert.equal(
 countMatches(/a/g, 'ababa'), 3);
assert.equal(
 countMatches(/b/g, 'ababa'), 2);
assert.equal(
 countMatches(/x/g, 'ababa'), 0);

如果str中存在一个或多个regex的匹配项,则.match()返回一个数组。如果没有匹配项,它不幸地返回null(而不是空数组)。我们通过??运算符来修复这个问题。

我们也可以使用可选链:

return matchResult?.length ?? 0;

14.4.2 示例:为属性指定默认值

function getTitle(fileDesc) {
 return fileDesc.title ?? '(Untitled)';
}

const files = [
 {path: 'index.html', title: 'Home'},
 {path: 'tmp.html'},
];
assert.deepEqual(
 files.map(f => getTitle(f)),
 ['Home', '(Untitled)']);

14.4.3 使用解构来设置默认值

在某些情况下,解构也可以用于默认值 - 例如:

function getTitle(fileDesc) {
 const {title = '(Untitled)'} = fileDesc;
 return title;
}

14.4.4 传统方法:使用逻辑或(||)来设置默认值

在 ECMAScript 2020 之前和空值合并运算符之前,逻辑或被用于默认值。这有一个缺点。

||undefinednull的工作方式与预期相同:

> undefined || 'default'
'default'
> null || 'default'
'default'

但它还返回所有其他假值的默认值 - 例如:

> false || 'default'
'default'
> 0 || 'default'
'default'
> 0n || 'default'
'default'
> '' || 'default'
'default'

将其与??的工作方式进行比较:

> undefined ?? 'default'
'default'
> null ?? 'default'
'default'

> false ?? 'default'
false
> 0 ?? 'default'
0
> 0n ?? 'default'
0n
> '' ?? 'default'
''

14.4.5 空值合并赋值运算符(??=) [ES2021]

??=是一个逻辑赋值运算符。以下两个表达式大致等价:

a ??= b
a ?? (a = b)

这意味着??=是短路的:只有在aundefinednull时才进行赋值。

14.4.5.1 示例:使用??=添加丢失的属性
const books = [
 {
 isbn: '123',
 },
 {
 title: 'ECMAScript Language Specification',
 isbn: '456',
 },
];

// Add property .title where it’s missing
for (const book of books) {
 book.title ??= '(Untitled)';
}

assert.deepEqual(
 books,
 [
 {
 isbn: '123',
 title: '(Untitled)',
 },
 {
 title: 'ECMAScript Language Specification',
 isbn: '456',
 },
 ]);

14.5 undefinednull没有属性

undefinednull是 JavaScript 中唯一两个值,如果我们尝试读取属性,会得到异常。为了探索这一现象,让我们使用以下函数,该函数读取(“获取”)属性.foo并返回结果。

function getFoo(x) {
 return x.foo;
}

如果我们将getFoo()应用于各种值,我们可以看到它仅对undefinednull失败:

> getFoo(undefined)
TypeError: Cannot read properties of undefined (reading 'foo')
> getFoo(null)
TypeError: Cannot read properties of null (reading 'foo')

> getFoo(true)
undefined
> getFoo({})
undefined

14.6 未定义和 null 的历史

在 Java 中(它启发了 JavaScript 的许多方面),初始化值取决于变量的静态类型:

  • 具有对象类型的变量被初始化为null

  • 每种原始类型都有自己的初始化值。例如,int变量的初始化值为0

在 JavaScript 中,每个变量既可以保存对象值,也可以保存原始值。因此,如果null表示“不是对象”,JavaScript 还需要一个初始化值,表示“既不是对象也不是原始值”。该初始化值是undefined

测验

请参阅测验应用程序。

评论

十五、布尔值

原文:exploringjs.com/impatient-js/ch_booleans.html

译者:飞龙

协议:CC BY-NC-SA 4.0


  • 15.1 转换为布尔值

  • 15.2 假值和真值

    • 15.2.1 检查真值或假值
  • 15.3 基于真值的存在性检查

    • 15.3.1 陷阱:基于真值的存在性检查不精确

    • 15.3.2 用例:是否提供了参数?

    • 15.3.3 用例:属性是否存在?

  • 15.4 条件运算符(? :

  • 15.5 二进制逻辑运算符:And(x && y),Or(x || y

    • 15.5.1 值保留

    • 15.5.2 短路

    • 15.5.3 逻辑与(x && y

    • 15.5.4 逻辑或(||

  • 15.6 逻辑非(!


原始类型boolean包括两个值 - falsetrue

> typeof false
'boolean'
> typeof true
'boolean'

15.1 转换为布尔值

  “转换为[type]” 的含义

“转换为[type]”是“将任意值转换为[type]类型的值”的简称。

有三种方法可以将任意值x转换为布尔值。

  • Boolean(x)

    最具描述性;推荐使用。

  • x ? true : false

    使用条件运算符(在本章后面解释)。

  • !!x

    使用逻辑非运算符(!)。此运算符将其操作数强制转换为布尔值。然后再次应用它以获得非否定的结果。

Tbl. 4 描述了各种值如何转换为布尔值。

表 4:将值转换为布尔值。

x Boolean(x)
undefined false
null false
boolean x (no change)
number 0 false, NaN false
其他数字 true
bigint 0 false
其他数字 true
string '' false
其他字符串 true
symbol true
object 始终为 true

15.2 假值和真值

在检查if语句、while循环或do-while循环的条件时,JavaScript 的工作方式与您可能期望的不同。例如,考虑以下条件:

if (value) {}

在许多编程语言中,此条件等同于:

if (value === true) {}

然而,在 JavaScript 中,它等同于:

if (Boolean(value) === true) {}

也就是说,JavaScript 检查将value转换为布尔值时是否为true。这种检查非常常见,因此引入了以下名称:

  • 如果将值转换为布尔值时为true,则称该值为真值

  • 如果将值转换为布尔值时为false,则称该值为假值

每个值都是真值或假值。通过查阅表 4,我们可以列出所有假值的详尽列表:

  • undefined

  • null

  • 布尔值:false

  • 数字:0NaN

  • Bigint:0n

  • 字符串:''

所有其他值(包括所有对象)都是真值:

> Boolean('abc')
true
> Boolean([])
true
> Boolean({})
true

15.2.1 检查真值或假值

if (x) {
 // x is truthy
}

if (!x) {
 // x is falsy
}

if (x) {
 // x is truthy
} else {
 // x is falsy
}

const result = x ? 'truthy' : 'falsy';

在最后一行使用的条件运算符在本章后面有解释。

  练习:真值

exercises/booleans/truthiness_exrc.mjs

15.3 基于真值的存在性检查

在 JavaScript 中,如果你读取一个不存在的东西(例如,一个缺失的参数或一个缺失的属性),通常会得到undefined作为结果。在这些情况下,存在性检查等同于将一个值与undefined进行比较。例如,以下代码检查对象obj是否具有属性.prop

if (obj.prop !== undefined) {
 // obj has property .prop
}

由于undefined是假值,我们可以将这个检查缩短为:

if (obj.prop) {
 // obj has property .prop
}

15.3.1 陷阱:基于真实性的存在性检查不够精确

基于真实性的存在性检查有一个陷阱:它们不是很精确。考虑这个之前的例子:

if (obj.prop) {
 // obj has property .prop
}

如果:

  • obj.prop是丢失的(在这种情况下,JavaScript 返回undefined)。

然而,如果:

  • obj.propundefined

  • obj.prop是任何其他假值(null0'',等等)。

实际上,这很少会引起问题,但你必须意识到这个陷阱。

15.3.2 用例:是否提供了参数?

真实性检查经常用于确定函数的调用者是否提供了参数:

function func(x) {
 if (!x) {
 throw new Error('Missing parameter x');
 }
 // ···
}

好的一面是,这种模式已经被建立并且很简短。它可以正确地抛出undefinednull的错误。

不好的一面是,之前提到的陷阱:代码也会对所有其他假值抛出错误。

另一种方法是检查undefined

if (x === undefined) {
 throw new Error('Missing parameter x');
}

15.3.3 用例:属性是否存在?

真实性检查也经常用于确定属性是否存在:

function readFile(fileDesc) {
 if (!fileDesc.path) {
 throw new Error('Missing property: .path');
 }
 // ···
}
readFile({ path: 'foo.txt' }); // no error

这种模式也已经建立,并且有一个通常的警告:它不仅在属性丢失时抛出错误,而且在属性存在并且具有任何假值时也会抛出错误。

如果你真的想检查属性是否存在,你必须使用 in 运算符:

if (! ('path' in fileDesc)) {
 throw new Error('Missing property: .path');
}

15.4 条件运算符(? :

条件运算符是if语句的表达式版本。它的语法是:

«condition» ? «thenExpression» : «elseExpression»

它的评估如下:

  • 如果condition为真值,则评估并返回thenExpression

  • 否则,评估并返回elseExpression

条件运算符也被称为三元运算符,因为它有三个操作数。

例子:

> true ? 'yes' : 'no'
'yes'
> false ? 'yes' : 'no'
'no'
> '' ? 'yes' : 'no'
'no'

以下代码演示了通过条件选择的两个分支“then”和“else”,只有一个分支被评估。另一个分支不会被评估。

const x = (true ? console.log('then') : console.log('else'));

// Output:
// 'then'

15.5 二进制逻辑运算符:与(x && y),或(x || y

二进制逻辑运算符&&||值保留短路的。

15.5.1 值保留

值保留意味着操作数被解释为布尔值,但返回不变:

> 12 || 'hello'
12
> 0 || 'hello'
'hello'

15.5.2 短路

短路意味着如果第一个操作数已经确定了结果,那么第二个操作数就不会被评估。唯一延迟评估其操作数的其他运算符是条件运算符。通常,在执行操作之前会评估所有操作数。

例如,逻辑与(&&)如果第一个操作数为假,则不评估第二个操作数:

const x = false && console.log('hello');
// No output

如果第一个操作数为真值,则执行console.log()

const x = true && console.log('hello');

// Output:
// 'hello'

15.5.3 逻辑与(x && y

表达式a && b(“ab”)的评估如下:

  1. 评估a

  2. 结果是否为假?返回它。

  3. 否则,评估b并返回结果。

换句话说,以下两个表达式大致等价:

a && b
!a ? a : b

例子:

> false && true
false
> false && 'abc'
false

> true && false
false
> true && 'abc'
'abc'

> '' && 'abc'
''

15.5.4 逻辑或(||

表达式a || b(“ab”)的评估如下:

  1. 评估a

  2. 结果是否为真值?返回它。

  3. 否则,评估b并返回结果。

换句话说,以下两个表达式大致等价:

a || b
a ? a : b

例子:

> true || false
true
> true || 'abc'
true

> false || true
true
> false || 'abc'
'abc'

> 'abc' || 'def'
'abc'
15.5.4.1 逻辑或(||)的传统用例:提供默认值

ECMAScript 2020 引入了空值合并运算符(??)用于默认值。在此之前,逻辑或被用于此目的:

const valueToUse = receivedValue || defaultValue;

有关??和在这种情况下||的缺点的更多信息,请参见§14.4“空值合并运算符(??)用于默认值[ES2020]”

传统练习:通过或运算符(||)提供默认值

exercises/booleans/default_via_or_exrc.mjs

15.6 逻辑非 (!)

表达式!x(“非x”)的求值如下:

  1. 求值x

  2. 它是真值吗?返回false

  3. 否则,返回true

例子:

> !false
true
> !true
false

> !0
true
> !123
false

> !''
true
> !'abc'
false

  Quiz

参见 quiz app。

Comments

十六、数字

原文:exploringjs.com/impatient-js/ch_numbers.html

译者:飞龙

协议:CC BY-NC-SA 4.0


  • 16.1 数字既用于浮点数也用于整数

  • 16.2 数字文字

  • 16.3 算术运算符

    • 16.3.1 二进制算术运算符

    • 16.3.2 一元加号 (+) 和否定 (-)

    • 16.3.3 递增 (++) 和递减 (--)

  • 16.4 转换为数字

  • 16.5 错误值

    • 16.5.1 错误值:NaN

    • 16.5.2 错误值:Infinity

  • 16.6 数字的精度:小心处理小数部分

  • 16.7 (高级)

  • 16.8 背景:浮点数精度

    • 16.8.1 浮点数的简化表示
  • 16.9 JavaScript 中的整数

    • 16.9.1 转换为整数

    • 16.9.2 JavaScript 中整数的范围

    • 16.9.3 安全整数

  • 16.10 位运算符

    • 16.10.1 在内部,按位运算符使用 32 位整数

    • 16.10.2 按位取反

    • 16.10.3 二进制位运算符

    • 16.10.4 位移运算符

    • 16.10.5 b32(): 以二进制表示无符号 32 位整数

  • 16.11 快速参考:数字

    • 16.11.1 数字的全局函数

    • 16.11.2 Number 的静态属性

    • 16.11.3 Number 的静态方法

    • 16.11.4 Number.prototype 的方法

    • 16.11.5 来源


JavaScript 有两种数值类型:

  • Numbers 是 64 位浮点数,也用于较小的整数(范围在正负 53 位之内)。

  • Bigints 代表具有任意精度的整数。

本章涵盖了数字。大整数将在本书的后面进行介绍。

16.1 数字既用于浮点数也用于整数

在 JavaScript 中,类型 number 用于整数和浮点数:

98
123.45

然而,所有数字都是双精度,64 位浮点数,根据IEEE 浮点算术标准(IEEE 754)实现。

整数就是没有小数部分的浮点数:

> 98 === 98.0
true

请注意,在底层,大多数 JavaScript 引擎通常能够使用真正的整数,带有所有相关的性能和存储大小优势。

16.2 数字文字

让我们来看看数字的文字。

16.2.1 整数字面值

几个整数字面值让我们可以用不同的基数表示整数:

// Binary (base 2)
assert.equal(0b11, 3); // ES6

// Octal (base 8)
assert.equal(0o10, 8); // ES6

// Decimal (base 10)
assert.equal(35, 35);

// Hexadecimal (base 16)
assert.equal(0xE7, 231);

16.2.2 浮点数文字

浮点数只能用十进制表示。

分数:

> 35.0
35

指数:eN 表示 ×10^N

> 3e2
300
> 3e-2
0.03
> 0.3e2
30

16.2.3 语法陷阱:整数字面值的属性

访问整数字面值的属性会导致一个陷阱:如果整数字面值紧接着一个点,那么该点会被解释为十进制点:

7.toString(); // syntax error

有四种方法可以避开这个陷阱:

7.0.toString()
(7).toString()
7..toString()
7 .toString()  // space before dot

16.2.4 下划线 (_) 作为数字字面值中的分隔符 [ES2021]

将数字分组以使长数字更易读具有悠久的传统。例如:

  • 1825 年,伦敦有 1,335,000 名居民。

  • 地球和太阳之间的距离是 149,600,000 公里。

自 ES2021 以来,我们可以在数字字面值中使用下划线作为分隔符:

const inhabitantsOfLondon = 1_335_000;
const distanceEarthSunInKm = 149_600_000;

对于其他进制,分组也很重要:

const fileSystemPermission = 0b111_111_000;
const bytes = 0b1111_10101011_11110000_00001101;
const words = 0xFAB_F00D;

我们还可以在分数和指数中使用分隔符:

const massOfElectronInKg = 9.109_383_56e-31;
const trillionInShortScale = 1e1_2;
16.2.4.1 我们可以在哪里放置分隔符?

分隔符的位置受到两种限制:

  • 我们只能在两个数字之间放下划线。因此,以下所有数字字面值都是非法的:

    3_.141
    3._141
    
    1_e12
    1e_12
    
    _1464301  // valid variable name!
    1464301_
    
    0_b111111000
    0b_111111000
    
  • 我们不能连续使用多个下划线:

    123__456 // two underscores – not allowed
    

这些限制背后的动机是保持解析简单,避免奇怪的边缘情况。

16.2.4.2 使用分隔符解析数字

以下用于解析数字的函数不支持分隔符:

  • Number()

  • Number.parseInt()

  • Number.parseFloat()

例如:

> Number('123_456')
NaN
> Number.parseInt('123_456')
123

其理念是数字分隔符是为了代码。其他类型的输入应该以不同的方式处理。

16.3 算术运算符

16.3.1 二进制算术运算符

Tbl. 5 列出了 JavaScript 的二进制算术运算符。

表 5:二进制算术运算符。

运算符 名称 例子
n + m 加法 ES1 3 + 4 7
n - m 减法 ES1 9 - 1 8
n * m 乘法 ES1 3 * 2.25 6.75
n / m 除法 ES1 5.625 / 5 1.125
n % m 余数 ES1 8 % 5 3
-8 % 5 -3
n ** m 指数 ES2016 4 ** 2 16
16.3.1.1 % 是余数运算符

% 是一个余数运算符,而不是模运算符。其结果具有第一个操作数的符号:

> 5 % 3
2
> -5 % 3
-2

有关余数和模运算之间的区别的更多信息,请参阅博文“余数运算符 vs. 模运算符(带有 JavaScript 代码)” 在 2ality 上。

16.3.2 一元加 (+) 和否定 (-)

Tbl. 6 总结了两个运算符 一元加 (+) 和 否定 (-)。

表 6:一元加 (+) 和否定 (-) 运算符。

运算符 名称 例子
+n 一元加 ES1 +(-7) -7
-n 一元否定 ES1 -(-7) 7

这两个运算符都会将它们的操作数强制转换为数字:

> +'5'
5
> +'-12'
-12
> -'9'
-9

因此,一元加让我们将任意值转换为数字。

16.3.3 递增 (++) 和递减 (--)

递增运算符 ++ 存在前缀版本和后缀版本。在两个版本中,它都会破坏性地将其操作数加一。因此,它的操作数必须是可以更改的存储位置。

递减运算符 -- 的工作方式相同,但是从其操作数中减去一个。下面的两个例子解释了前缀和后缀版本之间的区别。

Tbl. 7 总结了递增和递减运算符。

表 7:递增运算符和递减运算符。

运算符 名称 例子
v++ 递增 ES1 let v=0; [v++, v] [0, 1]
++v 递增 ES1 let v=0; [++v, v] [1, 1]
v-- 递减 ES1 let v=1; [v--, v] [1, 0]
--v 递减 ES1 let v=1; [--v, v] [0, 0]

接下来,我们将看一些使用这些运算符的例子。

前缀 ++ 和前缀 -- 改变它们的操作数,然后返回它们。

let foo = 3;
assert.equal(++foo, 4);
assert.equal(foo, 4);

let bar = 3;
assert.equal(--bar, 2);
assert.equal(bar, 2);

后缀 ++ 和后缀 -- 返回它们的操作数,然后改变它们。

let foo = 3;
assert.equal(foo++, 3);
assert.equal(foo, 4);

let bar = 3;
assert.equal(bar--, 3);
assert.equal(bar, 2);
16.3.3.1 操作数:不仅仅是变量

我们还可以将这些运算符应用于属性值:

const obj = { a: 1 };
++obj.a;
assert.equal(obj.a, 2);

以及数组元素:

const arr = [ 4 ];
arr[0]++;
assert.deepEqual(arr, [5]);

  练习:数字运算符

exercises/numbers-math/is_odd_test.mjs

16.4 转换为数字

这些是将值转换为数字的三种方法:

  • Number(value)

  • +value

  • parseFloat(value)(避免;与其他两种方法不同!)

建议:使用描述性的Number()。Tbl. 8 总结了它的工作原理。

表 8:将值转换为数字。

x Number(x)
未定义 NaN
null 0
布尔值 false 0true 1
数字 x(无变化)
大整数 -1n -11n 1,等等。
字符串 '' 0
其他解析的数字,忽略前导/尾随空格
符号 抛出TypeError
对象 可配置的(例如通过.valueOf()

例子:

assert.equal(Number(123.45), 123.45);

assert.equal(Number(''), 0);
assert.equal(Number('\n 123.45 \t'), 123.45);
assert.equal(Number('xyz'), NaN);

assert.equal(Number(-123n), -123);

对象如何转换为数字可以进行配置-例如,通过覆盖.valueOf()

> Number({ valueOf() { return 123 } })
123

  练习:转换为数字

exercises/numbers-math/parse_number_test.mjs

16.5 错误值

当发生错误时,会返回两个数字值:

  • NaN

  • Infinity

16.5.1 错误值:NaN

NaN是“不是一个数字”的缩写。讽刺的是,JavaScript 认为它是一个数字:

> typeof NaN
'number'

何时返回NaN

如果无法解析数字,则返回NaN

> Number('$$$')
NaN
> Number(undefined)
NaN

如果无法执行操作,则返回NaN

> Math.log(-1)
NaN
> Math.sqrt(-1)
NaN

如果操作数或参数是NaN,则返回NaN(以传播错误):

> NaN - 3
NaN
> 7 ** NaN
NaN
16.5.1.1 检查NaN

NaN是唯一一个不严格等于自身的 JavaScript 值:

const n = NaN;
assert.equal(n === n, false);

这是检查值x是否为NaN的几种方法:

const x = NaN;

assert.equal(Number.isNaN(x), true); // preferred
assert.equal(Object.is(x, NaN), true);
assert.equal(x !== x, true);

在最后一行,我们使用比较技巧来检测NaN

16.5.1.2 在数组中查找NaN

一些数组方法无法找到NaN

> [NaN].indexOf(NaN)
-1

其他可以:

> [NaN].includes(NaN)
true
> [NaN].findIndex(x => Number.isNaN(x))
0
> [NaN].find(x => Number.isNaN(x))
NaN

遗憾的是,没有简单的经验法则。我们必须检查每种方法如何处理NaN

16.5.2 错误值:Infinity

何时返回错误值Infinity

如果数字太大,则返回无穷大:

> Math.pow(2, 1023)
8.98846567431158e+307
> Math.pow(2, 1024)
Infinity

如果除以零,则返回无穷大:

> 5 / 0
Infinity
> -5 / 0
-Infinity
16.5.2.1 Infinity作为默认值

Infinity大于所有其他数字(除了NaN),使其成为一个很好的默认值:

function findMinimum(numbers) {
 let min = Infinity;
 for (const n of numbers) {
 if (n < min) min = n;
 }
 return min;
}

assert.equal(findMinimum([5, -1, 2]), -1);
assert.equal(findMinimum([]), Infinity);
16.5.2.2 检查Infinity

这是检查值x是否为Infinity的两种常见方法:

const x = Infinity;

assert.equal(x === Infinity, true);
assert.equal(Number.isFinite(x), false);

  练习:比较数字

exercises/numbers-math/find_max_test.mjs

16.6 数字的精度:小心处理小数

在内部,JavaScript 浮点数使用基数 2 来表示(根据 IEEE 754 标准)。这意味着十进制小数(基数 10)不能总是精确表示:

> 0.1 + 0.2
0.30000000000000004
> 1.3 * 3
3.9000000000000004
> 1.4 * 100000000000000
139999999999999.98

因此,在 JavaScript 中进行算术运算时,我们需要考虑舍入误差。

继续阅读对这一现象的解释。

  测验:基础

请参阅测验应用程序的解释。

16.7 (高级)

本章的所有其余部分都是高级的。

16.8 背景:浮点精度

在 JavaScript 中,使用数字进行计算并不总是产生正确的结果-例如:

> 0.1 + 0.2
0.30000000000000004

要理解为什么,我们需要探索 JavaScript 如何在内部表示浮点数。它使用三个整数来表示,总共占用 64 位存储空间(双精度):

组件 大小 整数范围
符号 1 位 [0, 1]
分数 52 位 [0, 2⁵²−1]
指数 11 位 [−1023, 1024]

由这些整数表示的浮点数计算如下:

(-1)^(符号)× 0b1.fraction × 2^(exponent)

这种表示无法编码零,因为它的第二个组件(涉及分数)总是有一个前导 1。因此,零通过特殊指数-1023 和分数 0 来编码。

16.8.1 浮点数的简化表示

为了使进一步讨论更容易,我们简化了先前的表示:

  • 我们使用基数 10(十进制)而不是基数 2(二进制),因为大多数人更熟悉十进制。

  • 分数是一个被解释为分数的自然数(小数点后的数字)。我们切换到尾数,一个被解释为自身的整数。因此,指数的使用方式不同,但其基本作用并未改变。

  • 由于尾数是一个整数(带有自己的符号),我们不再需要单独的符号。

新的表示方法如下:

尾数 × 10^(指数)

让我们尝试一下这种表示方法来表示一些浮点数。

  • 对于整数−123,我们主要需要尾数:

    > -123 * (10 ** 0)
    -123
    
  • 对于数字 1.5,我们想象尾数后有一个点。我们使用负指数将该点向左移动一位:

    > 15 * (10 ** -1)
    1.5
    
  • 对于数字 0.25,我们将小数点向左移动两位:

    > 25 * (10 ** -2)
    0.25
    

具有负指数的表示也可以写成分母中具有正指数的分数:

> 15 * (10 ** -1) === 15 / (10 ** 1)
true
> 25 * (10 ** -2) === 25 / (10 ** 2)
true

这些分数有助于理解为什么有些数字我们的编码无法表示:

  • 1/10 可以表示。它已经具有所需的格式:分母中的 10 的幂。

  • 1/2 可以表示为5/10。我们通过将分子和分母乘以 5,将分母中的 2 转换为 10 的幂。

  • 1/4 可以表示为25/100。我们通过将分子和分母乘以 25,将分母中的 4 转换为 10 的幂。

  • 1/3 无法表示。没有办法将分母转换为 10 的幂。(10 的质因数是 2 和 5。因此,任何只有这些质因数的分母都可以通过乘以足够多的 2 和 5 来转换为 10 的幂。如果分母有其他质因数,那么我们就无能为力了。)

为了结束我们的探讨,我们再次切换到基数 2:

  • 0.5 = 1/2 可以用基数 2 表示,因为分母已经是 2 的幂。

  • 0.25 = 1/4 可以用基数 2 表示,因为分母已经是 2 的幂。

  • 0.1 = 1/10 无法表示,因为分母无法转换为 2 的幂。

  • 0.2 = 2/10 无法表示,因为分母无法转换为 2 的幂。

现在我们可以看到为什么0.1 + 0.2不能产生正确的结果:在内部,这两个操作数都无法精确表示。

精确计算小数部分的唯一方法是在内部切换到基数 10。对于许多编程语言,基数 2 是默认值,基数 10 是一个选项。例如,Java 有类BigDecimal,Python 有模块decimal。有计划向 JavaScript 添加类似的功能:ECMAScript 提案“Decimal”

16.9 JavaScript 中的整数

整数是没有小数部分的正常(浮点)数:

> 1 === 1.0
true
> Number.isInteger(1.0)
true

在本节中,我们将看一些处理这些伪整数的工具。JavaScript 还支持bigints,这些是真正的整数。

16.9.1 转换为整数

将数字转换为整数的推荐方法是使用Math对象的其中一种四舍五入方法:

  • Math.floor(n): 返回最大的整数in

    > Math.floor(2.1)
    2
    > Math.floor(2.9)
    2
    
  • Math.ceil(n): 返回最小的整数in

    > Math.ceil(2.1)
    3
    > Math.ceil(2.9)
    3
    
  • Math.round(n): 返回与n“最接近”的整数,其中__.5四舍五入为上述整数,例如:

    > Math.round(2.4)
    2
    > Math.round(2.5)
    3
    
  • Math.trunc(n): 移除n的任何小数部分(小数点后),因此将其转换为整数。

    > Math.trunc(2.1)
    2
    > Math.trunc(2.9)
    2
    

有关四舍五入的更多信息,请参阅§17.3 “Rounding”。

16.9.2 JavaScript 中整数的范围

这些是 JavaScript 中整数的重要范围:

  • 安全整数:可以被 JavaScript“安全”表示(在下一小节中会详细介绍)

    • 精度:53 位加符号

    • 范围:(−2⁵³, 2⁵³)

  • 数组索引

    • 精度:32 位,无符号

    • 范围:[0, 2³²−1)(不包括最大长度)

    • 类型化数组具有 53 位的更大范围(安全且无符号)

  • 按位运算符(按位或等)

    • 精度:32 位

    • 无符号右移(>>>)的范围:无符号,[0, 2³²)

    • 所有其他按位运算符的范围:有符号,[−2³¹, 2³¹)

16.9.3 安全整数

这是 JavaScript 中安全的整数范围(53 位加上符号):

[–(2⁵³)+1, 2⁵³–1]

如果一个整数由一个 JavaScript 数字精确表示,则该整数是安全的。鉴于 JavaScript 数字被编码为乘以 2 的指数幂的分数,更高的整数也可以表示,但它们之间存在间隙。

例如(18014398509481984 是 2⁵⁴):

> 18014398509481984
18014398509481984
> 18014398509481985
18014398509481984
> 18014398509481986
18014398509481984
> 18014398509481987
18014398509481988

Number的以下属性有助于确定整数是否安全:

assert.equal(Number.MAX_SAFE_INTEGER, (2 ** 53) - 1);
assert.equal(Number.MIN_SAFE_INTEGER, -Number.MAX_SAFE_INTEGER);

assert.equal(Number.isSafeInteger(5), true);
assert.equal(Number.isSafeInteger('5'), false);
assert.equal(Number.isSafeInteger(5.1), false);
assert.equal(Number.isSafeInteger(Number.MAX_SAFE_INTEGER), true);
assert.equal(Number.isSafeInteger(Number.MAX_SAFE_INTEGER+1), false);

  练习:检测安全整数

exercises/numbers-math/is_safe_integer_test.mjs

16.9.3.1 安全计算

让我们来看看涉及不安全整数的计算。

以下结果是不正确和不安全的,即使它的操作数都是安全的:

> 9007199254740990 + 3
9007199254740992

以下结果是安全的,但不正确。第一个操作数是不安全的;第二个操作数是安全的:

> 9007199254740995 - 10
9007199254740986

因此,表达式a op b的结果是正确的当且仅当:

isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)

也就是说,操作数和结果都必须是安全的。

16.10 按位运算符

16.10.1 按位运算符在内部使用 32 位整数

在内部,JavaScript 的按位运算符使用 32 位整数。它们通过以下步骤产生结果:

  • 输入(JavaScript 数字):首先将 1-2 个操作数转换为 JavaScript 数字(64 位浮点数),然后转换为 32 位整数。

  • 计算(32 位整数):实际操作处理 32 位整数并产生 32 位整数。

  • 输出(JavaScript 数字):在返回结果之前,它被转换回 JavaScript 数字。

16.10.1.1 操作数和结果的类型

对于每个按位运算符,本书提到了它的操作数和结果的类型。每种类型总是以下两种之一:

类型 描述 大小 范围
Int32 有符号 32 位整数 32 位包括符号 −2³¹, 2³¹)
Uint32 无符号 32 位整数 32 位 [0, 2³²)

考虑到前面提到的步骤,我建议假装按位运算符在内部使用无符号 32 位整数(步骤“计算”),而 Int32 和 Uint32 只影响 JavaScript 数字如何转换为整数和从整数转换为(步骤“输入”和“输出”)。

16.10.1.2 将 JavaScript 数字显示为无符号 32 位整数

在探索按位运算符时,有时将 JavaScript 数字显示为二进制表示的无符号 32 位整数会有所帮助。这就是b32()的作用(其实现稍后会显示):

assert.equal(
 b32(-1),
 '11111111111111111111111111111111');
assert.equal(
 b32(1),
 '00000000000000000000000000000001');
assert.equal(
 b32(2 ** 31),
 '10000000000000000000000000000000');

16.10.2 按位非

表 9:按位非运算符。

操作 名称 类型签名
~num 按位非,补码 Int32 Int32 ES1

按位非运算符(tbl. [9)反转其操作数的每个二进制位:

> b32(~0b100)
'11111111111111111111111111111011'

所谓的补码对于某些算术运算类似于负数。例如,将整数加上它的补码总是-1

> 4 + ~4
-1
> -11 + ~-11
-1

16.10.3 二进制按位运算符

表 10:二进制按位运算符。

操作 名称 类型签名
num1 & num2 按位与 Int32 × Int32 Int32 ES1
num1 ¦ num2 按位或 Int32 × Int32 Int32 ES1
num1 ^ num2 按位异或 Int32 × Int32 Int32 ES1

二进制按位运算符(tbl. 10)将它们的操作数的位组合起来产生它们的结果:

> (0b1010 & 0b0011).toString(2).padStart(4, '0')
'0010'
> (0b1010 | 0b0011).toString(2).padStart(4, '0')
'1011'
> (0b1010 ^ 0b0011).toString(2).padStart(4, '0')
'1001'

16.10.4 按位移动运算符

表 11:位移运算符。

操作 名称 类型签名
num << count 左移 Int32 × Uint32 Int32 ES1
num >> count 有符号右移 Int32 × Uint32 Int32 ES1
num >>> count 无符号右移 Uint32 × Uint32 Uint32 ES1

位移运算符(见表 11)将二进制数字向左或向右移动:

> (0b10 << 1).toString(2)
'100'

>>保留最高位,>>>不保留:

> b32(0b10000000000000000000000000000010 >> 1)
'11000000000000000000000000000001'
> b32(0b10000000000000000000000000000010 >>> 1)
'01000000000000000000000000000001'

16.10.5 b32(): 以二进制表示无符号 32 位整数

我们现在已经使用了b32()几次。以下代码是它的一个实现:

/**
 * Return a string representing n as a 32-bit unsigned integer,
 * in binary notation.
 */
function b32(n) {
 // >>> ensures highest bit isn’t interpreted as a sign
 return (n >>> 0).toString(2).padStart(32, '0');
}
assert.equal(
 b32(6),
 '00000000000000000000000000000110');

n >>> 0表示我们将n向右移动零位。因此,原则上,>>>运算符什么也不做,但它仍然将n强制转换为无符号 32 位整数:

> 12 >>> 0
12
> -12 >>> 0
4294967284
> (2**32 + 1) >>> 0
1

16.11 快速参考:数字

16.11.1 用于数字的全局函数

JavaScript 有以下四个用于数字的全局函数:

  • isFinite()

  • isNaN()

  • parseFloat()

  • parseInt()

然而,最好使用Number的相应方法(Number.isFinite()等),它们有更少的陷阱。它们是在 ES6 中引入的,并在下面讨论。

16.11.2 Number的静态属性

  • .EPSILON: number ^([ES6])

    1 和下一个可表示的浮点数之间的差异。一般来说,机器 epsilon提供了浮点运算中舍入误差的上限。

    • 大约为:2.2204460492503130808472633361816 × 10^(-16)
  • .MAX_SAFE_INTEGER: number ^([ES6])

    JavaScript 可以明确表示的最大整数(2⁵³−1)。

  • .MAX_VALUE: number ^([ES1])

    最大的正有限 JavaScript 数字。

    • 大约为:1.7976931348623157 × 10³⁰⁸
  • .MIN_SAFE_INTEGER: number ^([ES6])

    JavaScript 可以明确表示的最小整数(−2⁵³+1)。

  • .MIN_VALUE: number ^([ES1])

    最小的正 JavaScript 数字。大约为 5 × 10^(−324)。

  • .NaN: number ^([ES1])

    与全局变量NaN相同。

  • .NEGATIVE_INFINITY: number ^([ES1])

    -Number.POSITIVE_INFINITY相同。

  • .POSITIVE_INFINITY: number ^([ES1])

    与全局变量Infinity相同。

16.11.3 Number的静态方法

  • .isFinite(num: number): boolean ^([ES6])

    如果num是一个实际数字(既不是Infinity也不是-Infinity也不是NaN),则返回true

    > Number.isFinite(Infinity)
    false
    > Number.isFinite(-Infinity)
    false
    > Number.isFinite(NaN)
    false
    > Number.isFinite(123)
    true
    
  • .isInteger(num: number): boolean ^([ES6])

    如果num是一个数字并且没有小数部分,则返回true

    > Number.isInteger(-17)
    true
    > Number.isInteger(33)
    true
    > Number.isInteger(33.1)
    false
    > Number.isInteger('33')
    false
    > Number.isInteger(NaN)
    false
    > Number.isInteger(Infinity)
    false
    
  • .isNaN(num: number): boolean ^([ES6])

    如果num是值NaN,则返回true

    > Number.isNaN(NaN)
    true
    > Number.isNaN(123)
    false
    > Number.isNaN('abc')
    false
    
  • .isSafeInteger(num: number): boolean ^([ES6])

    如果num是一个数字并且明确表示一个整数,则返回true

  • .parseFloat(str: string): number ^([ES6])

    将其参数强制转换为字符串并将其解析为浮点数。对于将字符串转换为数字,通常使用Number()(它忽略前导和尾随空格)比使用Number.parseFloat()(它忽略前导空格和非法的尾随字符,并且可能隐藏问题)更好。

    > Number.parseFloat(' 123.4#')
    123.4
    > Number(' 123.4#')
    NaN
    
  • .parseInt(str: string, radix=10): number ^([ES6])

    将其参数强制转换为字符串并将其解析为整数,忽略前导空格和非法的尾随字符:

    > Number.parseInt('  123#')
    123
    

    参数radix指定要解析的数字的基数:

    > Number.parseInt('101', 2)
    5
    > Number.parseInt('FF', 16)
    255
    

    不要使用此方法将数字转换为整数:强制转换为字符串是低效的。在第一个非数字之前停止不是去除数字的小数部分的好算法。这里有一个它出错的例子:

    > Number.parseInt(1e21, 10) // wrong
    1
    

    最好使用Math的一个舍入函数将数字转换为整数:

    > Math.trunc(1e21) // correct
    1e+21
    

16.11.4 Number.prototype的方法

Number.prototype是存储数字方法的地方。)

  • .toExponential(fractionDigits?: number): string ^([ES3])

    返回表示数字的指数表示的字符串。使用fractionDigits,我们可以指定应显示与指数相乘的数字的位数(默认情况下,显示所需的位数)。

    示例:数字太小,无法通过.toString()获得正指数。

    > 1234..toString()
    '1234'
    
    > 1234..toExponential() // 3 fraction digits
    '1.234e+3'
    > 1234..toExponential(5)
    '1.23400e+3'
    > 1234..toExponential(1)
    '1.2e+3'
    

    示例:小数部分不够小,无法通过.toString()获得负指数。

    > 0.003.toString()
    '0.003'
    > 0.003.toExponential()
    '3e-3'
    
  • .toFixed(fractionDigits=0): string ^([ES3])

    返回不带指数的数字表示,四舍五入到fractionDigits位数。

    > 0.00000012.toString() // with exponent
    '1.2e-7'
    
    > 0.00000012.toFixed(10) // no exponent
    '0.0000001200'
    > 0.00000012.toFixed()
    '0'
    

    如果数字大于或等于 10²¹,甚至.toFixed()也会使用指数:

    > (10 ** 21).toFixed()
    '1e+21'
    
  • .toPrecision(precision?: number): string ^([ES3])

    类似于.toString(),但precision指定应显示多少位数字。如果缺少precision,则使用.toString()

    > 1234..toPrecision(3)  // requires exponential notation
    '1.23e+3'
    
    > 1234..toPrecision(4)
    '1234'
    
    > 1234..toPrecision(5)
    '1234.0'
    
    > 1.234.toPrecision(3)
    '1.23'
    
  • .toString(radix=10): string ^([ES1])

    返回数字的字符串表示。

    默认情况下,我们得到一个以 10 为底的数字作为结果:

    > 123.456.toString()
    '123.456'
    

    如果我们希望数字以不同的基数表示,可以通过radix指定:

    > 4..toString(2) // binary (base 2)
    '100'
    > 4.5.toString(2)
    '100.1'
    
    > 255..toString(16) // hexadecimal (base 16)
    'ff'
    > 255.66796875.toString(16)
    'ff.ab'
    
    > 1234567890..toString(36)
    'kf12oi'
    

    Number.parseInt()提供了反向操作:它将包含给定基数的整数(无小数部分!)数字的字符串转换为数字。

    > Number.parseInt('kf12oi', 36)
    1234567890
    

16.11.5 来源

  测验:高级

请参阅测验应用程序。

评论

十七、数学

原文:exploringjs.com/impatient-js/ch_math.html

译者:飞龙

协议:CC BY-NC-SA 4.0


  • 17.1 数据属性

  • 17.2 指数、根、对数

  • 17.3 舍入

  • 17.4 三角函数

  • 17.5 其他各种函数

  • 17.6 来源


Math是一个具有数据属性和用于处理数字的方法的对象。您可以将其视为一个简陋的模块:它是在 JavaScript 拥有模块之前创建的。

17.1 数据属性

  • Math.E: number ^([ES1])

    欧拉数,自然对数的底数,约为 2.7182818284590452354。

  • Math.LN10: number ^([ES1])

    10 的自然对数,约为 2.302585092994046。

  • Math.LN2: number ^([ES1])

    2 的自然对数,约为 0.6931471805599453。

  • Math.LOG10E: number ^([ES1])

    以 10 为底的e的对数,约为 0.4342944819032518。

  • Math.LOG2E: number ^([ES1])

    e的以 2 为底的对数,约为 1.4426950408889634。

  • Math.PI: number ^([ES1])

    数学常数π,圆的周长与直径的比值,约为 3.1415926535897932。

  • Math.SQRT1_2: number ^([ES1])

    1/2 的平方根,约为 0.7071067811865476。

  • Math.SQRT2: number ^([ES1])

    2 的平方根,约为 1.4142135623730951。

17.2 指数、根、对数

  • Math.cbrt(x: number): number ^([ES6])

    返回x的立方根。

    > Math.cbrt(8)
    2
    
  • Math.exp(x: number): number ^([ES1])

    返回e^(x)(e为欧拉数)。是Math.log()的反函数。

    > Math.exp(0)
    1
    > Math.exp(1) === Math.E
    true
    
  • Math.expm1(x: number): number ^([ES6])

    返回Math.exp(x)-1。是Math.log1p()的反函数。非常小的数字(接近 0 的分数)以更高的精度表示。因此,当.exp()返回接近 1 的值时,此函数返回更精确的值。

  • Math.log(x: number): number ^([ES1])

    返回x的自然对数(以e为底,即欧拉数)。是Math.exp()的反函数。

    > Math.log(1)
    0
    > Math.log(Math.E)
    1
    > Math.log(Math.E ** 2)
    2
    
  • Math.log1p(x: number): number ^([ES6])

    返回Math.log(1 + x)。是Math.expm1()的反函数。非常小的数字(接近 0 的分数)以更高的精度表示。因此,当.log()的参数接近 1 时,您可以提供此函数更精确的参数。

  • Math.log10(x: number): number ^([ES6])

    返回x的以 10 为底的对数。是10 ** x的反函数。

    > Math.log10(1)
    0
    > Math.log10(10)
    1
    > Math.log10(100)
    2
    
  • Math.log2(x: number): number ^([ES6])

    返回x的以 2 为底的对数。是2 ** x的反函数。

    > Math.log2(1)
    0
    > Math.log2(2)
    1
    > Math.log2(4)
    2
    
  • Math.pow(x: number, y: number): number ^([ES1])

    返回x^(y), xy次方。与x ** y相同。

    > Math.pow(2, 3)
    8
    > Math.pow(25, 0.5)
    5
    
  • Math.sqrt(x: number): number ^([ES1])

    返回x的平方根。是x ** 2的反函数。

    > Math.sqrt(9)
    3
    

17.3 舍入

舍入意味着将任意数字转换为整数(没有小数部分的数字)。以下函数实现了不同的舍入方法。

  • Math.ceil(x: number): number ^([ES1])

    返回最小的(最接近-∞)整数i,使得xi

    > Math.ceil(2.1)
    3
    > Math.ceil(2.9)
    3
    
  • Math.floor(x: number): number ^([ES1])

    返回最大的(最接近+∞)整数i,使得ix

    > Math.floor(2.1)
    2
    > Math.floor(2.9)
    2
    
  • Math.round(x: number): number ^([ES1])

    返回最接近x的整数。如果x的小数部分是.5,则.round()会向上舍入(到更接近正无穷大的整数):

    > Math.round(2.4)
    2
    > Math.round(2.5)
    3
    
  • Math.trunc(x: number): number ^([ES6])

    去除x的小数部分并返回结果整数。

    > Math.trunc(2.1)
    2
    > Math.trunc(2.9)
    2
    

Tbl. 12 显示了几个代表性输入的舍入函数的结果。

表 12:Math的舍入函数。请注意,由于“更大”始终意味着“更接近正无穷大”,因此负数会导致结果发生变化。

-2.9 -2.5 -2.1 2.1 2.5 2.9
Math.floor -3 -3 -3 2 2 2
Math.ceil -2 -2 -2 3 3 3
Math.round -3 -2 -2 2 3 3
Math.trunc -2 -2 -2 2 2 2

17.4 三角函数

所有角度均以弧度指定。使用以下两个函数在度和弧度之间进行转换。

function degreesToRadians(degrees) {
 return degrees / 180 * Math.PI;
}
assert.equal(degreesToRadians(90), Math.PI/2);

function radiansToDegrees(radians) {
 return radians / Math.PI * 180;
}
assert.equal(radiansToDegrees(Math.PI), 180);
  • Math.acos(x: number): number ^([ES1])

    返回 x 的反余弦(反余弦)。

    > Math.acos(0)
    1.5707963267948966
    > Math.acos(1)
    0
    
  • Math.acosh(x: number): number ^([ES6])

    返回 x 的反双曲余弦。

  • Math.asin(x: number): number ^([ES1])

    返回 x 的反正弦(反正弦)。

    > Math.asin(0)
    0
    > Math.asin(1)
    1.5707963267948966
    
  • Math.asinh(x: number): number ^([ES6])

    返回 x 的反双曲正弦。

  • Math.atan(x: number): number ^([ES1])

    返回 x 的反正切(反正切)。

  • Math.atanh(x: number): number ^([ES6])

    返回 x 的反双曲正切。

  • Math.atan2(y: number, x: number): number ^([ES1])

    返回商 y/x 的反正切。

  • Math.cos(x: number): number ^([ES1])

    返回 x 的余弦。

    > Math.cos(0)
    1
    > Math.cos(Math.PI)
    -1
    
  • Math.cosh(x: number): number ^([ES6])

    返回 x 的双曲余弦。

  • Math.hypot(...values: number[]): number ^([ES6])

    返回 values 的平方和的平方根(毕达哥拉斯定理):

    > Math.hypot(3, 4)
    5
    
  • Math.sin(x: number): number ^([ES1])

    返回 x 的正弦。

    > Math.sin(0)
    0
    > Math.sin(Math.PI / 2)
    1
    
  • Math.sinh(x: number): number ^([ES6])

    返回 x 的双曲正弦。

  • Math.tan(x: number): number ^([ES1])

    返回 x 的正切。

    > Math.tan(0)
    0
    > Math.tan(1)
    1.5574077246549023
    
  • Math.tanh(x: number): number; ^([ES6])

    返回 x 的双曲正切。

17.5 其他各种功能

  • Math.abs(x: number): number ^([ES1])

    返回 x 的绝对值。

    > Math.abs(3)
    3
    > Math.abs(-3)
    3
    > Math.abs(0)
    0
    
  • Math.clz32(x: number): number ^([ES6])

    计算 32 位整数 x 中前导零位的数量。用于 DSP 算法。

    > Math.clz32(0b01000000000000000000000000000000)
    1
    > Math.clz32(0b00100000000000000000000000000000)
    2
    > Math.clz32(2)
    30
    > Math.clz32(1)
    31
    
  • Math.max(...values: number[]): number ^([ES1])

    values 转换为数字并返回最大值。

    > Math.max(3, -5, 24)
    24
    
  • Math.min(...values: number[]): number ^([ES1])

    values 转换为数字并返回最小值。

    > Math.min(3, -5, 24)
    -5
    
  • Math.random(): number ^([ES1])

    返回一个伪随机数 n,其中 0 ≤ n < 1。

    /** Returns a random integer i with 0 <= i < max */
    function getRandomInteger(max) {
     return Math.floor(Math.random() * max);
    }
    
  • Math.sign(x: number): number ^([ES6])

    返回一个数字的符号:

    > Math.sign(-8)
    -1
    > Math.sign(0)
    0
    > Math.sign(3)
    1
    

17.6 来源

评论

十八、大整数 - 任意精度整数 [ES2020](高级)

原文:exploringjs.com/impatient-js/ch_bigints.html

译者:飞龙

协议:CC BY-NC-SA 4.0


  • 18.1 为什么 bigints?

  • 18.2 Bigints

    • 18.2.1 超越 53 位整数

    • 18.2.2 示例:使用 bigints

  • 18.3 大整数文字

  • 18.4 重用 bigints 的数字运算符(重载)

    • 18.4.1 算术运算符

    • 18.4.2 排序运算符

    • 18.4.3 位运算符

    • 18.4.4 松散相等(==)和不等(!=

    • 18.4.5 严格相等(===)和不等(!==

  • 18.5 包装构造函数BigInt

    • 18.5.1 BigInt作为构造函数和作为函数

    • 18.5.2 BigInt.prototype.* 方法

    • 18.5.3 BigInt.* 方法

    • 18.5.4 强制转换和 64 位整数

  • 18.6 将 bigints 强制转换为其他原始类型

  • 18.7 64 位值的 TypedArrays 和 DataView 操作

  • 18.8 Bigints 和 JSON

    • 18.8.1 将 bigints 转换为字符串

    • 18.8.2 解析大整数

  • 18.9 常见问题:Bigints

    • 18.9.1 我如何决定何时使用数字,何时使用 bigints?

    • 18.9.2 为什么不像 bigints 一样增加数字的精度?


在本章中,我们将看一下bigints,JavaScript 的整数,其存储空间会根据需要增长和缩小。

18.1 为什么 bigints?

在 ECMAScript 2020 之前,JavaScript 处理整数如下:

  • 浮点数和整数只有一个类型:64 位浮点数(IEEE 754 双精度)。

  • 在幕后,大多数 JavaScript 引擎透明地支持整数:如果一个数字没有小数位并且在某个范围内,它可以在内部存储为真正的整数。这种表示称为小整数,通常适合 32 位。例如,在 V8 引擎的 64 位版本上,小整数的范围是从-2³¹到 2³¹-1(来源)。

  • JavaScript 数字也可以表示超出小整数范围的整数,作为浮点数。在这里,安全范围是加/减 53 位。有关此主题的更多信息,请参见§16.9.3“安全整数”。

有时,我们需要超过有符号的 53 位 - 例如:

  • Twitter 使用 64 位整数作为推文的 ID(来源)。在 JavaScript 中,这些 ID 必须存储为字符串。

  • 金融技术使用所谓的大整数(具有任意精度的整数)来表示货币金额。在内部,金额被乘以一个数,以便十进制数消失。例如,美元金额被乘以 100,以便消失掉美分。

18.2 Bigints

Bigint是一个新的整数原始数据类型。大整数的存储大小没有固定的位数;它们的大小会根据它们所表示的整数而调整:

  • 小整数的位数比大整数少。

  • 可以表示的整数没有负的下限或正的上限。

大整数文字是一个由一个或多个数字组成的序列,后面跟着一个 n - 例如:

123n

诸如 -* 这样的运算符被重载并且可以与大整数一起使用:

> 123n * 456n
56088n

大整数是原始值。typeof 为它们返回一个新的结果:

> typeof 123n
'bigint'

18.2.1 超过 53 位的整数

JavaScript 数字在内部表示为一个乘以指数的分数(有关详细信息,请参见§16.8“背景:浮点精度”)。因此,如果我们超过了最高的安全整数 2⁵³−1,仍然有一些整数可以表示,但它们之间存在间隙:

> 2**53 - 2 // safe
9007199254740990
> 2**53 - 1 // safe
9007199254740991

> 2**53 // unsafe, same as next integer
9007199254740992
> 2**53 + 1
9007199254740992
> 2**53 + 2
9007199254740994
> 2**53 + 3
9007199254740996
> 2**53 + 4
9007199254740996
> 2**53 + 5
9007199254740996

大整数使我们能够超过 53 位:

> 2n**53n
9007199254740992n
> 2n**53n + 1n
9007199254740993n
> 2n**53n + 2n
9007199254740994n

18.2.2 示例:使用大整数

这就是使用大整数的样子(基于提案中的一个例子):

/**
 * Takes a bigint as an argument and returns a bigint
 */
function nthPrime(nth) {
 if (typeof nth !== 'bigint') {
 throw new TypeError();
 }
 function isPrime(p) {
 for (let i = 2n; i < p; i++) {
 if (p % i === 0n) return false;
 }
 return true;
 }
 for (let i = 2n; ; i++) {
 if (isPrime(i)) {
 if (--nth === 0n) return i;
 }
 }
}

assert.deepEqual(
 [1n, 2n, 3n, 4n, 5n].map(nth => nthPrime(nth)),
 [2n, 3n, 5n, 7n, 11n]
);

18.3 大整数文字

与数字文字一样,大整数文字支持几种基数:

  • 十进制:123n

  • 十六进制:0xFFn

  • 二进制:0b1101n

  • 八进制:0o777n

负大整数是通过在数字前加上一元减号操作符来产生的:-0123n

18.3.1 大整数文字中的下划线(_)作为分隔符 [ES2021]

就像在数字文字中一样,我们可以在大整数文字中使用下划线(_)作为分隔符:

const massOfEarthInKg = 6_000_000_000_000_000_000_000_000n;

大整数经常用于在金融技术领域表示货币。分隔符在这里也有帮助:

const priceInCents = 123_000_00n; // 123 thousand dollars

与数字文字一样,有两个限制:

  • 我们只能在两个数字之间放一个下划线。

  • 我们最多可以连续使用一个下划线。

18.4 重用大整数的数字运算符(重载)

对于大多数运算符,我们不允许混合使用大整数和数字。如果这样做,就会抛出异常:

> 2n + 1
TypeError: Cannot mix BigInt and other types, use explicit conversions

这个规则的原因是没有一般的方法来强制一个数字和一个大整数转换为一个公共类型:数字无法表示超过 53 位的大整数,大整数无法表示分数。因此,这些异常警告我们可能导致意外结果的拼写错误。

例如,以下表达式的结果应该是 9007199254740993n 还是 9007199254740992

2**53 + 1n

以下表达式的结果也不清楚:

2n**53n * 3.3

18.4.1 算术运算符

二进制 +,二进制 -*** 的工作方式与预期相同:

> 7n * 3n
21n

混合使用大整数和字符串是可以的:

> 6n + ' apples'
'6 apples'

/% 向零舍入(就像 Math.trunc()):

> 1n / 2n
0n

一元 - 的工作方式与预期相同:

> -(-64n)
64n

大整数不支持一元 +,因为很多代码依赖于它将其操作数强制转换为数字:

> +23n
TypeError: Cannot convert a BigInt value to a number

18.4.2 排序运算符

排序运算符 <>>=<= 的工作方式与预期相同:

> 17n <= 17n
true
> 3n > -1n
true

比较大整数和数字不会带来任何风险。因此,我们可以混合使用大整数和数字:

> 3n > -1
true

18.4.3 按位运算符

18.4.3.1 数字的按位运算符

按位运算符将数字解释为 32 位整数。这些整数可以是无符号的,也可以是有符号的。如果它们是有符号的,那么一个整数的负数是它的二进制补码(将一个整数加上它的二进制补码 - 忽略溢出 - 会得到零):

> 2**32-1 >> 0
-1

由于这些整数具有固定的大小,它们的最高位表示它们的符号:

> 2**31 >> 0 // highest bit is 1
-2147483648
> 2**31 - 1 >> 0 // highest bit is 0
2147483647
18.4.3.2 大整数的按位运算符

对于大整数,按位运算符将负号解释为无限的二进制补码 - 例如:

  • -1···111111(1 无限扩展到左边)

  • -2···111110

  • -3···111101

  • -4···111100

也就是说,负号更像是一个外部标志,而不是实际表示为一个位。

18.4.3.3 按位取反(~

按位取反(~)反转所有位:

> ~0b10n
-3n
> ~0n
-1n
> ~-2n
1n
18.4.3.4 二进制按位运算符(&|^

将二进制按位运算符应用于大整数的工作方式类似于将它们应用于数字:

> (0b1010n |  0b0111n).toString(2)
'1111'
> (0b1010n &  0b0111n).toString(2)
'10'

> (0b1010n | -1n).toString(2)
'-1'
> (0b1010n & -1n).toString(2)
'1010'
18.4.3.5 按位有符号移位运算符(<<>>

bigint 的有符号移位运算符保留数字的符号:

> 2n << 1n
4n
> -2n << 1n
-4n

> 2n >> 1n
1n
> -2n >> 1n
-1n

回想一下,-1n 是一个向左无限延伸的一系列数字。这就是为什么将其向左移动不会改变它的原因:

> -1n >> 20n
-1n
18.4.3.6 位无符号右移运算符 (>>>)

bigint 没有无符号右移运算符:

> 2n >>> 1n
TypeError: BigInts have no unsigned right shift, use >> instead

为什么?无符号右移的想法是从“左边”移入一个零。换句话说,假设是有限数量的二进制数字。

然而,对于 bigint,没有“左”,它们的二进制数字无限延伸。这在处理负数时尤为重要。

有符号右移即使在有无限位数的情况下也可以工作,因为最高位数字被保留。因此,它可以适应 bigint。

18.4.4 宽松相等 (==) 和不等 (!=)

宽松相等 (==) 和不等 (!=) 强制转换值:

> 0n == false
true
> 1n == true
true

> 123n == 123
true

> 123n == '123'
true

18.4.5 严格相等 (===) 和不等 (!==)

严格相等 (===) 和不等 (!==) 只有在它们具有相同类型时才被认为是相等的:

> 123n === 123
false
> 123n === 123n
true

18.5 包装构造函数 BigInt

类似于数字,bigint 有关联的包装构造函数 BigInt

18.5.1 BigInt 作为构造函数和作为函数

  • new BigInt(): 抛出 TypeError

  • BigInt(x) 将任意值 x 转换为 bigint。这类似于 Number(),但有几个不同之处,这些不同之处在 tbl. 13 中总结,并在以下小节中详细解释。

表 13:将值转换为 bigint。

x BigInt(x)
undefined 抛出 TypeError
null 抛出 TypeError
布尔值 false 0n, true 1n
数字 例子:123 123n
非整数 抛出 RangeError
bigint x (不变)
字符串 例子:'123' 123n
无法解析的 抛出 SyntaxError
符号 抛出 TypeError
对象 可配置的(例如通过 .valueOf()
18.5.1.1 转换 undefinednull

如果 xundefinednull,则抛出 TypeError

> BigInt(undefined)
TypeError: Cannot convert undefined to a BigInt
> BigInt(null)
TypeError: Cannot convert null to a BigInt
18.5.1.2 转换字符串

如果一个字符串不表示整数,BigInt() 抛出 SyntaxError(而 Number() 返回错误值 NaN):

> BigInt('abc')
SyntaxError: Cannot convert abc to a BigInt

后缀 'n' 是不允许的:

> BigInt('123n')
SyntaxError: Cannot convert 123n to a BigInt

bigint 文字的所有基数都是允许的:

> BigInt('123')
123n
> BigInt('0xFF')
255n
> BigInt('0b1101')
13n
> BigInt('0o777')
511n
18.5.1.3 非整数数字会产生异常
> BigInt(123.45)
RangeError: The number 123.45 cannot be converted to a BigInt because
it is not an integer
> BigInt(123)
123n
18.5.1.4 转换对象

如何将对象转换为 bigint 可以进行配置 - 例如,通过覆盖 .valueOf()

> BigInt({valueOf() {return 123n}})
123n

18.5.2 BigInt.prototype.* 方法

BigInt.prototype 包含了原始 bigint “继承”的方法:

  • BigInt.prototype.toLocaleString(locales?, options?)

  • BigInt.prototype.toString(radix?)

  • BigInt.prototype.valueOf()

18.5.3 BigInt.* 方法

  • BigInt.asIntN(width, theInt)

    theInt 转换为 width 位(有符号)。这会影响值在内部的表示方式。

  • BigInt.asUintN(width, theInt)

    theInt 转换为 width 位(无符号)。

18.5.4 强制转换和 64 位整数

强制转换允许我们创建具有特定位数的整数值。如果我们想要限制自己只使用 64 位整数,我们必须始终进行强制转换:

const uint64a = BigInt.asUintN(64, 12345n);
const uint64b = BigInt.asUintN(64, 67890n);
const result = BigInt.asUintN(64, uint64a * uint64b);

18.6 将 bigint 强制转换为其他原始类型

这个表格展示了如果我们将 bigint 转换为其他原始类型会发生什么:

转换为 显式转换 强制转换(隐式转换)
布尔值 Boolean(0n) false !0n true
Boolean(int) true !int false
数字 Number(7n) 7 (例子) +int TypeError (1)
字符串 String(7n) '7' (例子) ''+7n '7' (例子)

注释:

  • (1) 由于很多代码依赖于它将其操作数强制转换为数字,因此 bigint 不支持一元 +

18.7 64 位值的 TypedArrays 和 DataView 操作

由于 bigint,Typed Arrays 和 DataViews 可以支持 64 位值。

  • 类型化数组构造函数:

    • BigInt64Array

    • BigUint64Array

  • DataView 方法:

    • DataView.prototype.getBigInt64()

    • DataView.prototype.setBigInt64()

    • DataView.prototype.getBigUint64()

    • DataView.prototype.setBigUint64()

18.8 Bigints 和 JSON

JSON 标准是固定的,不会改变。好处是旧的 JSON 解析代码永远不会过时。坏处是 JSON 无法扩展以包含 bigint。

序列化 bigint 会抛出异常:

> JSON.stringify(123n)
TypeError: Do not know how to serialize a BigInt
> JSON.stringify([123n])
TypeError: Do not know how to serialize a BigInt

18.8.1 序列化 bigint

因此,我们最好的选择是将 bigint 存储为字符串:

const bigintPrefix = '[[bigint]]';

function bigintReplacer(_key, value) {
 if (typeof value === 'bigint') {
 return bigintPrefix + value;
 }
 return value;
}

const data = { value: 9007199254740993n };
assert.equal(
 JSON.stringify(data, bigintReplacer),
 '{"value":"[[bigint]]9007199254740993"}'
);

18.8.2 解析 bigint

以下代码显示了如何解析诸如我们在前面示例中生成的字符串。

function bigintReviver(_key, value) {
 if (typeof value === 'string' && value.startsWith(bigintPrefix)) {
 return BigInt(value.slice(bigintPrefix.length));
 }
 return value;
}

const str = '{"value":"[[bigint]]9007199254740993"}';
assert.deepEqual(
 JSON.parse(str, bigintReviver),
 { value: 9007199254740993n }
);

18.9 常见问题:Bigints

18.9.1 我如何决定何时使用数字,何时使用 bigint?

我的建议:

  • 对于最多 53 位和数组索引,请使用数字。原因是:它们已经随处可见,并且大多数引擎都可以高效处理它们(特别是如果它们适合 31 位)。出现的情况包括:

    • Array.prototype.forEach()

    • Array.prototype.entries()

  • 对于大数值,请使用 bigint:如果您的无小数数值不适合 53 位,那么您别无选择,只能转为 bigint。

所有现有的 web API 只返回和接受数字,并且只会在特定情况下升级为 bigint。

18.9.2 为什么不像 bigint 一样增加数字的精度?

可以想象将number分成integerdouble,但这将给语言增加许多新的复杂性(几个仅限整数的运算符等)。我在a Gist中勾勒了后果。


致谢:

  • 感谢 Daniel Ehrenberg 对此内容的早期版本进行审查。

  • 感谢 Dan Callahan 对此内容的早期版本进行审查。

评论

十九、Unicode-简介(高级)

原文:exploringjs.com/impatient-js/ch_unicode.html

译者:飞龙

协议:CC BY-NC-SA 4.0


  • 19.1 码点与码元

    • 19.1.1 码点

    • 19.1.2 编码 Unicode 码点:UTF-32,UTF-16,UTF-8

  • 19.2 Web 开发中使用的编码:UTF-16 和 UTF-8

    • 19.2.1 内部源代码:UTF-16

    • 19.2.2 字符串:UTF-16

    • 19.2.3 文件中的源代码:UTF-8

  • 19.3 字形簇-真正的字符

    • 19.3.1 字形簇与字形

Unicode 是表示和管理世界上大多数书写系统的文本的标准。几乎所有处理文本的现代软件都支持 Unicode。该标准由 Unicode 联盟维护。每年都会发布标准的新版本(带有新的表情符号等)。Unicode 版本 1.0.0 于 1991 年 10 月发布。

19.1 码点与码元

了解 Unicode 的两个概念至关重要:

  • 码点是表示 Unicode 文本的原子部分的数字。它们大多表示可见符号,但也可以具有其他含义,例如指定符号的某个方面(字母的重音,表情符号的肤色等)。

  • 码元是编码码点的数字,用于存储或传输 Unicode 文本。一个或多个码元编码一个单个码点。每个码元的大小相同,取决于所使用的编码格式。最流行的格式 UTF-8 具有 8 位码元。

19.1.1 码点

Unicode 的第一个版本具有 16 位码点。从那时起,字符数量大大增加,码点的大小扩展到 21 位。这 21 位被分成 17 个平面,每个平面有 16 位:

  • 平面 0:基本多语言平面(BMP),0x0000–0xFFFF

    • 包含几乎所有现代语言的字符(拉丁字符,亚洲字符等)和许多符号。
  • 平面 1:补充多语言平面(SMP),0x10000–0x1FFFF

    • 支持历史书写系统(例如埃及象形文字和楔形文字)和其他现代书写系统。

    • 支持表情符号和许多其他符号。

  • 平面 2:补充表意文字平面(SIP),0x20000–0x2FFFF

    • 包含额外的 CJK(中文,日文,韩文)表意文字。
  • 平面 3-13:未分配

  • 平面 14:补充特殊用途平面(SSP),0xE0000–0xEFFFF

    • 包含标记字符和字形变体选择器等非图形字符。
  • 平面 15-16:补充专用区域(S PUA A/B),0x0F0000–0x10FFFF

    • 可供 ISO 和 Unicode 联盟之外的各方分配字符。未标准化。

飞机 1-16 被称为补充平面或星界平面

让我们检查一些字符的码点:

> 'A'.codePointAt(0).toString(16)
'41'
> 'ü'.codePointAt(0).toString(16)
'fc'
> 'π'.codePointAt(0).toString(16)
'3c0'
> '

标签:运算符,JavaScript,number,整数,写给,程序员,true,Math
From: https://www.cnblogs.com/apachecn/p/17982462

相关文章

  • 深入 JavaScript:理论和技术(上)
    第一部分:前言原文:exploringjs.com/deep-js/pt_frontmatter.html译者:飞龙协议:CCBY-NC-SA4.0下一步:1关于本书一、关于这本书原文:exploringjs.com/deep-js/ch_about-book.html译者:飞龙协议:CCBY-NC-SA4.01.1 这本书的主页在哪里?1.2 这本书包括什么?1.3 ......
  • 黎活明给程序员的忠告
    黎活明,传智播客教育总裁,JavaEE高级架构师、咨询顾问、高级讲师,国内第一本ejb3.0书籍的作者,有着丰富的b/s系统开发与网站运营经验。现任传智播客教育科技有限公司JavaEE高级架构师与咨询顾问。其中一些我认为经典的地方,做了标红处理。展望未来,总结过去10年的程序员生涯,给程序员小......
  • 计算机编程中的黑魔法编程是什么?如何求解一个浮点数的平方根倒数?计算机中的浮点数是如
    原视频:没有显卡的年代,这群程序员用4行代码优化游戏最原始的求解目标:(求一个浮点数的开方的导数)浮点数在计算机中的表示形式:对数的运算法则:A为a在计算机中的表示形式(二进制表示形式):求浮点数的平方根倒数的应用场景:这个情况,直白的说就......
  • 细说JavaScript事件处理(JavaScript事件处理详解)
    js语言的一个特色和就是它的动态性,即一时间驱动的方式对用户输入作出反应而不需要依赖服务器端程序。事件是指人机交互的结果,如鼠标移动、点击按钮、在表单中输入数据或载入新的Web洁面等。一、什么是事件事件是一些可以通过脚本响应的页面动作,当文档、浏览器、元素发生某些有趣......
  • JavaScript DOM表单相关操作之表单相关事件
    1、焦点事件焦点事件就是鼠标的光标事件,点到输入框中,叫做获得焦点事件,当鼠标离开这个输入框时叫做失去焦点事件。​<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>知数SEO_专注搜索引擎优化和品牌推广</title></head><body><form><h3>......
  • JavaScript DOM表单相关操作之获取表单数据的方式
    在与表单相关的操作中,我们用的最多的就是获取表单中的数据。想要获取指定输入框的数据,首先就需要获取到这个输入框对象。1、通过id属性获取表单数据​<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>知数SEO_专注搜索引擎优化和品牌推广</title></head......
  • 细说JavaScript事件处理(JavaScript事件处理详解)
    js语言的一个特色和就是它的动态性,即一时间驱动的方式对用户输入作出反应而不需要依赖服务器端程序。事件是指人机交互的结果,如鼠标移动、点击按钮、在表单中输入数据或载入新的Web洁面等。一、什么是事件事件是一些可以通过脚本响应的页面动作,当文档、浏览器、元素发生某些有......
  • 一文读懂JavaScript DOM节点操作(JavaScript DOM节点操作详解)
    一、什么是节点DOM模型是树状结构模型,二组成这棵树的就是一个个点,在网络术语中称之为节点。节点是一个模型中最基本的组成单位。DOM模型是由一个个节点组成的,DOM节点也有其不同的类型。二、节点类型DOM节点分为5种类型:文档节点(就是整个HTML文档,也就是document对象)元素节点......
  • JavaScript DOM表单相关操作之表单相关事件
    1、焦点事件焦点事件就是鼠标的光标事件,点到输入框中,叫做获得焦点事件,当鼠标离开这个输入框时叫做失去焦点事件。<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>知数SEO_专注搜索引擎优化和品牌推广</title></head><body><form><h3>输......
  • JavaScript DOM表单相关操作之获取表单数据的方式
    在与表单相关的操作中,我们用的最多的就是获取表单中的数据。想要获取指定输入框的数据,首先就需要获取到这个输入框对象。1、通过id属性获取表单数据<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>知数SEO_专注搜索引擎优化和品牌推广</title></head>......