1、手写深拷贝
- 什么是浅拷贝?
如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。 - 什么是深拷贝?
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
常用的深拷贝方法:
JSON.parse(JSON.stringify(obj))
缺点:不能拷贝 undefined、函数、正则会转成空对象、data 会调用toString()等。
手写深拷贝:
function clone (orgin) {
const type = getType(orgin)let resultdebuggerswitch (type) {
case 'object':result = {
}Object.keys(orgin).forEach(key => {
result[key] = ['object', 'array', 'date', 'regexp', 'function'].includes(getType(orgin[key])) ? clone(orgin[key]) : orgin[key]})return resultcase 'array':result = []orgin.forEach(item => {
['object', 'array', 'date', 'regexp', 'function'].includes(getType(item)) ? result.push(clone(item)) : result.push(item)})return resultcase 'date':return new Date(orgin.getTime())case 'regexp':return new RegExp(orgin)default:return orgin}}function getType(data) {
const intermediary = Object.prototype.toStringconst res = intermediary.call(data)const type = res.substring(8, res.length -1)return type.toLocaleLowerCase()}
2、防抖、节流
2.1 节流
节流之时间戳版
let throttle = function(func, delay){
let prev = Date.now();return function(){
var now = Date.now();if(now-prev>=delay){
func.apply(this, arguments);prev = Date.now();}}}
节流之定时器版
let throttle = function(func, delay) {
let timer = null;return function() {
if (!timer) {
timer = setTimeout(function() {
func.apply(this, arguments);timer = null;}, delay);}};};
定时器节流,由于定时器的特性,无法首次立即执行,但可以确保必定会执行最后一次
时间戳节流,可以首次立即执行,但无法确保会执行最后一次
2.2 防抖
function debounce(fn, wait) {
let timeout = null;return function() {
// 如果多次触发将上次记录延迟清除掉if (timeout !== null) clearTimeout(timeout);timeout = setTimeout(function() {
fn.apply(this, arguments);timeout = null;}, wait);};}
3、手写call、apply
改变this指向,语法:
fun.call(thisArg, arg1, arg2, ...),调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
func.apply(thisArg, [argsArray]),调用一个函数,以及作为一个数组(或类似数组对象)提供的参数。
手写call代码:
//通过对象调用方法来修改this指向
Function.prototype.call2 = function (context){
const ctx = context || windowctx.func = this// 测试用例中这个this打印出来是:函数test [function test]const args = [...arguments].slice(1) // 保存剩余参数const res = arguments.length > 1 ? ctx.func(...args) : ctx.func()delete ctx.func // 避免造成全局污染return res
}let foo = {
value: 1
}
function test(name) {
console.log(name)console.log(this.value);
}
test.call2(foo, 'black') // black 1
手写apply代码:
Function.prototype.apply2 = function (context){
const ctx = context || windowctx.func = this// 测试用例中这个this打印出来是:函数test [function test]const res = arguments[1] ? ctx.func(...arguments[1]) : ctx.func()delete ctx.func // 避免造成全局污染return res
}let foo = {
value: 1
}
function test(name) {
console.log(name)console.log(this.value);
}
test.call2(foo, 'black') // black 1
4、手写instanceof
instanceof 可以判断一个引用是否属于某构造函数;
还可以在继承关系中用来判断一个实例是否属于它的父类型
// left 必须是对象
// right 必须是函数
function myInstance(left, right){
// 验证如果为基本数据类型,就直接返回falseconst baseType = ['string', 'number','boolean','undefined','symbol']if(baseType.includes(typeof(L))) return false// 取 right 的显示原型let RP = right.prototype;//取 left 的隐式原型 left = left.__proto__;while(true){
// 找到最顶层if(left === null){
return false;}if(left === RP){
return true;}// 没找到继续向上一层原型链查找left = left.__proto__;}
}
5、手写promise
转载掘金 IT老班长
笔试题链接
转载掘金 ErDong
以上仅做笔试使用,如有侵权请联系删除!