二、题目
1. 防抖节流
这也是一个经典题目了,首先要知道什么是防抖,什么是节流。
- 防抖: 在一段时间内,事件只会最后触发一次。
- 节流: 事件,按照一段时间的间隔来进行触发。
实在不懂的话,可以去这个大佬的Demo地址玩玩防抖节流DEMO
// 防抖
function debounce(fn) {
let timeout = null;
return function () {
// 如果事件再次触发就清除定时器,重新计时
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, 500);
};
}
// 节流
function throttle(fn) {
let flag = null; // 通过闭包保存一个标记
return function () {
if (flag) return; // 当定时器没有执行的时候标记永远是null
flag = setTimeout(() => {
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为null(关键)
// 表示可以执行下一次循环了。
flag = null;
}, 500);
};
}
复制代码
这道题主要还是考查对 防抖 节流 的理解吧,千万别记反了!
2.一个正则题
要求写出 区号+8位数字,或者区号+特殊号码: 10010/110,中间用短横线隔开的正则验证。 区号就是三位数字开头。
例如 010-12345678
let reg = /^\d{3}-(\d{8}|10010|110)/g
复制代码
这个比较简单,熟悉正则的基本用法就可以做出来了。
3. 不使用a标签,如何实现a标签的功能
// 通过 window.open 和 location.href 方法其实就可以实现。
// 分别对应了a标签的 blank 和 self 属性
复制代码
4. 不使用循环API 来删除数组中指定位置的元素(如:删除第三位) 写越多越好
这个题的意思就是,不能循环的API(如 for filter之类的)。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 方法一 : splice 操作数组 会改变原数组
arr.splice(2, 1)
// 方法二 : slice 截取选中元素 返回新数组 不改变原数组
arr.slice(0, 2).concat(arr.slice(3,))
// 方法三 delete数组中的元素 再把这个元素给剔除掉
delete arr[2]
arr.join("").replace("empty", "").split("")
复制代码
5. 深拷贝
深拷贝和浅拷贝的区别就在于
- 浅拷贝: 对于复杂数据类型,浅拷贝只是把引用地址赋值给了新的对象,改变这个新对象的值,原对象的值也会一起改变。
- 深拷贝: 对于复杂数据类型,拷贝后地址引用都是新的,改变拷贝后新对象的值,不会影响原对象的值。
所以关键点就在于对复杂数据类型的处理,这里我写了两种写法,第二中比第一种有部分性能提升
const isObj = (val) => typeof val === "object" && val !== null;
// 写法1
function deepClone(obj) {
// 通过 instanceof 去判断你要拷贝的变量它是否是数组(如果不是数组则对象)。
// 1. 准备你想返回的变量(新地址)。
const newObj = obj instanceof Array ? [] : {
}; // 核心代码。
// 2. 做拷贝;简单数据类型只需要赋值,如果遇到复杂数据类型就再次进入进行深拷贝,直到所找到的数据为简单数据类型为止。
for (const key in obj) {
const item = obj[key];
newObj[key] = isObj(item) ? deepClone(item) : item;
}
// 3. 返回拷贝的变量。
return newObj;
}
// 写法2 利用es6新特性 WeakMap弱引用 性能更好 并且支持 Symbol
function deepClone2(obj, wMap = new WeakMap()) {
if (isObj(obj)) {
// 判断是对象还是数组
let target = Array.isArray(obj) ? [] : {
};
// 如果存在这个就直接返回
if (wMap.has(obj)) {
return wMap.get(obj);
}
wMap.set(obj, target);
// 遍历对象
Reflect.ownKeys(obj).forEach((item) => {
// 拿到数据后判断是复杂数据还是简单数据 如果是复杂数据类型就继续递归调用
target[item] = isObj(obj[item]) ? deepClone2(obj[item], wMap) : obj[item];
});
return target;
} else {
return obj;
}
}
复制代码
这道题主要是的方案就是,递归加数据类型的判断。
如是复杂数据类型,就递归的再次调用你这个拷贝方法 直到是简单数据类型后可以进行直接赋值
6. 手写call bind apply
call bind apply的作用都是可以进行修改this指向
- call 和 apply的区别在于参数传递的不同
- bind 区别在于最后会返回一个函数。
// call
Function.prototype.MyCall = function (context) {
if (typeof this !== "function") {
throw new Error('type error')
}
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
context = Object(context)
}
// 使用Symbol 来确定唯一
const fnSym = Symbol()
//模拟对象的this指向
context[fnSym] = this
// 获取参数
const args = [...arguments].slice(1)
//绑定参数 并执行函数
const result = context[fnSym](...args