字节网上面经整理(100道面试题)

为了准备面试,看了很多字节面经,整理出来,方便复习,答案一部分是自己看完网上的知识理解的,一部分是网上摘过来的。
1、let map={} 和newMap()的区别?
考察的是字面量创建对象和构造函数创建对象的区别。
字面量创建对象:1、代码量少易读。2、运行速度更快,在解析的时候会被优化,即不需要作用域解析,解析器需要顺着作用域链从当前作用域向上查找,如果在当前作用域找到了名为Object()的函数就执行,如果没找到,就继续顺着作用域链往上照,直到找到全局Object()构造函数为止。3、但是一次只可以创建一个对象,创建很多对象,会造成代码冗余度高。
构造函数创建对象:把对象实例的创建过程委托给一个内置的函数,函数的传参会影响对象的创建。
2、说说symbol。
①symbol是es6的新特性,它类似于唯一标识一个ID。②用typeof识别会返回Symbol,③用Symbol定义的变量,比较的结果为false。④如果对象里面的属性被定义为Symbol类型,则不能用Object.keys()for in 遍历出来,因为该属性不在对象的自身属性名集合中。⑤用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外。⑥可以用Symbol来定义私有属性。⑦如果要获取以Symbol方式定义的对象属性,可以用object的API:Object.getOwnPropertySymbols()或者用新增的反射API:Reflect.ownKeys()

3、说说js垃圾回收机制(网络答案)
一般来说没有被引用的对象就是垃圾,就是要被清除。
现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。
1、标记清除
这是javascript中最常用的垃圾回收方式。当变量进入执行环境时,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
  垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。
2、引用计数
另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。
出自:链接:js垃圾回收机制
4、循环对象的方法及区别
1.使用for in。可以用来枚举对象的属性,也可以用来循环数组。但也会把原型上的方法名也遍历出来。遍历对象,是获取对应元素的序号,for…in是获取对象的key。
2.使用for of。遍历对象,可以获取对应的元素,是遍历获取对象中的value。
3.Object.keys().找到对象中所有key值的一个数组,,再forEach取得其value。
5、判断数据类型的方法
typeofinstanceofconstructorObject.prototype.toString.call
6、transition和animation的区别,以及触发transition的条件
transition是过渡属性,强调过渡,设置一个开始关键帧和一个结束关键帧。animation是动画属性,可以设置多个关键帧完成动画,设定好时间后自己执行,无需触发事件。触发transition的条件是触发一个事件,比如鼠标移动上去,焦点,点击等。
7、js有哪些类数组,es5 es6有哪些方法可以将其转化为数组?
arguments、NodeList、HTMLCollection.
方法:Array.prototype.slice.call()[].concat.apply([],arr)Function.prototype.call.bind(Array.prototype.slice)[].slice.call(arr)ES6类数组转化为数组 Array.from(arr)
8、0.1+0.2 === 0.3吗?为什么?怎么才能得到想要的结果?
不等于,在JavaScript中的二进制的浮点数0.1和0.2并不是十分精确,在他们相加的结果并非正好等于0.3,而是一个比较接近的数字 0.30000000000000004 ,所以条件判断结果为false。可以通过以下两种方法实现:
1.ES6提供的Number.EPSILON方法

Number.EPSILON=(function(){   //解决兼容性问题
        return Number.EPSILON?Number.EPSILON:Math.pow(2,-52);
      })();
//上面是一个自调用函数,当JS文件刚加载到内存中,就会去判断并返回一个结果,相比if(!Number.EPSILON){
  //   Number.EPSILON=Math.pow(2,-52);
  //}这种代码更节约性能,也更美观。
function numbersequal(a,b){ 
    return Math.abs(a-b)<Number.EPSILON;
  }
//接下来再判断   
    var a=0.1+0.2, b=0.3;
  console.log(numbersequal(a,b)); //这里就为true了

2、把计算数字 提升 10 的N次方 倍 再 除以 10的N次方。N>1.

(0.1*1000+0.2*1000)/1000==0.3

9、重绘、重排。
重排:当渲染树因为元素尺寸、布局、隐藏等改变而需要重建,这就称为重排(回流)。每个页面至少需要一次重排(回流),就是在页面第一次加载的时候。
重绘:当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
10、html文件解析过程。
解析html文件,生成dom树,然后解析css生成Css树,然后dom树和Css树结合生成render树,然后给render树布局,然后渲染render树,然后交给电脑的GPU排版合成。
11、flex布局的grow和shink是干嘛的?
flex弹性布局,如果子元素宽度之和大于或者小于父元素宽度,空间就会存在剩余和不够,flex默认不换行,除非设置flex-wrap,那么这种情况下,有两个重要的属性,flex-grow和flex-shrink.
当子元素宽度之和小于父元素宽度时,给子元素设置flex-grow,会将剩余的尺寸按照比例分配的子元素。
当子元素宽度之和大于父元素宽度时,给子元素设置flex-shink,会将多出的尺寸按照比例(元素宽度占总宽度)从子元素中减去。
12、什么是同源策略?为什么要提出这个?
协议、域名、端口号一致,表示同源策略。设置同源策略的主要目的是为了安全,如果没有同源限制,在浏览器中的cookie等其他数据可以任意读取,不同域下的DOM任意操作,ajax任意请求其他网站的数据,包括隐私数据。
13、说说浏览器渲染机制,为什么用虚拟DOM?直接操作DOM有什么弊端?
解析html文件,生成dom树,然后解析css生成Css树,然后dom树和Css树结合生成render树,然后给render树布局,然后渲染render树,然后交给电脑的GPU排版合成。用虚拟DOM可以使页面的性能更好,减少重排与重绘。直接操作DOM会影响页面的性能,会导致页面的重排和重绘。
14、说说类数组对象。
类数组对象和数组一样具有length与index属性,但是它是一个Object。它是js提供的一种访问原始二进制数据的机制。
15、如何判断一个变量是数组?
instanceof Array.isArray() Object.prototype.toString.call()
16、如何理解html语义化?
语义化,其实就是用恰当的标签,去展示恰当的内容。通过使用恰当语义的标签,可以让页面具有良好的结构和含义。它更省代码,有利于搜索引擎,使页面有更清晰的代码结构,可读性高,互用性强。
17、var、let、const的区别?
var声明的变量会挂载到全局window上,存在变量提升,同一作用域下,var可以声明同名变量。
let和const不存在变量提升,并且会形成块级作用域,同一作用域下,不可以声明同名变量。
const声明的基本数据类型,不能进行再次更改,复杂的数据类型,如,对象,可以更改其属性。
暂时性死区:如果使用let const,变量在未声明之前就访问会报错,因为let const不存在变量提升,而var可以。
18、说说闭包,列举闭包的用处。
要理解闭包,必须要知道js变量是有局部变量和全局变量,全局变量在函数内外都可以访问到,但是局部变量,在函数内可以访问,函数外就不可以访问了,如果我们必须要拿到函数f1内部的变量,我们可以在函数内部再定义一个函数f2,根据Js的作用域链,f2这个函数可以访问到f1的变量,但是f1访问不到f2的,这时,我们再返回f2,这样以来,我们在外部调用f1函数,就可以访问到其内部的变量了,这就形成了闭包。简单的说,闭包就是访问其他函数内部变量的函数,使用闭包会造成内存泄露,因为内部变量无法释放。我们可以用立即执行函数解决闭包问题。比如,我们要给页面上的li绑定点击事件,打印出顺序。

  //给每一个li添加一个绑定事件,输出他们的顺序。
        function test() {
            var li = document.querySelectorAll('li');
            //方法一:用let声明i,而不是var
            for (let i = 0; i < li.length; i++) {
                   li[i].addEventListener('click',function(){
                       console.log(i)
                   },false)
                   //方法二:用立即执行函数
                // (function (j) {
                //     li[j].addEventListener('click', function () {
                //         console.log(j)
                //     }, false)
                // }, (i))

            }
        }
        test();

19、在网址上输入URL后,浏览器的变化。
首先会在浏览器缓存里查找,是否有记录,如果没有,则再本地的hosts里查找,如果没有则查找DNS服务器,得到服务器的ip地址后,浏览器会根据ip和相应的端口号构造一个http请求,并将这个http请求封装成TCP包,这个TCP包会经过四层传输到服务器,服务器拿到之后进行解析,返回一个html给浏览器,然后浏览器开始解析html文件,渲染页面。
21、说说BFC。
BFC:块级格式化上下文,它是指一个独立的块级渲染区域,只有Block-level Box 参与,该区域拥有一套渲染规则来约束块级盒子的布局,且与区域外部无关。通俗的讲就是父元素和子元素或者兄弟元素之间发生margin合并。触发BFC可以用:(1) position:absolute(2)display:inline-block(3)float:left/right(4)overflow:hidden
22、为什么a标签之间会有空白?
a标签是行级元素,行级元素和行块级元素都是文本类元素,带有inline的元素,文本类元素会受到文本分隔符的影响,只要有空格或者回车都会空一格。
23、如何判断JS对象为空对象?
1.将json对象转化为字符串,判断字符串是否为空。
2.用for in遍历对象。
3、用Object.getOwnPropertyNames
4.用Object.keys()
5.用jq的$.isEmptyObject()
24、说说TreeShaking。
Tree shaking指的就是当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码,这就需要借助webpack里面自带的Tree Shaking这个功能,帮助我们实现。Tree Shaking只支持ES Module(import…) 不支持require…
25、get与post的区别。
如果没有任何前提,不使用任何规范,只考虑语法的理论上的http协议,则get和post几乎没有什么区别,只有名字不一样。如果是基于RFC规范,理论上,get和post具有相同得到语法,但具有不同的语义,get用来获取数据,post用于发送数据,实际上,有以下不同点:

  • get请求参数有长度限制,而post没有。
  • get请求的数据在url中是可见的,post请求不显示在url中。
  • get请求后按后退或者刷新按钮没有影响,而post请求会被再次提交。
  • get请求的数据可以收藏为标签,但是post不可以。
  • get的历史参数会被保存在浏览器中,而post的不会。
  • get的安全性较差,因为请求的数据在url中是可见的。
  • get只允许ASCII,而post没有编码限制,允许发二进制的。
  • get编码类型:application/x-www-form-url.post编码类型encodeapplication/x-www-form-urlencoded。

26、Promise.then抛出的错误能否被catch捕获到?
Promise.then在第一个函数里抛出错误,在第二个函数里捕获不到,在catch里可以捕获到。如果promise.then在第二个函数里抛出错误,catch捕获不到。

  const pro = new Promise((resolve,reject)=>{
            resolve('111');
        })
        pro.then(data=>{
            console.log(data);//111
        },err=>{
            console.log(err);
        }).then(function(){
            throw new Error('dhdhf');
        },function(err){
            console.log(err);//打印不出来
        }).catch(err=>{
            console.log(err);//打印出抛出的错误
        })

        pro.then(function(){
        },function(){
            throw new Error('error');
        }).catch(err=>{
            console.log(err);//打印不出来,
        })

27、说说Vue事件修饰符。
.prevent:阻止默认行为。
.stop:阻止事件冒泡
.capture:事件捕获
.self:只有点击当前元素的时候才会触发事件
.once:只触发一次事件触发函数
28、Vue自定义事件会冒泡吗?
目前还没有。只能用递归组件,监听一次emit,再emit一次。
29、vue事件是冒泡时触发还是捕获时触发?
30、为什么会有Vuex这样的状态管理库?
简单数据在组件中传值我们可以使用组件通信,当涉及到复杂大量的数据或者复杂组件的数据传值时我们就需要一个存数据的库,vuex就是处理组件间复杂数据的共享问题的。
31、说说vue双向数据绑定的实现,vue3.x为什么用proxy实现?
vue的双向数据绑定是由数据劫持结合发布-订阅者模式实现的,利用Object.definePropery()来劫持对象属性的setter和getter操作,在数据变动时做你想做的事情。vue中使用v-model实现,主要原理是绑定了DOM对象的value属性,当它初次绑定的时候,就会触发getter,watcher就会触发, watcher通知Vue生成新的VDOM树。再通过render函数进行渲染,生成真实DOM。Object.defineProperty的缺点:(1)在Vue中,Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。(2)Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue里,是通过递归以及遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升。proxy的优点:

  1. 可以劫持整个对象,并返回一个新对象
  2. 有13种劫持操作。
    Proxy是 ES6 中新增的一个特性,翻译过来意思是"代理",用在这里表示由它来“代理”某些操作。 Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。其功能非常类似于设计模式中的代理模式。
    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
    使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。 从而可以让对象只需关注于核心逻辑,达到关注点分离,降低对象复杂度等目的。
    在这里插入图片描述
    vue3.x中用proxy实现双向数据绑定:

observe(data) {
  const that = this;
  let handler = {
   get(target, property) {
      return target[property];
    },
    set(target, key, value) {
      let res = Reflect.set(target, key, value);
      that.subscribe[key].map(item => {
        item.update();
      });
      return res;
    }
  }
  this.$data = new Proxy(data, handler);

32、说说vue-router按需加载原理?为什么要按需加载?
vue-router的懒加载是vue异步组件和webpack的【代码分块点】功能结合。

const App = () => import('../component/Login.vue');

vue这种单页面应用,如果没有懒加载,在webpack打包后的文件将会非常大,使用vue-router的懒加载即按需加载,可以有效的分担首页加载所要承担的压力,减少首页加载用时。
33、说说Vue组件通信?
34、说说webpack工作流程以及基本配置。
在这里插入图片描述
基本配置webpack.config.js

var path = require('path');

module.exports = {
  mode: 'development',
  entry: './foo.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'foo.bundle.js'
  }
};

35、说说前端工程化。
可以使用模块化、组件化、规范化、自动化实现前端工程化。
36、说说loader和plugin的区别。
loader是一个转换器,操作的是文件,比如,将A文件编译为B文件。plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。
37、说说跨域以及你知道的实现跨域的方法。
当要请求的url的协议、域名、端口号其中任意一个与当前页面的url不一致的时候就是跨域。
1、jsonp.其原理是动态添加script标签,创建回调函数,script标签没有跨域限制。
2、设置document.domain解决无法读取非同源网页的 Cookie问题.
3、跨文档通信 API:window.postMessage()
4、CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
【前端设置】根据xhr.withCredentials字段判断是否带有cookie
原文链接:跨域
38、说说XSS攻击,了解CSRF吗?
在这里插入图片描述
39、说说CommonJS和ES6模块化。
在这里插入图片描述ES6模块化
代表:Vue
语法:
导入:import xxx from、import {xxx} from
导出:export、export default
特点:
this指向undefined
编译时输出接口
可以单独加载其中的某个接口(方法)
静态分析,动态引用。输出的是值的引用
CommonJS模块化
代表:node.js
语法:
导入:require()
导出:module.exports、exports
特点:
this 指向当前模块
运行时加载。CommonJS脚本代码在require的时候,就会全部执行。一旦出现某个模板被“循环加载”,就只能输出已经执行的部分,还未执行的部分不会输出。
加载的是整个模块,即将所有的接口全部加载进来。
输出的是一个值的拷贝
链接:https://www.jianshu.com/p/08ae4f9fa802
完整的前端模块化看链接前端模块化详解(完整版)
40、说说websocket与ajax.
ajax是异步javascript和XML,websocket是html5的一种协议,实现了服务器和浏览器的全双工通信。ajax是短连接,数据发送完和接收完之后就会断开,websocket是长连接,在一个会话中一直保持。ajax请求回来的数据是用户自己看的,websocket请求回来的数据其他用户也可以看到。
41、说说fetch和普通request的区别。
fetch:

  • 是基于promsie实现的接口,避免了ajax出现的回调地狱。
  • 以数据流的形式返回数据,数据量大和传输文件有优势。
  • 具有cors的支持。
  • Fetch API引入三个新的对象(也是构造函数):Headers, Request和Response。headers用于设置请求头还是比较方便的。
  • fetch默认不携带cookie,需要进行配置。
  • fetch只对网络请求报错,400 500错误都会被视为成功。

普通请求(如axios):

  • 支持promsie API
  • 客户端支持防止CSRF

42、说说vue的set原理。
Vue.set数组实现的原理:其实Vue.set()对于数组的处理其实就是调用了splice方法.
43、说说js的继承。
es5的继承方法:

  • 传统的原型链继承
  • 构造函数继承。
  • 对象组合继承(相当于构造函数继承和原型链继承的组合)
  • 寄生组合继承。构造一个空类,使父元素的原型等于空类的原型。子元素的原型等于空类构造出来的实例对象。

es6的继承方法:super extends

44、说说TCP三次握手和TCP四次挥手。
可以参考:TCP的三次握手与四次挥手理解及面试题(很全面)
45、说说TCP是如何实现数据的可靠性传输的。TCP和UDP有什么区别?
可以参考:TCP/IP是如何实现可靠传输的
TCP和UDP区别
46、说说http、https的区别。对称加密和非对称加密,http2.0和http1.0的区别?
47、说说常见的状态码?301 302有什么区别?浏览器有什么变化?
200:请求成功。
301:资源(网页)被永久转移到其他url,
302:资源(网页)被临时移动到其他url,
400:客户端请求的语法错误,服务器无法理解
401:请求要求用户的身份认证。
402:保留,将来使用
403:服务器理解客户端的请求,但拒绝执行此请求
404:请求的资源不存在
500:内部服务器出现错误
502:作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效响应。

301和302:一个是永久的,一个是临时的,302错误通常,浏览器遇到该响应,会将访问的地址加入到历史记录中,然后重新去请求新的地址。

48、应用层有哪些协议?
HTTP协议、FTP协议、SMTP协议(简单邮件传输协议)
49、说说浏览器缓存机制,请求头,响应头。
缓存分为强缓存和协商缓存,根据响应的header内容来决定。
强缓存相关字段有expires,cache-control,如果cache-control和expires同时存在,则前者的优先级高于后者。
协商缓存的相关字段有Last-Modified/If-Modified-Since 、Etag / If-None-Match
深入理解浏览器的缓存机制
http请求中请求头和响应头包含哪些内容
50、说说前端性能优化。
前端性能优化有七大手段:减少请求数量,减少资源大小,优化网络连接,优化资源加载,减少重绘重排,使用性能更好的API,构建优化。

  减少请求数量:通过减少重定向,使用缓存,不适用CSS@import,避免使用空的src和href等手段

  减少资源大小:通过压缩HTML,CSS, JS,图片,此外在安卓下可以使用webp格式的图片,它具有更优的图像数据压缩算法,能带来更小的图片体积,还可以开启gzip, gzip编码是以后总用来改进web应用程序性能的技术,

  优化网络连接:使用CDN,使用DNS预解析,并行连接,

  优化资源加载,通过优化资源加载位置和时机,使用资源预加载preload和资源预读取prefetch

  减少重绘回流,1:避免使用层级较深的CSS选择器,以提高CSS渲染效率2、避免使用CSS表达式,3、给元素适当的定义高度或最小高度,否则元素的动态内容载入时,会出现页面晃动,造成回流,4、不要使用table 布局,5、能用CSS实现的效果,尽量使用CSS而不用JS实现

51、说说js异步加载。
52、说说CSS3的剪切属性。
background-clip :表示背景图片从哪儿截断不显示,border-box(默认值)/padding-box/content-box/text(用文字的部分截背景图片,除了文字体的区域展示背景图片,其他都不展示,只有在-webkit下好使(-webkit-4/background-clip),要配合-webkit-text-fill-color:transparent使用,此时文字变成了背景的一部分,文字阴影在背景的上面)
53、用typeof能检测的8种值。
String Number Boolean Object fuction null undefined Symbol
54、为什么出现浮动,如何清除浮动?
浮动float最开始出现的意义是为了让文字环绕图片而已,但人们发现,如果想要三个块级元素并排显示,都给它们加个float来得会比较方便。
清除浮动方法:
1.给父元素加上display:inline-block
2.使用伪元素
3.给父元素添加overflow:hidden
4.添加一个clear样式
55、说说你知道的垂直居中的方法。
56、如何在一个图片列表使用懒加载进行性能优化。
57、说说原型链。
58、说说三栏布局。
59、数组和链表的区别。
数组的存储区间是连续的,存储数据之前需要先申请一个存储空间,数组的增加和删除比较困难,查找比较简单,数组的空间是从栈分配的。
链表的存储空间是分散的,可以在任何地方,链表中的元素有两个属性,一个是值,一个是指针,存储空间不需要指定大小,是动态申请的,空间是从堆分配的。
60、说说ES6有哪些新增的东西。
61、说说贪心算法和分治算法。
贪心算法是解决问题的时候总是选择最好的,并希望通过一系列的最优选择,选择问题的全局最优解。
分治算法:把一个复杂的问题分成相同或相似的子问题,再把小问题分词更小的问题,直到最后的小问题可以简单的求解,然后把处理结果合并。
61、说说进程和线程,进程间通信的方法。调度算法。
进程是资源分配的最小单位,线程是程序执行的最小单位。
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
进程间的通信方法:管道,消息队列,共享内存,信号量,socket,信号,文件锁。
调度算法:操作系统常见的调度算法
62、说说cookie session.
63、写一个深克隆。

function deepClone(obj){
	var tmp = obj instanceof Array ? [] : {};
	for(let key in obj){
		if(obj.hasOwnProperty(key)){
			tmp[key] = typeof obj[key] === "Object" ? deepClone(obj[key]) : obj[key];
		}
	}
	return tmp;
}

64、写一个防抖或者节流函数。

 //防抖  隔段时间之后才执行
        function debounce(callback, wait) {
            var timer;
            return function () {
                var self = this;
                var arg = arguments;
                clearTimeout(timer);
                timer = setTimeout(function () {
                    callback.apply(self, arg);
                }, wait)
            }

        }
        //节流 如果大于或等于规定的时间就执行
        function thorttle(callback, wait) {
            var time
            var self = this;
            return function () {
                if (!time || Date.now() - time > wait) {
                    callback.apply(self, arguments);
                    time = Date.now();
                }
            }
        }

65、写一个函数,使得sum(1,2,3).valueOf的值和sum(1)(2)(3).valueOf的值相等。(考察柯里化)。

 function sum(a, b, c) {
            if (arguments[1]) {//如果存在第二个参数
                var result = 0;
                if (arguments.length > 0) {
                    for (let i = 0; i < arguments.length; i++) {
                        result += arguments[i];
                    }
                }
                return result;
            } else {
                return function (b) {
                    return function (c) {
                        return a + b + c;
                    }
                }
            }
        }
//柯里化 固定函数的某些参数,得到该函数剩余参数返回的新函数,如果没有剩余参数则调用
        function curry(callback){
            var self = this;
            var args = Array.prototype.slice.call(arguments,1);
            return function(){
                var curArgs = Array.from(arguments);//当前调用的参数
                var totalArgs = args.concat(curArgs);//所有参数
                if(totalArgs.length >= callback.length){
                    //没有剩余参数了就调用
                    callback.apply(null,totalArgs);
                }else{
                    //还有参数
                    totalArgs.unshift(callback);
                    self.curry.callback(self,totalArgs);
                }
            }
        }

66、写一个Event Bus。
组件通信,一个触发与监听的过程

 //实现一个Eventbus  事件的监听和触发
        class EventEmitter {
            constructor() {
                //存储事件
                this.events = this.events || new Map();
            }
            //监听事件
            addListener(type, fn) {
                if (!this.events.get(type)) {
                    this.events.set(type, fn);
                }
            }
            //触发事件
            emit(type){
                let handle = this.events.get(type);
                handle.apply(this,[...arguments].slice(1));
            }
        }

67、写jsonp原理。
68、用promise封装ajax。

69、手写实现promise/promise.all。

 class MyPromise {
            constructor(fn) {
                this.state = "pending";
                this.value = undefined;
                let resolve = data => {
                    if (this.state !== "pending") {
                        return;
                    }
                    this.state = "resolve";
                    this.value = data;
                }
                let reject = reason => {
                    if (this.state !== "pending") {
                        return;
                    }
                    this.state = "rejected";
                    this.value = reason;
                }
                try {
                    fn(resolve, reject);
                } catch (err) {
                    reject(err);
                }
            }
            //then
            then(onResolved, onRejected) {
                switch (this.state) {
                    case "resolved":
                        onResolved(this.value);
                        break;
                    case "rejected":
                        onRejected(this.value);
                        break;
                    default:
                        break;
                }
            }
        }

70、用reduce实现map。

71、写一个函数实现第i次执行输出i。

var test = (function(){
            var i=0;
            return function(){
                return i++;
            }
        })();
        console.log(test());
        console.log(test());

72、写一个函数实现大数相加。

73、实现一个双向数据绑定。

 //实现一个双向数据绑定
        var input = document.getElementById('input');
        var span = document.getElementById('span');
        var obj = {};
        Object.defineProperty(obj,'text',{
            enumerable:true,
            configurable:true,
            get(){
                console.log('数据更新了');
            },
            set(newVal){
                input.value = newVal;
                span.innerHTML = newVal;
            }
        })
        input.addEventListener('keyup',function(e){
            obj.text = e.target.value;
        })

74、如何判断一行汉字有没有出现省略号。
75、手动实现Array.reduce()
76、实现bind。

//实现bind
        //目标函数和原函数的参数要结合
        //返回一个新函数
        //当new的时候,构造函数依然是A,不是你的目标对象,
        function myBind(target){
            var self = this;
            var args = [].slice.call(arguments,1);
            var tmp = function(){};
            var fn = function(){
                var arg = [].slice.call(arguments,0);
                //检测this的原型链上有没有tmp构造函数的原型
                self.apply(this instanceof tmp ? this : (target|| window),args.concat(arg));
            }
            tmp.prototype = self.prototype;
            fn.prototype = new tmp();
            return fn;
        }

77、如何实现回型数组。
78、手写匹配URL的正则,包括协议、域名、端口、path、hash、queryString。
79、跳台阶问题。一次可以跳1个或者2个,有多少种跳法。
80、实现一个双向链表。
81、寻找2个二叉树节点的第一个公共父节点。
82、找出二叉树第k小的节点。
83、写出二叉树的后序遍历。
84、2个有序链表去重合并。
85、根据时间按顺序每秒钟输出一个元素寄下标。
86、查找最长无重复字符串。
87、CSS实现一个模态窗口,从下面向上弹的动画。
88、说说rem和em区别。
89、说说盒模型。
90、CSS实现三角形原理?写一个
91、一个页面2个Tab键,点击可以切换不同内容。
92、git merge和git rabase的区别。
git merge:将两个分支,合并提交为一个新提交,并且新提交有2个parent。
git rebase:会取消分支中的每个提交,并把他们临时存放,然后把当前分支更新到最新的origin分支,最后再把所有提交应用到分支上。
93、单行和多行的css省略号。
94、promise捕获错误的方式。
promise中捕获错误用try catch,但是它只能捕获到同步的错误,异步错误类似于setTimeout无法捕获到。在then的第一个函数里抛出的错误,在catch里可以捕获到,第二个函数抛出错误后面的catch不能捕获到。
95、用过jest单元测试吗?
96、说说项目中的移动端适配。
97、content-type:From-data、json等 及其之间的区别。

  • 后台需要Form-data(表单数据),传送的数据为key:value。
  • json:请求格式是一个json的字符串,后台需要json格式的数据。
  • x-www-form-urlencoded:当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2…),然后把这个字串append到url后面,用?分割,加载这个新的url。
  • multipart/form-data:enctype="multipart/form-data"是上传二进制数据;它告诉我们传输的数据要用到多媒体传输协议,由于多媒体传输的都是大量的数据,所以规定上传文件必须是post方法,的type属性必须是file。

98、H5 request animation.frame。
99、自己实现new函数。

function createNew(){
	let obj = {};//创建一个新对象
	//将第一个参数拿出来
	let constructor = [].shift.call(arguments);
	//连接到原型链
	obj.__proto__ = constructor.prototype;
	//绑定this值
	let result = constructor.apply(obj,arguments);
	//返回新对象
	return typeof result === "object" ? result : obj;
}

100、vuex的mutation和action的区别,为什么要设立两个而不是直接都用mutation操作state。
“相应视图---->修改state”,拆分成两部分,视图触发action,action再触发mutation。mutation专注于修改state,理论上是修改state的唯一途径,action是处理业务代码,异步请求。mutation是同步实现对state状态的更改,action是异步通过提交一个mutation让其更改state状态。

发布了14 篇原创文章 · 获赞 19 · 访问量 4963

猜你喜欢

转载自blog.csdn.net/qq_40619263/article/details/104499675