获取块级作用域的值:在块级作用域之前加上do,返回内部最后执行的表达式的值。
let x = do {
if (foo()) { f() }
else if (bar()) { g() }
else { h() }
};
抛出错误
- 行首:throw语句
- 不是行首:throw表达式
// 报错
console.log(throw new Error());
读取对象内部的某个属性。判断是否为null或undefined。如果是的,就不再往下运算,而是返回undefined
- 短路机制
- delete 运算符
- 括号不改变运算顺序
- 报错场合
- 右侧不得为十进制数值
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value
// 以下写法是禁止的,会报错。
// 1.构造函数
new a?.()
new a?.b()
// 2.链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 3.链判断运算符的左侧是 super
super?.()
super?.foo
// 4.链运算符用于赋值运算符左侧
a?.b = c
只有运算符左侧的值为null或undefined时,才会返回右侧的值
||运算符
:null 、undefined、 '' 、 false、0
??运算符
:null 、undefined
const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
?占位符
:单个参数的占位符...占位符
:多个参数的占位符
注意点:
- 函数的部分执行是基于原函数的,如果原函数发生变化,部分执行生成的新函数也会立即反映这种变化
- 如果预先提供的那个值是一个表达式,那么这个表达式并不会在定义时求值,而是在每次调用时求值
- 如果新函数的参数多于占位符的数量,那么多余的参数将被忽略
...
只会被采集一次,如果函数的部分执行使用了多个...
,那么每个...
的值都将相同
let obj = {
f(x, y) { return x + y; },
};
const g = obj.f(?, 3);
g(1) // 4
函数的部分执行是基于原函数的,如果原函数发生变化,部分执行生成的新函数也会立即反映这种变化
let f = (x, y) => x + y;
const g = f(?, 3);
g(1); // 4
// 替换函数 f
f = (x, y) => x * y;
g(1); // 3
如果预先提供的那个值是一个表达式,那么这个表达式并不会在定义时求值,而是在每次调用时求值。
let a = 3;
const f = (x, y) => x + y;
const g = f(?, a);
g(1); // 4
// 改变 a 的值
a = 10;
g(1); // 11
如果新函数的参数多于占位符的数量,那么多余的参数将被忽略。
const f = (x, ...y) => [x, ...y];
const g = f(?, 1);
g(2, 3, 4); // [2, 1]
...
只会被采集一次,如果函数的部分执行使用了多个...
,那么每个...
的值都将相同
const f = (...x) => x;
const g = f(..., 9, ...);
g(1, 2, 3); // [1, 2, 3, 9, 1, 2, 3]
可以把前一个操作的值传给后一个操作
- 只能传递一个值:函数必须是一个单参数函数,如果是多参数函数,就必须进行柯里化,改成单参数的版本
function doubleSay (str) {
return str + ", " + str;
}
function capitalize (str) {
return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
return str + '!';
}
// 传统的写法
exclaim(capitalize(doubleSay('hello')))
// "Hello, hello!"
// 管道的写法
'hello'
|> doubleSay
|> capitalize
|> exclaim
// "Hello, hello!"
较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性
- 不能在数值的最前面(leading)或最后面(trailing)。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的e或E前后不能有分隔符。
let budget = 1_000_000_000_000;
budget === 10 ** 12 // true
// 二进制
0b1010_0001_1000_0101
// 十六进制
0xA0_B0_C0
JavaScript 所有数字都保存成 64 位浮点数
- 数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的
- 二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity
BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示
// 超过 53 个二进制位的数值,无法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// 超过 2 的 1024 次方的数值,无法表示
Math.pow(2, 1024) // Infinity
const a = 2172141653n;
const b = 15346349309n;
// BigInt 可以保持精度
a * b // 33334444555566667777n
// 普通整数无法保持精度
Number(a) * Number(b) // 33334444555566670000
0b1101n // 二进制
0o777n // 八进制
0xFFn // 十六进制
42n === 42 // false,因为BigInt 与普通整数是两种值,它们之间并不相等。
typeof 123n // 'bigint'
Math.sign()用来判断一个值的正负,但是如果参数是-0,它会返回-0。
- 如果参数是NaN,返回false
- 如果参数是-0,返回true
- 如果参数是负值,返回true
- 其他情况返回false
Math.signbit(2) //false
Math.signbit(-2) //true
Math.signbit(0) //false
Math.signbit(-0) //true
“函数绑定”(function bind)运算符,用来取代call、apply、bind调用 函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);