当前位置 博文首页 > 纸飞机博客:2021前端面试js题目总结,不妨看看有没有属于你的那

    纸飞机博客:2021前端面试js题目总结,不妨看看有没有属于你的那

    作者:[db:作者] 时间:2021-09-07 10:04

    1. 实用js写红绿灯的效果?

    <ul id="traffic" class="">
     <li id="green"></li>
     <li id="yellow"></li>
     <li id="red"></li>
    </ul>
    
    ul {
     position: absolute;
     width: 200px;
     height: 200px;
     top: 50%;
     left: 50%;
     transform: translate(-50%,-50%);
    }
     /*画3个圆代表红绿灯*/
     ul >li {
      width: 40px;
      height: 40px;
      border-radius:50%;
      opacity: 0.2;
      display: inline-block;
     }
     /*执行时改变透明度*/
     ul.red >#red, 
     ul.green >#green,
     ul.yellow >#yellow{
      opacity: 1.0;
     }
     /*红绿灯的三个颜色*/
     #red {background: red;}
     #yellow {background: yellow;}
     #green {background: green;}
    
    function timeout(timer){
      return function(){ 
       return new Promise(function(resolve,reject){
       setTimeout(resolve,timer) 
       })  
      }
      }
     var green = timeout(3000);
     var yellow = timeout(4000);
     var red = timeout(5000);
     var traffic = document.getElementById("traffic");
     (function restart(){
      'use strict'      //严格模式
      console.log("绿灯"+new Date().getSeconds()) //绿灯执行三秒 
      traffic.className = 'green';
      green()
      .then(function(){
       console.log("黄灯"+new Date().getSeconds()) //黄灯执行四秒
       traffic.className = 'yellow';
       return yellow();
      })
      .then(function(){
       console.log("红灯"+new Date().getSeconds()) //红灯执行五秒
       traffic.className = 'red';
       return red();
      }).then(function(){
       restart()
      })
      })();
    

    2. axios是否需要promise封装?

    需要

    import axios from 'axios'
    const http = ({
    	url,method,params,headers
    }) => {
    return new Promise ( (resolve,reject) => {
    	axios({
    		url,
    		method,
    		params,
    		headers
    	})
    	.then( res => {
    		resolve(res)
    	})
    	.catch( error => {
    		throw error
    	})
    	})
    }
    export default http
    

    3. Promise内部发生错误,如果同时.then方法有第二个参数,也有.catch会调用哪个

    .catch

    4. 宏任务 微任务

    setTimeout(function(){
    		    console.log('1')
    		});
     
    		new Promise(function(resolve){
    		    console.log('2');
                        resolve();
    		}).then(function(){
    		    console.log('3')
    		});
     
    		console.log('4')
    

    settimeout肯定是异步的。 我也知道有一个event队列,你settimeout没设置时间应该直接就进入这个队列了吧,然后就是Promise的回掉函数进入event队列。 当时我二话不说给了个答案 2,4,1,3.并且很自信。然后面试官就问你不想想了?我说不想了。然后后半段他全程开始皱眉头了。我也凉凉。最后他让我回去看一下宏任务和微任务。

    首先说一下普通的异步函数的执行过程吧

    同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。

    那么如此看来我给的答案还是对的。但是js异步有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入eventqueue,然后在执行微任务,将微任务放入eventqueue最骚的是,这两个queue不是一个queue。当你往外拿的时候先从微任务里拿这个回掉函数,然后再从宏任务的queue上拿宏任务的回掉函数。 我当时看到这我就服了还有这种骚操作。

    而宏任务一般是:包括整体代码script,setTimeout,setInterval。

    微任务:Promise,process.nextTick。

    记住就行了。

    然后回到开头的代码。因为settimeout是宏任务,虽然先执行的他,但是他被放到了宏任务的eventqueue里面,然后代码继续往下检查看有没有微任务,检测到Promise的then函数把他放入了微任务序列。等到主线进程的所有代码执行结束后。先从微任务queue里拿回掉函数,然后微任务queue空了后再从宏任务的queue拿函数。

    所以正确的执行结果当然是:2,4,3,1。

    https://juejin.im/post/59e85eebf265da430d571f89

    5. Js 原型和原型链

    原型链的设计是js的精髓所在,比较抽象。需要从内部设计原理去理解这种设计思想,在纸上画画其中的关系会帮助理解。

    prototype对象

    prototype对象的引入:所有实例对象需要共享的属性和方法,都放在这个对象中,那些不需要共享的属性和方法,就放在构造函数中。以此来模拟类。

    function Animal(name) {
        this.name = name
    }
    
    Animal.prototype.getName = function() {
        console.log(this.name)
    }
    
    var animal1 = new Animal('Kate')
    var animal2 = new Animal('Lucy')
    
    //对象animal1 和 animal2共享方法getName
    animal1.getName()
    animal2.getName()
    

    原型链

    在javascript中,每个对象都有一个指向它的原型(prototype)对象的内部链接。每个原型对象又有自己的原型,直到某个对象的原型为null为止,组成这条链的最后一环。

    *proto写入es6标准

    当一个对象被创建时,它的__protp__属性和内部属性[[prototype]]指向相同的对象(也就是它的构造函数的prototype属性)。改变__proto__属性的值同时也会改变内部属性[[prototype]]的值,除非该对象是不可扩展的。
    在ES5中,所有构造函数的__proto__都指向Function.prototype
    **在ES6中,构造函数的__proto__指向它的父类构造函数

    obj.__proto__ === obj.[[prototype]]
    // ES5
    Cat.__proto__ === Function.prototype
    // ES6
    Cat.__proto__ === Animal
    

    构造函数继承

    有四种方式可以实现构造函数的继承
    1.调用apply方法

    function Animal() {
        this.species = '动物'
    }
    Animal.prototype.getName = function() {
        console.log('我是动物')
    }
    
    function Cat() {
        Animal.apply(this, arguments)
    }
    var cat = new Cat()
    cat.species    // 动物
    cat.getName()  // undefined
    

    这种方法可以继承父类构造函数的属性,但是无法继承prototype属性,即父类中共享的方法和属性

    2.改写prototype对象

    Cat.prototype = new Animal()
    Cat.prototype.constructor = Cat
    

    这是最常用的方法来模拟单继承,缺点是始终要保留Animal的对象,如果Animal对象比较大时,会消耗部分内存(其实很少),并且没有实现多继承

    3.直接继承prototype

    Cat.prototype = Animal.prototype
    Cat.prototype.constructor = Cat
    

    缺点是当修改了Cat.prototype上的方法时会影响Animal.prototype

    4.利用空对象作中介

    var F = function(){}
    F.prototype = Animal.prototype
    Cat.prototype = new F()
    Cat.prototype.constructor = Cat
    

    6. js的事件循环机制是什么

    进程、线程

    • 进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的。
    • 线程是进程的执行流,是CPU调度和分派的基本单位,同个进程之中的多个线程之间是共享该进程的资源的。

    浏览器内核

    • 浏览器是多进程的,浏览器每一个 tab 标签都代表一个独立的进程(也不一定,因为多个空白 tab 标签会合并成一个进程),浏览器内核(浏览器渲染进程)属于浏览器多进程中的一种。

    • 浏览器内核有多种线程在工作。

      • GUI 渲染线程:

        • 负责渲染页面,解析 HTML,CSS 构成 DOM 树等,当页面重绘或者由于某种操作引起回流都会调起该线程。
        • 和 JS 引擎线程是互斥的,当 JS 引擎线程在工作的时候,GUI 渲染线程会被挂起,GUI 更新被放入在 JS 任务队列中,等待 JS 引擎线程空闲的时候继续执行。
      • JS 引擎线程:

        • 单线程工作,负责解析运行 JavaScript 脚本。
        • 和 GUI 渲染线程互斥,JS 运行耗时过长就会导致页面阻塞。
      • 事件触发线程:

        • 当事件符合触发条件被触发时,该线程会把对应的事件回调函数添加到任务队列的队尾,等待 JS 引擎处理。
      • 定时器触发线程:

        • 浏览器定时计数器并不是由 JS 引擎计数的,阻塞会导致计时不准确。
        • 开启定时器触发线程来计时并触发计时,计时完成后会被添加到任务队列中,等待 JS 引擎处理。
      • http 请求线程:

        • http 请求的时候会开启一条请求线程。

        • 请求完成有结果了之后,将请求的回调函数添加到任务队列中,等待 JS 引擎处理。

        • JavaScript 引擎是单线程

          JavaScript 引擎是单线程,也就是说每次只能执行一项任务,其他任务都得按照顺序排队等待被执行,只有当前的任务执行完成之后才会往下执行下一个任务。