前端js面试题

1、原型、原型链

原型:js中一切皆对象,对象都有一个隐式的属性_proto_,它指向该对象的原型-prototype(原型对象)

原型链:我们调用对象的某个属性,如果该对象上没有该属性,则会向上在该对象的原型对象上找,有则返回,如果没有的话继续向原型的原型上找,直到Object的原型,Object的prototype为null,如果没有找到该属性则返回undefind。我们把整个访问的过程称为原型链

tips: 通过Object.create(null) 创建的对象没有原型对象

varA=function(){}

A.prototype.n=1

var b=new A()

A.prototype={n:2,m:3}

varc=newA()

console.log(b.n,b.m,c.n,c.m)   //1 undefind 2 3

varF=function(){};

Object.prototype.a=function(){

    console.log('a()')

};

Function.prototype.b=function(){

    console.log('b()')

}

var f=newF();

f.a()//? a()f.b()//? errF.a()//? a()F.b()//? b()

2、闭包、闭包的优缺点、闭包的应用场景

闭包:js的作用域中内部函数可以访问外部函数的变量,反之则不行,但是我们为能够达到这一目的,就使用内部函数应用外部函数的变量,把该内部函数通过外部函数return到外部去执行,就形成了闭包。

缺点:长期占用内存、容易造成内存泄漏

优点:私有话属性或变量、避免外部环境污染

应用场景:

A、防抖(清除旧定时器,开始新的定时器)、节流(上一个定时器没有结束,则return 不执行新的定时器)

B、函数柯里化

C、

  • 1
  • 2
  • 3

给li循环绑定点击事件,弹出 i

3、字符串的常用方法

indexOf()  查找某字符串对应的下标,如果找不到返回-1

split(sep)  将字符串按照指定的字符切割成数组元素,sep表示指定的字符

slice(start,end)  截取字符串,start开始的下标,end结束的下标,不包含end本身;如果end为空截取到最后,如果为负数表示倒数。

substr(start,count)  截取字符串,start开始的下标,count截取的长度,如果count为空截取到最后,如果start为负值表示倒数

substring(start,end)  截取字符串,start开始的下标,end结束的下标,不包含end本身,如果end为空截取到最后;如果下标为负数,自动转为0

4、Set、Map、和Array的异同、用法

Set:创建类似数组的数据结构,但成员是唯一(基本数据类型无重复,应用数据类型可以重复)

常用方法:size()、add()、delete()、clear()、has()可以使用该方法求交集/差集、keys()、values()、entries()、forEach()

Map:以键值对的行书存储数据,key可以是任何数据类型

常用方法:size()、set(key,value)、get(key)、delete()、clear()、has()、keys()、values()、entries()、forEach()

Set/Map转Array:

Array.from(set)

const arr = [ ...set ]

const arr = [ ...map.values ]

5、类数组转成数组的方法

const arr = Array.from(args)

const arr = Array.prototype.slice.call(args)或者[].splice.call(args)

const arr = [ ...args ]

6、宏任务微任务

js是单线程,但是它有个事件队列机制来处理异步操作,事件队列中有包含宏任务和微任务。像常用的setTimeout,setInterval就是宏任务,Promise()的.then(()=>{})/.catch(()=>{})/.finally(()=>{}) 回调都是微任务。js先执行主线程任务,然后看如果有可以执行的微任务就会被执行,再看如果有宏任务就被执行,执行完继续主线程任务-然后就一遍一遍这循环执行,这也就是它的事件循环机制

7、深拷贝、浅拷贝

js的数据类型分为基本数据类型(Number/String/Boolean/Null/undefind)和引用数据类型Object(包含Function

/Array/Date/Regex)。浅拷贝对基本数据类型来说拷贝的是值,但对引用数据类型来说拷贝的只是引用, 深拷贝针对的是引用数据类型,拷贝的是值。

我们常用的拷贝方法有:

1、Object.assgin()  仅第一层是深拷贝

2、ES6的扩展运算符(...)仅第一层是深拷贝

3、JSON.parse( JSON.stringify(target) )  会丢失函数、会把时间对象变成字符串、会把正则对象变成空对象

4、利用递归手写深拷贝方法

function deepClone1(obj) {

  var objClone = Array.isArray(obj) ? [] : {};

  if (obj && typeof obj === "object") {

    for (key in obj) {

      if (obj.hasOwnProperty(key)) {

        if (obj[key] && typeof obj[key] === "object") {

          objClone[key] = deepClone1(obj[key]);

        } else {

          objClone[key] = obj[key];

        }

      }

    }

  }

  return objClone;

}

8、数组去重

1、利用对象访问属性的方法,判断对象中是否存在key

2、利用reduce方法遍历数组,reduce第一个参数是遍历需要执行的函数,第二个参数是item的初始值

varobj={};

vararr=arr.reduce(function(item,next) {

obj[next.key]?'':obj[next.key]=true&&item.push(next);

returnitem;

 }, []);

3、利用new Set() 去重,去重之后可以通过Array.from(set)或者 [...set] 把set转回数组

9、cookie sessionstorage localstorage 的异同

相同点:都可以存储数据

不同点:

存储大小:cookie 4KB

    sessionstorage和localstorage 5MB

生命周期:

cookie 可以手动设置过期时间

sessionstorage 当前会话,关闭窗口或浏览器就被删除

localstorage 永久保存 除非手动删除

使用api:

cookie 操作的是字符串 比较麻烦需要手动封装方法

sessionstorage和localstorang的话有自己的getItem()和setItem()、clear()方法,使用起来比较方便

10、跨域

因为浏览器存在同源策略,协议+域名+端口 必须完全一致不然就会受到浏览器同源策略限制造成跨域。

跨域有如下常用方法:

1、proxy

2、jsonp 原理是利用不是同源策略限制

3、跨域资源贡献(CORS)服务端设置Access-Control-Allow-Origin即可

4、window.name + iframe 原理是window.name保存的字符串数据可以跨不同页面和不同的域

5、h5的postMessage() + window.onmessage() 再不同页面(或iframe)之间发送消息和接受消息

6、nginx代理 原理是因为跨域是存在与浏览器端,所以通过反向代理通过服务端获取数据

11、apply()、call()、bind() 之间的异同

相同点:三个都是改变this指向的。

区   别:call()和apply()第一个参数是指定的对象,call()之后的参数是传入该函数的值

apply()第二个参数是数组,数组中是函数执行需要的参数

bind()和call()的参数相同,不同的是bind()改变this的指向后不会立即执行,其他两个是立即执行的

tips   :使用bind()的时候最好不要直接绑定在Dom上,避免当Dom变化时需要重新绑定

12、防抖、节流函数

防抖:代码实现重在清零clearTimeout

functiondebounce(f,wait) {

lettimer

return(...args)=>{

clearTimeout(timer)

timer=setTimeout(()=>{

f(...args)

},wait)

 }

}

节流:代码实现重在开锁关锁timer=timeout; timer=null

functionthrottle(f,wait) {

lettimer

return(...args)=>{

if(timer) {return}

timer=setTimeout(()=>{

f(...args)

timer=null

},wait)

 }

}

13、数组降维

Array.prototype.concat.apply([],targetArr);

14、判断数据类型

function toType(obj) {

  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()

}

15、箭头函数和普通function的区别

1、写法不同

2、this指向不同

function中的this执行环境的不同而不同

箭头函数的this是它自己执行环境最近非箭头函数的this

3、function可以被new 实例化,箭头函数不可以

4、function可以被声明提前,箭头函数不可以,必须先声明后使用不然会报错

16、数组的常用方法

改变原数组的:push()、pop()、unshift()、shift()、splice() 、sort()、reverse()

不改变原数组:join()、slice()、map()、filter()、forEach()、some()、every()、find()、reduce()

17、new 操作符具体做了什么

1、创建了一个空对象 let obj = {}

2、继承了该函数的原型 obj._ protp_= Fn.prototype

3、改变了该函数的this指向 let result = Fn.call(obj,...args)

4、判断该函数的返回值,如果该返回值是基本数据类型则 return obj ,否则 return result

18、Promise

它用于异步操作,它是一个构造函数,接收一个回调函数,用于异步计算,可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果而且可以在对象之间传递和操作promise,帮助我们处理队列。

有三个状态:

1、pending[待定]初始状态

2、fulfilled[实现]操作成功

3、rejected[被否决]操作失败

常用方法:

1、then()、catch()、finally()

2、all([])接收一个数组,当所有的异步请求完成之后才完成,一旦有某个异步请求报错,则直接catch

3、race([])也接收一个数组,但它是抢占执行,一旦有一个异步请求完成就算完成了

19、函数柯里化

概念:把一个接收多个参数的函数变成接收单一参数 并且返回能够接收新参数的函数 

add(1)(2)(3)(4)=10;

function add(num){

    var sum=num;

    var fn=function(v){

        sum+=v;

        return fn

    };

    fn.toString=function(){

        return sum

    };

    return fn

}

console.log(add(1)(2)(3)(4)) // 10

20、斐波那契数列

eg:1、1、2、3、5、8、13...

function fibonacci(n) {

if(n == 1 || n == 2) {

return1

    };

returnfibonacci(n - 2) + fibonacci(n - 1);

}

fibonacci(30)

21、缓存策略

1、强缓存:

强缓存两个相关字段:【Expires】-过期时间,【Cache-Control】-过期时长。

强缓存分为两种情况,一种是发送HTTP请求,一种不需要发送。

首先检查强缓存,这个阶段不需要发送HTTP请求,通过查找不同的字段来进行,不同的HTTP版本不同

http1.0版本,使用的是expires,http1.1使用的是cache-control

expires即过期时间,时间是相对于服务器的时间而言的,存在于服务端返回的响应头中,在这个过期时间之·        前可以直接从缓存里面获取数据,无需再次请求。

cache-control,http1.1版本中,使用的是这字段,这个字段采用的时间是过期时长,对应的是max-age

注意点:当expires和cache-control同时存在时,优先考虑cache-control。

当缓存资源失效了,也就是没有命中强缓存,接下来就进入协商缓存

2、协商缓存

强缓存失效后,浏览器在请求头中携带响应的缓存etag来向服务器发送请求,服务器根据对应的tag,来决定是否使用缓存。

分为两种,【last-modified】和【etag】。两者个有优势

last-modified:这个字段表示的是【最后修改时间】,在浏览器第一次个服务器发送请求后,服务器会在响应头中加上这个字段。浏览器接收到后,【如果再次请求】,会在请求头中携带 if-modified-since 这个字段,这个字段的值也就是服务器传来的最后修改时间。服务器拿到请求头中的 if-modified-since 的字段后,其实会和这个服务器中 该资源的最后修改时间 做对比:

如果请求头中的这个值小于最后修改时间,说明要更新了,返回新的资源,跟常规的http请求响应的流程一样,否则返回304,告诉浏览器直接使用缓存。

etag:etag是服务器根据当前文件的内容,对文件生成唯一的标识,比如md5算法,只要里面的内容有改动,这个值就会修改,服务器通过响应头把这个字段给浏览器。浏览器接收到etag值,会在下次请求的时候,将这个值作为【if-none-match】这个字段的内容,发送给服务器。服务器接收到这个【if-none-match】字段后,会跟服务器上该资源的【etag】进行比较。

如果两者一样的话,直接返回304,告诉浏览器直接使用缓存,如果不一样的话,说明内容更新了,返回新的资源,跟常规的http请求响应的流程一样。

22、301、302、303、304状态码有什么区别

301表示永久重定向,请求的资源分配了新的url

302表示临时重定向,请求的资源分配了新的url,本次暂且使用新的url,下次请求可能会改变

303表示请求的资源路径发生改变,使用GET方法请求新url。她与302的功能一样,但是明确指出使用GET方法请求新url。

304表示请求的资源未更新。该状态码不应该认为是一种错误,而是对客户端有缓存情况下服务端的一种响应。

tips:新url指的是,第一次请求返回的location

23、深度优先、广度优先

  深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。递归

  要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历(我们前面使用的是先序遍历)。具体说明如下:

   先序遍历:对任一子树,先访问根,然后遍历其左子树,最后遍历其右子树。

   中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树。

   后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。

   广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止  遍历

24、介绍一下webpack

webpack是模块打包机(自动化构建工具),我们常用webpack构建我们的项目框架。在webpack中,包含entry,output,mode,module,plugin,module中我们一般会引入各种loader帮助我们翻译编译代码,(loader是从右往左执行)plugin会引入各种插件,优化构建出来的代码,mode是模式,一般分为development和production。entry是入口,output是出口。

你可能感兴趣的:(前端js面试题)