- js 提供了指数运算符:* 指数运算符:
x ** y
加法运算符
根本规定
- js 容许非数值的相加:布尔值主动转化为数值
true + true // 2
1 + true // 2
- 两个字符串相加,变成连贯运算符
'a' + 'bc' // "abc"
- 字符串 + 非字符串:非字符串转为字符串,在连贯在一起
1 + 'a' // "1a"
false + 'a' // "falsea"
- 运算子的不同,导致了相加和连贯的行为不同,这种景象称为重载。留神,因为从左到右的运算程序,字符串地位不同,运算后果不同
'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"
除了加法运算符,其余算术运算符(比方减法、除法和乘法)都不会产生重载。它们的规定是:所有运算子一律转为数值,再进行相应的数学运算。
对象的相加
当运算子为对象时,必须先转换成原始类型的值,而后再相加
var obj = {p: 1};
obj + 2 // "[object Object]2"
对象转换成原始类型的值的规定
- 首先调用对象的
valueOf
办法,返回对象本身
var obj = {p: 1};
obj.valueOf() // { p: 1}
- 而后调用对象的
toString
办法,将其转为字符串。留神,对象的toString
办法默认返回[object Object]
,所以就失去了最后面那个例子的后果。
var obj = {p: 1 ;}
obj.valueOf().toString() // "[object Object]"
失去规定之后,咱们就能够本人定义 valueOf
办法或 toString
办法,失去想要的后果。
- 自定义 valueOf
var obj = {valueOf: function () {return 1;}
};
obj + 2 // 3
下面代码中,咱们定义 obj
对象的 valueOf
办法返回 1
,于是obj + 2
就失去了 3
。这个例子中,因为valueOf
办法间接返回一个原始类型的值,所以不再调用 toString
办法。
- 自定义 toString
var obj = {toString: function () {return 'hello';}
};
obj + 2 // "hello2"
下面代码中,对象 obj
的toString
办法返回字符串hello
。后面说过,只有有一个运算子是字符串,加法运算符就变成连贯运算符,返回连贯后的字符串。
- 这里有一个特例,如果运算子是一个
Date
对象的实例,那么会优先执行toString
办法。
var obj = new Date();
obj.valueOf = function () { return 1};
obj.toString = function () { return 'hello'};
obj + 2 // "hello2"
下面代码中,对象 obj
是一个 Date
对象的实例,并且自定义了 valueOf
办法和 toString
办法,后果 toString
办法优先执行。
余数运算符
- 运算后果的正负号由第一个运算子正负号决定,不遵循算数法令
-1 % 2 // -1
1 % -2 // 1
因而,为了失去正数的正确余数值,能够先用绝对值函数
// 谬误的写法
function isOdd(n) {return n % 2 === 1;}
isOdd(-5) // false
isOdd(-4) // false
// 正确的写法
function isOdd(n) {return Math.abs(n % 2) === 1;
}
isOdd(-5) // true
isOdd(-4) // false
数值运算符、负数值运算符
数值运算符(+
)同样应用加号,但它是一元运算符(只须要一个操作数),而加法运算符是二元运算符(须要两个操作数)。
数值运算符的作用在于能够将任何值转为数值(与 Number
函数的作用雷同)。
+true // 1
+[] // 0
+{} // NaN
下面代码示意,非数值通过数值运算符当前,都变成了数值(最初一行 NaN
也是数值)。具体的类型转换规定,参见《数据类型转换》一章。
负数值运算符(-
),也同样具备将一个值转为数值的性能,只不过失去的值正负相同。连用两个负数值运算符,等同于数值运算符。
var x = 1;
-x // -1
-(-x) // 1
下面代码最初一行的圆括号不可少,否则会变成自减运算符。
数值运算符号和负数值运算符,都会返回一个新的值,而不会扭转原始变量的值。
指数运算符
留神,指数运算符是右联合,而不是左联合。即多个指数运算符连用时,先进行最左边的计算。
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
比拟运算符
比拟运算符能够分为相等比拟和非相等比拟。对于非相等比拟,先看两个运算子是否都是字符串,如果是,就依照字典程序比拟(实际上是比拟 Unicode 码点);否则,将两个运算子都转成数值,再比拟数值的大小
非相等运算符字符串的比拟
若两个都是字符串,依照字典程序进行比拟,即比拟 Unicode 码点
非相等运算符非字符串的比拟
只有两个运算子有一个不是字符串,就属于非字符串比拟
- 若都是原始类型,(字母、字符串、布尔值)先转换成数值再比拟。这里留神的是,任何值与 NaN 比拟,返回的都是 false(包含 NaN 自身)
- 对象会先调用
valueOf
办法;如果返回的还是对象,再接着调用toString
办法,最终转换为原始类型的值。在进行比拟
严格相等运算符
严格相等运算符(===)先比拟类型,后比拟值,只有一个不相等就返回 false;而相等运算符当类型不同时,会先转换成雷同类型,再比拟
- NaN 与任何值都不相等(包含自身),+0=-0
- 复合类型的数据比拟时,不是比拟值是否相等,而是比拟是否指向同一个地址
如果两个变量援用同一个对象,则它们相等。
var v1 = {};
var v2 = v1;
v1 === v2 // true
留神,对于两个对象的比拟,严格相等运算符比拟的是地址,而大于或小于运算符比拟的是值。
var obj1 = {};
var obj2 = {};
obj1 > obj2 // false
obj1 < obj2 // false
obj1 === obj2 // false
下面的三个比拟,前两个比拟的是值,最初一个比拟的是地址,所以都返回false
。
- undefined 和 null 与本身严格相等。另外,因为变量申明后默认值为 undefined,所以两个只申明未赋值的变量是相等的
var v1;
var v2;
v1 === v2 // true
相等运算符
- 原始类型的值转换成数值再比拟
- 对象转换成原始函数的值再比拟
- undefined 和 null 互相比拟为 true,与其余比拟为 false
布尔运算符:将表达式转为布尔值
!取反运算符
除了以下 6 个值取反后为 true,其余都是 false:
undefined
null
false
0
NaN
- 空字符串(
''
)
若对一个值间断两次取反运算,等于转为对应的布尔值,与 Boolean 函数作用雷同,是一种常见的类型转换写法
&& 且运算符
运算规定:若第一个为 true,则返回第二个的值;若第一个 false,间接返回第一个的值,不再对第二个求值
't' && ''//""
't' && 'f' // "f"
't' && (1 + 2) // 3
''&&'f'//""
''&&'' // ""
var x = 1;
(1 - 1) && (x += 1) // 0
x // 1
这种跳过第二个运算子的机制,被称为“短路”。有些程序员喜爱用它取代 if
构造,比方上面是一段 if
构造的代码,就能够用且运算符改写
if (i) {doSomething();
}
// 等价于
i && doSomething();
且运算符能够多个连用,后果会返回第一个为 false 的值;若所有表达式都为 false,则返回最初一个表达式的值
|| 或运算符
运算规定:
如果第一个运算子的布尔值为true
,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false
,则返回第二个运算子的值。
或运算符能够多个连用,这时返回第一个布尔值为 true
的表达式的值。如果所有表达式都为false
,则返回最初一个表达式的值。
或运算符罕用于为一个变量设置默认值。
function saveText(text) {
text = text || '';
// ...
}
// 或者写成
saveText(this.text || '')
下面代码示意,如果函数调用时,没有提供参数,则该参数默认设置为空字符串。
二进制运算符
位运算符只对整数起作用,若运算子不是整数,会主动转换为整数后再执行
i = i | 0;
下面这行代码的意思,就是将i
(不论是整数或小数)转为 32 位整数
二进制或运算符 |
- 将小数与 0 进行二进制或运算,可用于去除小数的小数局部
二进制与运算符 &
二进制否运算符~
~ 3 // -4
- 返回后果有时难以了解,因为波及到就三级外部的数值示意机制
3
的 32 位整数模式是00000000000000000000000000000011
,二进制否运算当前失去11111111111111111111111111111100
。因为第一位(符号位)是 1,所以这个数是一个正数。JavaScript 外部采纳补码模式示意正数,即须要将这个数减去 1,再取一次反,而后加上负号,能力失去这个正数对应的 10 进制值。这个数减去 1 等于11111111111111111111111111111011
,再取一次反失去00000000000000000000000000000100
,再加上负号就是-4
。思考到这样的过程比拟麻烦,能够简略记忆成,一个数与本身的取反值相加,等于 -1。
- 对一个整数间断两次二进制否运算,失去自身。对小数间断两次二进制否运算,达到取整成果
- 当对字符串等其余类型的值进行二进制否运算时,js 会先调用 Number 函数,将其转化为数值
异或运算符
- “异或运算”有一个非凡使用,间断对两个数
a
和b
进行三次异或运算,a^=b; b^=a; a^=b;
,能够调换它们的值。这意味着,应用“异或运算”能够在不引入长期变量的前提下,调换两个变量的值。 - 异或运算也能够用来取整。
12.9 ^ 0 // 12
利用:开关作用
位运算符能够用作设置对象属性的开关。
假设某个对象有四个开关,每个开关都是一个变量。那么,能够设置一个四位的二进制数,它的每个位对应一个开关。
var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000
下面代码设置 A、B、C、D 四个开关,每个开关别离占有一个二进制位。
而后,就能够用二进制与运算,查看以后设置是否关上了指定开关。
var flags = 5; // 二进制的 0101
if (flags & FLAG_C) {// ...}
// 0101 & 0100 => 0100 => true
下面代码测验是否关上了开关C
。如果关上,会返回true
,否则返回false
。
当初假如须要关上 A
、B
、D
三个开关,咱们能够结构一个掩码变量。
var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011
下面代码对 A
、B
、D
三个变量进行二进制或运算,失去掩码值为二进制的1011
。
有了掩码,二进制或运算能够确保关上指定的开关。
flags = flags | mask;
下面代码中,计算后失去的 flags
变量,代表三个开关的二进制位都关上了。
二进制与运算能够将以后设置中但凡与开关设置不一样的项,全副敞开。
flags = flags & mask;
异或运算能够切换(toggle)以后设置,即第一次执行能够失去以后设置的相同值,再执行一次又失去原来的值。
flags = flags ^ mask;
二进制否运算能够翻转以后设置,即原设置为0
,运算后变为1
;原设置为1
,运算后变为0
。
flags = ~flags;
void 运算符
- void 运算符的作用是执行一个表达式,而后不返回任何值。
var x = 3;
void (x = 5) //undefined
x // 5
- 留神的是,咱们倡议应用 void 运算符时用圆括号括起来,因为 void 运算符优先级很高,防止谬误后果。比方,
void 4 + 7
实际上等同于(void 4) + 7
。 - 这个运算符次要用于做浏览器的书签工具,以及在超级链接中插入代码避免页面跳转
<a href="javascript: void(document.form.submit())">
提交
</a>
如上边,用户点击链接提交表单,然而不产生页面跳转
,逗号运算符
- 逗号运算符用于对两个表达式求值,并返回后一个表达式的值
var x = 0;
var y = (x++, 10);
x // 1
y // 10
- 用处:在返回一个值前,进行一些辅助操作
var value = (console.log('Hi!'), true);
// Hi!
value // true
运算程序
- 留神括号的应用,来防止运算程序与预期不同
- 左联合和右联合。大部分都是左联合,然而赋值运算符、三元运算符和指数运算符是右联合
w = x = y = z;
q = a ? b : c ? d : e ? f : g;
w = (x = (y = z));
q = a ? b : (c ? d : (e ? f : g));
2 ** 3 ** 2
// 相当于 2 ** (3 ** 2)
// 512