前端面试题 —— JavaScript (一)

本文深入探讨JavaScript中的关键概念,包括map与Object的区别、脚本延迟加载、类数组对象、ES6模块特性、内存泄漏等问题,旨在帮助开发者更好地理解和掌握JavaScript。

目录

一、map和Object的区别

二、脚本延迟加载的顺序有哪些? 

三、 JavaScript 类数组对象的定义?

四、为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组? 

五、ES6模块与CommonJS模块有什么异同? 

六、如何判断一个对象是否属于某个类?

七、for...in和for...of的区别

 八、数组的遍历方法有哪些?

九、typeof null 的结果是什么,为什么?

十、isNaN 和 Number.isNaN 函数的区别?

十一、其他值到字符串的转换规则?

十二、其他值到数字值的转换规则?

十三、其他值到布尔类型的值的转换规则?

十四、JavaScript 中如何进行隐式类型转换?

十五、const对象的属性可以修改吗

十六、如果new一个箭头函数的会怎么样?

十七、箭头函数的this指向哪⾥?

十八、对 rest 参数的理解

十九、ES6中模板语法与字符串处理

二十、原型修改、重写

二十一、异步编程的实现方式?

二十二、setTimeout、Promise、Async/Await 的区别

二十三、Promise解决了什么问题

二十四、Promise.all和Promise.race的区别的使用场景

二十五、await 到底在等啥?

二十六、async/await的优势

二十七、哪些情况会导致内存泄漏?


一、map和Object的区别

Map Object
附加的键 Map默认情况不包含任何键,只包含显式插入的键。 Object 有一个原型, 原型链上的键名有可能和自己在对象上的设置的键名产生冲突。
键的类型 Map的键可以是任意值,包括函数、对象或任意基本类型。 Object 的键必须是 String 或是Symbol。
键的顺序 Map中的key是有序的。因此,当迭代的时候,Map对象以插入的顺序返回键值。 Object 的键是无序的。
Size Map 的键值对个数可以轻易地通过size属性获取。 Object 的键值对个数只能手动计算。
迭代 Map是iterable的,所以可以直接被迭代。 迭代Object需要以某种方式获取它的键然后才能迭代。
性能 在频繁增删键值对的场景下表现更好。 在频繁添加和删除键值对的场景下未作出优化。

二、脚本延迟加载的顺序有哪些? 

延迟加载就是等页面加载完成之后再加载 JavaScript 文件。

js 延迟加载有助于提高页面加载速度。

一般有以下几种方式:

  •  defer 属性:给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
  • async 属性:给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js 脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
  • 动态创建 DOM 方式:动态创建 DOM 标签的方式,可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
  • 使用 setTimeout 延迟方法:设置一个定时器来延迟加载js脚本文件。
  • 让 JS 最后加载:将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。

三、 JavaScript 类数组对象的定义?

一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组类似,但是不能调用数组的方法。常见的类数组对象有 arguments 和 DOM 方法的返回结果,还有一个函数也可以被看作是类数组对象,因为它含有 length 属性值,代表可接收的参数个数。

常见的类数组转换为数组的方法有这样几种:

  •  通过 call 调用数组的 slice 方法来实现转换
Array.prototype.slice.call(arrayLike);
  •  通过 call 调用数组的 splice 方法来实现转换
Array.prototype.splice.call(arrayLike, 0);
  •  通过 apply 调用数组的 concat 方法来实现转换
Array.prototype.concat.apply([], arrayLike);
  •  通过 Array.from 方法来实现转换
Array.from(arrayLike);

四、为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组? 

arguments是一个对象,它的属性是从 0 开始依次递增的数字,还有callee和length等属性,与数组相似;但是它却没有数组常见的方法属性,如forEach, reduce等,所以叫它们类数组。

要遍历类数组,有三个方法:

  • 将数组的方法应用到类数组上,这时候就可以使用`call`和`apply`方法,如:
function foo(){
  Array.prototype.forEach.call(arguments, a => console.log(a))
}
  • 使用Array.from方法将类数组转化成数组:‌
function foo(){
  const arrArgs = Array.from(arguments)
  arrArgs.forEach(a => console.log(a))
}
  •  使用展开运算符将类数组转化成数组
function foo(){
    const arrArgs = [...arguments]
    arrArgs.forEach(a => console.log(a))
}

五、ES6模块与CommonJS模块有什么异同? 

ES6 Module和CommonJS模块的区别:

  • CommonJS是对模块的浅拷⻉,ES6 Module是对模块的引⽤,即ES6 Module只存只读,不能改变其值,也就是指针指向不能变,类似const。
  • import的接⼝是read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对commonJS对重新赋值(改变指针指向),但是对ES6 Module赋值会编译报错。

ES6 Module和CommonJS模块的共同点:

  •  CommonJS和ES6 Module都可以对引⼊的对象进⾏赋值,即对对象内部属性的值进⾏改变。

六、如何判断一个对象是否属于某个类?

  • 第一种方式,使用 instanceof 运算符来判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
  • 第二种方式,通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写。
  • 第三种方式,如果需要判断的是某个内置的引用类型的话,可以使用 Object.prototype.toString() 方法来打印对象的[[Class]] 属性来进行判断。 

七、for...in和for...of的区别

for…of 是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值,和ES3中的for…in的区别如下

  • for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
  • for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
  • 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;

总结:for...in 循环主要是为了遍历对象而生,不适用于遍历数组;for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。

八、数组的遍历方法有哪些?

方法 是否改变原数组 特点
forEach() 取决于元素的数据类型 数组方法,没有返回值,是否会改变原数组取决于数组元素的类型是基本类型还是引用类型
map() 数组方法,不改变原数组,有返回值,可链式调用
filter()   数组方法,过滤数组,返回包含符合条件的元素的数组,可链式调用
for...of for...of遍历具有Iterator迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历普通的obj对象,将异步循环变成同步循环
every() 和 some() 数组方法,some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false

find() 和 findIndex()

数组方法,find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值
reduce() 和 reduceRight() 数组方法,reduce()对数组正序操作;reduceRight()对数组逆序操作

九、typeof null 的结果是什么,为什么?

typeof null 的结果是Object

在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的类型标签(1-3 bits)以及当前要存储值的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型:

000: object   - 当前存储的数据指向一个对象。
  1: int      - 当前存储的数据是一个 31 位的有符号整数。
010: double   - 当前存储的数据指向一个双精度的浮点数。
100: string   - 当前存储的数据指向一个字符串。
110: boolean  - 当前存储的数据是布尔值。

如果最低位是 1,则类型标签标志位的长度只有一位;如果最低位是 0,则类型标签标志位的长度占三位,为存储其他四种数据类型提供了额外两个 bit 的长度。

有两种特殊数据类型:

  • undefined的值是 (-2)30(一个超出整数范围的数字);
  • null 的值是 机器码 NULL 指针(null 指针的值全是 0)

那也就是说null的类型标签也是000,和Object的类型标签一样,所以会被判定为Object。

十、isNaN 和 Number.isNaN 函数的区别?

  • 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想散在风中

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值