【js】-【节流、防抖函数、常见手写】

28 篇文章 0 订阅
14 篇文章 0 订阅

1 函数节流throttle

throttle.js创建一个节流函数,在 wait 毫秒内最多执行 callback 一次

const input = document.getElementById("text");
 function throttle(fn, wait=100) {
  # 定义开始时间
  let start = 0;
  
  # 返回结果是一个函数
  return function (e) {
    // 获取当前时间戳
    let now = Date.now();
    // 判断
    if (now - start >= wait) {
      // 修正this指向问题
      fn.call(this, e);
      // 修改开始时间
      start = now
    }
  }
}
 function onPut() {
 	console.log(222);
 	console.log(this);
 }
 input.addEventListener("keyup", throttle(onPut, 1000));

这种方法,可以保证第一次一定被触发,后面就是间隔指定时间触发一次

2 函数防抖debounce

const button = document.querySelector("input");
防抖函数

function debounce(fn, time) {
  # 定时器变量
  var timeId = null;
  
  // 返回一个函数
  return function () {
    # 清空定时器
    clearTimeout(timeId);

    # 重新启动定时器
    timeId = setTimeout(() => {
      fn.apply(this);
    }, time);
  };
}
function onSubmit() {
  console.log(111);
  console.log(this);
}
button.addEventListener("click", debounce(onSubmit, 1000));

3 深拷贝

1 深拷贝乞丐版 函数(方法)属性会丢失,循环引用会出错

let obj3 = [1,2,3,'4565'];
let obj4 = JSON.parse(JSON.stringify(obj3));

2 递归深拷贝


function deepclone(obj) {
  
  if (typeof obj === "object" && obj !== null) {
    #1. 创建一个容器
    let cloneObj = Array.isArray(obj) ? [] : {};
    
    #2. 遍历obj 
      for (let item in obj) {
        if (typeof obj[item] === "object") {
          cloneObj[item] = deepclone(obj[item]);
        } else {
          cloneObj[item] = obj[item];
        }
      }
    }
    return cloneObj;
  }

4 数组扁平化

1 es6

let arr = [1, [2, [3, 4]]];

function flatten(arr) {
  # 判断arr里有没有子数组
   while (arr.some((item) => Array.isArray(item))) {
     arr = [].concat(...arr);
   } //ES6新方法
   return arr;
 }
console.log(flatten(arr));

2 reduce

const arr = [1, [2, [3, 4, { a: 1 }], null], undefined];
function flatten(arr) {
 return arr.reduce((prev, next) => {
   return prev.concat(Array.isArray(next) ? flatten(next) : next);
 }, []);
}
 console.log(flatten(arr));

3 递归

var arr = [[1, 2, 8, [6, 7]], 3, [3, 6, 9], 4];
function flatten(arr) {
    let newarr = [];
    for (let i = 0; i < arr.length; i++) {
      if (typeof arr[i] === "object") {
        newarr = newarr.concat(flatten(arr[i]));
      } else {
        newarr.push(arr[i]);
      }
    }
    return newarr;
  }

4 扁平化

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  return arr.flat(Infinity);
}
console.log(flatten(arr)); //  [1, 2, 3, 4,5]

5 单例模式

在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

利用函数闭包,定义一个变量,保存第一个实例对象的结果, 如果再次通过new调用时,此时就可以判断这个变量是否存在,如果存在就直接返回这个对象.

let CreateGirlFriend = (function () {
  let instance = null;
  return class {
    constructor(name, age) {
      if (instance) return instance;
      this.name = name;
      this.age = age;
      return (instance = this);
    }
  };
})();

let g1 = new CreateGirlFriend("毕小胖", 21);
let g2 = new CreateGirlFriend("丛乎乎", 22);
console.log('---------------------------');
console.log(g2);
console.log(g1 === g2);

6 数组去重

1 HashMap

var arr = [1, 2, 1, 1, '1', 3, 3];
const unique = function(arr){
   let res = []
   let tmp = []
   for(let item of arr) {
       if(!tmp[item]) {
           tmp[item] = 1
           res.push(item)
       }
   }
   return res
}
console.log(unique(arr));

2 ES5 filter

function method(arr) {
    return arr.filter((value, index, self) => {
        return self.indexOf(value) === index
    })
}

3 ES6 set

let arr1 = [1, 1, 1, 2, 3, 6, 5, 5, 8, 7, 7, 7, 8];
let arr2 = Array.from(new Set(arr1));
 // return [...new Set(arr)]
console.log(arr2);

6.1 只剩下不重复的

var array = [0,0,1,1,1,2,3,4,4,5]
var result = []
array.filter((item,index)=>{
   return array.indexOf(item) === array.lastIndexOf(item)
})

7 手写promise.all()race(),resolve(),reject()

接收的是一个promise数组promises
当传入的所有promise都成功,则返回一个成功的promise,值为所有成功promise的值的数组【注意顺序】
当传入的promises中有一个不成功,则返回一个失败的promise,其值为第一个失败的promise的值

promise.all 用法

const p1 = Promise.resolve('a');
const p2  = new Promise((resolve, reject) => {
	setTimeout(() => {
  		resolve('b');
  },500)
})
const p3  = new Promise((resolve, reject) => {
	setTimeout(() => {
  		resolve('b');
  },2000)
})
const result = Promise.all([p1, p2, p3]);
console.log(result);
result.then(
  value => {  console.log('成功了', value) }, 
  reason => {  console.log('失败了', reason)} 
)

手写promise.all

Promise.all =function(promises){
	return new Promise((resolve,reject)=>{
		let count =0;
		const values=[]; //结果
		for(let i=0;i<promises.length;i++){
			Promise.resolve(promises[i]).then(value=>{
				count++;
				values[i]=value;
				if(count=== promises.length){
					resolve(values)'
				}
			},reason=>reject(reason)
			)
		}
	}
}

promise.race接收的是一个promise数组promises
返回一个promise,状态和值都取决于第一个改变状态的promise

promise.race用法

const p1 = Promise.resolve('a');
const p2  = new Promise((resolve, reject) => {
	setTimeout(() => {
  		resolve('b');
  },500)
})
const p3  = new Promise((resolve, reject) => {
	setTimeout(() => {
  		resolve('b');
  },2000)
})
const result = Promise.race([p1, p2, p3]);
console.log(result);
result.then(
  value => {  console.log('成功了', value)}, 
  reason => {  console.log('失败了', reason)} 
)
//成功了 a

promise.race手写

Promise.race = function(promises){
	return new Promise((resolve,reject)=>{
		for(let i=0; i<promises.length;i++){
			Promise.resolve(promises[i]).then(
				value=>resolve(value),
				reason=>reject(reason)
			)
		}
	})
}

Promise.resolve()手写
说明:用于快速返回一个状态为fulfilled或rejected的Promise实例对象
备注:传入值可能为:(1)非Promise类型、(2)Promise 对象
如果Promise对象是失败的,返回就失败的

Promise.resolve = function(value){
	return new Promise((resolve,reject)=>{
		if(value instanceof Promise)
			value.then(resolve,reject);
		else 
			resolve(value);
	})
}

Promise.reject()手写
用于快速返回一个状态必为rejected的Promise实例对象

Promise.reject = function(reason){
	return new Promise((resolve,reject)=>{
		reject(reason);
	})
}

8 模拟实现new

new操作符具体做了什么

  1. 创建了一个空的对象
  2. 将空对象的原型,指向于构造函数的原型 ** Foo.protoType=new Foo()proto
  3. 将 空对象 作为构造函数的 上下文(改变this指向)
  4. 对构造函数有返回值的处理判断
function newObject(Fn, ...args){
	// 1.创建了一个空的对象
	let obj={};
	
	// 2.将空对象的原型,指向于构造函数的原型
	obj.__proto__ = Fn.prototype;
	
	// 3 将 空对象 作为构造函数的 上下文(改变this指向)
	let result = Fn.call(obj, ...args);
	
	// 4. 返回新对象
	//与new保持一直,如果构造函数有返回值,返回值是对象a就返回对象a,否则返回实例对象
	return result instanceof Object ? result : obj;
}

测试

function Person(name,age){
	this.name=name;
	this.age=age;
}
let o1 = newObject(Person, "liming", 22);
console.log(o1);

9 手写callapplybind

apply()call()都是在特定的作用域中调用函数,用于扩充函数赖以运行的作用域。区别仅在于传入参数的形式的不同。

  • apply() 第一个参数是对象,第二个参数是参数数组;
  • call() 第一个参数是对象,其余参数必须逐个列举出来;
  • bind() 通过传入一个对象,返回这个对象绑定的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。

call 方法比 apply 快的原因:call 方法的参数格式正是内部方法所需要的格式。

函数bind两次后this指向:在第一次bind完this就已经确定了,结果返回一个函数,这个函数体内不存在this问题,后续无论bind多少次,this都指向第一次bind传入的context,但是后面bind再传入的参数会生效。

1.手写call

Function.prototype.myCall=function(context,...args){
	let context= context||window;
	//改变指针
	context.fn = this;
	args=args?args:[];
	//有参数,则传参
	const result= args.length>0? context.fun(...args):context.fun();
	delete context.fn;
	return result;
}

2.手写apply

Function.prototype.myApply=function(context,args=[]){
	let context= context||window;
	//改变指针
	context.fn = this;
	
	//有参数,则传参
	const result= args.length>0? context.fun(...args):context.fun();
	delete context.fn;
	return result;
}

3.手写bind

Function.prototype.myBind=function(context,...args){
	let context= context||window;
	 //新建一个变量赋值为this,表示当前函数
	const fn = this;
	args=args?args:[];
	//把第二次的参数也一起加入到总的参数数组中
	return function {
		args.push(...arguments);
		return fn.apply(context,agrs)
		//return fn.call(context,...args,...arguments)
	}
}

10 模拟Object.create()实现

function myCreate(obj){
	//声明一个函数
	function F(){};

	//将函数的原型指向obj
	F.prototype = obj;
	
	//返回这个函数的实例化对象
	return new F();
}
let obj1 = {
  name: "小芳",
  age: 18,
};
let obj2 = obj1;
obj2.age = 22;
console.log(obj2);
console.log(obj1);
console.log("------------------------");

let obj3 = myCreate(obj1);
obj3.age = 25;
console.log(obj3);
console.log(obj1);
console.log('------------------------');

let obj4 = Object.create(obj1);
console.log(obj4)

11 千分位分隔符

const formatPrice=number=>{
	number=''+number;
	const [integer,decimal=''] = number.split('.');
	
	return integer.replace(/\B(?=(\d{3})+$)/g,',')+(decimal?'.'+decimal:'')
}
console.log(formatPrice(123456789.3343)) // 123,456,789.3343

+匹配前面的子表达式一次或多次(大于等于1次)
$匹配输入行尾

12 柯里化 curry

柯里化是指将一个接受多个参数的函数转换为固定部分参数,然后返回一个接受剩余参数的函数。
function.length 属性指明函数的形参个数。

function curry(fn){
	return function recursize(...args){
		if(fn.length>args.length)
			return (...rest)=>recursize(...args,...rest);
		else return fn(...args);
	}
}

13 实现instanceof

在这里插入图片描述

function myInstanceof(left,right){
	let letVal=Object.getPrototypeOf(left);
	const rightVal=right.prototype;
	while(leftVal!==null){
		if(leftVal===rightVal) return true;
		leftVal= Object.getPrototypeOf(leftVal);
	}
	return false;
}

14 url参数解析

function parseUrlParams(url){
   url = decodeURI(url);
   let params=url.split('?')[1];
   let obj={};
   if(!params) return obj;
   let arr=params.split('&');
   for(let i=0;i<arr.length;i++){
      let key =arr[i].split('=')[0];
      let val=arr[i].split('=')[1]==undefined?true:arr[i].split('=')[1];
      if(!obj[key]){
         obj[key]=val;
      }else if(obj[key] && typeof obj[key]==='string'){
         obj[key] = [obj[key]];
         obj[key].push(val);
      }else{
         obj[key].push(val)
      }
   }
   return obj;
}
let url='http://www.4399.com/?use=sss&od=www&od=ww&jjs=sss&shsh'
console.log(parseUrlParams(url));
{ use: 'sss', od: [ 'www', 'ww' ], jjs: 'sss', shsh: true }

15 实现数组元素求和

let arr=[1,2,3,4,5,6,7,8,9,10]

let arr=[1,2,3,4,5,6,7,8,9,10]
let sum = arr.reduce( (total,i) => total += i,0);
console.log(sum);

var = arr=[1,2,3,[[4,5],6],7,8,9]

var = arr=[1,2,3,[[4,5],6],7,8,9]
let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);
console.log(arr);

递归实现:

let arr = [1, 2, 3, 4, 5, 6] 

function add(arr) {
    if (arr.length == 1) return arr[0] 
    return arr[0] + add(arr.slice(1)) 
}
console.log(add(arr)) // 21

16 现数组的push方法

let arr = [];
Array.prototype.push = function() {
	for( let i = 0 ; i < arguments.length ; i++){
		this[this.length] = arguments[i] ;
	}
	return this.length;
}

17 实现数组的filter方法

Array.prototype._filter = function(fn) {
    if (typeof fn !== "function") {
        throw Error('参数必须是一个函数');
    }
    const res = [];
    for (let i = 0, len = this.length; i < len; i++) {
        fn(this[i]) && res.push(this[i]);
    }
    return res;
}

18 实现数组的map方法

Array.prototype._map = function(fn) {
   if (typeof fn !== "function") {
        throw Error('参数必须是一个函数');
    }
    const res = [];
    for (let i = 0, len = this.length; i < len; i++) {
        res.push(fn(this[i]));
    }
    return res;
}

19 实现字符串的repeat方法

输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。

function repeat(s, n) {
    return (new Array(n + 1)).join(s);
}

递归:

function repeat(s, n) {
    return (n > 0) ? s.concat(repeat(s, --n)) : "";
}

20 实现字符串翻转

String.prototype._reverse = function(a){
    return a.split("").reverse().join("");
}
var obj = new String();
var res = obj._reverse ('hello');
console.log(res);    // olleh

21 判断数组的方式

  1. 通过原型链做判断
obj.__proto__ === Array.prototype;
  1. 通过instanceof做判断
obj instanceof Array
  1. 通过Array.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(obj)
  1. 通过ES6的Array.isArray()做判断
Array.isArrray(obj);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值