2021前端面试js题目总结,不妨看看有没有属于你的那道题

Posted 纸 飞机

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021前端面试js题目总结,不妨看看有没有属于你的那道题相关的知识,希望对你有一定的参考价值。

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

        HTML5 中提出了 Web-Worker API,主要是为了解决页面阻塞问题,但是并没有改变 JavaScript 是单线程的本质。了解 Web-Worker

        JavaScript 事件循环机制

        JavaScript 事件循环机制分为浏览器和 Node 事件循环机制,两者的实现技术不一样,浏览器 Event Loop 是 HTML 中定义的规范,Node Event Loop 是由 libuv 库实现。这里主要讲的是浏览器部分。

        Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

        • JS 调用栈

          JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

        • 同步任务、异步任务

          JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行,异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。

        • Event Loop

          调用栈中的同步任务都执行完毕,栈内被清空了,就代表主线程空闲了,这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

7.前端跨域的方式

前端跨域的方案:

1、通过jsonp跨域
2、postMessage跨域
3、跨域资源共享(CORS)
4、nginx代理跨域
5、nodejs中间件代理跨域
6、WebSocket协议跨域
7.反向代理

<u>https://segmentfault.com/a/1190000011145364</u>

8.Promise的理解,和promise都有哪些方法

Promise,就是一个对象,用来传递异步操作的消息,避免了层层嵌套的回调函数。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。 
(1)对象的状态不受外界影响。有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)。 
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果

9.原型和原型链的理解

我们创造的每一个函数都有一个prototype(原型)属性。这个属性是一个指针,指向原型对 象。在默认情况下,所有的原型对象都会有一个constructor(构造函数)属性,这个属性包含一个指向prototype属相所在的指针。当调用构造函数创建一个新实例之后,该实例内部将包含一个指针(内部属性),指向构造函数的原型对象。

<u>https://juejin.im/post/5ae95290518825672c00c0a4</u>

10:异步方式

1. Promise



2. Generator 



3. Async-await

4. Node.js 中的nextTick()和setimmediate()



5. async

11:修复bug或添加新功能的常见工作流(git命令)是什么?空和未定义的javascript有什么区别?


git flow feature start f1 添加新特性,这个操作创建了一个基于develop的特性分支,并切换到这个分支之下。

git flow feature finish f1 完成新特性,这个操作会合并f1分支到develop分支,并删除特性分支,切换回develop分支。

git flow feature publish f1 发布新分支,发布新特性分支到远程服务器,其它用户也可以使用这分支。

修复bug:

git flow hotfix start VERSION [BASENAME] 创建hotfix分支,VERSION 参数标记着修正版本,[BASENAME]为finish release时填写的版本号。

12:您使用什么框架来编写单元测试,写下一个案例来验证调用的函数

一、问题描述: 
在一个升序数组中,使用折半查找得到要查询的值的索引位置。如:
var a=[1,2,3,4,5,6,7,8,9];
search(a,3);//返回2
search(a,1);//左边界,返回0
search(a,9);//右边界,返回8
search(a,0);//比最小的值还小,返回"您查找的数值不存在"
search(a,10);//比最大的值还大,返回"您查找的数值不存在"

注:折半查找必须在有序数组中才有效,无序的数组不能实现查找功能。比如:在[10,5,6,7,8,9,20]中查找10,中间索引位置的值为7,比较得出7比10小,因而应该在右子数组中查找,实际上不可能找到10;
二、我的实现

  function search(arr,num) {
            var l=arr.length;
            var left=0;
            var right=l-1;
            var center=Math.floor((left+right)/2);
            while(left<=l-1&&right>=0){
                if (arr[center]==num) return center;
                if (left==right) return "您查找的数不存在";
                if (arr[center]>num) {
                    right=center-1;
                    center=Math.floor((left+right)/2);
                }else if (arr[center]<num) {
                    left=center+1;
                    center=Math.floor((left+right)/2);
                }
            }
        }
        var a=[1,2,3,4,5,6,7,8,9];
        console.log(search(a,-2));
说明: 
1、基本思路: 
每次比较,如果数组中间索引位置的值比要查找的值大,就转而在数组中间位置之前的子数组中查找;相反,如果数组中间索引位置的值比要查找的值大,就转而在数组中间位置之后的子数组中查找;如果数组中间索引位置的值恰好等于要查找的值,就返回该索引位置。

2、left定义查找范围的起始位置,right定义查找范围的结束位置,center定义查找范围的中间位置。

3、while中的逻辑说明: 
(1)由于不知道具体查找查找多少次,while是比较好的选择; 
(2)循环结束条件: 
a、一旦当right小于0时,就不再查找,再纠缠也不会有结果。例如:在a=[1,2,3,4,5,6,7,8,9]中查找0,当查找范围变为left=0,right=0,center=0时,进入while语句,由于arr[center]>0,故执行 
right=center-1;center=Math.floor((left+right)/2); 
得到right=-1此时应不再进入循环; 
b、一旦当left>l-1时,就不再查找,同样再纠缠也不会有结果。例如:在a=[1,2,3,4,5,6,7,8,9]中查找10,当查找范围变为left=8,right=8,center=8时,进入while语句,由于arr[center]<10,故执行 
left=center;center=Math.floor((left+right)/2); 
得到left=9,此时应不再进入循环;

4、始终是通过center匹配到要查找的值;

5、Math.floor处理了查找范围长度为偶数的情况;

6、当left==right了,而arr[center]==num却没执行,可以得出结论查找不到的;

7、当arr[center]==num时,整个函数都结束了,后面语句是不会执行的。

13.编写一个regex表达式以查找内容,内容以2个数字开头,以结尾

var reg = /1[0-9]$/

14.push 添加数组后, 是怎么响应的

push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度

注释:该方法会改变数组的长度。

语法:

arrayObject.push(newelement1,newelement2,…,newelementX)

参数描述

newelement1 必需。要添加到数组的第一个元素。

newelement2 可选。要添加到数组的第二个元素。

newelementX 可选。可添加多个元素。

push() 方法可把它的参数顺序添加到 arrayObject 的尾部。它直接修改 arrayObject,而不是创建一个新的数组。push() 方法和 pop() 方法使用数组提供的先进后出栈的功能。

15.js基本数据类型

Undefined、Null、Bollean、Number、String

16.js中=的区别是什么

前者会自动转换类型

后者不会

17.for和for in 区别

语法结构上不同,

for 一般用来遍历数组的,是比较简单的操作

for in 一般用来遍历对象,虽然for in 也能遍历数组,但是会存在

以下几个问题:

1、index索引为字符串型数字,不能直接进行几何运算

2、遍历顺序有可能不是按照实际数组的内部顺序

3、使用for in会遍历数组所有的可枚举属性,包括原型。例如上栗

的原型方法method和name属性

这也是为什么用for不用for in的区别,如果是遍历普通数组的话,

用for是最好的选择,但是如果是对象,用for in就好了。

18.js中=的区别是什么


操作数1 == 操作数2,  操作数1 === 操作数2


双等号==:   
(1)如果两个值类型相同,再进行三个等号(===)的比较  
(2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:   
1)如果一个是null,一个是undefined,那么相等    
2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较


三等号===:  
(1)如果类型不同,就一定不相等  
(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相      等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)  
(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。  
(4)如果两个值都是true,或是false,那么相等  
(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等  
(6)如果两个值都是null,或是undefined,那么相等

19:for和for in 区别

for in:

1.for...in 语句用于对数组或者对象的属性进行循环操作。

2.for ... in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。

3.for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。

for :
1.for循环是对数组的元素进行循环,而不能引用于非数组对象。

20:数组去重的方法

第一种:

function uniq(array){
   var temp = []; //一个新的临时数组
   for(var i = 0; i < array.length; i++){
       if(temp.indexOf(array[i]) == -1){
           temp.push(array[i]);
       }
   }
   return temp;
}

var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));

第二种:对象键值法去重

function uniq(array){
    var temp = {}, r = [], len = array.length, val, type;
    for (var i = 0; i < len; i++) {
        val = array[i];
        type = typeof val;
        if (!temp[val]) {
            temp[val] = [type];
            r.push(val);
        } else if (temp[val].indexOf(type) < 0) {
            temp[val].push(type);
            r.push(val);
        }
    }
    return r;
}

var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));

第三种:排序后相邻去除法

function uniq(array){
    array.sort();
    var temp=[array[0]];
    for(var i = 1; i < array.length; i++){
        if( array[i] !== temp[temp.length-1]){
            temp.push(array[i]);
        }
    }
    return temp;
}

var aa = [1,2,"2",4,9,"a"以上是关于2021前端面试js题目总结,不妨看看有没有属于你的那道题的主要内容,如果未能解决你的问题,请参考以下文章

前端面试题总结二(js原型继承)

2021前端面试总结

React高频面试题梳理,看看面试怎么答?(上)

2021.6月最新前端面试题总结(JavaScriptHtml5小程序ReactES6Vue.js源码全栈)

前端面试题总结一(js变量和函数声明提前相关)

2017年五月前端面试题目的总结