前端常见面试题(七)@郝晨光


简述同步和异步的区别

众所周知,javascript是单线程的语言,所谓的单线程,就是从上至下,依次执行,当然这里的依次执行要抛开javascript的预解析机制。
这样做的原因是因为javascript最初是为了操作DOM,运行在浏览器环境下的,而操作DOM的时候,不能是异步的,不然的话两个异步任务同时修改DOM结构的话,会导致浏览器不知道该执行哪一个。
但是这样做也有缺点,当遇到一个响应时间特别长的任务时,容易导致页面加载错误或者浏览器未响应的情况。

同步就是所有的任务都处在同一队列中,不可以插队,一个任务执行完接着开始执行下一个,相对于浏览器而言,同步的效率过低,一些耗费时间比较长的任务应该用异步来执行。

异步就是将一个任务放入到异步队列中,当这个任务执行完成之后,再从异步队列中提取出来,插队到同步队列中,拿到异步任务的结果,可以提升代码执行的效率,不需要因为一个耗费时长的代码而一直等待。

javascript异步的几种实现方式
  1. 回调函数 callback
    最简单的回调函数的实现方式,利用setTimeout会进入计时器队列,而不是正常队列的特性,来实现回调函数
    计时器队列与正常队列同时运行,不影响正常队列的运行,在正常队列空闲的时候,并且计时器队列的任务倒计时已经结束,就将计时器队列的任务提取到正常队列中获得最终结果。

    • function syncMethod(callback) {
        setTimeout(function() {
        	callback(count);
        	console.log('第一次调用回调函数!');
            setTimeout(function() {
        		callback(count);
        		console.log('第二次调用回调函数!');
        	})
        	for(let i=0;i<100;i++) {
        		count++;
        	}
        }, 0);
        let count = 1;
        for(let i=0;i<100;i++) {
        	count++;
        }
      }
      syncMethod(function(count) {
          console.log(count); // 调用两次,第一次返回101,第二次返回201
      })
      
      callback示例返回值
  2. 事件驱动

    • javascript是基于事件驱动的语言,当我们给元素绑定事件的时候,并不会立即执行,而是当触发指定的事件时,才会执行对应的方法
    •  const btn = document.querySelector('button');
       btn.addEventlistener('click', function() {
           console.log('clickBtn')
       })
      
  3. 发布者订阅者模式(观察者模式)

    • 发布者订阅者模式是通过保存事件,然后在需要使用的时候直接发布事件,就可以触发保存的回调
    • 	function Watcher() {
        	this.events = {}; // 定义事件对象,用来保存需要触发的事件
        };
        // $on方法,用来监听事件,订阅者
      	Watcher.prototype.$on = function(event, handler) {
        	this.events[event] = this.events[event] || []; 
            // 如果当前事件已经被监听,则在原基础上进行添加新的事件,如果没有的话新建一个数组,用来存储事件
        	this.events[event].push({
            	handler: handler
            })
            // 将事件存储到对应的事件模型中
        };
        // $emit方法,用来发布事件,发布者
        Watcher.prototype.$emit = function(event,...arg) {
           // 如果没有订阅当前事件的话,直接返回
        	if(!this.events[event]) {
        		return
        	}
            // 循环遍历对应事件,将对应的事件根据填入的先后顺序触发
        	for(let i = 0; i< this.events[event].length; i++) {
        		this.events[event][i].handler(...arg)
        	}
        };
        // 关闭监听事件
        Watcher.prototype.$close = function(event) {
            // 如果没有订阅当前事件的话,直接返回
        	if(!this.events[event]) {
        		return
        	}
            // 删除订阅事件
        	delete this.events[event];
      	};
        // 实例化一个观察者
      	let watcher= new Watcher();
        // 监听事件,并传入事件需要触发的函数
        watcher.$on('handler1', function(name, age) {
        	console.log(name, age, '我是第一个函数')
        });
        // 监听不同的事件
        watcher.$on('handler2', function(sex) {
        	console.log(sex)
        });
        // 监听同一个事件,触发不同的函数
        watcher.$on('handler1', function(name, age) {
        	console.log(name, age, '我是第二个函数')
        });
        // 发布事件,并传参
        watcher.$emit('handler2', "男");
        watcher.$emit('handler1', '张三',20);
        // 关闭事件
        watcher.$close('handler1');
        // 事件不会触发,因为已经关闭了对应事件的监听
        watcher.$emit('handler1', '张三',20);
      
      Image 3.jpg
  4. promise 异步编程的解决方案

    • Promise是ES6新增的异步编程的解决方案,它有三种状态分别是pending进行中、resolved已完成、rejected已失败。
    • Promise可以链式调用,避免层层嵌套,异步操作更加容易方便,提升代码可读性。
    • Promise一旦创建就不可以取消,一旦在resolvedrejected中确立一种状态,就不可以再发生改变
    •  function asynMethod(flag) {
         	return new Promise((resolve, reject) => {
        		setTimeout(() => {
            		if(flag) {
            			resolve('郝晨光精心写作,如有错误请指出,谢谢!');
            		}else {
            			reject('错误信息!')
            		}
            	}, 3000)
        	})
        }
        asynMethod(true).then(data => {
      		console.log(data);
        	return asynMethod(true)
        }).then(data => {
        	console.log(data);
        	return asynMethod(false)
        }).catch(err =>{
        	console.log(err);
        })
      


数组去重(手写代码)

  1. 使用ES6的Set去重
    • Set是ES6新增的数据类型,Set 的成员具有唯一性
    • function distinct(arr) {
        return Array.from(new Set(arr));
      }      
      
  2. 使用ES6的Set去重(超级简化版)
    • [...new Set(arr)]   // [...new Set(需要去重的数组)]
      
  3. 使用splice配合两重for循环去重
    • function distinct(arr) {
        for(let i = 0; i < arr.length; i++) {
        	for(let j = i + 1; j < arr.length; j++) {
        		if(arr[i] === arr[j]) {
        			arr.splice(j, 1);
        			j--;
        		}
        	}
        }
        return arr;
      }
      
  4. 使用for循环配合indexOf去重
    •  function distinct(arr) {
       	let newArr = [];
       	for(let i = 0; i < arr.length; i++) {
       		if(newArr.indexOf(arr[i]) === -1) {
       			newArr.push(arr[i]);
       		}
       	}
       	return newArr;
      }
      
  5. 使用for循环配合sort排序去重
    • function distinct(arr) {
        	arr = arr.sort();
        	let newArr = [];
        	for(let i = 0; i < arr.length; i++) {
        		if(arr[i] !== arr[i-1]) {
            		newArr.push(arr[i]);
        		}
        	}
        	return newArr;
        }
      
  6. 使用for循环配合includes去重
    •   function distinct(arr) {
        	let newArr = [];
        	for(let i = 0; i < arr.length; i++) {
        		if(!newArr.includes(arr[i])) {
        			newArr.push(arr[i]);
        		}
        	}
        	return newArr;
       }
      
  7. 使用filter配合indexOf去重
    • function distinct(arr) {
        	return arr.filter((item,index, arr) => arr.indexOf(item) === index);
      }
      


在JavaScript中什么是伪数组?如何将伪数组转化为标准数组

javascript中的伪数组(类数组):不具有数组的push,pop等方法,但是具有length,以及可以利用for循环遍历等特性,例如函数的 argument 参数集合,还有通过document.getElementsByTagName等方法获取的NodeList等都是类数组

如何将数组转化为标准数组

  1.   let nodeList = document.getElementsByTagName('li');
      let nodeArr = Array.from(nodeList);
    
  2.    let nodeList = document.getElementsByTagName('li');
       let nodeArr = Array.prototype.slice.call(nodeList);
    

当转化为标准数组以后,就可以调用数组实例上的方法了,如push、pop、splice、filter、map、forEach等等



SPA路由history模式,打包上线都遇到了哪些问题?你是如何解决的?

  1. 资源路径404问题
    • vue-cli3环境下,在根目录新建vue.config.js,在该文件中写入如下
    •  module.exports = {
           publicPath: './'
       }
      
  2. 页面刷新404问题


JavaScript中callee和caller的作用

  1. callee是函数arguments对象内的指针,它指向当前的函数,使得在函数内部递归调用当前函数时,不需要调用函数名称,减少函数内部对于函数名的依赖
    function test() {
    	console.log(arguments.callee);
    }
    test();
    
  2. caller是函数的一个属性,它指向调用当前函数的函数,如果当前函数在其他函数内被调用,则返回调用它的那个函数,如果是在全局环境下被调用,则返回 null
    我们可以利用caller的特性跟踪函数的调用链
    function fn1() {
    	console.log(fn1.caller);
    	console.log(arguments.callee.caller);
    }
    function fn2() {
    	fn1()
    }
    fn2();
    


本文 简书 地址: 前端常见面试题(七)@郝晨光

结言
感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值