跳到主要内容

Number.isNaN() 和 isNaN 区别

Number.isNaN()isNaN() 是 JavaScript 中用于检测值是否为 NaN 的两个函数,但它们的行为和使用场景有显著不同。以下是它们的主要区别:

1. 类型判断

  • isNaN():

    • isNaN() 会先将传入的值转换为数字,然后判断该值是否是 NaN
    • 这意味着 isNaN() 对于非数字类型的输入也会返回 true,例如:
      console.log(isNaN('hello')); // 输出: true
      console.log(isNaN(undefined)); // 输出: true
  • Number.isNaN():

    • Number.isNaN() 不会进行类型转换,只会在传入的值严格为 NaN 时返回 true
    • 这样可以避免类型转换带来的误判:
      console.log(Number.isNaN('hello')); // 输出: false
      console.log(Number.isNaN(undefined)); // 输出: false
      console.log(Number.isNaN(NaN)); // 输出: true

2. 用法场景

  • isNaN():
    • 适合用于需要判断一个值是否能够被视为数字的场景,尤其是在进行数值计算时。
  • Number.isNaN():
    • 适合用于需要严格判断值是否为 NaN 的场景,避免由于类型转换引起的误判。

3. 性能

  • isNaN():

    • 由于 isNaN() 进行类型转换,性能上相对较低,特别是在处理大量数据时。
  • Number.isNaN():

    • 由于没有进行类型转换,性能上更好。

总结

  • 使用 isNaN() 时要注意它的类型转换特性,可能会对非数字类型的输入产生误判。
  • 使用 Number.isNaN() 可以更安全地判断一个值是否严格为 NaN,并避免意外的类型转换问题。

示例对比

console.log(isNaN('NaN')); // true
console.log(Number.isNaN('NaN')); // false

console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true

console.log(isNaN(undefined)); // true
console.log(Number.isNaN(undefined)); // false

通过以上区别,你可以根据具体需求选择使用哪一个函数。如果你有其他问题或需要进一步的帮助,请随时问我!

a in obj 和 obj.hasOwnProperty(a) 有什么区别?

in 会检查对象以及原型链的属性,而 hasOwnProperty 只会查找对象上的属性,不查找原型链。

var obj = Object.create({ a: 1234 })

obj.b = 456

console.log("a" in obj);//true
console.log("b" in obj);//true

console.log(obj.hasOwnProperty("a"));//false
console.log(obj.hasOwnProperty("b"));//true

Proxy 中为什么需要 reciver 参数呢?

let obj = {
name: "老马",
company: {
name: "阿里巴巴",
title: "负责人"
}
}

let newObj = {
name: '小白'
}

let proxyObj = new Proxy(obj, {
get(target, key, reciver) {
console.log('reciver === proxyObj -> ', reciver === proxyObj);
console.log('reciver === newObj -> ', reciver === newObj);
return Reflect.get(target, key, reciver);
},
});

console.log(proxyObj.name);

Object.setPrototypeOf(newObj, proxyObj)

console.log(newObj.name);
console.log(newObj.company.name);

// reciver === proxyObj -> true
// reciver === newObj -> false
// 老马
// 小白
// reciver === proxyObj -> false
// reciver === newObj -> true
// 阿里巴巴

正常情况下,如访问 proxyObj.name。可以看到 reciver 时等于 proxyObj。说明一般情况下 reciver 等于代理之后的对象。

但是如 newObj,当有一个新的对象继承自代理对象时,reciver 指向了新的对象。

这说明 receiver 不仅仅会表示代理对象本身同时也还有可能表示继承于代理对象的对象。也就是 reciver 表示了上下文对象。

你可以简单的将 Reflect.get(target, key, receiver) 理解成为 target[key].call(receiver),不过这是一段伪代码,但是这样你可能更好理解。

Weakmap

WeakMap 是一种键值对的集合,其中的键必须是对象或非全局注册的符号,且值可以是任意的 JavaScript 类型,并且不会创建对它的键的强引用。

Proxy可以代理非对象值吗?

不可以。Proxy 只能代理对象,无法代理非对象值,例如字符串、布尔值等。

为什么需要Reflect?

下面两个读取属性的方法,操作上时等价的。

 const obj = { foo: 1 }

// 直接读取
console.log(obj.foo) // 1
// 使用 Reflect.get 读取
console.log(Reflect.get(obj, 'foo')) // 1

既然这两个等价,那么Proxy为什么需要Reflect呢?

实际上区别主要在于Reflect还能接收第三个参数receiver。而这个receiver可以简单理解为函数调用过程中的this。

const obj = { foo: 1 }
console.log(Reflect.get(obj, 'foo', { foo: 2 })) // 输出的是 2 而不是 1

代理对象的 get 拦截函数接收第三个参数receiver,它代表谁在读取属性。例如:

p.bar // 代理对象 p 在读取 bar 属性

如何区分一个对象是普通对象还是函数呢?一个对象在什么情况下才能作为函数调用呢?

答案是,通过内部方法和内部槽来区分对象,例如函数对象会部署内部方法 [[Call]],而普通对象则不会。

什么是常规对象,什么是异质对象?

对象必要的内部方法

对象必要的内部方法

额外的必要内部方法

额外的必要内部方法

  • 对于上图1列出的内部方法,必须使用 ECMA 规范 10.1.x 节给出的定义实现;

  • 对于内部方法 [[Call]],必须使用 ECMA 规范 10.2.1 节给出的定义实现;

  • 对于内部方法 [[Construct]],必须使用 ECMA 规范 10.2.2 节给出的定义实现。

而所有不符合这三点要求的对象都是异质对象。