2021面试题---js部分

243 阅读17分钟

这个文章只是作为自己准备面试阶段记录的笔记的作用,里面的答案也是从网上找的以及一些自己理解以后的描述,后续也会根据实际情况不断补充,如果有错误的地方欢迎指出进行订正修改。

js对象的创建方式有哪些?

  • 字面量创建方式var a={}
  • 通过构造函数的方式来创建对象 var a=new Object()
  • 通过object.create({})创建对象,使用原来的对象作为新对象的proto new Object()创建方式和Object.create()创建方式的区别的话就在于它的原型链上,new创建的proto指向的是Object.prototype,而Object.create(args)创建出来的对象是跟着后面的第一个参数走的,如果第一个参数是null,新对象就是个空对象,不继承任何对象,也没有原型,如果有对象值,则指向的为args的prototype

js数组的创建方式有哪些?

  • 通过字面量的方式创建 var a=[1,2,3]
  • 通过new Array(N)的方式创建,如果N不填,则为空数组,如果N填写了,则创建一个长度为N的数组
  • Array.from({length:10})原理在于Array.from会将一个具有length属性的对象转换为数组,所以可以通过这种方式去创建

Array.from的作用以及使用方式?

  • 作用:从一个类数组或者可迭代的对象创建一个新的数组实例
  • 使用方式: Array.from (obj, mapFn) obj为类数组或者可迭代对象,mapFn为第二个参数,新数组中的每个元素会执行该回调函数。
Array.from(new Set([1,2,3,4]), x => x*3) //[3,6,9,12]

javascript如何创建一个包含1...N的数组?

  • 利用for循环创建数组
  • 利用ES6中的方法
Array(N).fill(1).map((val,index)=>index+1)
Array.from(Array(N),(val,index)=>index+1)
Array.from({length:N},(val,index)=>index=1)

js将对象转换为数组有几种方式?

利用Array.from将一个带有length属性的可迭代的对象转换为数组,但是这种只能转换key是按照index的顺序排列的对象,length大于当前值的数量的话后面的会用undefined的来填充

Array.from({0:'a',1:'b',length:3})
// ["a", "b", undefined]

利用Array.prototype.slice.call(arguments,0)也是将带有length属性的可迭代的对象转换为数组,局限性同Array.from()

Array.prototype.slice.call({0:'a',length:1},0)
//['a']

const、let、var 的区别在哪里?

  • 首先const是用来定义常量、常量的值进行定义以后不可以发生修改的,除非定义的是个对象
  • let和var都是用来定义变量的,但是不同点在于它的var声明的变量存在变量提升的问题,而let声明的变量因为存在暂时性死区,所以不会有变量提升的问题,而且let声明的变量只在它定义的这个块作用域范围内生效

js如何模拟let?

可以使用IIFE将变量包裹到作用域范围内,例如:

 for(var i=1;i<4; i++){         
  // 利用函数作用域
        (function f(a){
           setTimeout(function(){
             console.log(a);
       }, 1000);
         })(i);
}

js中去重的方式有哪些?

  • 通过js中提供的一些判断值是否存在的方法和遍历的方法结合使用达到去重的目的,比如:for循环/forEach/filter/map+(indexof,findIndex,find,includes)
  • 第二种方式利用es6中的Set方法
var a=[1,2,22,3,4,3,2]
var b=new Set(a)
Array.from(b)/[...b]

js中排序的方式有哪些?

  • 一是利用循环进行的几种排序方式:冒泡排序、选择排序、插入排序、快速排序等等
  • 二是使用js提供的API进行排序:arr.sort(fn)

js数据类型的有哪些?

  • 基础数据类型:String,Number,Boolean,Null,Undefined,Symbol
  • 引用数据类型:Object,Function

js判断数据类型的几种方式以及优缺点?

比较详细的可以参考这篇文章 www.cnblogs.com/onepixel/p/… 主要分为以下几种:

  • typeof:返回值为6个字符串(string,object,function,boolean,number,undefined),但是在判断null、array、object和new实例的时候返回的都是object
`typeof``''``;``// string 有效`
`typeof``1;``// number 有效`
`typeof``Symbol();``// symbol 有效`
`typeof``true``;``//boolean 有效`
`typeof``undefined;``//undefined 有效`
`typeof``null``;``//object 无效`
`typeof``[] ;``//object 无效`
`typeof``new``Function();``// function 有效`
`typeof``new``Date();``//object 无效`
`typeof``new``RegExp();``//object 无效`
  • instanceof:判断该对象是谁的实例(参数必须为对象,number,string等不可以)
var a={} a instanceof Object  //true
  • constructor:通过指向的构造函数来判断
  • object.prototype.toString.call():判断某个对象属于哪种类型,这种方式可以判断所有的数据类型,缺点是需要自己再拆分数据类型字符串

普通函数和箭头函数的区别有哪些?

  • 创建方式的不同,普通函数通过function来创建,而箭头函数通过=>创建
  • 箭头函数无法使用arguments作为多参数的传递方式,只能使用...rest的方式,而普通函数两种都可以
  • 箭头函数无法作为构造函数,而普通的函数可以通过new的方式作为实例的构造函数
  • 箭头函数没有自己this,所以他的this指向是借用了上下文的this指向,也就是说,调用函数的最外层指向的是谁,箭头函数的this就指向谁,因为他没有this,所以也就无法通过显示绑定的方式去修改this指向(call,apply,bind)

this指向?

  • 全局环境中的this指向,浏览器中的this指向window对象,node环境中的this指向的是空对象{}
  • 使用new绑定,并且new 的构造函数不是返回的引用类型的值,则this指向new的新实例
  • 使用call,apply,bind进行显示绑定,this指向绑定对象,如果绑定对象为null,undefined,则this会指向全局对象(显示绑定)
  • 隐式绑定,函数的调用是由某个对象触发的,存在上下文对象,指向触发对象,如果调用前存在多个对象,则this指向距离自己最近的对象
var a={b:function(){this}} //this=>a
var c={e:a}//this=>a
  • 默认绑定,调用时无任何前缀的情况,浏览器环境下非严格模式指向window,严格模式指向undefined
function a(){console.log(this)}//this=>window
  • 箭头函数,取决于外层作用域的this指向,外层作用域或函数指向谁,箭头函数的this便指向谁

bind,call和apply的区别?

  • 三者都是用来修改this显示修改this指向,区别在于传递参数的方式和是否立即执行的区别
  • bind和call的传递参数的方式是一样的,区别在于call的是立即执行的,而bind需要二次调用
Func.bind(Obj,arg,arg1,arg2...)
Func.call(obj,arg,arg1,arg2...)
  • call和apply的都是会立即执行一次,但是两个参数传递的方式不太一样
Func.call(obj,arg,arg1,arg2...)
Func.apply(obj,[arg,arg1,arg2])

js中for循环的变量作用域是什么样的?

这题要结合实际的题目

       for(var i=0;i<5;i++){
            setTimeout(() => {
                console.log(i)
            }, 1000);
        }
        console.log(i)
        //5
        //5 5 5 5 5
  • 这里需要理解函数作用域,我们的函数作用域有两种,一种是全局作用域,一种是函数作用域,但是因为for循环体并不是函数,所以他的作用域最后是跟随for循环使用的外层的作用域的,所以最底下打印的i实际上就是for循环体中定义的i,而i又经过循环++的操作,最后变成了5
  • 这里的var 如果改为let 就不行了,因为let的块级作用域的存在,最后这个会错误抛出

前端跨域的方式有哪些?

JSONP跨域、CORS跨域、nginx/apache反向代理、前端node跨域

跨域限制是浏览器行为还是服务器行为?

浏览器行为。具体流程的话就是页面发送请求,浏览器会根据同源策略做出判定,如果是同源请求,直接发送出去,如果是跨域请求,在HTTP HEADER加上Origin字段,或者先发送一次预检请求。服务端接收请求,根据自身的跨域的配置(比如允许哪些域名,什么样的method访问),返回文件头,若未配置过任何允许跨域,则文件头不包含Access-control-allow-origin字段,如果配置了,则返回Access-Control-Allow-origin+对应配置规则的域名,浏览器接收到响应,根据返回值做匹配,符合条件就发起请求,不符合条件就抛出跨域错误

说一说对js执行上下文栈和作用域链的理解?

这里推荐这篇文章,讲的很细节也很清楚: blog.csdn.net/qqchenyufei…

  • 执行上下文可以理解为在当前的执行环境中,形成了一个作用域,作者是在执行栈中开辟了一块空间,来执行当前的函数,执行上下文的声明周期分为两个阶段,一个是创建阶段,一个是执行阶段,创建阶段会创建变量对象,建立作用域链,以及确定this的指向,而执行阶段会完成变量赋值,函数引用,以及执行其他代码,变量对象在创建阶段为变量对象,在执行阶段会变为活动对象,变量对象的时候是不可以进行操作的,而活动对象是可以进行操作的
  • 作用域链:是由当前环境和上层环境大的一系列变量对象组成的,它保证了当前执行环境对符合访问权限的变量和函数的有序访问,白话就是保证你能访问能访问的变量,不能访问无法访问的变量

什么是变量对象?

就是在当前执行上下文创建的一个对象,里面存储这个上下文中的一切事物,变量,函数等等

闭包是什么?

闭包简单的说就是函数A里面有一个变量以及一个函数B,函数B访问了这个变量,并且把函数B进行了返回,函数A就是个闭包。闭包的作用主要是为了实现变量私有化(隐藏变量),进行一些功能的封装。Js拥有自动的垃圾回收机制,当一个值,在内存中失去引用时,就会根据标记清除的算法对这个值进行回收,释放内存,那么我们在执行上下文执行完毕以后,该函数就会失去引用,其占用的内存就会被垃圾回收释放,但是闭包的存在阻止了这一个过程,它让引用被保留了下来

原型和原型链?

  • 原型:通俗的说就是一个对象模板, 通过原型可以创建一摸一样的新对象
  • 原型链:对象拥有一个属性_proto_,而函数因为它也属于对象的大类,所以它既有_proto_,也有prototype,对象的_proto_指向的是其构造函数的prototype,构造函数又有自己_proto_所以它又会指向它构造函数的prototype,根据这个规则我们就组成了一个链条,通过这个链条去查找属性,最终指向null,如果能够找到就将值返回,如果不能找到就返回undefined

new的实现原理?

  • 首先创建一个空对象
  • 将空对象的_proto_指向需要构造函数的prototype
  • 通过call或者apply执行构造函数,将构造函数中的属性和方法绑定到创建的空对象中(修改this指向,使得构造函数的this指向空对象)
  • 再判断构造函数执行返回的是否为对象,如果为对象,直接将这个对象返回
  • 如果不是对象,将这个新的空对象返回 (ps:模仿的new执行的时候,如果返回值是常规意义上的基本类型的数据(string,number,bolean等),new函数会返回该函数的实例对象,如果函数返回的是一个引用类型的数据(object,array,function),则会直接返回结果,相当于调用了函数,而不是new了一个实例)

new.png

前端性能优化的方式有哪些?

性能优化的话主要从两个方面来优化,一个是网络加载速度,一个是页面渲染

  • 网页加载速度可以做的优化的话首先可以压缩文本大小,通过webpack配置,清除一些无用代理,业务逻辑上增强代码的重用性,引入的模块可以使用CDN进行加载而不是全部打包起来,并且可以通过配置实现按需加载,首屏只加载首屏需要的内容,js文件放置到cdn服务器上,利用cdn服务器的就近原则,减少转发次数,代码的合并,文件合并尽量放到一个域名服务器上,减少DNS解析的域名个数,对于图片资源比较多的首屏页面,第一也是对图片进行压缩,然后展示可以采用图片懒加载的方式减少首次请求图片个数,而对于第二次进入页面的话可以利用浏览器的缓存技术,将第一次 请求的资源进行缓存,第二次对于变动不那么频繁的资源可以直接使用缓存
  • 页面渲染的话,第一个就是文件的引入顺序严格按照解析规则来走,css放头部,js放尾部,css的嵌套不宜过深,增加解析的时间,js里面减少dom操作,或者采用document.createDocumentFragment创建碎片节点的方式,减少页面重新渲染次数

ES6中的set是什么,如何使用?

ES6中的weak是什么,如何使用?

前端模块化的规范有哪些,区别是什么?

CMD、AMD、commonJS和ES6module

  • AMD是requirejs的执行过程中发展的规范,requirejs通过define定义,采用依赖前置的方式,在一开头先将需要依赖的包引入,然后再去使用依赖中的模块,它的第一个参数采用数组的形式,这样可以同时引入多个模块,同时模块与模块之间可以是依赖关系,但是被依赖的一定要写在依赖的前面
define([module1,module2],function(){
  return val
})
  • CMD是seajs的执行过程中衍生出来的规范,定义规则也是通过define,但是跟CMD的区别在于,他是依赖就近,不需要将依赖的模块放到前面去引入,而是根据实际的需求在需要的地方通过require引入
define(function(){
    require(module)
})
  • commonjs是采用的module.exports的方式进行模块导出的,require('')的方式导入。目前的主要使用在node服务端,它主要是用做同步加载。它每个文件代表一个模块,有自己独立的作用域,node会将模块进行缓存,第二次加载就会直接从缓存中获取
module.exports=module
require(module)
  • ESmodule 是采用exports到处,import引入,js引擎对脚本进行分析的时候,需要模块的加载只会生成一个只读引用,不会去加载,而是等到脚本执行的时候才会通过引用模块获取值,而且他是动态的,在加载到执行这个阶段,如果发生了变化,导入的也会发生变化,不会缓存值。旨在浏览器端和node端都可以使用,node端可以使用require引入
var add=function(){}
export {add}
import {add} from './add.js'

Promise是异步还是同步?

var a=new Promise((res,rej)=>{
    console.log(1)
    res(2)
})
a.then((val)=>{console.log(val)})
console.log(3)
//1
//3
//2

由上面的例子可以知道,promise是同步的,then是异步的

Promise的状态有哪些?

  • 三种状态,pending(等待),fullfilled(已完成),rejected(已拒绝)
  • promise的状态转换不能逆转,发生改变以后也不能再修改

Promise的方法有哪些?

  • Promise.prototype.then()
  • Promise.prototype.catch()
  • Promise.prototype.finally()
  • Promise.all()
  • Promise.race()
  • Promise.resolve()
  • Promise.reject()
  • Promise.try()
  • Promise.allsetted()

Promise如何中断或者取消?

juejin.cn/post/684490… promise本身是无法取消或者中断的,但是通过利用promise的状态和提供的一些方法去模拟 第一种使用返回一个状态pending 的新对象,原Promise链将会中止执行

Promise.resolve().then(()=>{
    console.log('ok')
    return new Promise(()=>{})
}).then(()=>{
    //后续的函数不会被调用,也就是这一步不会再执行
    console.log('ok2')
})

第二种就是使用Promise.race()进行竞速,只会返回最早响应的promise

var p1=new Promise((res,rej)=>{
    settimeout(()=>{res(111),10})
})

var p2=new Promise((res,rej)=>{
    res(222)
})
Promise.race([p1,p2]).then((res,rej)=>{
    console.log(res)
})

第三种利用错误处理,抛出错误,让catch捕获

为何使用Promise来处理异步,优缺点?

return、break、和continue的区别?

  • return:跳出所有循环体,不管嵌套几层
  • break:跳出当前循环体,如果多层嵌套,只跳出最近这一层循环体
  • continue:跳出当前循环,不跳出循环体,执行下次循环

for、foreach、map和filter的区别?

  • for的循环体和foreach,map,filter不同,for是结构体,而foreach,map,filter是函数,他的作用域是跟着外层的作用域,for循环可以使用break,return去退出,而foreach这些不行,因为是函数体,无法直接退出,只能通过错误抛出的方式
  • forEach、map、filter的使用方式是一样的,都是回调函数的形式,回调函数接收三个参数,循环的值,循环的下标,数组本身
  • forEach就是对数组里面的每一项执行一次回调函数的操作,不能直接操作回调函数中的值,但是可以通过下标的方式修改
var a=[1,2,3,4,5]
a.forEach((item,index)=>{
    item=1  //无效修改
    a[index]=1 //有效修改
})
  • map是根据条件将每一项的值进行映射,最后返回一个操作过后的新数组,不能修改原数组
var a=[1,2,3,4,5]
var b=a.map(item=>item*2)
console.log(b,a)
//[2,4,6,8,10]  [1,2,3,4,5]
  • filter是根据回调函数中的条件过滤每一项的值,将满足条件的组成一个新数组返回,不能修改原数组
var a=[1,2,3,4,5,6]
var b=a.filter(item=>item>3)
console.log(b,a)
//[4,5,6] [1,2,3,4,5]

JS异步加载脚本的方式有哪些,为什么要异步加载脚本?

  • 首先要了解js加载的几种方式:同步加载,异步加载,延迟加载、预加载
  • 同步加载就是直接通过script标签将js进行加载,同步加载会阻塞dom的解析和渲染,因为js中可能包含dom操作
  • 异步加载js加载脚本不会阻塞当前dom的解析,下载js的同时还会进行后面页面的处理,实现的方式有几种:1、通过onload里面动态创建script标签,去进行数据加载 2、通过async属性的设置,去异步加载脚本,会阻塞onload事件,加载完了以后会立即执行
<script async src='url'>
  • 延迟加载:页面只需要加载立即执行的js,而其他的可以后面再加载,就可以使用延迟加载,主要通过设置defer
<script defer src='url'>
  • 预加载:可以理解为一些资源我当前页面用不到,但是在下个页面用得到,而我目前有多余的时间可以做别的事情,就可以对下个页面的资源先进行预加载,可以通过preload/prefetch对资源进行预加载
<link rel='preload' href='url'>

defer和async的区别?

  • defer是异步加载,告诉告诉浏览器去立即加载,但是延期执行,然后defer加载的脚本的执行顺序是按照他们出现的先后顺序来的,并且他们会在DOMContentLoaded执行前触发
  • async也是异步加载,加载完成以后立即执行,然后也不按照顺序加载

DOMContentLoaded和onload的区别?

  • DOMContentLoaded是在dom树构建完成以后触发,不需要等待所有资源加载完成
  • onload是需要等待所有资源加载完成以后才能触发

preload和prefetch的区别?

  • preload和prefetch都是用来进行预加载的,preload是用来预加载当前页面的资源,而prefetch是用来加载将来页面可能使用到的资源
  • 两个都可以支持跨域请求

节流函数和防抖函数?

  • 节流函数:长时间内一定时间执行一次函数,可以执行多次,一定时间周期内触发都不会执行,只有在时间到了以后才会执行,进入下一次时间周期
  • 防抖函数:一段时间内只执行一次,等待一定的时间,如果在等待时间内触发了就重置这个等待时间,重新开始等待,只能满足了等待时间才会触发

函数柯里化?

就是将函数的简单的传参方式复杂化,一次性的传参变为多次传参,每次新的传参都能在上次参数返回的结果的基础上进行操作,让使用者对函数的过程更好的控制

fn(1,2,3)==>fn(1)(2)(3)

componse函数?

浏览器的渲染原理?

www.jianshu.com/p/05eb1b17b…

点击屏幕上的按钮会发生什么?

link和@import引用css的区别?