面试题汇总

艺术喵 2 年前端面试心路历程(字节跳动、YY、虎牙、BIGO)| 掘金技术征文
2021前端面试经常被问到的题(附答案)

1、h5和css3新特性

h5新特性

  • 语义化标签:header、footer、section、nav、aside、article
  • 增强型表单:input的type多了color、date、email、number、tel、url、week
  • 新增表单属性:placehoder、autofocus、min 和 max
  • 音频视频:audio、video
  • canvas
  • 地理定位、拖拽api
  • 新事件:onresize、onscroll、onerror
  • WebSocket:单个 TCP 连接上进行全双工通讯的协议

css新特性

  • 新增选择器:
    :last-child /* 选择元素最后一个孩子 /
    :first-child /
    选择元素第一个孩子 /
    :nth-child(n) /
    按照第几个孩子给它设置样式 /
    :nth-child(even) /
    按照偶数 /
    :nth-child(odd) /
    按照奇数 /
    :disabled /
    选择每个禁用的E元素 /
    :checked /
    选择每个被选中的E元素 /
    ::selection /
    选择被用户选取的元素部分 */
  • background-size、background-origin、border-radius、box-shadow / text-shadow、border-image、
  • 文本效果:text-shadow、@font-face 自定义字体
  • 2D/3D 转换、动画、过渡
  • 多列布局:column-count、column-gap、column-rule

2、浏览器由那部分组成

用户界面、浏览器引擎(渲染引擎、js引擎)、网络、UI后端 、数据存储
在这里插入图片描述
3、说说DOM、BOM
javascript 有三部分构成,ECMAScript,DOM和BOM
DOM(文档对象模型): 处理网页内容的方法和接口
BOM (浏览器对象模型): 访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器); BOM的核心是Window,而Window对象又具有双重角色,它既是通过js访问浏览器窗口的一个接口,又是一个Global(全局)对象;
Window对象包含属性:document、location、navigator、screen、history、frames

4、Promise 对象方法

then()、catch()、all()、race():将多个 Promise 实例,包装成一个新的 Promise 实例 resolve ()、reject()

1、判断js类型的方式

		typeof: 能检测出的数据类型有 number、string、boolean、undefined、object、function 缺点不能细分对象、数组,并且null返回'object'
    	instanceof:([1,2,3] instanceof Array)  判断是否是某个类的实例,所以左侧要是一个对象 缺点:不能判断基本数据类型
    	constructor: ([1,2,3] .constructor==Array)  constructor是prototype对象上的属性,指向构造函数。根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。 缺点:不能判断null、undefined,constructor 所指向的的构造函数   可以被修改的
    	Object.prototype.toString.call(): Object.prototype.toString对任何变量都会返回这样一个字符串"[object class]",class 就是 JS 内置对象 构造函数的名字。 call是用来改变调用函数作用域的。

2、Es5和es6分别几种声明变量方式区别是什么

   let、const声明变量没有提升,并且const、let声明变量并不会同步window

3、闭包的概念?优缺点?解决方案

   函数执行形成一个全新的执行上下文,进栈执行后所创建的一些变量被外部所引用,不能出栈销毁,就形成了闭包。
   作用:延长了局部变量的生命周期,保护全不被污染
   缺点:函数不能被出栈销毁,频繁使用会造成内存泄漏问题。
   解决方法:手动销毁 fn=null

4、数组去重

    let ary = [1,2,1,5,4,6,6,1,2,8,3]
    let arr = new Set(ary)
        ary = [...arr]
    ```
    ```js
    let ary = [1,2,1,5,4,6,6,1,2,8,3]
    let newAry = []
    for(let i = 0;i < ary.length;i++){
     if(newAry.includes(ary[i])){
         continue
     }   
     newAry.push(ary[i])
    }

5、Dom事件有哪些阶段?谈谈对事件代理的理解

   Dom事件阶段:捕获、目标、冒泡
   事件代理:事件不直接绑定到某个元素上,而是绑定到该元素的父元素上,通过事件触发冒泡,在判断是否是目标元素从而执行相应的逻辑。
优化:代码简单,节省内存

6、讲讲Js事件队列

   队列结构特点:
   先进先出,只允许在队列的最开始删除(出),只允许在队列的末尾添加(进);
   特殊情况:优先队列

   一个队列具有:1、队列容器2、进去队列的方法3、出队列的方法4、可以查看队列的方法5、查看队列的内容方法
    // 自己实现一个普通队列
    function Queue(){
        // 创建一个队列容器
        this.container = []
    }
    Queue.prototype = {
        construntor:Queue,
        // 进入队列 element进入队列的元素
        enter:function(element){
            this.container .push(element)
        },
        // 移除队列
        leave:function(){
            if(this.container .length === 0 ) return;
            this.container .shift()	
        },
        // 查看队列长度
        size:function(){
            return this.container .length
        },
        // 查看队列的内容
        value:function(){
            // 深度克隆是为了保证外面接收到值后更改不会影响内容容器的值
            return JSON.parse(JSON.stringfy(this.container ))
        }
    }
    let qe = new Queue()
优先队列:可以在插入的时候指定优先级
   // 实现一个优先级队列
   function Queue(){
       // 创建一个队列容器
       this.container = []
   }
   Queue.prototype = {
       construntor:Queue,
       // 进入队列 priority 优先级,默认都是0,数值越大,优先级越高
       enter:function(element,priority  = 0){
           let obj = {
           value : element,
           priority  :priority  
           }
           if(priority  === 0){
           // 不指定优先级(默认的优先级),直接存储到末尾即可
                       this.container .push(element)
                       return
           }
           // 指定优先级,我们需要从最后一项一次来比较
           // 遇到比自己大的直接插到这项末尾
           let flag = false
           for(let i = this.container.length - 1;i>=0;i--){
               let item = this.container[i]
               if(item.priority  >= priority ){
               // 插入到比较项的后面
               this.container.splice(i + 1,0,obj)
               flag = true
               return 
               }
           }
           // 优先级没有比我大的,我就是最大的,直接插入到容器的最开始位置即可
           !flag ? this.container.unshift(obj) : null
           
           
       },
       // 移除队列
       leave:function(){
           if(this.container .length === 0 ) return;
           this.container .shift()	
       },
       // 查看队列长度
       size:function(){
           return this.container .length
       },
       // 查看队列的内容
       value:function(){
           // 深度克隆是为了保证外面接收到值后更改不会影响内容容器的值
           return JSON.parse(JSON.stringfy(this.container ))
       }
   }
   let qe = new Queue()
    事件队列和事件循环
    js是单线程的只有等主线程执行完毕,才会去执行异步的任务。
    异步任务放在任务队列中,任务队列分为微任务和宏任务 先执行微任务在执行宏任务

    微任务:promise、async、await
    宏任务:定时器

    事件循环:主线程执行完代码后去任务队列中查找可执行异步任务,先找微任务找到执行,在查找执行,微任务没有后再找宏任务,查找执行;这一套机制称之微事件循环机制。

7、说说async 和 await

    ES7 引入了 async/await,这是 JavaScript 异步编程的一个比较大的改进。我们可以像写同步代码一些编写异步代码,避免了回调地狱,同时也代码也比 Promise 更易于阅读。async 就是异步的意思,在函数的定义前加上 async 关键字,表示这是一个异步函数,意味着该函数的执行不会阻塞后面代码的执行。await 就是等待的意思,即等待请求或者资源。await 后面可以接任何普通表达式,但一般会在 await 后面放一个返回 promise 对象的表达式。
    注意 :await 关键字只能放到 async 函数里面。

8、es6的class和构造函数的区别

    es5中没有类使用构造函数来模拟类。不同点:类的内部所有定义的方法,都是不可枚举的;类的构造函数,不使用new是没法调用的,会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行;Class不存在变量提升(hoist),这一点与ES5完全不同;
    // 使用构造函数实现类
    function Animal(){
        // 首先判断 不是通过new来调用的 判断依据 this是不是构函数的实例
        if(!(this instanceof Animal)){
            throw new Error('NOT NEW')
        }
        this.name = {name:'zf'}; //实例上的属性 
        this.age = 10;  // 实例上的属性 
    }
    Animal.prototype.say  =function(){  // 公共属性
        console.log('say')
    }
    let a1 = new Animal(); 
    let a2 = new Animal();

9、实现new方法

    创建一个空的对象;
    绑定this指向,执行构造函数;
    链接到原型;
    确保返回的是对象;
    new关键字的大致实现原理
    function mockNew(A){
        // 创建一个空的对象;
        let obj = {}
        // 绑定this指向,执行构造函数;
        let returnVal = A.call(obj);
        // 如果一个类返回了一个引用空间 那么实例将这个空间
        if((typeof returnVal === 'object' && returnVal !== null) || typeof returnVal === 'function'){
            return returnVal;
        }

        // 链接到原型;
        obj.__proto__ = A.prototype
        return obj
    }

10、实现继承

    原型继承:(继承方法)

        function Parent(){
            this.name = '名字'
            this.age = 18
        }

    Parent.protptype = sun(){
        console.log('唱歌')
    }
    function Son(){

    }
    // 子类的实例指向父类的实例,子类实例在调用方法的时候可以通过__proto__向上查找
    Son.prototype = new Son()

    借用call继承:(继承属性)

    function Son(){
        // 借用call来执行构造函数,传入this
        Parent.call(this);
    }

    组合继承:(原型继承和借用call继承一起使用)

11、实现promise
12、实现一个call函数
13、实现一个apply函数
14、实现一个bind函数
15、浅拷贝、深拷贝的实现

   浅拷贝:
   for···in只循环第一层:
       function simpleCopy(obj1) {
           var obj2 = Array.isArray(obj1) ? [] : {};
           for (let i in obj1) {
           obj2[i] = obj1[i];
           }
           return obj2;
       }
   Object.assign方法
   ... 扩展运算符
   深拷贝:
   1、通过JSON对象来实现深拷贝
   let newObj = { ...school, ...my };
   newObj = JSON.parse(JSON.stringify(newObj)); 
   缺点: 无法实现对对象中方法的深拷贝,无法拷贝属性值为undefined的、正则对象的...
   2、lodash函数库实现深拷贝
   let result = _.cloneDeep(test)
   3、通过jQuery的extend方法实现深拷贝
   var array = [1,2,3,4];
   var newArray = $.extend(true,[],array); // true为深拷贝,false为浅拷贝
   4、手动实现深拷贝
   const deepClone = (value ,hash = new WeakMap) => {
       if(value == null) return value; // 排除掉null 和undefine 的情况
       if(typeof value !== 'object') return value; // 这里包含了函数类型
       if(value instanceof RegExp) return new RegExp(value);
       if(value instanceof Date) return new Date(value);
       // .....
       let instance = new value.constructor; // 根据当前属性构造一个新的实例  attay||object

       if(hash.has(value)){        // 先去hash中查看一下是否存在过 ,如果存在就把以前拷贝的返回去 
           return hash.get(value); // 返回已经拷贝的结果
       }
       hash.set(value,instance);   // 没放过就放进去  
       
       // 拷贝的人可能是一个对象 或者是一个数组 (循环)  for in 
       for(let key in value){ // 一层
           if(value.hasOwnProperty(key)){ // 将hash 继续向下传递 保证这次拷贝能拿到以前拷贝的结果
               instance[key] = deepClone(value[key],hash); // 递归--产生的就是一个新的拷贝后的结果
           }// 过滤掉原型链上的属性
       }
       return instance
   };

16、实现一个防抖函数

    n秒内函数只会执行一次,如果n秒内事件再次被触发,则重新计算时间,事件触发时间会无限延长。
    // 防抖函数返回值为一个匿名函数,其中设置定时器来执行某函数
    function debounce(fn,delay){
        var timer = null
        return (...args) {
            clearTimeout(timer)
            timer = window.setTimeout(()=>{
                fn.apply(this,args)
            },delay)
        }
    }

17、实现一个节流函数

    高频事件在规定时间内只会执行一次,执行一次后,只有大于设定的执行周期后才会执行第二次
    function throttle(fn,delay){
        var timer = null
        let flag = true // 是否在单位时间内执行的判断依据
        return (...args) {
            if(!flag) return 
            clearTimeout(timer)
            flag = flase 
            timer = window.setTimeout(()=>{
                fn.apply(this,args)
                flag = true 
            },delay)
        }
    }

18、Instanceof的原理
19、柯里化函数的实现
20、Object.create的基本实现原理

   // Object.create()使用指定的原型对象及其属性去创建一个新的对象,并使新对象的__proto_指向这个原型对象  ,想对于Object.setPrototypeOf唯一有些不同的是他在中间加了一层; 
   function create(parentProto){
       function Fn(){}
       Fn.prototype = parentProto;
       let fn = new Fn();
       fn.constructor = Tiger
       return fn;
   }

21、实现一个基本的Event Bus

    // 组件通信,一个触发与监听的过程
    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))
    }
    }
    
    // 测试
    let emitter = new EventEmitter()
    // 监听事件
    emitter.addListener('ages', age => {
    console.log(age)
    })
    // 触发事件
    emitter.emit('ages', 18)  // 18

22、实现一个双向数据绑定(思路:劫持数据、监听input值变化并修改)

    <input type="text" id="input_1">
    <span id="span_1"></span>
    复制代码
    var obj = {};
    Object.defineProperty(obj, 'test', {
      set: (newVal)=>{
        document.getElementById('input_1').value = newVal;
        document.getElementById('span_1').innerHTML = newVal;
      }
    });
    document.addEventListener('keyup', (e)=>{
      obj.test = e.target.value;
    })

23、实现一个简单路由

   // hash路由
   class Route{
   constructor(){
       // 路由存储对象
       this.routes = {}
       // 当前hash
       this.currentHash = ''
       // 绑定this,避免监听时this指向改变
       this.freshRoute = this.freshRoute.bind(this)
       // 监听
       window.addEventListener('load', this.freshRoute, false)
       window.addEventListener('hashchange', this.freshRoute, false)
   }
   // 存储
   storeRoute (path, cb) {
       this.routes[path] = cb || function () {}
   }
   // 更新
   freshRoute () {
       this.currentHash = location.hash.slice(1) || '/'
       this.routes[this.currentHash]()
   }   
   }

24、实现懒加载
25、手写ajax

                window.onload = function(){
                var oBtn = document.getElementById("btn1");
                oBtn.onclick = function(){              
              //1、创建ajax对象
                var xhr = null;
                try{
                    xhr = new XMLHttpRequest();
                }catch(error){
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
               //2、等待数据响应
               //必须在调用open()方法之前指定onreadystatechange事件处理程序才能确保跨域浏览器兼容性                //问题
               //只要readyState属性的值有变化,就会触发readystatechange事件
                xhr.onreadystatechange = function(){
                    if(xhr.readyState == 4){
                        //判断本次下载的状态码都是多少 304表示请求的资源没有被修改
                        if((xhr.status >= 200 && xhr.status<300)||xhr.status ==304){
                            alert(xhr.responseText);
                        }else{
                            alert("Error:" + xhr.status);
                        }
                    }
                }
                //3、调用open
                
                xhr.open("get", "1.get.php?username=yyy&age=19&password=123abc", true);

                //4、调用send
                xhr.send();
            }
        }

26、如何理解mvvm原理?

    mvvm 双向绑定,采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

27、V-model实现原理?

    model只不过是一个语法糖而已,真正的实现靠的还是
    v-bind:绑定响应式数据
    触发oninput事件并传递数据   

28、Vue双向数据绑定原理(响应式数据的原理)

    核心点: Object.defineProperty
    默认 Vue 在初始化数据时,会给 data 中的属性使用 Object.defineProperty 重新定义所有属
    性(只会劫持已经存在的属性)多层对象通过递归实现劫持,当页面取到对应属性时,会进行依赖收集(收集当前组件的watcher) 如果属性发生变化会通知相关依赖进行更新操作。

29、描述一下vue从初始化页面–修改数据–更新页面ui的过程

    默认 Vue 在初始化数据时,会给 data 中的属性使用 Object.defineProperty 重新定义所有属
    性(只会劫持已经存在的属性)多层对象通过递归实现劫持,当页面取到对应属性时,会进行依赖收集(收集当前组件的watcher) 如果属性发生变化会通知相关依赖进行更新操作。

30、虚拟dom实现原理

    虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象状态变更时,记录新树和旧树的差异最后把差异更新到真正的dom中

31、Vue中key值的作用

    当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key 的作用主要是为了高效的更新虚拟DOM。

32、Vue的生命周期

    beforeCreated、created、beforeMounted、mounted、brforeUpdated、updated、beforeDestroy、destoryed

33、Vue的组件通信方式

    自定义属性\props 自定义事件\$emit 事件总线 $parent/$children $attrs/$listeners provide/inject依赖注入

34、Watch、methods和computed的区别

    watch 为了监听某个响应数据的变化。computed 是自动监听依赖值的变化,从而动态返回内容,主要目的是简化模板内的复杂运算。所以区别来源于用法,只是需要动态值,那就用 computed ;需要知道值的改变后执行业务逻辑,才用watch。
    methods是一个方法,它可以接受参数,而computed 不能,computed 是可以缓存的,methods 不会。computed 可以依赖其他 computed,甚至是其他组件的 data。

35、Vue中怎么重置data

    使用Object.assign(),vm.$data可以获取当前状态下的data,vm.$options.data(this)可以获取到组件初始化状态下的data。
    Object.assign(this.$data, this.$options.data(this))

36、组件中写name有什么作用

    组件递归是用、keep-alive有用、调试用

37、vue-router有哪些钩子函数

    beforeEach、afterEach、beforeEnter、beforeRouteEnter

38、Route和router的区别和router的区别

    Route:路由信息对象,path、query、name等路由信息
    router:路由实例对象,包含路由调转的方法

39、Vue的nextTick的原理是什么?

    nextTick的回调是dom更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,可以获取更新后的dom。原理:就是异步方法(promise/mutationObserver/setImmediate/setTimeout)
    补充回答:vue多次更新数据,最终会把watcher存放到一个自定义队列中,采用异步的方法(异步方法会在同步代码执行完毕在执行达到延迟更新)进行批量更新。内部调用的是nextTick实现延迟更新,用户自定义的nextTick回调会被延迟到更新完后调用,从而获取到更新后的dom

40、Vuex有哪些属性

    state、 mutation (改变state的状态不能书写异步代码)、action、Getter、modules

41、Vue首屏加载优化

    CDN优化、路由懒加载、组价按需引入

42、Vue-cli替我们做了哪些工作?
43、对前端性能优化有什么方案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值