前言
面试中经常会遇到一些手写XXX函数的题目,今天自己复习总结下这一类题目。
bind,call,apply比较
- 相同点:xxx.bind(obj,,y1,y2,...),xxx.call(obj,y1,y2,...),xxx.apply(obj,[y1,y2,y3]),修改xxx函数的this指向为obj,obj不传默认为window
- 不同点: 1.调用时机:apply和call,会直接调用执行,bind不会直接调用,2.传参方式:bind,call接受的是参数列表,apply接受的是参数数组。
// 手写call
/**
* call 做了哪些事情
* 普通函数this指向当前调用者这个对象,xxx.call(yyy,...zzz),即this此时指向xxx
* call传入参数,是运行时该函数的执行上下文,即this
* 1.判断传入对象是否为空,为空默认为window
* 2.运行时需要执行的函数赋值给该对象
* 3.执行该对象的函数,那么该函数就可以使用该对象上任意属性,此时函数执行上下文就变成了yyy
* 4.返回函数执行结果
* 修改调用者this到,指定对象上
* @param {*} context 可选,在 function 函数运行时使用的 this
* @param {...any} args 参数列表
*/
Function.prototype.myCall = function(context,...args){
context = context || window;
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
}
// 手写apply
/**
* call 和apply ,作用一样可以修改当前函数执行的上下文,只不过传参不一样,apply,接受数组形式,call接受参数列表
* 1.判断传入对象是否存在,不存在默认取window
* 2.运行执行的函数赋值给该对象fn属性
* 3.判断入参第二个参数是否为空,即数组长度是否存在
* 4.执行fn函数,并返回结果
* @param {*} context 可选,在 function 函数运行时使用的 this
* @param {*Array} args 参数列表数组
*/
Function.prototype.myApply = function(context,args){
context = context || window;
context.fn = this;
let result;
if(args && args.length){
result = context.fn(...args)
}else{
result = context.fn();
}
delete context.fn;
return result;
}
// 手写bind
/**
* bind和call,apply的作用是一样的,修改当前函数的执行上下文为context,且是一个新的函数,不会立马执行,传参为参数列表
* 1.判断传入对象是否存在,不存在默认取window
* 2.运行执行的函数赋值给该对象fn属性
* 3.返回新函数,函数内执行该对象的fn函数
* @param {*} context
* @param {...any} args
* @returns 修改this指向后的新函数
*/
Function.prototype.myBind = function(context,...args){
context = context || window;
context.fn = this;
return (arguments) => {
return context.fn(...args,...arguments)
}
}
let obj = {
name : 'test',
sayGoodBye:function(){
console.log(this.name)
}
}
let obj2 = {
name: 'obj2-test',
sayHello: function(age,sex){
console.log(this.name)
age && console.log(age);
sex && console.log(sex)
}
}
obj.sayGoodBye.myCall(obj2)
obj2.sayHello.myApply(obj,[23])
obj2.sayHello.myBind(obj,23)('女')
实现一个new关键字
// 手写new
/**
* 实例化一共做了哪些事情
* 1.实例化后生成一个新对象,且实例的__proto__指向构造函数的原型
* 2.创建一个空对象
* 3.将构造函数的原型赋值给空对象的__proto__
* 4.修改构造函数的this指向
* 5.返回修改this指向后的对象
* @param {*} context 需要实例化的对象
* @param {...any} args 实例化传入的参数
*/
function myNew(context,...args){
let obj = Object.create(null);
obj.__proto__ = context.prototype;
const result = context.myApply(obj, args);
return typeof result === 'object' ? result : obj;
}
let a = myNew(Person,'test-person-name')
console.log(a.name)
节流防抖
- 场景介绍:
- 防抖场景:一些高频触发的事件,例如需要记录最后一次的操作结果,例如连续点赞,取消点赞,input快速快速输入等
- 节流场景:一些高频触发的事件,只想单位时间内执行一次,例如 onrize,onscroll等
- 节流实现
// 手写节流函数
/**
* 1.单位时间内只执行一次
* @param {*节流函数} fn
* @param {*延时ms数} delay
* 方式1:使用setTimeout实现
*/
function throttle(fn, delay) {
// 方式1:setTimeout实现
// let timer = null;
// return function(){
// if(timer) return;
// setTimeout(() => {
// fn.apply(this,arguments)
// timer = null;
// },delay)
// }
// 方式2: 判断时间间隔实现
let endTime = null;
return function(){
let startTime = Date.parse(new Date())
//判断当前时间和上一次执行时间间隔是否超过delay时间,没有不执行函数
if(startTime - endTime <= delay) return;
fn.apply(this,arguments)
endTime = Date.parse(new Date());
}
}
3.防抖实现
// 手写防抖函数
/**
* 2.每n秒触发一次,如果n秒内再次触发重新开始计时
* @param {*防抖函数} fn
* @param {*Number} delay 延时ms数
* @param {*Boolean} immediate 是否立即执行
*/
function debounce(fn,delay,immediate){
let timer = null;
return function(){
// n 秒内再次触发重新计时
if(timer) clearTimeout(timer)
if(immediate){
if(!timer) fn.apply(this,arguments)
}else{
timer = setTimeout(() => {
fn.apply(this,arguments)
},delay)
}
}
}
4.深拷拷贝简单实现
// 手写深拷贝
function deepClone(obj) {
let result = {};
for (const key in obj) {
// 此处只做了对象类型判断,还需要判断function,array,还有一些异常情况判断
if (Object.prototype.toString.call(obj[key]) === "[object Object]") {
result[key] = deepClone(obj[key]);
} else {
result[key] = obj[key];
}
}
return result;
}
5.instanceof原理(使用场景一般用来判断某个实例对象是不是某构造函数的实例)
/**
* 考虑子实例对象,是不是构造函数parentFn的实例
* 即 childObj.__proto__ === parentFn.prototype,即证明
* 基于原型链属性查找规则验证,如果在子对象的原型链对象上找到,就说明字实例对象是构造函数parentFn的实例
*/
function myInstanceOf(childObj, parentFn) {
let __proto__ = parentFn.prototype;
let child = childObj.__proto__;
while (true) {
if (child === null) return false;
if (child === __proto__) {
return true;
}
child = child.__proto__;
}
}
function Person(name) {
this.name = name;
}
let p1 = new Person("ljz");
console.log(myInstanceOf(p1, Person));