# 1. Object.hasOwn
有时,我们想知道对象上是否存在某个属性,一般会使用in
操作符或obj.hasOwnProperty
,但它们都有各自的缺陷。
如果指定的属性位于对象或其原型链中,in
运算符将返回true
。
const Person = function (age) {
this.age = age
}
Person.prototype.name = 'fatfish'
const p1 = new Person(24)
console.log('age' in p1) // true
console.log('name' in p1) // true !!!
2
3
4
5
6
7
8
hasOwnProperty
方法会返回一个布尔值,表示对象自身属性中是否具有对应的值(原型链上的属性不会读取)。
const Person = function (age) {
this.age = age
}
Person.prototype.name = 'fatfish'
const p1 = new Person(24)
console.log(p1.hasOwnProperty('age')) // true
console.log(p1.hasOwnProperty('name')) // fasle !!!
2
3
4
5
6
7
8
obj.hasOwnProperty
已经可以过滤掉原型链上的属性,但在某些情况下,它还是不安全。
Object.create(null).hasOwnProperty('name')
// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function
2
Object.create()
方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype
)。
使用Object.hasOwn
来避免这两个问题,这比obj.hasOwnProperty
方法更加方便、安全。
let object = { age: 24 }
Object.hasOwn(object, 'age') // true
let object2 = Object.create({ age: 24 })
Object.hasOwn(object2, 'age') // false
let object3 = Object.create(null)
Object.hasOwn(object3, 'age') // false
2
3
4
5
6
# 2. 类的私有变量
我们将使用 # 符号表示类的私有变量。这样就不需要使用闭包来隐藏不想暴露给外界的私有变量。
以前,我们一般用_
表示私有属性,但它并不靠谱,还是会被外部修改。
class Person {
constructor (name) {
this._money = 1
this.name = name
}
get money () {
return this._money
}
set money (money) {
this._money = money
}
showMoney () {
console.log(this._money)
}
}
const p1 = new Person('fatfish')
console.log(p1.money) // 1
console.log(p1._money) // 1
p1._money = 2 // 依旧可以从外部修改_money属性,所以这种做法并不安全
console.log(p1.money) // 2
console.log(p1._money) // 2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
使用“#”实现真正私有属性
class Person {
#money = 1;
constructor(name) {
this.name = name;
}
get money() {
return this.#money;
}
set money(money) {
this.#money = money;
}
showMoney() {
console.log(this.#money);
}
}
const p1 = new Person("fatfish");
console.log("p1.money:",p1.money); // 1
//console.log(p1.#money); // 报错:属性“#money”在类“Person”之外不可访问,因为它具有私有标识符。
// p1.#money = 2 // 没法从外部直接修改
p1.money = 2;
console.log("输出p1:",p1);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3. 数字分隔符
const sixBillion = 6000000000
// ❌ 难以阅读
const sixBillion2 = 6000_000_000
// ✅ 更加易于阅读
console.log(sixBillion2) // 6000000000
2
3
4
5
也可以使用"_"用于计算
const sum = 1000 + 6000_000_000 // 6000001000
# 4. 可选链操作符
可选链运算符(?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
运算符的功能类似于 .
链式运算符,不同之处在于,在引用为空 (null
或者 undefined
) 的情况下不会引起错误,该表达式短路返回值是 undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined
。
你可能碰到过这样的情形:当需要访问嵌套在对象内部好几层的属性时,会得到错误Cannot read property 'stop' of undefined
,需要修改代码来处理来处理属性链中每一个可能的undefined
对象,比如:
let nestedProp = obj && obj.first && obj.first.second;
在访问obj.first.second
之前,obj
和obj.first
的值要被确认非null
(且不是undefined
)。目的是为了防止错误发生,如果简单直接的访问obj.first.second
而不对obj
和obj.first
进行校验就有可能产生错误。
有了可选链式调用 ,你只要这样写就可以做同样的事情:
let nestedProp = obj?.first?.second;
?. 的一般用法
obj?.prop
对象属性obj?.[expr]
对象属性obj.func?.(...args)
执行函数
# 5. BigInt
JS中超过Number.MAX_SAFE_INTEGER
的数字计算将是不安全的。
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
Math.pow(2, 53) // 9007199254740992
Math.pow(2, 53) + 1// 还是等于9007199254740992
2
3
使用BigInt
完全可以避免这个问题
BigInt(Math.pow(2, 53)) === BigInt(Math.pow(2, 53)) + BigInt(1) // false