JS 运算符
要进行各种各样的运算,就要使用不同的运算符号。
1、算术运算符:+、-、*、/、%、++、--
A = 10 + 20;
A = 10 – 20;
A = 10 * 20;
A = 10 / 20;
(1)“%”取余运算符,两个数相除,取余数。
A = 10 % 3; // A = 1,如果余数不为0,则两个不能整除
A = 10 % 2; // A = 0,如果余数为0,则两个数能除尽
(2)“++”加1运算符、自加1
“++”可以作前缀(++i),也可以作后缀(i++)。
如果不赋值的话,i++和++i的结果是一样的。
如果要赋值的话,i++和++i的结果就不一样了
(3)“--”减1运算符,自减1
“--”可以作前缀(--i),也可以作后缀(i--)。
如果不赋值的话,i--和--i的结果是一样的。
如果要赋值的话,i++和++i的结果就不一样了
2、赋值运算符:=、+=、-=、*=、/=
“+=”先加后等。如:a += 10 //展开后 a = a + 10
“-=”先减后等。如:a -= 10 //展开后 a = a - 10
“*=”先乘后等。如:a *= 10 //展开后 a = a * 10
“/=”先除后等。如:a /= 10 //展开后 a = a / 10
3、字符串运算符:+、+=
字符串只能进行“连接”运算,不能进行其它运算。
var a = “abc”;
var b = a + “def”; // 结果b = a + “def” = “abc” + “def” = “abcdef”
var a = “abc”;
a += 10; // 结果a = a + 10 = “abc” + 10 = “abc10”
4、比较运算符:>、<、>=、<=、==、!=、===、!==
比较运算符的运算结果是布尔值(true或false)。
A = 10 > 20; // 结果A=false
A = 20>=20; // 结果A=true
A = 10%2 == 0; // 结果A=true
A = 10%2 == “0”; // 结果A=true
A = 10%3 != 0; // 结果A=true
A = 10%2 === “0”; //结果A=false
“=”是赋值号。如:a = 10
“==”等于。只比较两个变量的值,而不管类型。只要值一样,就返回true,否则返回false。
“===”全等于。既比较变量,也判断类型。如果类型和值都一样,返回true,否则返回false。
5、逻辑运算符:&&、||、!
逻辑运算符的运算结果有两个true或false。
“&&”逻辑与(并且关系)。如果左右两个操作数都为true,则结果为true,否则,结果为false。
逻辑与,就是两个条件同时满足时,则结果为true。
“||”逻辑或。左右两个条件,只要有一个满足,则返回true,否则,返回false。
“!”取反运算。!true = false 、 !false = true 、 !100 = false
6、三元运算符:?:
所谓“三元运算符”就是指三个操作数。
语法:条件表达式 ? 结果1 : 结果2
语法:操作数1 ? 操作数2 : 操作数3
含义:如果条件为true,则执行“结果1”的代码;如果条件为false,则执行“结果2”的代码。
其实:三元运算符,就是if else的变形形式。
JS 位运算
js按位运算符及其妙用
大多数语言都提供了按位运算符,恰当的使用按位运算符有时候会取得的很好的效果。
在我看来按位运算符应该有7个:
1、& 按位与
&是二元运算符,它以特定的方式的方式组合操作数中对应的位,如果对应的位都为1,那么结果就是1, 如果任意一个位是0 则结果就是0。
1 & 3的结果为1
那我们来看看他是怎么运行的
1的二进制表示为 0 0 0 0 0 0 1
3的二进制表示为 0 0 0 0 0 1 1
根据 & 的规则 得到的结果为 0 0 0 0 0 0 0 1,十进制表示就是1
2、| 按位或
|运算符跟&的区别在于如果对应的位中任一个操作数为1 那么结果就是1。
1的二进制表示为 0 0 0 0 0 0 1
3的二进制表示为 0 0 0 0 0 1 1
所以 1 | 3的结果为3
3、^ 按位异或
^运算符跟|类似,但有一点不同的是 如果两个操作位都为1的话,结果产生0。
1的二进制表示为 0 0 0 0 0 0 1
3的二进制表示为 0 0 0 0 0 1 1
所以 1 ^ 3的结果为2
4、~ 按位非
~运算符是对位求反,1变0,0变1,也就是求二进制的反码
1的二进制表示为 0 0 0 0 0 0 1
所以 ~1 的结果是-2
5、>> 右移
>>运算符使指定值的二进制所有位都右移规定的次数,对于其移动规则只需记住符号位不变,左边补上符号位即按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位的空位补符号位,即正数补零,负数补1。
1的二进制表示为 0 0 0 0 0 0 1
所以 1>>1的结果为0
6、<< 左移
<<运算符使指定值的二进制所有位都左移规定的次数,对于其移动规则只需记住丢弃最高位,0补最低位即按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。
1的二进制表示为 0 0 0 0 0 0 1
所以 1<<1的结果为2 7、>>> 无符号右移
>>>运算符忽略了符号位扩展,0补最高位,但是只是对32位和64位的值有意义。
位运算符在js中的妙用:
1、使用&运算符判断一个数的奇偶
偶数 & 1 = 0
奇数 & 1 = 1
那么0&1=0,1&1=1
2、使用~~,>>,<<,>>>,|来取整
~~3.14 = 3
3.14 >> 0 = 3
3.14 << 0 = 3 3.14 | 0 = 3 3.14 >>> 0 = 3(>>>不可对负数取整)
注意:~~-3.14 = -3 其它的一样
3、使用<<,>>来计算乘除
乘法:
1*2 = 2
1<>1 = 1(2/2的一次方)
4、利用^来完成比较两个数是否相等
1 ^ 1 = 0
1 ^ 非1数 !=0
所以同一个数……同一个数等于0,否则不等于0
5、使用^来完成值交换
a = 1
b = 2
a ^= b
b ^= a
a ^= b
结果a=2,b=1
6、使用&,>>,|来完成rgb值和16进制颜色值之间的转换
16进制颜色值转RGB:
1 2 3 4 5 6 7 |
|
RGB转16进制颜色值:
1 2 3 4 5 |
|
运行hexToRGB("#ffffff")返回"rgb(255,255,255)"
运行RGBToHex("rgb(255,255,255)")返回"#ffffff"
位运算应用场景:
- 偷懒简写(|): 取整、断言
- 标志位判断 (&): 权限控制
- 去掉高位/低位(&): 比较特定位、子网掩码
- 数值交换(^): 加密算法、生成随机数/哈希
- 构造属性集(|): 边界判断
- 缺省数值类型值(|):默认端口
- 位填充(>>): HEX2RGB、leftPad
- 试图提升算术运算性能
位运算示例:
1. 求 2 的 n 次方:
bitwise | ES5 | ES6+ |
---|---|---|
1 << n | Math.pow(2, n) | 2 ** n |
原理: 左移一位, 相当于将原数值与 2 相乘一次 , 即 m << n === m * Math.pow(2, n)
2. 求中间索引
bitwise | ES5 | ES6+ |
---|---|---|
arr.length >> 1 | Math.floor(arr.length / 2)) | - |
原理: 右移一位,相当于原数值除 2 后向下取整
3. 判断索引存在
bitwise | ES5 | ES6+ |
---|---|---|
!!~'abc'.indexOf('d') | 'abc'.indexOf('d') !== -1 | 'abc'.includes('d') |
原理: 按位非 ~-1 === 0
4. 变量交换
bitwise | ES5 | ES6+ |
---|---|---|
a ^= b; b ^= a; a ^= b; | t = a; b = a; b = t; | [b, a] = [a, b] |
原理: 按位异或 1 ^ 1 === 0, 0 ^ 0 === 1
5. 分支开关
bitwise | ES5 | ES6+ |
---|---|---|
flag ^= 1; | flag = flag ? 0 : 1; | - |
原理: 按位异或 1 ^ 1 === 0, 0 ^ 0 === 1
6. 截取整数位
bitwise | ES5 | ES6+ |
---|---|---|
~~-9.9; -9.9>>0; | parseInt(-9.9) | Math.trunc(-9.9) |
原理: 位运算会默认将非数字类型转换成数字类型再进行运算
注意:
- 以上值(还有
-9.9|0
) 都为-9
, 而Math.floor(-9.9)
值为 -10- 位运算取整方法不适用超过32位整数最大值 2147483647 的数:
2147483649.4 | 0
的值为-2147483647
ES6 扩展运算符 ...的使用
1 含义
扩展运算符( spread )是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
-
console.log(...[1, 2, 3])
-
// 1 2 3
-
console.log(1, ...[2, 3, 4], 5)
-
// 1 2 3 4 5
-
[...document.querySelectorAll('div')]
-
// [<div>, <div>, <div>]
该运算符主要用于函数调用。
-
function push(array, ...items) {
-
array.push(...items);
-
}
-
function add(x, y) {
-
return x + y;
-
}
-
var numbers = [4, 38];
-
add(...numbers) // 42
上面代码中,array.push(...items)和add(...numbers)这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。
扩展运算符与正常的函数参数可以结合使用,非常灵活。
-
function f(v, w, x, y, z) { }
-
var args = [0, 1];
-
f(-1, ...args, 2, ...[3]);
2 替代数组的 apply 方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
-
// ES5 的写法
-
function f(x, y, z) {
-
// ...
-
}
-
var args = [0, 1, 2];
-
f.apply(null, args);
-
// ES6 的写法
-
function f(x, y, z) {
-
// ...
-
}
-
var args = [0, 1, 2];
-
f(...args);
下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。
-
// ES5 的写法
-
Math.max.apply(null, [14, 3, 77])
-
// ES6 的写法
-
Math.max(...[14, 3, 77])
-
// 等同于
-
Math.max(14, 3, 77);
上面代码表示,由于 JavaScript 不提供求数组最大元素的函数,所以只能套用Math.max函数,将数组转为一个参数序列,然后求最大值。有了扩展运算符以后,就可以直接用Math.max了。
另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。
-
// ES5 的写法
-
var arr1 = [0, 1, 2];
-
var arr2 = [3, 4, 5];
-
Array.prototype.push.apply(arr1, arr2);
-
// ES6 的写法
-
var arr1 = [0, 1, 2];
-
var arr2 = [3, 4, 5];
-
arr1.push(...arr2);
上面代码的 ES5 写法中,push方法的参数不能是数组,所以只好通过apply方法变通使用push方法。有了扩展运算符,就可以直接将数组传入push方法。
下面是另外一个例子。
-
// ES5
-
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
-
// ES6
-
new Date(...[2015, 1, 1]);
3 扩展运算符的应用
( 1 )合并数组
扩展运算符提供了数组合并的新写法。
-
// ES5
-
[1, 2].concat(more)
-
// ES6
-
[1, 2, ...more]
-
var arr1 = ['a', 'b'];
-
var arr2 = ['c'];
-
var arr3 = ['d', 'e'];
-
// ES5 的合并数组
-
arr1.concat(arr2, arr3);
-
// [ 'a', 'b', 'c', 'd', 'e' ]
-
// ES6 的合并数组
-
[...arr1, ...arr2, ...arr3]
-
// [ 'a', 'b', 'c', 'd', 'e' ]
( 2 )与解构赋值结合
扩展运算符可以与解构赋值结合起来,用于生成数组。
-
// ES5
-
a = list[0], rest = list.slice(1)
-
// ES6
-
[a, ...rest] = list
-
下面是另外一些例子。
-
const [first, ...rest] = [1, 2, 3, 4, 5];
-
first // 1
-
rest // [2, 3, 4, 5]
-
const [first, ...rest] = [];
-
first // undefined
-
rest // []:
-
const [first, ...rest] = ["foo"];
-
first // "foo"
-
rest // []
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
-
const [...butLast, last] = [1, 2, 3, 4, 5];
-
// 报错
-
const [first, ...middle, last] = [1, 2, 3, 4, 5];
-
// 报错
( 3 )函数的返回值
JavaScript 的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。
-
var dateFields = readDateFields(database);
-
var d = new Date(...dateFields);
上面代码从数据库取出一行数据,通过扩展运算符,直接将其传入构造函数Date。
( 4 )字符串
扩展运算符还可以将字符串转为真正的数组。
-
[...'hello']
-
// [ "h", "e", "l", "l", "o" ]
上面的写法,有一个重要的好处,那就是能够正确识别 32 位的 Unicode 字符。
-
'x\uD83D\uDE80y'.length // 4
-
[...'x\uD83D\uDE80y'].length // 3
上面代码的第一种写法, JavaScript 会将 32 位 Unicode 字符,识别为 2 个字符,采用扩展运算符就没有这个问题。因此,正确返回字符串长度的函数,可以像下面这样写。
-
function length(str) {
-
return [...str].length;
-
}
-
length('x\uD83D\uDE80y') // 3
凡是涉及到操作 32 位 Unicode 字符的函数,都有这个问题。因此,最好都用扩展运算符改写。
-
let str = 'x\uD83D\uDE80y';
-
str.split('').reverse().join('')
-
// 'y\uDE80\uD83Dx'
-
[...str].reverse().join('')
-
// 'y\uD83D\uDE80x'
上面代码中,如果不用扩展运算符,字符串的reverse操作就不正确。
( 5 )实现了 Iterator 接口的对象
任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组。
-
var nodeList = document.querySelectorAll('div');
-
var array = [...nodeList];
上面代码中,querySelectorAll方法返回的是一个nodeList对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList对象实现了 Iterator 接口。
对于那些没有部署 Iterator 接口的类似数组的对象,扩展运算符就无法将其转为真正的数组。
-
let arrayLike = {
-
'0': 'a',
-
'1': 'b',
-
'2': 'c',
-
length: 3
-
};
-
// TypeError: Cannot spread non-iterable object.
-
let arr = [...arrayLike];
上面代码中,arrayLike是一个类似数组的对象,但是没有部署 Iterator 接口,扩展运算符就会报错。这时,可以改为使用Array.from方法将arrayLike转为真正的数组。
( 6 ) Map 和 Set 结构, Generator 函数
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。
-
let map = new Map([
-
[1, 'one'],
-
[2, 'two'],
-
[3, 'three'],
-
]);
-
let arr = [...map.keys()]; // [1, 2, 3]
Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
-
var go = function*(){
-
yield 1;
-
yield 2;
-
yield 3;
-
};
-
[...go()] // [1, 2, 3]
上面代码中,变量go是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
如果对没有iterator接口的对象,使用扩展运算符,将会报错。
-
var obj = {a: 1, b: 2};
-
let arr = [...obj]; // TypeError: Cannot spread non-iterable object
ES5 和 ES6处理字符串的方法
ES5
检索字符串
indexOf
与数组方法类似。
stringObject.indexOf(searchvalue,fromindex)
fromindex规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。
lastIndexOf
与indexOf相反。
indexOf和lastIndexOf方法对大小写敏感!
截取字符串
slice(start, end)
- start,要抽取的片断的起始下标,如果为负,则从尾部算起。
- end,要抽取的片段的结尾的下标,如果为负,则从尾部算起。
String.slice() 与 Array.slice() 相似
substring(start, stop)
- start必需。要抽取的片断的起始下标,不能为负。
- stop可选。比要提取的子串的最后一个字符的位置多1,不能为负。
如果参数 start 与 stop 相等,那返回一个空串。
如果 start 比 stop 大,那在提取子串之前会先交换这两个参数。
如果同时为负,则返回空串。
如果一个值为负,则转为0,而且如果start 比 stop 大,会交换值。
substr(start, length) --不推荐
- start,要抽取的片断的起始下标,如果为负,则从尾部算起。
- length,如果为负,则转为0。
trim(),trimLeft(),trimRight()
去除首尾空格。
正则相关
split
把一个字符串分割成字符串数组。
stringObject.split(separator, howmany) //separator必需,字符串或正则表达式
var str="How are you doing today?";
str.split(/\s+/); //"How", "are", "you", "doing", "today?"
如果把空字符串 ("") 用作 separator,那么 stringObject 中的每个字符之间都会被分割。
String.split() 执行的操作与 Array.join 执行的操作是相反的。
match
可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
stringObject.match(searchvalue | reg)
字符串检索
var str="Hello world!";
str.match('w'); //["w", index: 6, input: "Hello world!", groups: undefined]
正则检索
// 全局匹配
var str="1 plus 2 equal 3";
str.match(/\d+/g); //["1", "2", "3"]
var str="1 plus 2 equal 3";
str.match(/\d+/); //["1", index: 0, input: "1 plus 2 equal 3", groups: undefined]
replace
用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
stringObject.replace(reg | substr, replacement)
字符串替换
'aaaccc'.replace('ccc', 'b'); //'aaab'
'aaaccc'.replace('bbb', 'b'); //'aaaccc'
'aaaccc'.replace('a', 'b'); //"baaccc"
正则替换
'aaaccc'.replace(/\w/, 'b') //"baaccc"
//全局匹配
'aaaccc'.replace(/\w/g, 'b') //"bbbbbb"
replacement
replacement 可以是字符串,也可以是函数。但是 replacement 中的 $ 字符具有特定的含义。
字符 | 替换文本 |
---|---|
字符 | 替换文本 |
$1、$2、...、$99 | 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。 |
$& | 与 regexp 相匹配的子串。 |
$` | 位于匹配子串左侧的文本。 |
$' | 位于匹配子串右侧的文本。 |
$$ | 直接量符号。 |
'aa123BB'.replace(/([a-z]+)(\d+)([A-Z]+)/g, '$1'); // "aa"
'aa123BB'.replace(/([a-z]+)(\d+)([A-Z]+)/g, '$4'); // "$4"
'aa123BB'.replace(/([a-z]+)(\d+)([A-Z]+)/g, '$&'); //"aa123BB"
'aa123BB'.replace(/(\d+)/g, '$`'); //"aaaaBB"
'aa123BB'.replace(/(\d+)/g, "$'"); //"aaBBBB"
'aa123BB'.replace(/(\d+)/g, '$$'); //"aa$BB"
ECMAScript v3 规定,replace() 方法的参数 replacement 可以是函数而不是字符串。在这种情况下,每个匹配都调用该函数,它返回的字符串将作为替换文本使用。
'aaaccc'.replace(/\w/g, function() {
return 'b';
});
//"bbbbbb"
'aaaccc'.replace(/\w/, function() {
return 'b';
});
//"baaccc"
search
stringObject.search(searchvalue | reg)
'aaaccc'.search('ccc'); //3
'aaaccc'.search(/ccc/); //3
var str="Visit W3School!";
str.search(/W3School/); //6
search() 对大小写敏感
其他
- big() 用大号字体显示字符串。
- blink() 显示闪动字符串。
- bold() 使用粗体显示字符串。
- sup() 把字符串显示为上标。
- sub() 把字符串显示为下标。
- strike() 使用删除线来显示字符串。
- small() 使用小字号来显示字符串。
- charAt() 返回在指定位置的字符。
- charCodeAt() 返回在指定的位置的字符的 Unicode 编码。
- toLocaleLowerCase() 把字符串转换为小写。
- toLocaleUpperCase() 把字符串转换为大写。
- toLowerCase() 把字符串转换为小写。
- toUpperCase() 把字符串转换为大写。
- toSource() 代表对象的源代码。
- toString() 返回字符串。
- valueOf() 返回某个字符串对象的原始值。
ES6
includes(), startsWith(), endsWith()
- includes(searchValue, start):返回布尔值,表示是否找到了参数字符串。
- startsWith(searchValue, start):返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith(searchValue, start):返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';
s.startsWith('world', 6); // true
s.endsWith('Hello', 5); // true
s.includes('Hello', 6); // false
s.includes('o'); // true
repeat(value)
返回一个新字符串,表示将原字符串重复n次。
- 如果value数是负数或者Infinity,会报错。但是,如果参数是 0 到-1 之间的小数,则等同于 0。
- 如果value是字符串,则会先转换成数字。
- value为NaN等同于 0。
- value如果是小数,会被取整。
'a'.repeat(3); // "aaa"
'a'.repeat(-1); //VM449:1 Uncaught RangeError: Invalid count value
'a'.repeat('3n'); //""
'a'.repeat('3'); //"aaa"
'a'.repeat(NaN); //""
'a'.repeat(1.6); //"a"
padStart(),padEnd()
- padStart(length, string)用于头部补全。
- padEnd(length, string)用于尾部补全。
'aaa'.padStart(2, 'ab'); //"aaa"
'aaa'.padStart(10, '0123456789'); //"0123456aaa"
'aaa'.padStart(10) //" aaa"
'aaa'.padEnd(6, 'cd') //"aaacdc"
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?` //"Hello Bob, how are you today?"
模板字符串中嵌入变量,需要将变量名写在${}之中。
JS表达式,可以进行运算,以及引用对象属性
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}` //"1 + 2 = 3"
var obj = { a: 'eee'}
`obj ${obj}` //"obj [object Object]"
调用函数
function fn() {
return "Hello World";
}
`foo ${fn()} bar` //"foo Hello World bar"
标签模板
模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)。
alert`123`; 等同于alert(123);
如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。
let a = 5;
let b = 10;
function tag(s, v1, v2) {
console.log(s);
console.log(v1);
console.log(v2);
return "OK";
}
tag`Hello ${ a + b } world ${ a * b}`;
// 等同于 tag(['Hello ', ' world ', ''], 15, 50);
//["Hello ", " world ", "", raw: Array(3)]
//15
//50
//"OK"