前端常见需要手写的代码

28 min read
实现 call/apply/bind
//call 和 apply 只是参数传递方式不同。
//apply 传递的是个参数数组
Function.prototype.myApply = function(context,args){
let fn = this;
let key = Symbol();
context[key] = fn;
let res = context[key](...args);
delete context[key];
return res;
}
function a (...x){console.log(this.a,x)}
a.myApply({a:"myapply"},[1,2,3])
a.apply({a:"myapply"},[1,2,3])
//call 传递的是参数列表
Function.prototype.myCall = function(context,...args){
let fn = this;
let key = Symbol();
context[key] = fn;
let res = context[key](...args);
delete context[key];
return res;
}
function a (...x){console.log(this.a,x)}
a.myCall({a:"myCall"},111,22)
a.call({a:"myCall"},111,22)
//bind 返回一个绑定了this的函数
Function.prototype.myBind = function(context,...bindArgs){
let fn = this;
let key = Symbol();
context[key] = fn;
return function(...args){
let finalArgs = [...bindArgs,...args];
let res = context[key](...finalArgs);
delete context[key];
return res;
}
}
b = a.myBind({a:"myBind"},1,2)
b(3,4)
b = a.bind({a:"myBind"},1,2)
b(3,4)
实现 new
new操作符做了如下几件事:
- 新建一个对象
- 把对象关联到构造函数的prototype属性
- 以新建对象为this执行构造函数
- 判断构造函数的返回值,对象就返回对象,不是就返回新建对象
function myNew(fn,...args){
let obj = Object.create(null);
obj.__proto__ = fn.prototype;
let res = fn.apply(obj,args);
return typeof res === "object"?res:obj;
}
function a (word) {
this.temp = "myNew"
this.word = word
}
a.prototype.myNew = ()=>{console.log("myNew")}
myNew(a,"hello")
实现 节流防抖
节流防抖目的都是同一个,防止事件触发太频繁。区别在于 防抖:不停触发事件,事件最终只响应一次。在间隔时间后,再次触发才会再次响应。 节流:不停触发事件,事件会按照设定的间隔时间,间隔响应。
//防抖
function debounce(fn,wait){
let timer;
return function(...args){
if(timer)clearTimeout(timer);
timer = setTimeout(()=>{
fn(...args);
},wait);
}
}
//节流 这个不太精确
function throttle(fn,wait){
let time = 0;
return function(...args){
if(Date.now() - time > wait){
fn(...args);
time = Date.now();
}
}
}
a = function(){console.log("onscroll")}
b = debounce(a,100)
c = throttle(a,1000)
window.onscroll = a;
//滚动试试
window.onscroll = b;
//滚动试试
window.onscroll = c;
//滚动试试
实现深拷贝
function deepclone(obj,cache=[]){
if(obj === null || typeof obj !== "object"){
return obj;
}
const has = cache.filter(cache=>cache.orignal === obj)[0];
if(has){
return has.copy;
}
const copy = Array.isArray(obj)?[]:{};
cache.push({
orignal: obj,
copy
});
Object.keys(obj).forEach(key => {
copy[key] = deepClone(obj[key], cache);
});
return copy;
}
实现Promise(todo)
做这个之前,最好浏览一遍promise的规范链接。
实现promise的构造函数。
promise 主要有三个状态:
pending/fulfilled/rejected
且状态只能由pending->fulfilled 或者 pending->rejected
promise初始化 状态为pending。
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
const resolvePromise = (res,resolve,reject)=>{
if(res && typeof res === 'object' || typeof res === 'function' ){
let then = res.then;
if(typeof then === 'function'){
then.call(res,(r)=>{
resolvePromise(r,resolve,reject);
},(r)=>{
reject(r);
})
}else{
reject(x);
}
}else{
resolve(res);
}
};
function XPromise(fn) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCB = [];
this.onRejectedCB = [];
let that = this;
function resolve(value) {
setTimeout(() => {
if (that.status === PENDING) {
that.status = FULFILLED;
that.value = value;
that.onFulfilledCB.forEach(cb => cb(that.value));
};
},0)
}
function reject(reason) {
setTimeout(() => {
if (that.status === PENDING) {
that.status = REJECTED;
that.reason = reason;
that.onRejectedCB.forEach(cb => cb(reason));
}
},0)
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
};
XPromise.prototype.then = function (onFulfill, onReject) {
onFulfill = typeof onFulfill === 'function' ? onFulfill : r=>r;
onReject = typeof onReject === 'function' ? onReject : r=>{throw(r)};
const that = this;
return new XPromise((resolve,reject)=>{
if (that.status === PENDING){
that.onFulfilledCB.push((value)=>{
setTimeout(()=>{
try {
const res = onFulfill(value);
resolvePromise(res,resolve,reject);
} catch (error) {
reject(error);
}
},0);
});
that.onRejectedCB.push((reason)=>{
setTimeout(()=>{
try {
const res = onReject(reason);
resolvePromise(res,resolve,reject);
} catch (error) {
reject(error);
}
},0);
});
}else if (that.status === FULFILLED){
setTimeout(()=>{
try {
const res = onFulfill(that.value);
// 根据res来执行后续
resolvePromise(res,resolve,reject);
} catch (error) {
reject(error);
}
},0);
}else if (that.status === REJECTED){
setTimeout(()=>{
try {
const res = onReject(that.reason);
// 根据res来执行后续
resolvePromise(res,resolve,reject);
} catch (error) {
reject(error);
}
},0);
}
});
};
const p = new XPromise((resolve,reject)=>{
setTimeout(()=>reject("test"),1000);
});
p2 = p.then(v=>{console.log(v);return 'sdsds'}).then(v=>console.log(v),(r)=>console.log(r));
将一个数组奇数在前偶数在后
function test(array){
array.sort(function(a,b){
if(a%2 == 0 && b%2!=0){
return 1;
}else{
return -1;
}
})
console.log(array);
}
打印一个三角形
const getStr = (i, n) => {
let str = "";
let emptyStrLength = n - i;
let notEmptyStrLen = 1+(2*(i-1));
let loop = 0;
while (loop < emptyStrLength) {
str += " ";
loop++;
}
loop = 0;
while (loop < notEmptyStrLen) {
str += '*';
loop++;
}
loop = 0;
while (loop < emptyStrLength) {
str += " ";
loop++;
}
return str;
};
const trig = (n) => {
let i = 1;
while (i <= n) {
console.log(getStr(i, n));
i++;
}
};
trig(6);
WorkerMan
class WorkerMan{
tasks = [];
sleep = (time)=>new Promise((r)=>setTimeout(r,time * 1000));
taskName(name){
this.name = name;
return this;
}
do(task){
this.tasks.push({ type : "normal", txt : `> do ${task}`});
return this;
}
wait(num){
this.tasks.push({ type : "wait", num });
return this;
}
waitFirst(num){
this.tasks.unshift({ type : "wait", num });
return this;
}
async execute(){
console.log(`${this.name} 开始做任务了`);
for(let i = 0; i<this.tasks.length;i++){
const task = this.tasks[i];
const { type } = task;
if(type === "normal"){
console.log(task.txt);
}else if(type === "wait"){
console.log(`等待${task.num}秒`);
await this.sleep(task.num);
}
}
this.tasks = [];
}
}
const man = new WorkerMan();
man.taskName("zack").do("敲代码").wait(1).do("拿外卖").waitFirst(2).execute();
man.taskName("zack").do("敲代码").wait(1).do("拿外卖").execute();