前端面试题-JS
[‘1’, ‘2’, ‘3’].map(parseInt) what & why ?
map(function callback(currentValue[, index[, array]]))
这个 callback 一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。
parseInt(string, radix)
接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
答案是:[1,NaN,NaN]
运算过程:
1 | (1)[("1", "2", "3")].map(parseInt); |
在 radix 为 undefined,或者 radix 为 0 或者没有指定的情况下,JavaScript 作如下处理:
- 如果字符串 string 以”0x”或者”0X”开头, 则基数是 16 (16 进制).
- 如果字符串 string 以”0”开头, 基数是 8(八进制)或者 10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用 10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出 radix 参数的值。
- 如果字符串 string 以其它任何值开头,则基数是 10 (十进制)。
什么是防抖和节流?有什么区别?如何实现?
防抖
触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
思路
每次触发事件时都取消之前的延时调用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => {
// 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log("防抖成功");
}
var inp = document.getElementById("inp");
inp.addEventListener("input", debounce(sayHi)); // 防抖
apply()函数
function.apply(thisArg, argsArray)
- thisArg: 要设置函数执行上下文的对象。
- argsArray: 要传递给函数的参数数组
- 示例
1 | const obj = { |
在这个例子中,sayHello() 函数的this 关键字被设置为 obj 对象,因此函数内部的 name 属性将引用 obj 对象的 name 属性。
节流
高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
- 思路
每次触发事件时都判断当前是否有等待执行的延时函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => {
// 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener("resize", throttle(sayHi));
介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
- Set
- 成员唯一、无序且不重复
- [value, value],键值与键名是一致的(或者说只有键值,没有键名)
- 可以遍历,方法有:add、delete、has
- WeakSet
- 成员都是对象
- 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏
- 不能遍历,方法有 add、delete、has
- Map
- 本质上是键值对的集合,类似集合
- 可以遍历,方法很多可以跟各种数据格式转换
- WeakMap
- 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
- 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
- 不能遍历,方法有 get、set、has、delete
有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣 Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()
Object.prototype.toString.call()
每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type],其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用 call 或者 apply 方法来改变 toString 方法的执行上下文。
1 | const an = ["Hello", "An"]; |
这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
1 | Object.prototype.toString.call("An"); // "[object String]" |
instanceof
instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
使用 instanceof 判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。
1 | [] instanceof Array; // true |
但 instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。
1 | [] instanceof Object; // true |
Array.isArray()
- 功能:用来判断对象是否为数组
- instanceof 与 isArray
当检测 Array 实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以检测出 iframes1
2
3
4
5
6
7
8
9var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length - 1].Array;
var arr = new xArray(1, 2, 3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false
- Array.isArray() 与 Object.prototype.toString.call()
Array.isArray()是 ES5 新增的方法,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。1
2
3
4
5if (!Array.isArray) {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) === "[object Array]";
};
}