undefined 和 null 都是基本数据类型。它们的定义是:
undefined 意味着变量已经声明了但是没有赋值。null 是空值,可以作为对象的初始值。undefined 不是 undeclared
undeclared 是指变量从未在代码中出现.使用未声明的变量就会报错:”ReferenceError: cat is not defined“。undefined 则是声明了但是值是 undefined 或者值并不存在。如何获取安全的 undefined 值?
因为 undefined 是一个标识符,所以可以被当作变量来使用和赋值,但是这样会影响 undefined 的正常判断。
可以使用 void 0 获得安全的值。
console.log(void 0); // undefined?? 和 ??=
空值合并运算符(??)
空值合并运算符(??)是一个逻辑运算符,判断左边的值是否是 null 或 undefined。如代码 a ?? b,如果 a 是 null 或 undefined,返回 b, 反之,返回 a。
a ?? b 可以理解为: a !== undefined && a !== null ? a : b
我们可以给值为 null 或 undefined 的变量一个默认值。如下面的代码所示:
let firstName = null, lastName = 'Sun';
let fullName = firstName ?? lastName
console.log(fullName); // Sun?? vs ||
?? 和 || 的相同点:都可以为值为 null 或 undefined 的变量赋默认值。如下面代码:
let firstName = null, lastName = 'Sun';
let fullName = firstName ?? lastName
console.log(fullName); // Sun
fullName = firstName || lastName;
console.log(fullName); // Sun不同点在于:
?? 只判断值 null 和 undefined|| 是任何假值(0, '', NaN, null, undefined)都不会被返回。这导致如果你使用 0,'' 或 NaN 作为有效值,就会出现不可预料的后果。如下面代码,判断值是 '' 时两者的不同表现:
let firstName = '', lastName = 'Sun';
let fullName = firstName ?? lastName
console.log(fullName); // ''
fullName = firstName || lastName;
console.log(fullName); // Sun逻辑空赋值运算符 ??=
逻辑空赋值运算符(x ??= y)仅在 x 是空值(null 或 undefined)时对其赋值。
a ??= b 可以理解为: a ?? (a = b) 。
如下面的代码:
let firstName = null;
firstName ??= 'yangyang'
console.log(firstName); // 'yangyang'JavaScript 中,typeof null 是 object,这是不对的,因为 null 是基本数据类型,不是对象。这是个 bug,但是因为修复这个 bug 会影响现存的代码,所以就一直没改。
这个 bug 是 JavaScript 第一版的遗留物,这个版本中,值都是 32 位存储单元,由类型标签(1-3位)和实际的值组成。类型标签存在单元的低位里,有下面五种:
低位如果是 1 位,类型标签就是 1 位长度(如整数类型),如果是 0,类型标签是 3 位长度,提供两个额外的位,如其余的四个类型。
使用 == 会发生隐式类型转换,=== 会严格判断两者的类型是否相等。我们也可以显式调用类型转换方法去转换类型,如使用 toString() 等。
通常来说,隐式类型转换的难点主要有两点:
作为一元运算符,转换成数字
作为二元运算符,将两个操作数转成字符串,再进行拼接
任意对象都有 toString 方法,将返回 [object Object]
但是数组例外,toString 方法将返回由逗号分隔的一系列数字组成的字符串
小练习
给出下列代码输出结果:
console.log(1 + "2" + "2"); // 122
console.log(1 + +"2" + "2"); // 32
console.log(1 + -"1" + "2"); // 02
console.log(+"1" + "1" + "2"); // 112
console.log( "A" - "B" + "2"); // NaN2
console.log( "A" - "B" + 2); // NaN
console.log("10,11" == [[[[10]],11]]) // 10,11 == 10,11, 答案: true
console.log("[object Object]" == {name: "test"}) // true答案:标注在代码注释中。
JavaScript 提供三种不同的值比较操作:
=====Object.is (ECMAScript 2015/ ES6 新特性)简而言之,在比较两件事情时:
== 将执行类型转换=== 将进行相同的比较,不做类型转换Object.is 除下面三个值外,表现与 === 相同-0 和 +0 不相等
Object.is(NaN,NaN) 为 true
// ===
console.log(0 === -0); // true
console.log(NaN === NaN); // false
// Object.is
console.log(Object.is(0, -0)); // false
console.log(Object.is(NaN, NaN)); // true有四种方法:
方法一:typeof
typeof 运算符返回一个字符串,表示操作数的类型。下图是使用 typeof 判断类型的汇总:

可以看到,typeof 判断类型有两个缺点:
null 值的判断是 object,这个是历史遗留问题object 的判断区分不出具体的对象类型方法二: instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
// 定义构造函数
function C(){}
function D(){}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype方法三:Object.prototype.constructor
constructor 属性返回 Object 的构造函数(用于创建实例对象)。
注意:此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
所有对象(使用 Object.create(null) 创建的对象除外)都将具有 constructor 属性。在没有显式使用构造函数的情况下,创建的对象(例如对象和数组文本)将具有 constructor 属性,这个属性指向该对象的基本对象构造函数类型。
const o = {}
o.constructor === Object // true
const o = new Object
o.constructor === Object // true方法四:Object.prototype.toString
Object.prototype.toString 方法返回一个表示该对象的字符串。
const toString = Object.prototype.toString;
toString.call(new Date()); // [object Date]
toString.call(new String()); // [object String]小练习
如何实现 instanceof?
答案:
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left); // 获取对象的原型
let prototype = right.prototype; // 获取构造函数的 prototype 对象
// 判断构造函数的 prototype 对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}阅读量:270
点赞量:0
收藏量:0