Modern Javascript Features

25-01-12 编程 #code #js

一些很甜的 JS 语法糖。

Nullish Coalescing Operator (??)

当值为 nullish 时使用默认值。注意,nullish 和 truthy 不一样。
nullish: null or undefined.

const foo = null ?? 'default string';
console.log(foo);
// Expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// Expected output: 0

Nullish coalescing assignment (??=)

当变量为 nullish 时则赋值,否则无变化。

const a = { duration: 50 };

a.speed ??= 25;
console.log(a.speed);
// Expected output: 25

a.duration ??= 10;
console.log(a.duration);
// Expected output: 50

Logical OR/AND assignment (||= &&=)

使用场景比较少。

const a = { duration: 50, title: '' };

a.duration ||= 10;
console.log(a.duration);
// Expected output: 50

a.title ||= 'title is empty.';
console.log(a.title);
// Expected output: "title is empty."

// &&= 使用场景比较少
let a = 1;
let b = 0;

a &&= 2;
console.log(a);
// Expected output: 2

b &&= 2;
console.log(b);
// Expected output: 0

Optional chaining (?.)

kotlin 笑而不语。超级有用的语法糖。
既可以访问对象的属性也可以访问 function。
注意如果变量是 undefined/null 时返回的是 undefined 而不是 null。

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah',
  },
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// Expected output: undefined

// call a function
console.log(adventurer.someNonExistentMethod?.());
// Expected output: undefined

Dynamic Import

import() 不是 function call,只是语法相似。所以不能使用 apply/call 等语法。
import 不需要指定<script type=“module”.>

<script>
  async function load() {
    let say = await import('./say.js');
    say.hi(); // Hello!
    say.bye(); // Bye!
    say.default(); // Module loaded (export default)!
  }
</script>

Private properties

变量名不能是 #constructor;
在 chrome devtool 中可以在外部访问 private properties,只是一个 chrome 开发特性,不是语法问题。
MDN doc:private properties

class ClassWithPrivate {
  #privateField;
  #privateFieldWithInitializer = 42;

  #privateMethod() {
    // …
  }

  static #privateStaticField;
  static #privateStaticFieldWithInitializer = 42;

  static #privateStaticMethod() {
    // …
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField; // Syntax error

error.cause

error 新增的属性,用来在 error 中传递原始错误信息。

try {
    conn = getDbConnection();
  } catch (err) {
    throw new Error('Connecting to database failed.', { cause: err });
}

Array functions

arr.toSorted 
const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => a - b);

arr.findLast
const found = array1.findLast((element) => element > 45);
arr.toReversed()
arr.toSpliced
arr.with()
arr.fromAsync()

apply,call, bind

三个方法都是用于修改函数本身的的 context。

call(this,arg1,arg2,arg3)
// 将一个 array like(有元素,length 方法但是没其他方法)对象变成 array
const slice = Array.prototype.slice;
slice.call(arguments);

apply(this,argsArray)
// e.g. const max = Math.max.apply(null, numbers);
// 和 rest parameters 语法非常相似:
function wrapper(...args) {
  return anotherFn(...args);
}
// 使用场景:将一个 array append 到另一个 array,
// array.push 方法只能接收一个元素,如果使用 concat 得到的是一个新的 array
const array = ["a", "b"];
const elements = [0, 1, 2];
array.push.apply(array, elements);
//等价于 array.push(...elements);


bind(this,arg1,arg2,arg3) like call but return the new function.
// 将上面的 slice.call 优化成 slice() 函数
// Same as "slice" in the previous example
// 给 call 方法设置一个 this 对象并返回新的 call 函数,
// 这个 call 函数的 this 是 Array.prototype.slice;
const slice = Function.prototype.call.bind(Array.prototype.slice);
slice(arguments);
// 为什么不是 Array.prototype.slice.call.bind(Array.prototype.slice) ?