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);
}
}
阅读量:226
点赞量:0
收藏量:0