JS面试笔记(更新中...)
JS
JS 有哪些数据类型,它们的区别?
- JS 共有八种数据类型,分别是 Undefined、String、Symbol、Object、Null、Number、Boolean、BigInt
- Symbol 和 BigInt 是 es6 新增的数据类型:
- Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
- BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数字已经超出了 Number 能够表示的安全整数范围。
- 这些数据可以分为原始数据类型和引用数据类型:
- 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
- 堆:引用数据类型(对象、数组、函数)
- 两种类型的区别在于存储的位置不同:
- 原始数据类型存储在栈(stack)中的简单数据段,占据空间小,大小固定,属于被频繁使用数据,所以放在栈中存储。
- 引用数据类型存储在堆(heap)中的对象,占据空间大,大小不固定。如果存储在栈中,将会影响程序的性能,引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
- 堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
- 栈中的数据的存取方式为先进后出
- 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定
- 在操作系统中,内存被分为栈区和堆区:
- 栈区内存有编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆区内存一般由开发者分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。
判断数组的方式有哪些?
-
通过 Object.prototype.toString.call()
1
Object.prototype.toString.call(obj).slice(8, -1) === "Array";
-
通过原型链做判断
1
obj.__proto__ = Array.prototype;
-
通过 ES6 的 Array.isArray()做判断
1
Array.isArray(obj);
-
通过 instanceof 做判断
1
obj instanceof Array
-
通过 Array.prototype.isPrototypeOf
1
Array.prototype.isPrototypeOf(obj);
null 和 undefined 的区别
- 首先 Undefined 和 null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null
- null 表示一个被明确赋值为空值的值。它通常被用来表示变量或者属性的空值。
- undefined 表示一个变量或者属性尚未定义或没有赋值,当声明一个变量但是没有给它赋值时,该变量被默认为 undefined,或者,如果一个对象中不存在某个属性,访问该属性时也会得到 undefined。
- null 的使用场景:
- 表示变量或属性的空值。
- 明确指示某个值为无效或不存在
- 通常由程序员赋值
- 可以用来重置变量,将其显式地设置为空。
- undefined 的使用场景:
- 变量被声明但未被赋值。
- 对象访问属性时,该属性不存在。
- 函数没有明确返回值时,默认返回 undefined。
数据类型检测的方式有哪些?
-
typeof
1
2
3
4
5
6
7
8console.log(typeof 2); //number
console.log(typeof true); //boolean
console.log(typeof "str"); //string
console.log(typeof []); //object
console.log(typeof function () {}); //function
console.log(typeof {}); //object
console.log(typeof undefined); //undefined
console.log(typeof null); //object- 其中数组、对象、null 都会被判断为 object,其他判断都正确。
-
instanceof
instanceof 可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。
1
2
3
4
5
6
7console.log(2 instanceof Number); //false
console.log(true instanceof Boolean); //false
console.log("str" instanceof String); //false
console.log([] instanceof Array); //true
console.log(function () {} instanceof Function); //true
console.log({} instanceof Object); //true- 可以看到,instanceof 只能正确判断引用数据类型,而不能判断基本数据类型(没有原型链),instanceof 运算符可以测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
-
constructor
1
2
3
4
5
6console.log((2).constructor === Number); //true
console.log(true.constructor === Boolean); //true
console.log("str".constructor === String); //true
console.log([].constructor === Array); //true
console.log(function () {}.constructor === Function); //true
console.log({}.constructor === Object); //true-
constructor 有两个作用,一是判断数据类型,二是对象实例通过 constructor 对象访问它的构造函数。注意,如果创建一个对象来改变它的原型,constructor 就不能用来判断数据类型了。
1
2
3
4
5function Fn() {}
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor === Fn); //false
console.log(f.constructor === Array); //true
-
-
Object.prototype.toString.call()
Object.prototype.toString.call()使用 Object 对象的原型方法 toString 来判断数据类型。
1
2
3
4
5
6
7
8
9
10var a = Object.prototype.toString;
console.log(a.call(2)); //[object Number]
console.log(a.call(true)); //[object Boolean]
console.log(a.call("str")); //[object String]
console.log(a.call([])); //[object Array]
console.log(a.call(function () {})); //[object Function]
console.log(a.call({})); //[object Object]
console.log(a.call(undefined)); //[object Undefined]
console.log(a.call(null)); //[object Null]-
同样是检测对象 obj 调用 toString 方法,obj.toString()的结果和 Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为 toString 是 Object 的原型方法,而 Array、function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString()不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。
-
typeof null 的结果是什么?为什么?
-
typeof null 的结果是 Object
-
在 js 最初的实现中,它使用 32 位的数据来存储变量的值,其中 3 位用于表示变量的类型信息。在这个类型信息中,对象类型被表示为二进制的 000,而 null 被认为是一个空指针对象,它的二进制表示正好是全部为 0。
-
000: object - 当前存储的数据指向一个对象。 1: int - 当前存储的数据是一个 31 位的有符号整数。 010: double - 当前存储的数据指向一个双精度的浮点数。 100: string - 当前存储的数据指向一个字符串。 110: boolean - 当前存储的数据是布尔值。
1
2
3
4
5
6
7
8
9
- 当 js 在判断一个值类型时,会查看该值的类型信息,由于 null 的类型信息全部为 0,与对象类型一致,因此 typeof null 返回的是 Object
```js
let n1 = 0.1,
n2 = 0.2;
console.log(n1 + n2); // 0.30000000000000004 -
要想得到 0.3,可以进行 bao’liu’lang’wei’xiao’shu
1 | (n1 + n2).toFixed(2); |
- 计算机是通过二进制的方式存储数据的,所以计算机计算 0.1+0.2 的时候,实际上是计算两个数的二进制的和。0.1 的二进制是 0.00011001100110011001100…(1100 循环),0.2 的二进制是 0.00110011001100…(1100 循环),这两个数的都是无限循环的数。
- 一般我们认为数字包括小数和整数,但是 js 中只有一种数字类型:Number,它的实现遵循 IEEE 745 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数。在二进制科学表示法中,双精度浮点数的小数部分最多只能保留 52 位,再加上前面的 1,其实就是保留 53 位有效数字,剩余的需要舍去,遵从“0 舍 1 入”的原则。
typeof NaN 的结果是什么?
-
typeof NaN 的结果是 number
-
NaN 指“不是一个数字”,NaN 是一个“警戒线”,用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”
-
NaN 是一个特殊值,它和本身不相等,是唯一一个非自反的值,而 NaN!==NaN 为 true
-
在 JavaScript 中,NaN 代表非数字(Not-a-Number)。当涉及到 NaN 时,有一个特殊的规则:NaN 与任何值(包括 NaN 本身)进行比较时,结果都是 false。
这包括相等运算符(==和===),因此 NaN 与 NaN 进行比较会得到 false(NaN !== NaN)。这是因为 NaN 表示一个特殊的数值状态,它被认为是不可比较的、不等于任何其他值的。
这种设计是基于 JavaScript 中的 IEEE 754 浮点数标准,其中 NaN 是一种特殊的数值。它用于表示一些不确定或无效的运算结果,例如 0 除以 0 或负数的平方根。
Number.isNaN 和 isNaN 的区别?
- 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的值都会返回 true,因此非数字值传入也会返回 true,会影响 NaN 的判断。
- 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字就继续判断是否为 NaN,不会进行数据类型的转换,这种方法更准确。
其他值到字符串的转换规则
- Null 和 Undefined 类型,null 转换为’null’,undefined 转换为"undefined"
- Boolean 类型,true 转换为“true”,false 转换为"false"
- Number 类型的值直接转换,不过那些极小和极大数会使用指数形式。
- Symbol 类型的值直接转换,但是只允许显示强制类型转换,使用隐式强制类型转换会产生错误。
- 对于普通对象来说,除非自定义 toString()方法,否则会调用 toString()(Object.prototype.toString())来返回内部属性值,如"[Object Object]",如果对象有自己的 toString()方法,字符串化时会调用该方法并使用其返回值。
Object.is()与操作符"==“、”==="的区别?
- 使用双等号进行相等判断时,如果两边类型不一致,则会进行强制类型转化后再进行比较。
- 使用三等号进行相等判断时,如果两边的类型不一致,不会做强制类型转换,直接返回 false
- 使用 Object.is()来进行相等判断时,一般情况下和三等号的判断相同,他会处理一些特殊情况,比如:-0 和 0 不相等,两个 NaN 是相等的。