几道前端面试题的自我检测

1. 防抖和节流

防抖:连续触发事件中只有最后一次是执行成功的。

当连续触发事件时,一定时间段内没有在触发事件,事件处理才会执行一次,如果规定时间内又触发了事件,那么就重新开始计时。例如:用户输入的oninput事件, 用户输入后并不执行 fn,如果用户继续输入触发handleChange事件函数,则清除定时器重新计时。当用户停止输入400毫秒后触发fn。

		<input type="text" name="" id="num"  value="" oninput="handleChange()"/>
		function debounce(fn, wait) {
		    var timeout = null;
				console.log(timeout)
		    return function() {
		        if(timeout !== null) clearTimeout(timeout);
		        timeout = setTimeout(fn, wait);
		    }
		}
		var debounceinput = debounce(function () {
				console.log(document.getElementById('num').value)
			},400)
		function handleChange(){
			debounceinput()
		}

节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数

节流通俗解释就是我们要节省流量,减少请求次数。比如:登录功能,我们点击登录后请求一次接口,在一定时间内任何登录操作都是无效的,只有过时后在登录才再次触发登录接口。

 		var throttle = function(func, delay) {
            var timer = null;
            return function() {
                var context = this;
                var args = arguments;
                if (!timer) {
                    timer = setTimeout(function() {
                        func.apply(context, args);
                        timer = null;
                    }, delay);
                }
            }
        }

深考呗

其实深拷贝和浅拷贝都是针对的引用类型,JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据
基本类型:Boolean、number、null、string、undefined 是存在栈空间的
引用类型:object 包含 Array、function、Date 是存在堆空间的

  1. 赋值、浅拷贝与深拷贝的区别
项目是否指向同一对象第一次数据的基本数据原数据中包含子对象
赋值改变会使原数据一同改变改变会使原数据一同改变
浅拷贝改变不会使原数据一同改变改变会使原数据一同改变
深拷贝改变不会使原数据一同改变改变不会使原数据一同改变
  1. 赋值
// 基本类型
var a = 1;
var b = a;
a = 2;
console.log(a, b); // 2, 1 ,a b指向不同的数据

// 引用类型指向同一份数据
var a = {c: 1};
var b = a;
a.c = 2;
console.log(a.c, b.c); // 2, 2 全是2,a b指向同一份数据
  1. 浅拷贝的实现

浅拷贝的实现非常简单,其实就是遍历对象属性的问题

var a1 = {b: {c: {}};

var a2 = shallowClone(a1); // 浅拷贝
a2.b.c === a1.b.c // true
function shallowClone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }

    return target;
}
  1. 深拷贝的实现

数组扁平化

就是将多维数组装化成一位数组

es6提供的新方法flat(depth) 方法中的参数depth,代表展开嵌套数组的深度,默认是1.
参数depth还可以是 Infinity,无需知道数组的维度,直接将目标数组变成1维数组。

let a = [1,[2,3]];
a.flat(); // [1,2,3] 
a.flat(1); //[1,2,3]

let a = [1,[2,3,[4,5]]];
a.flat(); // [1,2,3,[4,5]] 
a.flat(2); // [1,2,3,4,5]

let a = [1,[2,3,[4,[5]]]];
a.flat(Infinity); // [1,2,3,4,5]  a是4维数组

for循环

var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
function flatten(arr) {
	var res = [];
	for(let i = 0, length = arr.length; i < length; i++) {
		if(Array.isArray(arr[i])) {
		     //res = res.concat(flatten(arr[i])); //concat 并不会改变原数组
			res.push(...flatten(arr[i])); //扩展运算符
		} else{
		        res.push(arr[i]);
		}
	}
	return res;
}
flatten(arr1) // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

单列模式

保证一个类仅有一个一个实例,并提供一个访问它的全局访问点。

es5实现方式

var Singleton = function(name) {
    this.name = name;
    //一个标记,用来判断是否已将创建了该类的实例
    this.instance = null;
}
// 提供了一个静态方法,用户可以直接在类上调用
Singleton.getInstance = function(name) {
    // 没有实例化的时候创建一个该类的实例
    if(!this.instance) {
        this.instance = new Singleton(name);
    }
    // 已经实例化了,返回第一次实例化对象的引用
    return this.instance;
}

es6实现方式

class Singleton {
    constructor(name) {
        this.name = name;
        this.instance = null;
    }
    // 构造一个广为人知的接口,供用户对该类进行实例化
    static getInstance(name) {
        if(!this.instance) {
            this.instance = new Singleton(name);
        }
        return this.instance;
    }
}

数组去重

		// set 方法 由于set 的key值不能重复
		function unique3 (arr) {
		  return [...new Set(arr)]
		}
		/**
		 * array.filter(function(currentValue,index,arr), thisValue)
		 * filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
		 * @currentValue 必须。当前元素的值
		 * @index 可选。当前元素的索引值
		 * @arr 可选。当前元素属于的数组对象
		 * @thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。如果省略了 			thisValue ,"this" 的值为 "undefined"
		 * */
		function unique2(arr){
			let res =  arr.filter(function(value, index, array){
				return array.indexOf(value) == index
			})
			return res;
		}
		// indexOf包含
		function unique1(arr){
			let res = []
			for (var i = 0; i < arr.length; i++) {
				if(res.indexOf(arr[i]) == -1){
					res.push(arr[i])
				} 
			}
			return res
		}
		// 循环法
		function unique(arr){
			for (var i = 0; i < arr.length - 1; i++) {
					for(let j = i+1; j < arr.length; j++){
						if(arr[j] == arr[i]){
							arr.splice(j,1)
						} 
					}
			}
			return arr
		}

手写promise.all和promise.race

Promise.all

Promise.myAll()返回的肯定是一个promise对象,所以可以直接写一个return new Promise((resolve, reject) => {})(这应该是一个惯性思维)
遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
关键点是何时"决议",也就是何时resolve出来,在这里做了计数器(count),每个内部promise对象决议后就将计数器加一,并判断加一后的大小是否与传入对象的数量相等,如果相等则调用resolve(),如果任何一个promise对象失败,则调用reject()方法。
一些细节:
官方规定Promise.all()接受的参数是一个可遍历的参数,所以未必一定是一个数组,所以用Array.from()转化一下
使用for…of进行遍历,因为凡是可遍历的变量应该都是部署了iterator方法,所以用for…of遍历最安全

Promise.all = function (iterator) {  
    let count = 0//用于计数,当等于len时就resolve
    let len = iterator.length
    let res = []//用于存放结果
    return new Promise((resolve,reject) => {
        for(var item of iterator){
            Promise.resolve(item)//先转化为Promise对象
            .then((data) => {
                res[count++] = data
                if(count === len){
                    resolve(res)
                }
            })
            .catch(e => {
                reject(e)
            })
        }
    })
}
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },5000)
})
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('failed')
  },1000)
})
let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('then')
  },1000)
})
Promise.all([p1, p3, p2]).then((res)=>{
	console.log(res)
})

promise.race
谁先决议那么就返回谁,所以将all的计数器和逻辑判断全部去除掉就可以了。

Promise.all = function (iterator) {  
    let count = 0//用于计数,当等于len时就resolve
    let len = iterator.length
    let res = []//用于存放结果
    return new Promise((resolve,reject) => {
        for(var item of iterator){
            Promise.resolve(item)//先转化为Promise对象
            .then((data) => {
                res[count++] = data
                if(count === len){
                    resolve(res)
                }
            })
            .catch(e => {
                reject(e)
            })
        }
    })
}

模拟实现new

参考这里

实现call/apply/bind

参考这里

模拟Object.creact()的实现

千分位分隔符

实现三角形

.triangle {
  width: 0;
  height: 0;
  margin: 50px auto;
  border-bottom: 100px solid #dc5947;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  cursor: pointer;
  transform: scale(1.2);
  transition: 0.5s;
}

实现三栏布局,双兰布局

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值