1、手写深拷贝
什么是浅拷贝?
如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
什么是深拷贝?
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
常用的深拷贝方法:
JSON.parse(JSON.stringify(obj))
缺点:不能拷贝 undefined、函数、正则会转成空对象、data 会调用toString()等。
手写深拷贝:
function deepClone(obj){
if(obj===null||typeof obj !=='object') return obj
if(obj instanceof RegExp) return new RegExp(obj)
if(obj instanceof Date) return new Date(obj)
if(obj instanceof Function) return new Function(obj)
if(Array.isArray(obj)) return obj.map(item=>deepClone(item))
const newObj={}
for(let i in obj){
if(obj.hasOwnProperty(i)){
newObj[i]= deepClone(obj[i])
}
}
return newObj
}
const obj={
name:'zhangssn',
address:{
x:100,
y:200
}
}
let obj1=deepClone(obj)
console.log(obj,obj1)
console.log(obj==obj1)
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 || window
ctx.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 || window
ctx.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){
// 验证如果为基本数据类型,就直接返回false
const 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__;
}
}