【记录】前端知识点 - JS手写题

防抖

function deboundce(f, delay){
	let timer;
	return function(...args){
		if(timer) clearTimeout(timer);
		timer = setTimout(()=>{
			f.apply(this, args);
		}, delay);
	}
}

节流

//定时器
function throttle(f, delay){
	let timer;
	return function(...args){
		if(timer) return;
		timer = setTimeout(()=>{
			f.apply(this, args);
			timer = null;
		}, delay);
	}
}
//date对象
function throttle(f, delay){
	let pre = 0;
	return function(...args){
		let now = new Date();
		if(now - pre > delay){
			f.apply(this, args);
			pre = now;
		}
	}
}

new

function myNew(f, ...args){
	let obj = {};
	obj.__proto__ = f.prototype;
	f.apply(obj, args);
	return obj;
}

instanceof

function myInstanceof(a, b){
	let ap = a.__proto__;
	let bp = b.prototype;
	while(ap){
		if(ap === bp){
			return true;
		}
		ap = ap.__proto__;
	}
	return false;
}

call

Function.prototype.myCall = function(obj, ...args){
	obj = obj || window;
	
	const fn = Symbol();
	obj[fn] = this;
	
	const res = obj[fn](...args);
	delete obj[fn];
	
	return res;
}

apply

Function.prototype.myApply = function(obj, args){
	obj = obj || window;
	const fn = new Symbol();
	obj[fn] = this;
	const res = obj[fn](...args);
	delete obj[fn];
	return res;
}

bind

Function.prototype.myBind = function(obj, ...args){
	obj = obj || window;
	const _this = this;
	const res = function(...innerArgs){
		if(this instanceof res){
			_this.apply(this, [...args, ...innerArgs]);
		}else{
			_this.apply(obj, [...args, ...innerArgs]);
		}
	};
	res.prototype = _this.prototype;
	return res;
}

继承

//原型链继承
/*
	缺点:1、想为子类添加属性和方法,必须在new语句之后。
		 2、创建子实例时,无法向父实例传参。
		 3、不支持多继承。
*/
function Children(){};
Children.prototype = new Father();
Children.prototype.name = 'a';
//构造继承
/*
	缺点:1、子类实例不是父类实例。
*/
function Children(name){
	Father.call(this);
	this.name = name;
}
//实例继承
/*
	缺点:1、实例是父类实例,不是子类实例。
		 2、不支持多继承。
*/
function Children(name){
	let instance = new Father();
	instance.name = name;
	return instance;
}
//拷贝继承
/*
	缺点:1、效率低。
		 2、无法获取父类的不可枚举属性。
*/
function Children(name){
	let father = new Father();
	for(let p in father){
		this.prototype[p] = father[p];
	}
	this.name = name;
}
//组合继承(构造继承+原型链继承)
/*
	缺点:调用两次父类构造函数,生成两份实例。
*/
function Children(name){
	Father.call(this);
	this.name = name;
}
Children.prototype = new Father();
Children.constructor = Children;
//寄生组合继承
function Children(name){
	Father.call(this);
	this.name = name;
}
let Super = function(){};
Super.prototype = Father.prototype;
Children.prototype = new Super();
//class继承
/*
	不是所有的浏览器都支持。
*/
class Father{
	constructor(name, age){
		this.name = name;
		this.age = age;
	}
}
class Children extend Father{
	constructor(name, age, color){
		super(name, age);
		this.color = color;
	}
}

创建

//new
var obj = new Object()l
obj.name = 'tom';
//字面量
var obj = {
	name: 'tom'
};
//工厂模式
function createObj(name, age){
	var obj = {
		name: name,
		age: age
	};
	return obj;
}
//构造函数
function obj(name, age){
	this.name = name;
	this.age = age;
}
//构造函数+原型
function obj(name, age){
	this.name = name;
	this.age = age;
}
obj.prototype.setName = function(){};

函数重载

function addMethod(object, name, fn){
	var old = object[name];
	object[name] = function(){
		if(fn.length === arguments.length){
			return fn.apply(this, arguments);
		}else if(typeof old === 'function'){
			return old.apply(this, arguments);
		}	
	}
}

setTimeout模拟setInterval

为什么要用setTimeout模拟setInterval?

function mysetInterval(fn, delay){
	let timer = null;
	function interval(){
		fn();
		timer = setTimout(interval, delay);
	}
	interval();
	return {
		cancel: ()=>{
			clearTimeout(timer);
		}
	}
}
//setInterval模拟setTimeout
function mysetTimeout(fn, delay){
	let timer = setInterval(()=>{
		clearInterval(timer);
		fn();
	}, delay);	
}

柯里化

// 实现1
const add = (a, b, c) => a + b + c;
const a = currying(add, 1);
console.log(a(2, 3)); // 6

function currying(fn, ...args1){
  let args = [...arg1];
  const res = (...args2) => {
    args = [...args, ...args2];
    if(args.length === fn.length){
      return fn(...args);
    }else{
      return res;
    }
  }
  return res;
}

//实现2
console.log(sum(1)(2)()); // 3
console.log(sum(1,2,3)(4)());// 10
function sum(...args1){
	let args = [...args1];
	const res = (...args2) => {
		if(!args2.length) return res.toString();
		args = [...args, ...args2];
		return res;
	};
	res.toString = () => args.reduce((pre, nex) => pre + nex);
	return res;
}

promise

Promise/A+规范

class myPromise{
	constuctor(executor){
		this.initValue();
		this.initBind();
		this.onFulfilledCallbacks = [];
		this.onRejectedCallbacks = [];
		//throw
		try{
			executor(this.resolve, this.reject);
		}catch(e){
			this.reject(e);	
		}
	}
	initValue(){
		this.PromiseState = 'pending';
		this.PromiseResult = null;
	}
	initBind(){
		this.resolve = this.resolve.bind(this);
		this.reject = this.reject.bind(this);
	}
	resolve(value){
		//状态不可变
		if(this.PromiseState !== 'pending') return;
		this.PromiseState = 'fullfilled';
		this.PromiseResult = value;
		//清空
		while(this.onFulfilledCallbacks.length){
			this.onFullfilledCallbacks.shift()(this.PromiseResult);	
		}
	}
	reject(reason){
		if(this.PromiseState !== 'pending') return;
		this.PromiseState = 'rejected';
		this.PromiseResult = reason;
		while(this.onRejectedCallbacks.length){
			this.onRejectedCallbacks.shift()(this.PromiseResult);
		}
	}
	then(onFulfilled, onRejected){
		//参数检验
		onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
		onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
		
		//链式调用
		var thenPromise = new myPromise((resolve, reject)=>{
			const resolvePromise = cb =>{
				//setTimeout模拟微任务(?
				//queueMicrotask
				setTimeout(()=>{
					try{
						const x = cb(this.PromiseResult);
						if(x === thenPromise){
							throw new Error('循环引用出错啦');
						}
						if(x instanceof myPromise){
							//是promise对象
							//返回值为成功,新promise就是成功
							//返回值为失败,新promise就是失败
							x.then(resolve, reject);
						}else{
							//非promise直接成功
							resolve(x);
						}
					}catch(err){
						reject(err);
						throw new Error(err);
					}
				})
			}
			
			if(this.PromiseState === 'fulfilled'){
				resolvePromise(onFulfilled);
			}else if(this.PromiseState === 'rejected'){
				resolvePromise(onRejected);
			}else if(this.PromiseSate === 'pending'){
				//定时器,暂存
				this.onFulfilledCallbacks.push(onFulfilled.bind(this));
				this.onRejectedCallbacks.push(onRejected.bind(this));
			}
		
		});
		return thenPromise;	
	}
	all(promises){
	//接受一个Promise数组,其中如果有非Promise项,则此项当作成功。
	//如果所有的promise都成功,则返回成功结果数组
	//如果有一个promise失败,则返回这个失败结果
		return new myPromise((resolve, reject)=>{
			let count = 0;
			let result = [];
			const len = promises.length;
			
			if(len === 0) return resolve([]);
			
			promises.forEach((p, i)=>{
				//有的不是Promise对象,转化一下
				myPromise.resolve(p).then(res=>{
					//保存结果
					result[i] = res;
					count ++;
					//等于数组长度,返回
					if(count === len) resolve(result);
					//有一个失败,整个失败
				}).catch(reject);
			})
		})
	}
	race(promises){
	//接受一个Promise数组,其中如果有非promise项,则此项当作成功。
	//哪个promise最快得到结果,就返回那个结果。
		return new myPromise((resolve, reject)=>{
			promises.forEach(p=>{
				//转换
				MyPromise.resolve(p).then(resolve).catch(reject);
			})
		})
	}
	allSettled(promises){
	//接受一个Promise数组,其中如果有非promise项,则此项当作成功。
	//把每个promise的结果合并成数组返回。
		return new myPromise((resolve, reject)=>{
			let count = 0;
			let result = [];
			const len = promises.length;
			
			if(len === 0) return res([]);
			
			promises.forEach((p, i)=>{
				myPromise.resolve(p).then(res=>{
					result[i] = {
						status: 'fulfilled',
						value: res
					};
					count ++;
					if(count === len){
						resolve(result);
					}
				}).catch(err=>[
					result[i] = {
						status: 'rejected',
						value: err
					};
					count++;
					if(count === len){
						resolve(result);
					}
				])
			});
		})
	}
	any(promises){
	//接受一个Promise数组,其中如果有非Promise项,则此项当作成功。
	//如果所有的promise都失败,则报错
	//如果有一个promise成功,则返回这个成功结果
		return new myPromise((resolve, reject)=>{
			let count = 0;
			const len = promises.length;
			
			if(len === 0) return reject([]);
			
			promises.forEach((p, i)=>{
				myPromise.resolve(p).then(res=>{
					resolve(res);
				}).catch(err=>{
					count++;
					if(count === len){
						reject(new AggregateError('All promises were rejected'))
					}
				})
			});
			
		})
	}
}

浅拷贝

const obj1 = { name: 'a' };

// Object.assign
const obj2 = Object.assign({}, obj1);
// ...扩展运算符
const obj2 = {...obj1};

深拷贝

//拷贝函数等其他引用类型,循环引用时存在缺陷。
JSON.parse(JSON.stringfy(obj));

//只实现了拷贝基本数据类型、可遍历引用数据类型array、object。
function deepClone(obj, map = new WeakMap()){
	if(typeof obj !== 'object' || obj === null) return obj;
	
	let result = Array.isArray(obj)? [] : {};
	if(map.get(obj)){
		return map.get(obj);
	}
	map.set(obj, result);
	Reflet.ownKeys(obj).forEach(key=>{
		result[key] = deepClone(obj[key], map);
	});
	return result;
}

发布订阅(事件总线)

class EventEmitter{
	constructor(){
		this.events = {};
	}
	//实现订阅
	on(key, callback){
		if(!this.events[key]){
			this.events[key] = [callback];
		}else{
			this.events[key].push(callback);
		}
	}
	//删除订阅
	off(key, callback){
		if(!this.events[key]) return;
		this.events[key] = this.events[key].filter(item=>item!==callback);
	}
	//触发事件
	emit(key, ...rest){
		this.events[key] && 
		this.events[key].forEach(event => event.apply(this, rest));
	}
	//只执行一次订阅事件
	once(key, callback){
		function fn(){
			callback();
			this.off(key, fn);
		}
		this.on(key, fn);
	}
}

观察者

class Notify{
	constructor(){
		this.observerList = [];
	}
	add(obj){
		this.observerList.push(obj);
	}
	remove(obj){
		const idx = this.observerList.findIndex(v=>v===obj);
		if(idx !== -1){
			this.observerList.splice(idx, 1);
		}
	}
	notify(){
		this.observerList.forEach(item =>{
			item.update();
		});
	}
}
class Watch{
	constructor(name){
		this.name = name;
	}
	update(){
		console.log(this.name);
	}
}

数组去重

function unique(arr){
	return [...new Set(arr)];
}

function unique(arr){
	return arr.filter(function(item, index, array){
		return array.indexOf(item) === index;
	});	
}

数组扁平化

function flatten(arr){
  while(arr.some(item => Array.isArray(item))){
    arr = [].concat(...arr);
  }
  return arr;
}

function flatten(arr){
	let result = [];
	for(let i = 0, len = arr.length; i < len; i ++){
		if(Array.isArray(arr[i]){
			result = result.concat(flatten(arr));
		}else{
			result.push(arr[i]);
		}
	}
	return result;
}

对象扁平化

const obj = {
 a: {
        b: 1,
        c: 2,
        d: {e: 5}
    },
 b: [1, 3, {a: 2, b: 3}],
 c: 3
};
console.log(flatten(obj));
// {
//  'a.b': 1,
//  'a.c': 2,
//  'a.d.e': 5,
//  'b[0]': 1,
//  'b[1]': 3,
//  'b[2].a': 2,
//  'b[2].b': 3
//   c: 3
// }


function isObject(val){
  return typeof val === 'object' && val !== null;
}
function flatten(obj){
  if(!isObject(obj)){
    return;
  }
  let res = {};
  const dfs = (cur, prefix) => {
    if(isObject(cur)){
      if(Array.isArray(cur)){
        cur.forEach((item, index)=>{
          dfs(item, `${prefix}[${index}]`);
        });
      }else{
        for(let k in cur){
          dfs(cur[k], `${prefix}${prefix ? '.' : ''}${k}`);
        }
      }
    }else{
      return res[prefix] = cur;
    }
  };
  dfs(obj, '');
  return res;
}

类数组转数组

const arrayLike = document.querySelectorAll('div');

// 扩展运算符
[...arrayLike]
// Array.from
Array.from(arrayLike)
// slice:不传参数时,会返回原数组的拷贝
Array.prototype.slice.call(arrayLike)
// concat
Array.prototype.concat.apply([], arrayLike);

对象数组转树

// 实现
const obj = [
    {
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
]
console.log(toTree(obj));
// [
//     {
//         id: 1,
//         text: '节点1',
//         parentId: 0,
//         children: [
//             {
//                 id:2,
//                 text: '节点1_1',
//                 parentId:1
//             }
//         ]
//     }
// ]

function toTree(data){
  let res = [];
  let map = new Map();
  data.forEach(node =>{
    map.set(node.id, node);
  })
  map.forEach((value, key)=>{
    if(+value.parentId !== 0){
      let parent = map.get(value.parentId);
      if(!parent.children){
        parent.children = [];
      }
      parent.children.push(value);
    }else{
      res.push(value);
    }
  });
  return res;
}

树转对象数组

// 实现
const obj = [
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
];
console.log(toArr(obj));
// [
//     {
//         id: 1,
//         text: '节点1',
//         parentId: 0 //这里用0表示为顶级节点
//     },
//     {
//         id: 2,
//         text: '节点1_1',
//         parentId: 1 //通过这个字段来确定子父级
//     }
//     ...
// ]
function toArr(data){
  let res = [];
  const dfs = (tree)=>{
    tree.forEach(item => {
      if(item.children){
        dfs(item.children);
        delete item.children;
      }
      res.push(item);
    });
  }
  dfs(data);
  return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值