JavaScript 高级程序设计 第三章---语言基础(2)

第三章—语言基础(2)

教材:JavaScript 高级程序设计
关键字: 数据类型 操作符

一. 数据类型

ECMAScript 有6 种简单数据类型(也称为原始类型):Undefined、Null、Boolean、Number、String 和Symbol。Symbol(符号)是ECMAScript 6 新增的。还有一种复杂数据类型叫Object(对象)。Object 是一种无序名值对的集合。

1. typeof操作符

用来检测数据类型,对一个值使用typeof 操作符会返回下列字符串之一:

  • "undefined"表示值未定义;
  • "boolean"表示值为布尔值;
  • "string"表示值为字符串;
  • "number"表示值为数值;
  • "object"表示值为对象(而不是函数)或null;
  • "function"表示值为函数;
  • "symbol"表示值为符号。

示例:

let message = "some string";
console.log(typeof message); // "string"
console.log(typeof(message)); // "string"
console.log(typeof 95); // "number"

typeof 是一个操作符而不是函数,所以不需要参数(但可以使用参数)。

注意typeof 在某些情况下返回的结果可能会让人费解,但技术上讲还是正确的。比如,调用typeof null 返回的是"object"。这是因为特殊值null 被认为是一个对空对象的引用。

Undefined类型

Undefined 类型只有一个值,就是特殊值undefined。
当使用var 或let 声明了变量但没有初始化时,就相当于给变量赋予了undefined 值

let message;
console.log(message == undefined); // true

但对未声明的变量调用typeof时返回值都是"undefined"。
所以最好在变量声明时同时进行初始化,这样当出现"undefined"时就代表了未声明的变量。

let message; // 这个变量被声明了,只是值为undefined
// age 没有声明
if (message) {
// 这个块不会执行
}
if (!message) {
// 这个块会执行
}
if (age) {
// 这里会报错
}

Null类型

Null 类型同样只有一个值,即特殊值null。逻辑上讲,null 值表示一个空对象指针,这也是给 typeof 传一个null 会返回"object"的原因:

let car = null;
console.log(typeof car); // "object"

undefined 值是由null 值派生而来的,因此ECMA-262 将它们定义为表面上相等,如下面的例子所示:

console.log(null == undefined); // true

但undefined是默认值,null需要在初始化时用null来填充。

let car = null;

与undefined相同,同样是一个假值:

let message = null;
if (message) {
// 这个块不会执行
}
if (!message) {
// 这个块会执行
}

Boolean类型

Boolean(布尔值)类型是ECMAScript 中使用最频繁的类型之一,有两个字面值:true 和false。

注意,布尔值字面量true 和false 是区分大小写的,因此True 和False(及其他大小混写形式) 是有效的标识符,但不是布尔值。

要将一个其他类型的值转换为布尔值,可以调用特定的Boolean()转型函数:

let message = "Hello world!";
let messageAsBoolean = Boolean(message);

message保持不变,messageAsBoolean变成true。

下表总结了对于不同数据类型的变量,当变量值处于哪种情况时,运Boolean()转型函数会返回true或者false。
在这里插入图片描述

Number类型

Number 类型使用IEEE 754 格式表示整数和浮点值(在某些语言中也叫双精度值)。

字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串

最基本的数值字面量格式是十进制整数,直接写出来即可:

let intNum = 55; // 整数

整数也可以用八进制(以8 为基数)或十六进制(以16 为基数)字面量表示。对于八进制字面量,第一个数字必须是零(0),然后是相应的八进制数字(数值0~7)。如果字面量中包含的数字超出了应有的范围,就会忽略前缀的零,后面的数字序列会被当成十进制数,如下所示:

let octalNum1 = 070; // 八进制的56
let octalNum2 = 079; // 无效的八进制值,当成79 处理
let octalNum3 = 08; // 无效的八进制值,当成8 处理

八进制字面量在严格模式下是无效的,会导致JavaScript 引擎抛出语法错误。

要创建十六进制字面量,必须让真正的数值前缀0x(区分大小写),然后是十六进制数字(0~ 9 以及A~F)。十六进制数字中的字母大小写均可。下面是几个例子:

let hexNum1 = 0xA; // 十六进制10
let hexNum2 = 0x1f; // 十六进制31

使用八进制和十六进制格式创建的数值在所有数学操作中都被视为十进制数值。

因为存储浮点值使用的内存空间是存储整数值的两倍,所以ECMAScript 总是想方设法把值转换为整数。在小数点后面没有数字的情况下,数值就会变成整数
类似地,如果数值本身就是整数,只是小数点后面跟着0(如1.0),那它也会被转换为整数,如下例所示:

let floatNum1 = 1.; // 小数点后面没有数字,当成整数1 处理
let floatNum2 = 10.0; // 小数点后面是零,当成整数10 处理

浮点值的精确度最高可达17 位小数,但在算术计算中远不如整数精确。例如,0.1 加0.2 得到的不是0.3,而是0.300 000 000 000 000 04。由于这种微小的舍入错误,导致很难测试特定的浮点值。比如下面的例子:

if (a + b == 0.3) { // 别这么干!
console.log("You got 0.3.");
}
值的范围

ECMAScript 可以表示的最小数值保存在Number.MIN_VALUE 中,可以表示的最大数值保存在Number.MAX_VALUE 中。
如果计算返回正Infinity 或负Infinity,则该值将不能再进一步用于任何计算。这是因为Infinity 没有可用于计算的数值表示形式。要确定一个值是不是有限大(即介于JavaScript 能表示的最小值和最大值之间),可以使用isFinite()函数,如下所示:

let result = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); // false
NaN

有一个特殊的数值叫NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。比如,用0 除任意数值在其他语言中通常都会导致错误,从而中止代码执行。但在ECMAScript 中,0、+0 或0 相除会返回NaN:

console.log(0/0); // NaN
console.log(-0/+0); // NaN

如果分子是非0 值,分母是有符号0 或无符号0,则会返回Infinity 或-Infinity:

console.log(5/0); // Infinity
console.log(5/-0); // -Infinity

NaN 有几个独特的属性。首先,任何涉及NaN 的操作始终返回NaN(如NaN/10),在连续多步计算时这可能是个问题。其次,NaN 不等于包括NaN 在内的任何值。例如,下面的比较操作会返回false:

console.log(NaN == NaN); // false

ECMAScript 提供了isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。把一个值传给isNaN()后,该函数会尝试把它转换为数值。某些非数值的值可以直接转换成数值,如字符串"10"或布尔值。任何不能转换为数值的值都会导致这个函数返回true。举例如下:

console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN("10")); // false,可以转换为数值10
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值1
数值转换

有3 个函数可以将非数值转换为数值:Number()、parseInt()和parseFloat()。

Number数值转换

例子:

let num1 = Number("Hello world!"); // NaN
let num2 = Number(""); // 0
let num3 = Number("000011"); // 11
let num4 = Number(true); // 1

parseInt()函数更专注于字符串是否包含数值模式。字符串最前面的空格会被忽略,从第一个非空格字符开始转换。

  • 如果第一个字符不是数值字符、加号或减号,parseInt()立即返回NaN。这意味着空字符串也会返回NaN(这一点跟Number()不一样,它返回0)。
  • 如果第一个字符是数值字符、加号或减号,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符。

可以识别八进制,十六进制整数。如果字符串以"0x"开头,就会被解释为十六进制整数。如果字符串以"0"开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为八进制整数。

let num1 = parseInt("1234blue"); // 1234
let num2 = parseInt(""); // NaN
let num3 = parseInt("0xA"); // 10,解释为十六进制整数
let num4 = parseInt(22.5); // 22
let num5 = parseInt("70"); // 70,解释为十进制值
let num6 = parseInt("0xf"); // 15,解释为十六进制整数

不同的数值格式很容易混淆,因此parseInt()也接收第二个参数,用于指定底数(进制数)。如果知道要解析的值是十六进制,那么可以传入16 作为第二个参数,以便正确解析:

let num = parseInt("0xAF", 16); // 175

事实上,如果提供了十六进制参数,那么字符串前面的"0x"可以省掉:

let num1 = parseInt("AF", 16); // 175
let num2 = parseInt("AF"); // NaN

在这个例子中,第一个转换是正确的,而第二个转换失败了。区别在于第一次传入了进制数作为参数,告诉parseInt()要解析的是一个十六进制字符串。而第二个转换检测到第一个字符就是非数值字符,随即自动停止并返回NaN。

parseFloat()函数的工作方式跟parseInt()函数类似,都是从位置0 开始检测每个字符。同样,它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。这意味着第一次出现的小数点是有效的,但第二次出现的小数点就无效了,此时字符串的剩余字符都会被忽略。因此,"22.34.5"将转换成22.34。

示例:

let num1 = parseFloat("1234blue"); // 1234,按整数解析
let num2 = parseFloat("0xA"); // 0
let num3 = parseFloat("22.5"); // 22.5
let num4 = parseFloat("22.34.5"); // 22.34
let num5 = parseFloat("0908.5"); // 908.5
let num6 = parseFloat("3.125e7"); // 31250000

String类型

String(字符串)数据类型表示零或多个16 位Unicode 字符序列。字符串可以使用双引号(")、单引号(’)或反引号(`)标示,因此下面的代码都是合法的:

let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`
字符串特点

ECMAScript 中的字符串是不可变的(immutable),意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。

如下所示:

let lang = "Java";
lang = lang + "Script";

这里,变量lang 一开始包含字符串"Java"。紧接着,lang 被重新定义为包含"Java"和"Script"的组合,也就是"JavaScript"。整个过程首先会分配一个足够容纳10 个字符的空间,然后填充上"Java"和"Script"。最后销毁原始的字符串"Java"和字符串"Script"

转换为字符串

使用几乎所有值都有的toString()方法。这个方法唯一的用途就是返回当前值的字符串等价物。

let age = 11;
let ageAsString = age.toString(); // 字符串"11"
let found = true;
let foundAsString = found.toString(); // 字符串"true"

toString()方法可见于数值、布尔值、对象和字符串值。(没错,字符串值也有toString()方法,该方法只是简单地返回自身的一个副本。)null 和undefined 值没有toString()方法。

String()转型函数,始终会返回表示相应类型值的字符串。String()函数遵循如下规则。

  • 如果值有toString()方法,则调用该方法(不传参数)并返回结果。
  • 如果值是null,返回"null"。
  • 如果值是undefined,返回"undefined"。

注意
用加号操作符给一个值加上一个空字符串""也可以将其转换为字符串

模板字面量

ECMAScript 6 新增了使用模板字面量定义字符串的能力。
模板字符串使用反引号来代替普通字符串中的用双引号和单引号。
与使用单引号或双引号不同,模板字面量保留换行字符,可以跨行定义字符串:

let myMultiLineString = 'first line\nsecond line';
let myMultiLineTemplateLiteral = `first line
second line`;
console.log(myMultiLineString);
// first line
// second line"
console.log(myMultiLineTemplateLiteral);
// first line
// second line
console.log(myMultiLineString === myMultiLinetemplateLiteral); // true

两者是一致的。即使用反引号时,内部的空格,换行会作为字符串的一部分保留下来。

字符串插值

模板字面量最常用的一个特性是支持字符串插值,也就是可以在一个连续定义中插入一个或多个值。技术上讲,模板字面量不是字符串,而是一种特殊的JavaScript 句法表达式,只不过求值后得到的是字符串。模板字面量在定义时立即求值并转换为字符串实例,任何插入的变量也会从它们最接近的作用域中取值。

字符串插值通过在${}中使用一个JavaScript 表达式实现:

let value = 5;
let exponent = 'second';

// 以前,字符串插值是这样实现的:
let interpolatedString =
value + ' to the ' + exponent + ' power is ' + (value * value);

// 现在,可以用模板字面量这样实现:
let interpolatedTemplateLiteral =
`${ value } to the ${ exponent } power is ${ value * value }`;
console.log(interpolatedString); // 5 to the second power is 25
console.log(interpolatedTemplateLiteral); // 5 to the second power is 25

所有插入的值都会使用toString()强制转型为字符串,而且任何JavaScript 表达式都可以用于插值。嵌套的模板字符串无须转义:

console.log(`Hello, ${ `World` }!`); // Hello, World!

插值表达式中可以调用函数和方法。

function capitalize(word) {
   return `${ word[0].toUpperCase() }${word.slice(1)}`
}
let message = `Hello world! ${capitalize('bob')} !`;
console.log(message) //Hello world! Bob !
模板字面量标签函数

模板字面量也支持定义标签函数(tag function),而通过标签函数可以自定义插值行为。
标签函数会接收被插值记号分隔后的模板和对每个表达式求值的结果。
标签函数本身是一个常规函数,通过前缀到模板字面量来应用自定义行为,如下例所示。标签函数接收到的参数依次是原始字符串数组和对每个表达式求值的结果。这个函数的返回值是对模板字面量求值得到的字符串。

let a = 6;
let b = 9;
function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
	console.log(strings);
	console.log(aValExpression);
	console.log(bValExpression);
	console.log(sumExpression);
	return 'foobar';
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(untaggedResult); // "6 + 9 = 15"
console.log(taggedResult); // "foobar"

即会根据插值将字符串进行分为多个组成部分,作为参数传递给标签函数。如上例将字符串和插值进行了识别,并作为参数依次传递给标签函数。

上例的函数更好的写法如下,设定可变数量的参数,使用剩余操作符(rest operator)将它们收集到一个数组中:

function simpleTag(strings, ...expressions) {
	console.log(strings);
	for(const expression of expressions) {
		console.log(expression);
	}
	return 'foobar';
}
原始字符串

使用默认的String.raw 标签函数,可以直接获取原始的模板字面量内容(如换行符或Unicode 字符),而不是被转换后的字符表示。

// 换行符示例
console.log(`first line\nsecond line`);
// first line
// second line
console.log(String.raw`first line\nsecond line`); // "first line\nsecond line"

换行符\n会作为字符串的一部分,而不是进行转义。

// 对实际的换行符来说是不行的
// 它们不会被转换成转义序列的形式
console.log(`first line
second line`);
// first line
// second line
console.log(String.raw`first line
second line`);
// first line
// second line

而对于实际的换行符来说因为不会被转义所以还是会有换行的存在。

Symbol类型

Symbol(符号)是ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

Object类型

ECMAScript 中的对象其实就是一组数据和功能的集合。对象通过new 操作符后跟对象类型的名称来创建。开发者可以通过创建Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法:

let o = new Object();

每个Object 实例都有如下属性和方法。

  • constructor:用于创建当前对象的函数。在前面的例子中,这个属性的值就是Object()函数。
  • hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属性。要检查的属性名必须是字符串(如o.hasOwnProperty(“name”))或符号。
  • isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。(第8 章将详细介绍原型。)
  • propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用(本章稍后讨论的)for-in语句枚举。与hasOwnProperty()一样,属性名必须是字符串。
  • toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象对应的字符串、数值或布尔值表示。通常与toString()的返回值相同。

因为在ECMAScript 中Object 是所有对象的基类,所以任何对象都有这些属性和方法。

二. 操作符

ECMA-262 描述了一组可用于操作数据值的操作符,包括数学操作符(如加、减)、位操作符、关系操作符和相等操作符等。ECMAScript 中的操作符是独特的,因为它们可用于各种值,包括字符串、数值、布尔值,甚至还有对象。在应用给对象时,操作符通常会调用valueOf()和/或toString()方法来取得可以计算的值。

1. 一元操作符

只操作一个值的操作符叫一元操作符(unary operator)。一元操作符是ECMAScript 中最简单的操作符。

1.1 递增递减操作符

++age--ageage--age++
对于前缀操作符,变量的值会在语句被求值之前改变。后缀操作符,变量的值会在求值之后改变。

let num1 = 2;
let num2 = 20;
let num3 = num1-- + num2; // 2 + 22 = 22
let num4 = --num1 + num2;// 0 + 20 = 20
console.log(num3); // 22
console.log(num4); // 20

递增和递减在语句中的优先级是相等的,因此会从左到右依次求值。

递增和递减操作符遵循如下规则。

  • 对于字符串,如果是有效的数值形式,则转换为数值再应用改变。变量类型从字符串变成数值。
  • 对于字符串,如果不是有效的数值形式,则将变量的值设置为NaN 。变量类型从字符串变成 数值。
  • 对于布尔值,如果是false,则转换为0 再应用改变。变量类型从布尔值变成数值。
  • 对于布尔值,如果是true,则转换为1 再应用改变。变量类型从布尔值变成数值。
  • 对于浮点值,加1 或减1。
  • 如果是对象,则调用其(第5 章会详细介绍的)valueOf()方法取得可以操作的值。对得到的值应用上述规则。如果是NaN,则调用toString()并再次应用其他规则。变量类型从对象变成 数值。
1.2 一元加减操作

相当于正负号。对其它类型的变量应用规则同上。

2. 位操作符

ECMAScript中的所有数值都以IEEE 754 64 位格式存储,但位操作并不直接应用到64 位表示,而是先把值转换为32 位整数,再进行位操作,之后再把结果转换为64 位。
有符号整数使用32 位的前31 位表示整数值。第32 位表示数值的符号,如0 表示正,1 表示负。这一位称为符号位(sign bit)。
正值以真正的二进制格式存储,
负值以一种称为二补数(或补码)的二进制编码存储(即将原本数字二进制的1,0互换后最低位加1)。

如果将位操作符应用到非数值,那么首先会使用Number()函数将该值转换为数值(这个过程是自动的),然后再应用位操作。最终结果是数值。

2.1 按位非,与,或,异或

按位非操作符用波浪符(~)表示,它的作用是返回数值的一补数。

let num1 = 25; // 二进制00000000000000000000000000011001
let num2 = ~num1; // 二进制11111111111111111111111111100110
console.log(num2); // -26

按位非的最终效果是对数值取反并减1
取反计算:

  1. 转换为二进制
  2. 01互换得到带符号二进制
  3. 保留符号位 取反再加1
  4. 转换十进制

按位与操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。
按位或操作符用管道符(|)表示,同样有两个操作数。
按位异或用脱字符(^)表示,同样有两个操作数。

2.2 左移

左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动。
会用零进行填充,并保留操作数的符号。

let oldValue = 2; // 等于二进制10
let newValue = oldValue << 5; // 等于二进制1000000,即十进制64
2.3 有符号右移

有符号右移由两个大于号(>>)表示,会将数值的所有32 位都向右移,同时保留符号(正或负)。有符号右移实际上是左移的逆运算。

let oldValue = 64; // 等于二进制1000000
let newValue = oldValue >> 5; // 等于二进制10,即十进制2

ECMAScript 会用符号位的值来填充这些空位,以得到完整的数值。

2.4 无符号右移

无符号右移用3 个大于号表示(>>>),会将数值的所有32 位都向右移。对于正数,无符号右移与有符号右移结果相同。对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补0,而不管符号位是什么。

3. 布尔操作符

布尔操作符一共有3 个:逻辑非、逻辑与和逻辑或。

3.1 逻辑非

逻辑非操作符由一个叹号(!)表示,可应用给ECMAScript 中的任何值。逻辑非操作符首先将操作数转换为布尔值,然后再对其取反。

console.log(!false); // true
console.log(!"blue"); // false
console.log(!0); // true
console.log(!NaN); // true
console.log(!""); // true
console.log(!12345); // false
  • 如果操作数是数值0,则返回true。
  • 如果操作数是非0 数值(包括Infinity),则返回false。
  • 如果操作数是null,则返回true。
  • 如果操作数是NaN,则返回true。
  • 如果操作数是undefined,则返回true。

逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用了转型函数Boolean()。

console.log(!!"blue"); // true
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!""); // false
console.log(!!12345); // true
3.2 逻辑与

逻辑与操作符由两个和号(&&)表示,应用到两个值。

  • 如果第一个操作数是对象,则返回第二个操作数。
  • 如果第二个操作数是对象,则只有第一个操作数求值为true 才会返回该对象。
  • 如果两个操作数都是对象,则返回第二个操作数。
  • 如果有一个操作数是null,则返回null。
  • 如果有一个操作数是NaN,则返回NaN。
  • 如果有一个操作数是undefined,则返回undefined。

逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值。

let result = (found && someUndeclaredVariable);
console.log(result);

在上面例子中,如果found为true,some未定义的话第一行会报错;但如果found为false,即使some未定义也不会报错(因为在第一个为false后不会再对第二个进行求值了)。

3.3 逻辑或

逻辑或操作符由两个管道符(||)表示

  • 如果第一个操作数是对象,则返回第一个操作数。
  • 如果第一个操作数求值为false,则返回第二个操作数。
  • 如果两个操作数都是对象,则返回第一个操作数。
  • 如果两个操作数都是null,则返回null。
  • 如果两个操作数都是NaN,则返回NaN。
  • 如果两个操作数都是undefined,则返回undefined。

与逻辑与操作类似,当一方为true后就不会对另一方进行判断。

let found = true;
let result = (found || someUndeclaredVariable); // 不会出错
console.log(result); // 会执行

4. 乘性操作符

ECMAScript 定义了3 个乘性操作符:乘法、除法和取模。

4.1 乘法操作符

乘法操作符由一个星号(*)表示,可以用于计算两个数值的乘积。

let result = 34 * 56;
  • 如果有任一操作数是NaN,则返回NaN。
  • 如果是Infinity 乘以0,则返回NaN。
  • 如果是Infinity 乘以非0 的有限数值,则根据第二个操作数的符号返回Infinity 或-Infinity。
  • 如果是Infinity 乘以Infinity,则返回Infinity。
  • 如果有不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。
4.2 除法操作符

除法操作符由一个斜杠(/)表示,用于计算第一个操作数除以第二个操作数的商。

  • 如果有任一操作数是NaN,则返回NaN。
  • 如果是Infinity 除以Infinity,则返回NaN。
  • 如果是0 除以0,则返回NaN。
  • 如果是非0 的有限值除以0,则根据第一个操作数的符号返回Infinity 或-Infinity。
  • 如果是Infinity 除以任何数值,则根据第二个操作数的符号返回Infinity 或-Infinity。
  • 如果有不是数值的操作数,则先在后台用Number()函数将其转换为数值,然后再应用上述规则。
4.3 取模操作符

取模(余数)操作符由一个百分比符号(%)表示。

  • 如果操作数是数值,则执行常规除法运算,返回余数。
  • 如果被除数是无限值,除数是有限值,则返回NaN。
  • 如果被除数是有限值,除数是0,则返回NaN。
  • 如果是Infinity 除以Infinity,则返回NaN。
  • 如果被除数是有限值,除数是无限值,则返回被除数。
  • 如果被除数是0,除数不是0,则返回0。
  • 如果有不是数值的操作数,则先在后台用Number()函数将其转换为数值,然后再应用上述规则。

5. 指数操作符

ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**

console.log(Math.pow(3, 2); // 9
console.log(3 ** 2); // 9

指数操作符也有自己的指数赋值操作符**=,该操作符执行指数运算和结果的赋值操作:

let squared = 3;
squared **= 2;
console.log(squared); // 9

6. 加性操作符

加性操作符,即加法和减法操作符,一般都是编程语言中最简单的操作符。

6.1 加法操作符
  • 如果有任一操作数是NaN,则返回NaN;
  • 如果是Infinity 加-Infinity,则返回NaN;
  • 如果是+0 加+0,则返回+0;
  • 如果是-0 加+0,则返回+0;
  • 如果是-0 加-0,则返回-0。
  • 如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面;
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。

如果有任一操作数是对象、数值或布尔值,则调用它们的toString()方法以获取字符串,然后再应用前面的关于字符串的规则。对于undefined 和null,则调用String()函数,分别获取"undefined"和"null"。

let num1 = 5;
let num2 = 10;
let message = "The sum of 5 and 10 is " + (num1 + num2);
console.log(message); // "The sum of 5 and 10 is 15"

字符串加法中涉及到的数值加法要用括号包围,如上例。

6.2 减法操作符

减法操作符(-)也是使用很频繁的一种操作符。

  • 如果有任一操作数是NaN,则返回NaN;
  • 如果是Infinity 加Infinity,则返回Infinity;
  • 如果是-Infinity 加-Infinity,则返回-Infinity;
  • 如果是Infinity 加-Infinity,则返回NaN;
  • 如果是+0 加+0,则返回+0;
  • 如果是-0 加+0,则返回+0;
  • 如果是-0 加-0,则返回-0;
  • 如果有任一操作数是字符串、布尔值、null 或undefined,则先在后台使用Number()将其转换为数值,然后再根据前面的规则执行数学运算。
  • 如果有任一操作数是对象,则调用其valueOf()方法取得表示它的数值。如果该值是NaN,则减法计算的结果是NaN。如果对象没有valueOf()方法,则调用其toString()方法,然后再将得到的字符串转换为数值。
let result1 = 5 - true; // true 被转换为1,所以结果是4
let result2 = NaN - 1; // NaN
let result3 = 5 - 3; // 2
let result4 = 5 - ""; // ""被转换为0,所以结果是5
let result5 = 5 - "2"; // "2"被转换为2,所以结果是3
let result6 = 5 - null; // null 被转换为0,所以结果是5

7. 关系操作符

关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=),这几个操作符会返回布尔值。

let result1 = 5 > 3; // true
let result2 = 5 < 3; // false
  • 如果操作数都是数值,则执行数值比较。
  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码。
  • 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
  • 如果有任一操作数是对象,则调用其valueOf()方法,取得结果后再根据前面的规则执行比较。如果没有valueOf()操作符,则调用toString()方法,取得结果后再根据前面的规则执行比较。
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较。

字符串大小比较:
比较的时候,从字符串左边开始,一次比较每个字符,直接出现差异、或者其中一个串结束为止。

比如ABC与ACDE比较,第一个字符相同,继续比较第二个字符,由于第二个字符是后面一个串大,所以不再继续比较,结果就是后面个串大。
再如ABC与ABC123比较,比较三个字符后第一个串结束,所以就是后面一个串大。
所以,长度不能直接决定大小,字符串的大小是由左边开始最前面的字符决定的。

8. 相等操作符

ECMAScript提供了两组操作符。第一组是等于和不等于,它们在比较之前执行转换。第二组是全等和不全等,它们在比较之前不执行转换。

8.1 等于和不等于

ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回true。不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回true。这两个操作符都会先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。

  • 如果任一操作数是布尔值,则将其转换为数值再比较是否相等。false 转换为0,true 转换 为1。
  • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否 相等。
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法取得其原始值,再 根据前面的规则进行比较。

在进行比较时,这两个操作符会遵循如下规则。

  • null 和undefined 相等。
  • null 和undefined 不能转换为其他类型的值再进行比较。
  • 如果有任一操作数是NaN,则相等操作符返回false,不相等操作符返回true。记住:即使两个操作数都是NaN,相等操作符也返回false,因为按照规则,NaN 不等于NaN
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象, 则相等操作符返回true。否则,两者不相等。
8.2 全等和不等

它们在比较相等时不转换操作数。全等操作符由3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回true

let result1 = ("55" == 55); // true,转换后相等
let result2 = ("55" === 55); // false,不相等,因为数据类型不同

不全等操作符用一个叹号和两个等于号(!==)表示,只有两个操作数在不转换的前提下不相等才返回true。

let result1 = ("55" != 55); // false,转换后相等
let result2 = ("55" !== 55); // true,不相等,因为数据类型不同

虽然null == undefined 是true(因为这两个值类似),但null === undefined
false,因为它们不是相同的数据类型。

9. 条件操作符

variable = boolean_expression ? true_value : false_value;

上面的代码执行了条件赋值操作,即根据条件表达式boolean_expression 的值决定将哪个值赋给变量variable 。如果boolean_expression 是true , 则赋值true_value ; 如果boolean_expression 是false,则赋值false_value。

10. 赋值操作符

简单赋值用等于号(=)表示,复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。

let num = 10;
num += 10;
  • 乘后赋值(*=
  • 除后赋值(/=
  • 取模后赋值(%=
  • 加后赋值(+=
  • 减后赋值(-=
  • 左移后赋值(<<=
  • 右移后赋值(>>=
  • 无符号右移后赋值(>>>=

这些操作符仅仅是简写语法,使用它们不会提升性能。

11. 逗号操作符

逗号操作符可以用来在一条语句中执行多个操作,如下所示:

let num1 = 1, num2 = 2, num3 = 3;

在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值:

let num = (5, 1, 4, 8, 0); // num 的值为0

在这个例子中,num 将被赋值为0,因为0 是表达式中最后一项。逗号操作符的这种使用场景并不多见,但这种行为的确存在。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值