js运算符

一元算术运算符

一元运算符作用于一个单独的操作数,并产生一个新值。在JS中,一元运算符具有很高的优先级,而且都是右结合。需要注意的是,“+”和“-”是一元运算符,也是二元运算符。

一元加法(+)

一元加法运算符把操作数转换为数字(或者NAN),并返回这个转换后的数字。如果操作数本身就是数字,则直接返回这个数字。

+1   ---------------> 1   数字类型返回数字
+'1' ---------------> 1   字符串数字返回数字
+'a'----------------> NaN 字符串返回NaN
+010 ---------------> 8   八进制返回十进制数字
+'010' -------------> 10  八进制字符串返回十进制数字
+0xb ---------------> 11  十六进制返回十进制数字
+'0xb' -------------> 11  字符串十六进制返回十进制数字
+0b10 --------------> 2   二进制返回十进制数字
+'0b10' ------------> 2   字符串二进制返回十进制数字
+[]  ---------------> 0   空数组返回0
+[1] ---------------> 1   数组长度为1且为数字,返回数字
+['a'] -------------> NaN 数组长度为1,不为数字,返回NaN
+[1,2] -------------> NaN 数组长度大于1,返回NaN
+{} ----------------> NaN 对象类型返回N
+undefined ---------> NaN undefined返回NaN
+null --------------> 0   null返回0
+NaN   -------------> NaN NaN返回NaN

一元减法(-)

当“-”用作一元运算符时,它会根据需要把操作数转换为数字,然后改变运算结果的符号(转换为负数)。

-1   ---------------> -1   数字类型返回负数
-'1' ---------------> -1   字符串数字返回负数
-010 ---------------> -8   八进制返回负十进制数字
-'010' -------------> -10  八进制字符串返回负十进制数字
-'a'----------------> NaN 字符串返回NaN
-0xb ---------------> -11  十六进制返回负十进制数字
-'0xb' -------------> -11  字符串十六进制返回负十进制数字
-0b10 --------------> -2   二进制返回负十进制数字
-'0b10' ------------> -2   字符串二进制返回负十进制数字
-[]  ---------------> -0   空数组返回-0
-[1] ---------------> -1   数组长度为1且为数字,返回负数
-['a'] -------------> NaN 数组长度为1,不为数字,返回NaN
-[1,2] -------------> NaN 数组长度大于1,返回NaN
-{} ----------------> NaN 对象类型返回N
-undefined ---------> NaN undefined返回NaN
-null --------------> -0   null返回-0
-NaN   -------------> NaN NaN返回NaN

递增(++)

递增“++”运算符对其操作数进行增量(加一)操作,操作数是一个左值(变量、数组元素或对象属性)。运算符将操作数转换为数字,然后给数字加一,并将加一后的数值重新赋值给变量、数组元素或者对象属性。

递增“++”运算符的返回值依赖于它相对于操作数的位置,当运算符在操作数之前,称为“前增量”运算符,它对操作数进行增量计算,并返回计算后的值。当运算符在操作数之后,称为“后增量”运算符,它对操作数进行增量计算,但返回未做增量计算的值。

var i = 1, j = ++i; // i = j = 2
var i = 1, j = i++; // i = 2, j = 1

注意:表达式++x跟x = x + 1并不完全一样,“++”不会进行字符串连接操作,会将字符串转换为数字并增1,如果x=“1”,++x结果为2,而x + 1结果为"11"。

递减(–)

递减“–”运算符的操作数也是一个左值,它把操作数转换为数字,然后减1,并将计算后的值重新复制给操作数。和“++”运算符一样,递减“–”运算符的返回值依赖于它相对操作数的位置,当递减运算符在操作数之前,操作数减1并返回减1之后的值。当递减运算符在操作数之后,操作数减1并返回减1之前的值。

var i = 1, j = --i; // i = j = 0
var i = 1, j = i--; // i = 0, j = 1

位运算符

位运算符可以对数字表示的二进制数据进行更低层级的按位运算。尽管它们不是传统的数学运算,但这里也将其归类为算术运算符,因为它们作用于数值类型的操作数并返回数字。

位运算符要求它的操作数是整数,这些整数表示为32位整型而不是64位浮点型。必要时,位运算符首先将操作数转换为数字,并将数字强制转为32位整型,这会忽略原格式中的小数部分和任何超过32位的二进制位。

位移运算符要求右操作数在0-31质检。在将其操作数转换为无符号32位整数后,它们将舍弃第五位之后的二进制位,以便生成一个位数正确的数字。

需要注意的是,位运算符会将NaN、Infinity和-Infinity都转换为0。

按位与(&)

位运算符“&”对它的整型操作数逐位执行布尔与操作。只有两个操作数中相对应的位都是1,结果中的这一位才是1。

0x1234 & 0x00ff = 0x0034;
// 转换为二进制
0001 0010 0011 0100
0000 0000 1111 1111
// &(与)
0000 0000 0011 0100 (0x0034)

按位或(|)

位运算符“|”对它的整型操作数逐位执行布尔或操作。如果其中一个操作数相应的位为1,或者两个操作数相应位都是1,那么结果中的这一位就为1。

0x1234 & 0x00ff = 0x0034;
// 转换为二进制
0001 0010 0011 0100
0000 0000 1111 1111
// |(或)
0001 0010 1111 1111 (0x0034)

按位异或(^)

位运算符“^”对它的整型操作数逐位执行布尔异或操作。异或是指第一个操作数为true或第二个操作数为true,但两者不能同时为true。如果两个操作数中只有一个相应位为1(不能同时为1),那么结果中的这一位就是1。

0x1234 & 0x00ff = 0x12cb;
// 转换为二进制
0001 0010 0011 0100
0000 0000 1111 1111
// |(或)
0001 0010 1100 1011 (0x12cb)

按位非(~)

运算符“”是一元运算符,位于一个整型参数之前,它将操作数的所有位取反。根据JS中带符号的整数的表示方法,对一个值使用“”运算符相当于改变它的符号并减1。

~0x0f = 0xfffffff0; //-16
// 转换为二进制
0000 0000 0000 0000 0000 0000 0000 1111
// 取反
1111 1111 1111 1111 1111 1111 1111 0000	(0xfffffff0)
// 二进制转换为十进制负数,需要转为补码(原码-反码-补码)
// 反码
1000 0000 0000 0000 0000 0000 0000 1111
// 补码
1000 0000 0000 0000 0000 0001 0000  (-16)

左移(<<)

将第一个操作数的所有二进制进行左移操作,移动的位数由第二个操作数指定,移动的位数是0~31之间的一个整数。(将一个值左移1位相当于它乘以1,左移两位相当于乘以4,以此类推)。

7 << 2 = 28;
// 转换为二进制
0000 0000 0000 0000 0000 0000 0000 0111
// <<(左移2位)
0000 0000 0000 0000 0000 0000 0001 1100 (28)

带符号右移(>>)

运算符“>>”将第一个操作数的所有位进行右移操作,移动的位数由第二个操作数指定,移动的位数是0~31之间的一个整数。右边溢出的位将忽略。填补在左边的位由原操作数的符号决定,以便保持结果的符号与原操作数一致。如果第一个操作数是正数,移位后用0填补最高位;如果第一个操作数是负的,移位后就用1填补高位。(将 一个值右移一位,相当于用它除以2,忽略余数,右移两位,相当于它除以4,以此类推)。

7 >> 1 = 3;
// 转换为二进制
0000 0111
// >>(右移1位)
0000 0011 (3)

-7 >> 1 = -4
// 转换为二进制原码
1000 0111                              
// 补码
1111 1001
// >>(右移1位)
1111 1100
// 反码
1000 0011
// 原码
1000 0010 (-4)

无符号右移(>>>)

运算符“>>>”和运算符“>>”一样,只是左边的高位总是填补0,与原来的操作数符号无关(负数右移必为正数)。

1 >>> 4 = 0;
0000 0000 0000 0001
// 右移4位
0000 0000 0000 0000 (0)

-1 >> 4 = -1;
-1 >>> 4 = 0x0fffffff;(268435455)

1111 1111 1111 1111 1111 1111 1111 1111
// 右移4位
0000 1111 1111 1111 1111 1111 1111 1111 (0x0fffffff)

134217728+67108864+33554432+16777216+8388608+4194304+2097152+1048576+
524288+262144+131072+65536+32768+16384+8192+4096+
2048+1024+512+256+128+64+32+16+8+4+2+1 = 268435455

关系表达式

关系运算符用于测试两个值之间的关系(比如“相等”,“小于”,或“是…的属性”),根据关系是否存在而返回true或false。关系表达式总是总是返回一个布尔值,通常在if、while或者for语句中使用关系表达式,用以控制程序的执行流程。

相等、不等运算符

“= =” 和 “= = = ” 运算符用于比较两个值是否相等,当然它们对相等的定义不尽相同。两个运算符允许任意类型的操作数,如果操作数相等则返回 true ,否则返回false 。 “= = =”也称为严格相等运算符( strict equality ) (有时也称做恒等运算符( identity operator ) ) ,它用来检测两个操作数是否严格相等。“= =”运算符称做相等运算符 ( equality operator ) ,它用来检测两个操作数是否相等,这里“相等”的定义非常宽松,可以允许进行类型转换。

Javascript 支持“ = ”、“ = = ”和“ = = = ”运算符。你应当理解这些(赋值、相等、恒等)运算符之间的区别,并在编码过程中小心使用。尽管它们都可以称做“相等” , 但为了减少概念混淆,应该把“ = ”称做“得到或赋值”,把“==”称做‘相等”,把“===”称做“严格相等”。

JavaScript对象的比较是引用的比较,而不是值得比较。对象和其本身是相等的,但和其他任何对象都不相等。如果两个不同的对象具有相同数量的属性,相同的属性名和值,它们依然是不相等的。相应位置的数组元素是相等的两个数组也是不相等的。

不等运算符

“!=”和"!"运算符的检测规则是“”和“=”运算符的求反。如果两个值通过“”的比较结果为true,那么通过“!=”的比较结果则为false。如果两值通过“=”的比较结果为true,那么通过“!”的比较结果则为false。“!”运算符是布尔非运算符。我们只要记住“!=”称做“不相等”、“!==”称做“不 严格相等”就可以了。

严格相等运算符

严格相等运算符“===”首先计算其操作数的值,然后比较这两个值,比较过程没有任何类型转换:

  • 如果两个值类型不相同,则它们不相等。
  • 如果两个值都是null或者都是undefined,则它们不相等。
  • 如果两个值都是布尔值true或都是布尔值false,则它们相等。
  • 如果其中一个值是NaN,或者两个值都是NaN,则它们不相等。NaN和其他任何值都是不相等的,包它本身。通过x!==x来判断x是否为NaN,只有在x为NaN的时候,这个表达式的值才为true。
  • 如果两个值为数字且数值相等,则它们相等。如果一个值为0,另一个值为-0,则它们同样相等。
  • 如果两个值为字符串,且所含的对应位上的16位数完全相等,则它们相等。如果它们的长度或内容不同,则它们不等。两个字符串可能含义完全一样且所显示出的字符也一样,但具有不同编码的16位值。JavaScript并不对Unicode进行标准化的转换,因此像这样的字符串通过“=“和””运算符的比较结果也不相等。String.localeCompare()提供了另外一种比较字符串的方法。
  • 如果两个引用值指向同一个对象、数组或函数,则它们是相等的。如果指向不同的对象,则它们是不等的,尽管两个对象具有完全一样的属性。

相等运算符

相等运算符“==”和恒等运算符相似,但相等运算符的比较并不严格。如果两个操作数不是同一类型,那么相等运算符会尝试进行一些类型转换,然后进行比较:

  • 如果两个操作数的类型相同,则和上文所述的严格相等的比较规则一样。如果严格相等,那么比较结果为相等。如果它们不严格相等,则比较结果为不相等。

  • 如果两个操作数类型不同,“==”相等操作符也可能会认为它们相等。检测相等将会遵守如下规则和类型转换:

    1.如果一个值是null,另一个是undefined,则它们相等。
    2.如果一个值是数字,另一个是字符串,先将字符串转换为数字,然后使用转换后的值进行比较。
    3.如果其中一个值是true,则将其转换为1再进行比较。如果其中一个值是false,则将其转换为0再进行比较。
    4.如果一个值是对象,另一个值是数字或字符串,则使用转换规则将对象转换为原始值,然后再进行比较。对象通过toString()方法或者valueOf()方法转换为原始值。JavaScript语言核心的内置类首先尝试使用valueOf(),再尝试使用toString(),除了日期类,日期类只使用toString()转换。那些不是JavaScript语言核心中的对象则通过各自的实现中定义的方法转换为原始值。
    5.其他不同类型之间的比较均不相等。

这里有一个判断相等的小例子:

'1' == true

这个表达式的结果是true,这表明完全不同类型的值比较结果为相等。布尔值true首先转换为数字1,然后再执行比较。接下来,字符串“1”也转换为了数字1,因为两个数字的值相等,因此比较结果为true。

比较运算符

比较运算符用来检测两个操作数的大小关系(数值大小或者字母表的顺序)。

小于(<)

如果第一个操作数小于第二个操作数,则“<”运算符的计算结果为true,否则为faIse。

大于(>)

如果第一个操作数大于第二个操作数,则运算符的计算结果为true,否则为false。

小于等于(<=)

如果第一个操作数小于或者等于第二个操作数,则“<=”运算符的计算结果为true,否则为false。

大于等于(>=)

如果第一个操作数大于或者等于第二个操作数,则“>=”运算符的计算结果为true,否则为false。

比较操作符的操作数可能是任意类型。然而,只有数字和字符串才能真正执行比较操作,因此那些不是数字和字符串的操作数都将进行类型转换,类型转换规则如下:
1.如果操作数为对象,那么这个对象将依照转换规则转换为原始值:如果va1ue0f()返回一个原始值,那么直接使用这个原始值。否则,使用tostring()的转换结果进行比较操作。
2.在对象转换为原始值之后,如果两个操作数都是字符串,那么将依照字母表的顺序对两个字符串进行比较,这里提到的“字母表顺序”是指组成这个字符串的16位Unicode字符的索引顺序。
3.在对象转换为原始值之后,如果至少有一个操作数不是字符串,那么两个操作数都将转换为数字进行数值比较。0和-0是相等的。 Infinity比其他任何数字都大(除了 Infinity本身),- Infinity比其他任何数字都小(除了它自身)。如果其中一个操作数是(或转换后是)NaN,那么比较操作符总是返回 false。

需要注意的是,JavaScript字符串是一个由16位整数值组成的序列,字符串的比较也只是两个字符串中的字符的数值比较。由 Unicode定义的字符编码顺序和任何特定语言或者本地语言宇符集中的传统字符编码顺序不尽相同。注意,字符串比较是区分大小写的,所有的大写的ASCII字母都“小于”小写的ASCII字母。如果不注意这条不起眼的规则的话会造成一些小麻烦。比如,使用“<”小于运算符比较“Zoo”和“aardvark”,结果为true。

参照 String.localCompare()方法来获取更多字符串比较的相关信息,String.localCompare()方法更加可靠,这个方法参照本地语言的字母表定义的字符次序。

对于那些不区分字母大小写的比较来说,则需要首先将字符串转全部换为小写字母或者大写字母,通过 String.toLowerCase()和 String.toUpperCase()做大小写的转换。

对于数字和字符串操作符来说,加号运算符和比较运算符的行为都有所不同,前者更偏爱字符串,如果它的其中一个操作数是字符串的话,则进行字符串连接操作。而比较运算符则更偏爱数字,只有在两个操作数都是字符串的时候,才会进行字符串的比较:

1 + 2          // 加法,结果是3
"1" + "2"      // 字符串连接,结果是"12"
"1" + 2        // 字符串连接,2转换为"2",结果是"12"
11 < 3	       // 数字的比较,结果为false
"11" < "3"     // 字符串的比较,结果为true
"11" < 3       // 数字的比较,"11"转换为11,结果为false
"one" < 3      // 数字的比较,"one"转换为NaN,结果为false

最后,需要注意的是,“<=”(小于等于)和“>=”(大于等于)运算符在判断相等的时候,并不依赖于相等运算符和严格相等运算符的比较规则。相反,小于等于运算符只是简单的“不大于”,大于等于运算符也只是“不小于”。只有一个例外,那就是当其一个操作数是(或者转换后是)NaN的时候,所有4个比较运算符均返回false。

in运算符

in运算符希望它的左操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象。如果右侧的对象拥有一个名为左操作数值的属性名,那么表达式返回ture,例如:

var point = { x: 1, y: 1 };
"x" in point;                 // true:对象拥有一个名为"x"的属性
"z" in point;                 // false:对象不存在名为"z"的属性
"toString" in point;          // true:对象继承了toString()方法

var data = [7, 8, 9];
"0" in data;                  // true:数组包含元素0  data[0] = 7
1 in data;                    // true:数字转换为字符串
3 in data;									  // false:没有索引为3的元素
"push" in data;               // true:数字继承了push()方法

instanceof运算符

instanceof运算符希望左操作数是一个对象,右操作数标识对象的类。如果左侧的对象是右侧类的实例,则表达式返回true;否则返回false。Javascript对象的类是通过初始化它们的构造函数来定义的。这样的话,instanceof的右操作数应当是一个函数。比如:

var d = new Date();   // 通过Date()构造函数来创建一个新对象
d instanceof Date;    // 计算结果为true,d是由Date()创建的
d instanceof Object;  // 计算结果为true,所有的对象都是Object的实例
d instanceof Number;  // 计算结果为 false,d不是一个 Number对象

var a = [1,2,3];      // 通过数组直接量的写法创建一个数组
a instanceof Array;   // 计算结果为true,a是一个数组
a instanceof Object;  // 计算结果为true,所有的数组都是对象
a instanceof RegExp;  // 计算结果为 false,数组不是正则表达式

需要注意的是,所有的对象都是Object的实例。当通过 instanceof判断一个对象是否是一个类的实例的时候,这个判断也会包含对“父类”的检测。如果instanceof的左操作数不是对象的话,instanceof返回false。如果右操作数不是函数,则抛出一个类型错误异常。

为了理解 instanceof运算符是如何工作的,必须首先理解“原型链”( prototype chain)。原型链作为Javascript的继承机制。为了计算表达式o instanceof f,JavaScript首先计算 f.prototype,然后在原型链中查找o,如果找到,那么o是f(或者的父类)的一个实例,表达式返回true。如果 f.prototype不在o的原型链中的话,那么o就不是f的实例,instanceof返回false。

条件运算符(?😃

条件运算符是 Javascript唯一的一个三元运算符(三个操作数),有时直接称做“三元运算符”。通常这个运算符写成“?:”,当然在代码中往往不会这么简写,因为这个运算符拥有三个操作数,第一个操作数在“?”之前,第二个操作数在“?”和“:”之间,第三个操作数在“:”之后,例如:

x > 0 ? x : -x // 求x的绝对值

条件运算符的操作数可以是任意类型。第一个操作数当成布尔值,如果它是真值,那么将计算第二个操作数,并返回其计算结果。如果第一个操作数是假值,那么将计算第三个操作数,并返回其计算结果。第二个和第三个操作数总是会计算其中之一,不可能两者同时执行。

其实使用if语句也会带来同样的效果,“?:”运算符只是提供了一种简写形式。这里是一个“?:”的典型应用场景,判断一个变量是否有定义(并拥有一个有意义的真值),如果有定义则使用它,如果无定义则使用一个默认值:

greeting = "hello " + (username ? username : "there");

下面使用语句的代码是等价的,但显然上面的代码更加简洁:

greeting = "hello";
if (username) {
 		greeting += username;
} else {
 		greeting += "there";
}

typeof运算符

typeof是一元运算符,放在其单个操作数的前面,操作数可以是任意类型。返回值为表示操作数类型的一个字符串。以下列出了任意值在 typed运算后的返回值:

xtypeof x
undefinedundefined
nullobject
true、falseboolean
任意数字、NaNnumber
任意字符串string
任意函数function
任意内置对象(非函数)object

delete运算符

delete是一元运算符,它用来删除对象属性或者数字元素。delete是用来做删除操作的,不是返回一个值的,例如:

var o = { x: 1, y: 2};
delete o.x;                // 删除一个属性,返回true
"x" in o;                  // false:这个属性在对象中不再存在
typeof o.x;                // undefined
delete o.x;                // 删除不存在的属性,返回true
delete o;                  // 不能删除通过var声明的变量,返回false

var a = [1, 2, 3];
delete a[2];               // 删除最后一个数组元素
2 in a;                    // false:元素2在数组中已经不存在了
a.length;                  // 3:注意,数组长度并没有改变,尽管上行代码删除了这个元素,
													 // 但是并没有改变数组长度

需要注意的是,删除属性或者删除数组元素不仅仅是设置了undefined的值。当删除一个属性时,这个属性将不再存在。读取一个不存在的属性将返回undefined,但是可以通过in运算符来检测这个属性是否在对象中存在。

delete希望他的操作数是一个左值,如果它不是左值,那么 delete将不进行任何操作同时返回true。否则,delete将试图删除这个指定的左值。如果删除成功,delete将返回true。然而并不是所有的属性都可删除,一些内置核心和客户端属性是不能删除的,用户通过var语句声明的变量不能删除。同样,通过 function语句定义的函数和函数参数也不能删除。

void运算符

void是一元运算符,它出现在操作数之前,操作数可以是任意类型。这个运算符并不是经常使用:操作数会照常计算,但忽略计算结果返回undefined。由于void会忽略操作数的值,因此在操作数具有副作用的时候使用void来让程序更具语义。

用处:

  1. 立即调用的函数表达式
    在使用立即执行的函数表达式时,可以利用 void 运算符让 JavaScript 引擎把一个function关键字识别成函数表达式而不是函数声明(语句)。
void function iife() {
    var bar = function () {};
    var baz = function () {};
    var foo = function () {
        bar();
        baz();
     };
    var biz = function () {};

    foo();
    biz();
}();
  1. JavaScript URLs
    当用户点击一个以 javascript: 开头的URI 时,它会执行URI中的代码,然后用返回的值替换页面内容,除非返回的值是undefined。void运算符可用于返回undefined。例如:
<a href="javascript:void(0);">
  这个链接点击之后不会做任何事情,如果去掉 void(),
  点击之后整个页面会被替换成一个字符 0</a>
<p>chrome中即使<a href="javascript:0;">也没变化,firefox中会变成一个字符串0 </p>
<a href="javascript:void(document.body.style.backgroundColor='green');">
  点击这个链接会让页面背景变成绿色。
</a>

注意,虽然这么做是可行的,但利用 javascript: 伪协议来执行 JavaScript 代码是不推荐的,推荐的做法是为链接元素绑定事件。

  1. 在箭头函数中避免泄露
    箭头函数标准中,允许在函数体不使用括号来直接返回值。 如果右侧调用了一个原本没有返回值的函数,其返回值改变后,则会导致非预期的副作用。 安全起见,当函数返回值是一个不会被使用到的时候,应该使用 void 运算符,来确保返回 undefined(如下方示例),这样,当 API 改变时,并不会影响箭头函数的行为。
button.onclick = () => void doSomething();

确保了当 doSomething 的返回值从 undefined 变为 true 的时候,不会改变函数的行为。

逗号运算符(,)

逗号运算符是二元运算符,它的操作数可以是任意类型。它首先计算左操作数,然后计算右操作数,最后返回右操作数的值,例如:

i = 0, j = 1, k = 2;    // 计算结果为2,等价于i = 0; j = 1; k = 2;

alert(2*5, 2*4);        // 输出10,逗号运算符优先级最低,先执行函数运算符,再执行逗号运算符
alert((2*5, 2*4));      // 输出8

总是会计算左侧的表达式,但计算结果忽略掉,也就是说,只有左侧表达式具有副作用,才会使用逗号运算符让代码变得更通顺。逗号运算符最常用的场景是在for循环中,for循环通常具有多个循环变量:

/**
	for循环中的第一个逗号是var语句的一部分
  第二个逗号是逗号运算符
  它将两个表达式(i++、j--)放在一条语句中
*/
for (var i = 0, j = 10; i < j; i++, j--) {
  console.log(i + j)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值