第二章:JavaScript数据类型—下-灵析社区

懒人学前端

第二章:JavaScript数据类型—下

四、谈谈 undefined 和 null ?

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 nullundefined,返回 b, 反之,返回 a

a ?? b 可以理解为: a !== undefined && a !== null ? a : b

我们可以给值为 nullundefined 的变量一个默认值。如下面的代码所示:

let firstName = null, lastName = 'Sun';
let fullName = firstName ?? lastName
console.log(fullName); // Sun

?? vs ||

?? || 的相同点:都可以为值为 nullundefined 的变量赋默认值。如下面代码:

let firstName = null, lastName = 'Sun';
let fullName = firstName ?? lastName
console.log(fullName); // Sun

fullName = firstName || lastName;
console.log(fullName); // Sun

不同点在于:

  • ?? 只判断值 null undefined
  • || 是任何假值(0''NaN nullundefined)都不会被返回。这导致如果你使用 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'

五、typeof null 的结果是什么?

JavaScript 中,typeof null object,这是不对的,因为 null 是基本数据类型,不是对象。这是个 bug,但是因为修复这个 bug 会影响现存的代码,所以就一直没改。

这个 bug 是 JavaScript 第一版的遗留物,这个版本中,值都是 32 位存储单元,由类型标签(1-3位)和实际的值组成。类型标签存在单元的低位里,有下面五种:

  • 000: 对象,数据是对象类型
  • 1:整数,存储的数据是一个 31 位的有符号整数。
  • 010:浮点数,存的数据是双精度浮点数
  • 100: 字符串,存的数据是字符串
  • 110:布尔,存的数据是布尔

低位如果是 1 位,类型标签就是 1 位长度(如整数类型),如果是 0,类型标签是 3 位长度,提供两个额外的位,如其余的四个类型。

六、JavaScript 如何做类型转换?

使用 == 会发生隐式类型转换,=== 会严格判断两者的类型是否相等。我们也可以显式调用类型转换方法去转换类型,如使用 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

答案:标注在代码注释中。

七、==、 === 和 Object.is() 的区别是什么?

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

八、JavaScript 判断数据类型有哪些方法?

有四种方法:

方法一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