美文网首页
四 运算符

四 运算符

作者: 智勇双全的小六 | 来源:发表于2018-08-08 11:20 被阅读0次

1 算数运算符

https://wangdoc.com/javascript/operators/index.html

1.1 概述

指数:x ** y
余数:x % y
自增:++x x++
自减:x-- --x

1.2 加法运算符

1.2.1 基本规则

JS 允许非数值的相加

true + true // 2
1+ true // 2

如果是两个字符串相加,则会拼接。如果1个是字符串,另外一个是非字符串,则会先把非字符串转化成字符串。

1 + "2" // "12"

加号这种有时表示相加,有时表示拼接的,这种现象叫做重载。

3+4+"5" // "75"

1.2.2 对象的相加

如果运算子是对象,必须先转成原始类型的值,再相加。

let obj = {p:1};
obj + 2;
// "[object object]2"

上面代码中,对象 obj 转成原始类型的值是 [object object],再加2就得到了上面的结果。
对象转成原始类型的值,规则如下。
首先,自动调用对象的 valueOf 方法。

var obj = {p: 1};
obj.valueOf() 
// {p : 1}

一般而言,valueOf() 总是返回对象自身,这时再自动调用对象的 toString 方法,将其转为字符串。

obj.valueOf().toString() 
// "[object object]"

知道这个规则后,就可以自定义 valueOf 方法或toString方法,得到想要的结果。—— 这一点是和Java 相同的,在Java 中调用 equal / toString 方法,很多时候是需要自己重新定义的。

var obj = {
  valueOf : function(){
    return 1;
  }
};
obj + 2 // 3

由于 obj 返回了一个原始类型值的方法,所以不再调用 toString 方法。
自定义 toString 方法。

var obj = {
  toString : ()=>{
    return "hello";
  }
}
obj + 1 // "hello1"

1.3 余数运算符

余数的值是由第一个运算子决定的。

-5 % 2 // -1

也就是说,余数在 JS 可能是负数的。Python 中也会出现负数的:

5 % (-2) // -1

负数的余数,这个是个数学问题,具体怎么实现的,应该按照语言来。
所以为了得到负数的正确余数值,应该先使用绝对值:

function isOdd(n){
  return Math.abs(n % 2) === 1;
}
isOdd(-5) // true

1.4 自增和自减运算符

++放在变量之后,会先返回变量操作前的值,再进行自增运算;
++放在变量之前,会先进行自增运算,然后再返回变量值。

var x = 1;
var y = 1;
console.log(x++); // 1
console.log(++x); // 2

1.5 数值运算符,负数值运算符

数值运算符(+)同样使用加号,但是是一元运算符(只需要一个操作数),而加法运算符是二元运算符(需要两个操作数)。

数值运算符的作用在于可以将任何值转为数值(与 Number函数的作用相同)。

+true // 1
+[] // 1
+{} // NaN

1.6 指数运算符

**

1.7 赋值运算符

就是等号

2 比较元素符

教程:https://wangdoc.com/javascript/operators/comparison.html

2.1 概述

运算符分为两类:相等比较和非相等比较。对于非相等比较,算法是比较两个是否是字符串,如果是,按照字典顺序比较(实际上是 Unicode);否则,会把两个运算子都转成数值,再比较数值的大小。

2.2 非相等运算符:字符串的比较

"cat" > "dog" // false

2.3 非相等运算符:非字符串的比较

如果两个运算子都不是字符串,分为三种情况:
(1) 原始类型值
如果两个值都是原始类型的值,那么先转成数值再比较。

true > false // true
2 > true // true 

(2) 如果运算子是对象,会转为原始类型的值,再比较。
对象转化为原始类型的值,算法会先调用 valueOf 方法,如果返回的还是对象,再调用 toString 方法

var  x = [2];
x > "3" // false;
// 等同于
x.valueOf().toString() > "3"

x.valueOf = ()=>{
  return 100;
}
x > 99; // true

两个对象之间的比较也是如此。

{a:2} >= {b:1} // true
// 等同于
({a:2}).valueOf().toString() >= ({b:3}).valueOf().toString()

(3)复合值类型

2.4 严格相等运算符

相等运算符(==)比较两个值是否相等,严格相等运算符(===)比较它们是否是“同一个值”。
如果两个值不是同一类型的值,严格相等运算符会直接返回 false,而相等运算符会把他们转化为同一个类型在进行比较。

(1) 不同类型的值

如果两个值的类型不同,直接返回 false

(2) 同一类型的原始值

同一类型的原始值,比较值是否相同

(3) 符合类型值

两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一地址。

{} === {} // false
[] === [] // false
(function(){} === function(){}) // false

这几个例子它们都指向不同的地址,所以都是 false。如果两个变量引用同一个对象,则它们相等。

var v1 = {};
var v2 = {};
v1 === v2 // true

对于两个对象的比较,严格相等运算符比较的是地址,而大于或小于运算符比较的值。

var obj1 = {};
var obj2 = {};
obj1 === obj2 // false
obj1 > obj2 // false
obj1 < obj2 // false
obj1 >= obj2 // true
(4) undefined 和 null

undefined 和 null 与自身严格相等。

undefined === undefined; // true
null === null // true

由于变量声明后默认值是 undefined,因此两个只声明未赋值的变量是相等的。

var v1;
var v2;
v1 === v2 // true

2.5 严格不相等运算符

!== 就是严格相等运算符的否定形式。

2.6 相等运算符

相等运算符用来比较相同类型的数据时,与严格相等运算符完全一样。

1.0 == 1
// 等同于
1 === 1.0
(1)原始类型值

原始类型的值会转换成数值再进行比较。

1 == true // true
// 等同于 1 == Number(true)
0 == false // false
// 等同于 0 === Number(false)
'1' == true // true
// 等同于 Number('1') == Number(true)
(2) 对象与原始类型值比较

对象(广义的对象,包括数组和函数)与原始类型的值比较时,对象转换成原始类型的值,再进行比较。

[1] == '1' // true
[1] == true // true
(3) undefinednull

undefinednull 与其他类型的值比较时,结果都为 false, 它们互相比较时结果为 true。

false == null; // false
false == undefined // false
0 == null // false
0 == undefined // false
undefined  == null // true
(4) 相等运算符的缺点

相等运算符隐藏的类型转化,会带来一些反直觉的结果

0 == ''; // true
0 == '0'; // true 
'' == '0'; // false
false == 'false'  // false
false == '0' // true

false == undefined // false 
'\t\r\n' == 0; // true

2.7 不相等运算符

不相等运算符(!=)的算法,先求相等运算符的结果,然后返回相反值。

1 != '1' // false
// 等同于
!(1 == '1')

3 布尔值运算符

https://wangdoc.com/javascript/operators/boolean.html

3.1 概述

3.2 取反运算符(!)

相当于 python 的 not,当前布尔值取反。
对于非布尔值,取反运算符会将其转为布尔值。以下六个值取反后为true,其他的值都为 false:

  • undefined
  • null
  • false
  • 0
  • NaN
  • 空字符串('')
    注意,空对象的布尔值是true
Boolean([]) // true
Boolean({}) // true

如果对一个值连续两次取反,效果跟使用 Boolean 函数一样。

!! x
// 等同于
Boolean(x)

3.3 且运算符(&&)

注意且运算符可以用来进行短路运算,即

let a = 0;
a && b
// 如果a为false,则直接返回,不会对b进行运算。
// 比如上面的 b 没有定义,但是编辑器根本不 care。

3.4 或运算符(||)

或运算也会短路。

3.5 三元条件运算符(? :)

Java 中 和这个一模一样,

condition ? a : b

注意三元表达式是一个表达式,会返回值。这是和 if ... else ... 不同的。

4 二进制运算符

https://wangdoc.com/javascript/operators/bit.html

4.1 概述

  • 二进制或运算符(or):符号为 |,表示若两个二进制都为0,则结果为0,否则为1
  • 二进制与运算符(and):符号为 &,表示如果两个二进制都为1,则结果为1,否则为0.
  • 二进制否运算符(not):符号为~, 表示对一个二进制取反。
  • 异或运算符(xor):符号为 ^,若两个二进制不相同,结果为1,否则为0.
  • 左移运算符(left shift), 符号为 << ,
  • 右移运算符 (right shift), 符号为 >>
  • 带符号位的右移运算符(zero filled right shift),符号为 >>>

这些位运算符直接处理每一个比特位(bit),所以是非常底层的运算,好处是速度快,坏处是不直观。

有一点需要特别注意,位运算符只对整数起作用,如果一个运算子不是整数,会自动转为整数后再执行。另外,虽然在 JS 内部,数值都是以 64 位浮点数的形式存储,但是做位运算的时候,是以 32 位带符号的整数进行运算的,并且返回值也是一个 32 位带符号的整数。

i = i | 0;

上行代码的意思,就是将 i(不管是整数还是小数) 转成 32 位整数。

利用这个特性,就可以写出一个函数,将任意数值转化为 32 位整数。

function toInt32(x){
  return x | 0;
}

上面这个函数将任意值与 0 进行一次或运算,这个位运算会自动将一个值转化为 32 位的整数。

toInt32(1.7) // 1
toInt32(-1) // 1

对于大于 2 的 32 次方的整数,大于 32 位的数位都会被舍弃。

4.2 二进制或运算符

二进制或运算符(|)逐位比较两个运算子,两个二进制位之中只要有一个为1 ,就返回1,否则返回0

0 | 3 // 3

上面的代码中,03 的二进制分别是 0011,所以二进制运算会得到 11(即 3)。—— 这段解释就是为啥二进制或运算能返回整数。概述中的解释,让我误以为只能返回 0 或 1.

4.3 二进制与运算符

二进制与运算符(&)的规则是逐位比较两个运算子,两个二进制之中只要有一个位是0,就返回0,否则返回 1.

0 & 3 // 0

4.4 二进制否运算符

二进制的否运算符(~)将每个二进制位都变为相反值(0 变成1,1 变成0.)。它的返回结果有时比较难理解,因为涉及到计算机内部的数值表示机制。

-3 // -4

3 的 32 位整数形式是 0000000000000000000000011,二进制否运算以后得到 111111111111111111100。由于第一位(第一位是符号位)是1,所以这个数是一个负数。JS 内部采用补码的形式表示负数,即需要将这个数减去 1,再取一次反,然后加上负号,才能得到这个负数对应的 10 进制值。这个数减去 1 等于 111111111111111111011,再取反得到0000000000000000000100,再加上负号就是 -4. 考虑到这样的过程比较麻烦,可以简单记忆,一个数与自身的取反值相加,等于 -1

~ -3 // 2

一个数连续进行两次取反,会得到它自身。

~~ 3 // 3

所有的位运算都只对整数有效。当二进制否运算遇到小数时,会将小数部分舍去,只保留整数部分。所以,对一个小数连续进行两次二进制否运算,能得到取整效果。

~~ 2.9 // 2

二进制否运算取整,是所有取整方法中最快的一种。
对字符串进行二进制否运算,JS 引擎会先调用 Number 函数,将字符串转为数值。

~"1.2" // 1

4.5 异或运算符

异或运算 (^) 在两个二进制位不同时返回1, 相同时返回0:

0 ^ 3 // 3

上面表达式中,0(二进制00)与3(二进制11)进行异或运算,它们每一个二进制位都不同,所以得到11(即3)。

“异或运算”有一个特殊运用,连续对两个数ab 进行三次异或运算,它们每一个二进制都不同,所以得到 113)。

“异或运算” 有一个特殊运用,连续对两个数ab进行三次异或运算,a ^= b; b ^= a; a ^= b;,可以互换它们的值。这意味着,使用“异或运算”可以在不引入临时变量的前提下,互换两个变量的值。

var a  = 10;
var b = 99;
a ^= b, b ^= a, a ^= b;

a // 99
b // 10

这是互换两个变量的值的最快方法。
异或运算也可以用来取整。

12.9 ^ 0 // 12

4.6 左移运算符

左移运算符(<<)表示将一个数的二进制值向左移动指定的位数,尾部补0,乘以 2的指定次方。

// 4 的二进制形式为 100
// 左移一位为 `1000` (即十进制的 8)
// 相当于乘以2的1次方
4 << 1
// 8
-4 << 1
// -8

4.7 右移运算符

右移运算符(>>)表示将一个数的二进制向右移动指定的位数,头部补0,除以2的指定次方。

4 >> 1
// 2
// 因为 4 的二进制是 000000000000000000000100,
// 右移动得到 00000000000000000010,
// 这是十进制的 2
-4 >> 1 
// -2
// -4 的二进制是 1111111111111111111111111100,
// 右移一位,头部补1,得到 111111111111111111111110,
即为十进制的 -2.

右移可以模拟 2 的整除运算。

5 >> 2
// 2
21 >> 2
// 21 / 4 = 5

4.8 带符号位的右移运算符

带符号位的右移运算符(>>>)表示将一个数的二进制形式向右移动,包括符号位也参与移动。所以,该运算总是能得到正值。对于正数,该运算的结果与右移运算符(>>)完全一致,区别在于负数。

4 >>> 1
// 2
-4 >>> 1 
// 2147483646
// 因为 -4 的二进制形式为 111111111111111111111111111100,
// 带符号位的右移一位,得到 011111111111111111111111110,
// 即十进制的 2147483646

这个运算实际上将一个值转为 32 位无符号整数位。
查看一个负整数在计算机内部的存储形式,最快的方法就是使用这个方法。

-1 >>> 0
// 4294967295

4.9 开关作用

位运算符可以用设置对象属性的开关。
假定某个对象有四个开关,每个开关都是一个变量。那么,可以设置一个四位的二进制数,它的每个位对应一个开关。

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、C三个开关,我们可以构造一个掩码变量。

var mask = FLAG_A | FLAG_B | FLAG_C;
// 0001 | 0010 | 0100 => 1011

有了掩码,二进制或运算可以确保打开指定的开关。
// 没看明白。。。

5 其他运算符,运算顺序

5.1 void 运算符

void 的作用是执行一个表达式,然后不返回任何值,或者说返回的是undefined

void 0 // undefined
void(0) // undefined

这两种写法都会,但是建议使用带括号的。因为 void 运算符的优先级很高,如不使用括号,容易造成错误。比如,void 4 + 7 实际上是,(void 4) + 7
这个运算符的主要用途是 浏览器的书签工具,以及在超级链接中插入代码防止网页跳转。

<script>
function f(){
  console.log("hello world");
}
</script>
<a href="http://example.com" onclick="f(); return false;">点击</a>

上面的代码,点击链接后,会先执行 onclick 的代码,由于 onclick 返回 false,所以浏览器不会跳转。
void 运算符可以取代上面的写法。

<a href = "javascript: void(f())">文字</a>

下面是一个更实际的例子,用户点击链接提交表单,但是不会产生页面跳转。

<a href="javascript: void(document.form.submit())">
提交
</a>

5.2 逗号运算符

逗号运算符用于对两个表达式求值,并返回后一个表达式的值。

1,2 // 2
var x = 0;
var y = (x++, 10);
x // 1
y // 10

逗号运算符的一个用途是,在返回一个值前,进行一些辅助性的操作。
—— 最典型的是用到 for 循环里

var value = (console.log("HI"), true);
// hi
value // true

5.3 运算顺序

5.3.1 优先级

运算符是有优先级的。

5.3.2 圆括号的作用

圆括号可以用来提升优先级,圆括号的优先级是最高的。
圆括号不是运算符,而是一种语法结构。一共有两种用法:一种是把表达式放在圆括号之中;另外一种是跟在函数后面,作用是调用函数。

5.3.3 左结合与右结合

一般是从左向右运算。
但也有例外,

w  = x = y = z;
q = a ? b : c ? d : e ? f : g;

上面的代码相当于:

w  = (x = (y = z));
q  = a ? b : (c ? d : (e ? f : g));

相关文章

  • 07 Go运算符

    一、算术运算符 二、关系运算符 三、逻辑运算符 四、位运算符 五、赋值运算符 六、其他运算符

  • Java运算符

    Java的运算符,分为四类: 算数运算符、关系运算符、逻辑运算符、位运算符。 算数运算符(9):+ - * / %...

  • Java的运算符

    Java的运算符,分为四类: 算数运算符、关系运算符、逻辑运算符、位运算符。 算数运算符(9):+ - * / %...

  • 运算符

    运算符主要包括四大类:数字运算符、逻辑运算符、比较运算符、赋值运算符、位运算符(了解) 1.数学运算符 + - *...

  • Kotlin基础认识 (7)运算符

    一、算术运算符 一元运算符 二元运算符 算术赋值运算符 二、关系运算符 三、逻辑运算符 四、位运算符 五、其他运算...

  • 重拾Java(2)-运算符

    Java提供了丰富的运算符,可以将之分为四种:算术运算符、位运算符、关系运算符、逻辑运算符 一、算术运算符 算术运...

  • MySQL 从零开始学(四) 常见运算符介绍

    MySQL 内部运算符很丰富,主要有四大类:算术运算符、比较运算符、逻辑运算符和位操作运算符。 算术运算符 示例:...

  • python基础(四)----运算符

    一.算术运算符(基本同Java) 二.比较运算符(基本同Java) 三.赋值运算符(基本同Java) 四.位运算符...

  • shell系列3-运算符

    一. 数值运算符 整数判断: 二. 算数运算符 三. 字符串判断 四. 逻辑运算符 五. 位运算符 位运算符在Sh...

  • Perl语言学习(三)Perl数据运算

    三、Perl数据运算 1、运算符 算术运算符用来进行常用的数据计算四则运算符包括加、减、乘、除四种(+、-、*、/...

网友评论

      本文标题:四 运算符

      本文链接:https://www.haomeiwen.com/subject/sevqmftx.html