JavaScript中的各种手撕代码(上)

js中的各种手撕代码

1. new

分析问题:new关键字用于生成一个构造函数的实例

手撕步骤:

  1. 创建一个对象
  2. 为该对象完善原型链
  3. 以该对象执行构造函数
  4. 返回该对象
function _new(fn){
    /*
    let obj = {};
    obj.__proto__ = fn.prototype
    */
    let obj = Object.create(fn.prototype);
    fn.apply(obj, arguments);
    return obj;
	// return obj = fn.apply(Object.create(fn.prototype))  一句话形式
}

2. Array.isArray

判断一个对象是否为数组的方式

  1. Array.isArray(arr)
  2. Object.prototype.toString.call(arr) === ‘[object Array]’
  3. arr instanceof Array
  4. arr.proto === Array.prototype
function _isArray(ob){
    return Object.prototype.toString.call(obj) === '[object Array]';
}

3. Array.prototype.reduce

Array中的方法都可以尝试使用迭代器模式进行手撕

reduce的用法

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PJdEbrLv-1594387590099)(./img/reduce参数.png)]

Array.prototype._reduce = function(fn, initial){
    let totalValue = initial ? initial : 0;
    for (let i = 0, l = this.length; i < l; i++){
        totalValue = fn.call(this[i], totalValue, this[i], i, this);
    }
    return totalValue;
}
const numbers = [65, 44, 12, 4];
 
function getSum(total, num) {
    return total + num;
}
numbers._reduce(getSum);  // 125
numbers._reduce(getSum, 1);  //126

4. instanceof

instanceof关键字用于判断一个对象是否是指定构造函数的实例

function _instanceof(left, right){
    let target = right.prototype;
    // iterateProto需要不断在原型链上进行搜索
    let iterateProto = left.__proto__;
    while (true){
        // 当proto为null时 代表失败
        if (iterateProto === null){
            return false;
        }
        if (iterateProto === target){
            return true;
        }
        // 原型链上不断搜索
        iterateProto = iterateProto.__proto__;
    }
}

5. Array.prototype.flat

flat方法用于数组扁平化,这个方法较为特别,难以使用迭代器模式来完成

arr.flat(n);
其中,n为扁平化的级数
n可以取值为Infinity,表示全部展平
// 默认num = 1,num代表展开的级数
// Number数据类型中 NaN和0都会被转换成false
Array.prototype._flat = function(num = 1) {
    if (!Array.isArray(this)){
        console.error('this is not an Array');
        return;
    }
    let result = [];
    let n = num;
    for (let item of this){
        if (Array.isArray(item)){
            n--;
            if (n < 0){
                result.push(item);
            }else {
                // 只要是数组就 递归 并 压入结果数组
                result.push(...item._flat(n));
            }
        } else {
			// 不是数组直接压入res中
            result.push(item);
        }
        n = num;
    }
    return result;
}
// 其他的一些数组扁平化的方法
// 第一种
let arr = [1, 2, [1, 2], 7, [1, [1, 2]]];
arr = arr.toString()
		 .split(',')
		 .map(res=>Number(res));
		 // toString变成以逗号分割的字符串,并且对每个字符串进行类型转换
// 第二种
while(arr.some(item=>Array.isArray(item))){  //some方法用于判断某一项是否满足条件,比如在此验证是否有数组,只要有一个就返回true
	arr = [].concat(...arr);  //只要有数组就去掉一级
	// eg. [].concat([1,2])===>[1,2]
}

6. 图片懒加载

<img src="loading.png" data-src="1.png" />
<img src="loading.png" data-src="2.png" />
<img src="loading.png" data-src="3.png" />
/*第一种方法*/
function lazyLoad(){
	let clientHeight = document.documentElement.clientHeight;  //获取屏幕可视高度
	let imgArr = document.getElementsByTagName('img');
	imgArr.forEach((item)=>{
		let targetTop = item.getBoundingClientRect().top;  //获取元素boundingclientrect对象
		if (targetTop < clientHeight + 100){  //其中.top是指元素距离可视窗口左上角的高度距离
			item.src = item.dataset.src;  //item.getAttribute('data-src')
		}
	})
}
function lazyLoad(){
	let clientHeight = document.documentElement.clientHeight;  //获取屏幕可视高度
	let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;  //获取页面滚动条偏移
	let imgArr = document.getElementsByTagName('img');
	imgArr.forEach((item)=>{
		let offsetTop = item.offsetTop;  //获取元素距离文档顶部的高度
		if(offsetTop < scrollTop - clientHeight){  //只有当元素距离文档顶部的高度 小于 屏幕可视高度 + 滚动条滚动偏移 图片才加载
			item.src = item.dataset.src;
		}
	})
}
lazyLoad();  //页面一进来的时候就判断加图片
window.onscroll = lazyLoad;  //添加页面滚动事件

// 补充:可以进行函数节流操作

7. 防抖节流

防抖

防抖的作用是在短时间内多次触发同一事件,只执行一次(执行最后一次或执行最开始的一次)

function debounce(fn, dealy){
	let timer = null;
	return function(){
		clearTimeout(timer);  //清除计时器
		timer = setTimeout(()=>{
			fn.apply(this, arguments);
		}, delay);  //当超过delay之后执行函数
	}
}

原理:用闭包封装一个变量timer,每次在执行函数时,首先清除计时器,如多多次触发事件,则计时器一直被清除和重新赋值。只有当一段时间(delay)之后,执行计时器内的回调函数

节流

节流的作用是在一段时间内,事件只触发一次

// 1.基于计时器的
function throttle(fn, delay){
	let timer = null;
	return function(){
		if(!timer){  //如果没有计时器,则创建计时器;如果有计时器,则等待计时delay
			timer = setTimeout(()=>{
				fn.apply(this, arguments);  //箭头函数没有arguments,也没有this,这里的arguments和this都是沿着作用域链向上寻找的
			}, delay)
		}
	}
}
// 2.基于Date对象的
function throttle(fn, delay){
	let preTime = Date().now();
	return function(){
		let nowDate = Date().now();
		if(nowDate - preDate > delay){
			fn.apply(this, arguments);
			preTime = nowTime;  // 记住需要重新赋值
		}
	}
}

节流原理:通过判断是否超过了一定时间,如果超过了一定时间就可以执行回调函数,如果还未超过指定时间则不可以执行。保证了在一段时间内只能操作一次

8. 数组去重

双重for循环
let uniqueArr = arr => {
	let res = Array.prototype.slice.call(arr);
	for(let i = 0; i < res.length; i++){
		for(let j = i+1; j < res.length; j++){
			if (res[i] === res[j]){
				res.splice(j, 1);
				j--;  //删除重复元素并且将下标往左移
			}
		}
	}
	return res;
}
使用indexOf
let uniqueArr = arr => {
	let res = arr.slice();
	return res.filter((item, index)=>{  //过滤,对每一个项目进行回调函数中的过滤判断
		return res.indexOf(item) === index;
	})
}
使用includes
let uniqueArr = arr => {
	let res = [];  //结果空数组
	for(let i = 0; i < arr.length; i++){
		if(!res.includes(arr[i])){  //也可以替换成 res.indexOf(arr[i]) === -1
			res.push(arr[i]);
		}
	}
	return res;
}
使用Set (!!!)
let uniqueArr = arr => {
	// new Set()创建一个set(可迭代类数组),使用扩展运算符扩展开
	return [... new Set(arr)];
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值