想成为一名好前端必须搞懂的类型 15 问

446 阅读4分钟

类型

JavaScript的语言类型有哪些?

JavaScript有七种语言类型:

语言类型typeof 运算结果
基本类型对象
nullobject
undefinedundefined
booleanboolean
numbernumber
stringstring
symbolsymbol
objectobject

如何正确的检测null值的类型?

let a = null;
!a && typeof a === 'object';

undefined 和 null 有什么区别?

undefined 表示未定义,我们可以通过全局变量 undefined 将自定义的变量指定为 undefined 类型,请注意 undefined 是一个 标识符 而不是JavaScript的关键字,所以它是可以被当作变量使用和赋值的。 null 表示已经定义了但是为空,null 是JavaScript的关键字,当你试图对它进行赋值操作时,引擎将抛出错误。

为什么说字符串具有不变性?

字符串的不变性是指字符串的成员方法不会改变其原始值,而是创建并返回一个新的字符串。

0.1 + 0.2 === 0.3?

从常规的数学运算的角度来看答案显然为true,但事实上在JavaScript中 0.1 + 0.2 = 0.30000000000000004 ,所以上面的问题的结果为false。

如何比较两个浮点数是否相等?

对于JavaScript来说,数字比较的误差范围值为 2^-52,当两数相减的结果小于误差范围值时,就认为这两个浮点数相等。

const a = 0.1 + 0.2;
const b = 0.3;

function numbersCloseEnoughToEqual(n1, n2) {
  return Math.abs(n1 - n2) < Math.pow(2, -52);
}

numbersCloseEnoughToEqual(a, b); // true;

如何判别一个number类型的变量是不是NaN?

ES6开始你可以使用 Number.isNaN(...) 来判断变量是不是NaN,请不要使用 window.isNaN(...) ,因为内建的全局函数存在缺陷。当我们不支持ES6的语法时,我们可以为其设置polyfill:

if (!Number.isNaN) {
  Number.isNaN = function(n) {
    return n !== n; // NaN是JavaScript中唯一一个不等于自身的值
  };
};

+0 === -0?

在JavaScript中 -0 是存在的,并且 +0 === -0 的结果为true,当我们将 -0 转换为字符串时我们得到的是 "0"。如果你需要判别 -0 ,那么你可以通过以下的方式:

function isNegZero(n) {
  return n === 0 && 1 / n === -Infinity;
}

如何安全的检查变量类型?

我们可以利用两个个知识点相结合来解决这一问题:

  1. 装箱,基本类型在调用方法时,JavaScript会将它包装成一个封装对象。
  2. 内置属性 [[Class]] ,该内置属性描述了变量所持有的值是什么类型,我们可以通过 Object.prototype.toString(...) 来访问该属性。
var a = 'abc';
Object.prototype.toString.call(a); // [object String]

var b = null;
Object.prototype.toString.call(a); // [object Null]

强制类型转换

强制类型转换是什么,它遵循怎样的规则?

从语言角度分析,值从一种类型转换为另一种类型称之为类型转换,而隐式的情况我们称之为强制类型转换,并且强制类型转换总是返回一个标量基本类型,下方列出了三种抽象操作的转换规则: ToString:

BooleanNumberNullUndefined
String'true' and 'false'遵循通用规则(极大与极小的值将以指数形式呈现)'null''undefined'

ToNumber:

BooleanStringNullUndefined
Numbertrue -> 1 false -> 0遵循数字常量的相关规则,处理失败时返回NaN0NaN

ToBoolean: undefined 、 null 、 false 、 +0 、 -0、 NaN 、 '' ''这七个假值在进行强制类型转换时会得到 false ,相对于所列出的假值列表以外的所有值将转换为 true。

parseInt(...)操作算是强制类型转换吗,你知道下方这道题的执行结果吗?

var a = '12a';
var b = parseInt(a, 10); // 12
var c = Number(a); // NaN

答案揭晓, parseInt(...) 操作我们普遍认为它属于解析而非强制类型转换,上述的这道题你会发现解析操作允许出现非数字字符,而强制类型转换则不允许,它将返回NaN。

对普通数组[1,2,3]进行ToString抽象操作,会得到什么结果?

对普通数组进行ToString抽象操作时,引擎会将所有元素进行强制类型转换,将它们都变为字符串类型,然后使用,拼接起来。

var arr = [1, 2, 3];
arr.toString(); // 1,2,3

你真的了解JSON.stringify(...)吗,以下对象的转换结果是什么?

var o = {
  name: 'O_c',
  character: ['open', undefined, 'sanguine'],
  getName: function() {
    return this.name;
  },
  [Symbol.toStringTag]: 'myObject'
};

JSON.stringify(o); // "{"name":"O_c","character":["open",null,"sanguine"]}"

上述对象的转换结果可能有些出乎意料,JSON.stringify在处理对象时,当属性为undefined、function、symbol时会自动忽略,当以上三种类型的值出现在数组中时,JavaScript引擎会用null进行替换,以保证单元位置不变。

a + "" 和 String(a) 两者是否存在区别?

在多数情况下,a + ""被简单的理解为将a的值转换为字符串类型,看似和String(a)并没有太大区别,但是请你运行一下下方的例子:

var o = {
  valueOf() {
  	return 10;
  },
  toString() {
  	return 1;
  }
};
o + "";
String(o);

显然 o + "" 的结果为 10,而 String(o) 的结果为 1,实际上当 + 运算符的某个操作数是字符串时,它会对其他的操作数进行隐式强制类型转换。当遇到对象时会执行 ToPrimitive 抽象操作,首先执行 valueOf() 方法,然后通过 ToString 抽象操作将 valueOf 的返回值转换为字符串,而 String() 则是直接调用 ToString 抽象操作。

为什么我们一直在避讳宽松相等 ==, "42" == true 的结果是?

在看到上述的题目时,我们的大脑下意识见会去考虑 "42" 是一个真值还是一个假值。很显然它是一个真值,所以我们会认为上述的问题的结果为 true ,但事实则是 false。在宽松相等比较中,当有一个操作数为布尔值时,会对该布尔值进行 ToNumber 抽象操作,既发生了一次隐式强制类型转换。所以 "42" == 1 的结果为 false ,其它类型的隐式强制类型转换机制如下:

  • 字符串和数字之间进行比较时,会对 String 类型的变量进行 ToNumber 抽象操作。
  • 其他类型和布尔值之间进行比较时,会对 Boolean 类型的变量进行 ToNumber 抽象操作。
  • 在宽松相等中,null 和 undefined 相等。
  • 对象与非对象之间进行比较时,会对 Object 类型的变量进行 ToPrimitive 抽象操作。
  • 对象与对象之间进行比较时,仅当两个对象指向的地址相同时才相等,不会发生任何强制类型转换。