前端手写代码整理
目录
- 一、JS基础
- 1. 手写 Object.create
- 2. 手写 instanceof方法
- 3. 手写 new 操作符
- 4. 手写 Promise
- 5. 手写防抖函数
- 6. 手写节流函数
- 7. 手写类型判断函数
- 8. 手写 call 函数
- 9. 手写 apply 函数
- 10. 手写 bind 函数
- 11. 函数柯里化
- 12. 实现AJAX请求
- 13. 使用Promise封装AJAX请求
- 14. 实现浅拷贝
- 15. 实现深拷贝
- 16. 实现sleep函数(使用Promise封装setTimeout)
- 17. 实现Object.assign
一、JS基础
1. 手写 Object.create
将传入的对象作为原型
function create(obj){function F(){}F,prototype=obj;return new F();
}
2. 手写 instanceof方法
instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
function myInstanceof(left,right){let proto=Object.getPrototypeOf(left);//获取对象的原型let prototype=right.prototype;//获取构造函数的原型while(true){if(!proto) return false;if(proto===prototype) return true;proto=Object.getPrototypeOf(proto);}
}
3. 手写 new 操作符
(1)首先创建了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
function myNew(Fn,...args){let obj={};obj._proto_=Fn.prototype;let res=Fn.call(obj,...args);if(res&& (typeof res==='object'||typeof res==='function')){return res;}elsereturn obj;
}
优化:
function myNew(){let obj=null;let constructor=Array.prototype.shift.call(arguments);let res=null;// 新建一个空对象,对象的原型为构造函数的 prototype 对象obj=Object.create(constructor.prototype);// 将 this 指向新建对象,并执行函数(为新对象添加属性)res=constructor.apply(obj,arguments);let flag=res&&(typeof res==='object'||typeof res==='function');return flag?res:obj;
}
myNew(构造函数, 初始化参数)
4. 手写 Promise
5. 手写防抖函数
(n秒后再执行回调)[ setTimeout(fn,delay) ]
函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
利用setTimeout给需要防抖的方法一个延时执行
let timer=null;
function debounce(fn, delay)(clearTimeout(timer);timer=setTimeout(fn,dalay);
)
优化:
function debounce(fn,delay){let timer=null; // 利用闭包,避免污染return function(){let context=this;// 获取上下文let args=arguments; // 获取当前参数if(timer){ // 如果此时存在定时器的话,则取消之前的定时器重新记时clearTimeout(timer);}// 设置定时器,使事件间隔指定事件后执行timer=setTimeout(()=>{ fn.apply(context,arguments);},delay);}
}
6. 手写节流函数
(n秒内只有一次回调)[ curTime-lastTime>=delay ]
函数节流是指单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
function throttle(fn,delay){let lastTime=Date.now(); //设置初始时间return function(){let context=this;let args=arguments;let curTime=Date.now();// 如果两次时间间隔超过了指定时间,则执行函数。if(curTime-lastTime>=delay){lastTime=Date.now();return fn.apply(context,args);}}
}
7. 手写类型判断函数
function getType(value){if(value===null) return value;// 判断数据是引用类型的情况if(typeof value==='object'){let valueClass=Object.prototype.toString.call(value);let type=valueClass.split(" ")[1].split(""); type.pop();// ' ] 'return type.join("").toLowerCase();}else{// 判断数据是基本数据类型的情况和函数的情况return typeof value;}
}
8. 手写 call 函数
A.fn.call(B) : 调用A的fn用于B的环境 【 在B中使用A的方法 】
传入参数:args=[…arguments].slice(1)
Function.prototype.myCall=function(context){//判断调用对象是否为函数if(typeof this!=="function"){console.log("error");}//获取参数,截取第一个参数后的所有参数let args=[...arguments].slice(1);let result=null;//判断context是否传入,如果未传入则设置为windowcontext = context || window;//将函数作为上下文对象的一个属性context.fn = this;//使用上下文对象来调用这个方法,并保存返回结果result = context.fn(...args);// 删除新增的属性,返回结果delete context.fn;return result;
}
9. 手写 apply 函数
同call,只是传入参数的区别:args=arguments[1]
Function.prototype.myApply = function(context){if(typeof this!=='funtion'){throw new TypeError("Error");}let args=arguments[1];let result=null;context = context || window;context.fn=this;if(args){result=context.fn(...args);}else{result=context,fn();}delete context.fn;return result;
}
10. 手写 bind 函数
函数返回:函数内部使用apply绑定函数调用,需要判断函数作为构造函数的情况
Function.prototype.myBind=function(){if(typeof this!=='function'){throw new TypeError("error");}let args=[...arguments].slice(1);let fn=this;//创建一个函数返回return function Fn(){//使用 apply 来绑定函数调用return fn.apply(//判断函数作为构造函数的情况://需要传入当前函数的this给apply调用//其余情况都传入指定的上下文对象this instanceof Fn?this:context,args.concat(...arguments);)}
}
11. 函数柯里化
将使用多个参数的一个函数转换成一系列使用一个参数的函数
function curry(fn,args){//获取函数需要的参数长度let length=fn.length;args = args||[];return function(){let subArgs = args.slice(0);//拼接得到现有的所有参数for(let i=0;i<arguments.length;i++){subArgs.push(arguments[i]);}//判断参数的长度是否已经满足函数所需参数的长度if(subArgs.length>=length){//满足,执行函数return fn.apply(this,subArgs);}else{//不满足,递归返回科里化函数,等待参数传入return curry.call(this,fn,subArgs);}}
}
//es6实现
function curry(fn,...args){return fn.length<=args.length?fn(...args):curry.bind(null,fn,...args);
}
使用demo:
function checkRegExp(regexp, string) {return regexp.test(string)
}// before:
console.log(checkRegExp(/^1\d{10}$/, '15770638063'))
// after:
let _curry = curry(checkRegExp)
let chekcPhone = _curry(/^1\d{10}$/)
chekcPhone('15770638063')
12. 实现AJAX请求
AJAX是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。
- 创建一个 XMLHttpRequest 对象。
- 在这个对象上使用 open 方法创建一个 HTTP 请求,open 方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息。
- 在发起请求前,可以为这个对象添加一些信息和监听函数。
一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发onreadystatechange 事件,可以通过设置监听函数,来处理请求成功后的结果。 - 当对象的属性和监听函数设置完成后,最后调用 sent 方法来向服务器发起请求,可以传入参数作为发送的数据体。
const SERVER_URL = '/server';
let xhr = new XMLHttpRequest();
//创建http请求,参数是 方法、地址、是否异步
xhr.open("GET",SERVER_URL,true);
xhr.onreadystatechange = function(){//4代表服务器返回的数据接收完成if(this.readyState !== 4) return;//当请求成功时if(this.status == 200){handle(this.response);}else{console.error(this.statusText);}
};
//设置请求失败时的监听函数
xhr.onerror=function(){console.error(this.statusText);
}
//设置请求头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept","application/json");
//发送http请求,参数为发送的数据体
xhr.send(null);
13. 使用Promise封装AJAX请求
//promise 封装实现:
function getJSON(url){//创建一个promise对象let promise = new Promise(function(resolve,reject){let xhr = new XMLHttpRequest();//创建http请求xhr.open("GET",url,true);//设置状态的监听函数xhr.onreadystatechange = function(){if(this.readyState !== 4) return;//当请求成功或失败时,改变promise的状态if(this.status === 200){resolve(this.response);}else{reject(new Error(this.statusText));}}//设置错误监听函数xhr.onerror = function(){reject(new Error(this.statusText));};//设置响应的数据类型xhr.responseType = "json";//设置请求头信息xhr.setRequestHeader("Accpet","application/json");//发送http请求xhr.send(null);})return promise;
}
14. 实现浅拷贝
一个新的对象对原始对象的属性值进行精确地拷贝
如果是基本数据类型,拷贝的就是基本数据类型的值.
如果是引用数据类型,拷贝的就是内存地址。
( 如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。)
- Object.assign(target, source_1, ···) 【 ES6中对象的拷贝方法 】
let target = {a:1}
let object2 = {b:2}
let object3 = {c:3}
Object.assign(target,object2,object3);
console.log(target)
- 扩展运算符
let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
//基本数据类型拷贝的是值
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
//引用数据类型拷贝的是内存地址
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}
- Array.prototype.slice
slice()不改变原始数组,不写参数就是一个浅拷贝。
let arr = [1,2,3,4];
console.log(arr.slice()); // [1,2,3,4]
console.log(arr.slice() === arr); //false
- Array.prototype.concat
concat()不改变原始数组,不写参数就是一个浅拷贝。
let arr = [1,2,3,4];
console.log(arr.concat()); // [1,2,3,4]
console.log(arr.concat() === arr); //false
- 手写
function shallowCopy(object){//只拷贝对象if(!object || typeof object !== "object") return;//根据object的类型判断是新建一个数组还是对象let newObject = Array.isArray(object)?[]:{};//遍历object,并且判断是object的属性才拷贝for(let key in object){if(object.hasOwnProperty(key)){newObject[key]=object[key];}}return newObject;
}
15. 实现深拷贝
如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。
16. 实现sleep函数(使用Promise封装setTimeout)
function timeout(delay){return new Promise(resolve=>{setTimeout(resolve,delay)})
}
17. 实现Object.assign
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
