面试题

66 阅读8分钟

模块化

首先编程中的模块化就是遵守固定的规则,把大文件拆分成独立并相互依赖的小模块
它的好处是:  提高代码的复用性,提高代码的可维护性,实现按需加载,防止变量的污染
模块化的规范就是对代码进行模块化的拆分和组合,需要遵守的规则
Es6模块  完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案
Es6的模块自动采用严格模式
CommonJs是通过module.exports导出  ,require导入 ,只能在node中使用,node是遵循commonjs规范
module.exports导出和exports导出的区别: 如果他俩都指向同一个对象,那么他们就会合并, 如果他俩分别指向不同的对象,那么就会遵循module.exports
export default 是默认导出 一个模块里只能有一个默认导出
import引入模块的时候默认导出的模块名可以随意写,按需导出是通过export导出,引入的模块名需要和导出的时候保持一致,并且加大括号

promise

promise是解决异步编程的一种方案,可以解决回调地狱的问题
(回调地狱:回调函数里面嵌套回调函数,就形成了回调地狱)
promise是一个构造函数,回调函数作为它的参数,该回调函数有两个参数分别是resolve、reject
异步请求成功执行resolve, 异步请求失败执行reject
promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
状态一旦改变不能在更改
promise有四种方法:
                then方法:  请求成功执行的方法
                catch方法: 请求失败执行的方法
                race方法:是一起处理多个promise请求 只要有一个成功就成功
                all方法:是一起处理多个promise请求, 全部成功就成功

闭包

内层函数访问外层函数的局部变量,叫做闭包
闭包的作用是:数据私有化,外部也可以访问函数内部的变量
闭包的缺点是:会引起内存的泄露
function out() {
        var i = 0
        function inner() {
          i++
          console.log(i)
        }
        return inner
      }
      
  var fun = out()
  fun()

防抖与节流

防抖 : 所谓防抖,就是指触发事件后在n秒后函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间.
节流 : 所谓节流,就是指连续触发事件但是在n秒钟只执行一次函数

this指向

全局作用域或者普通函数中this指向全局对象window,
定时器里面的this也是指向window
方法调用中谁调用this this指向谁
构造函数中this指向构造函数实例

call、apply、bind修改this指向

都可以改变函数内部的this指向
call和apply会调用函数,并且改变函数的内部的this指向;
call和apply参数的方式是不一样的,call参数是以列表的方式传递,apply参数是以数组的方式传递;
bind不会调用函数,可以改变函数内部this指向;

作用域

作用域(scope) 规定了变量能够被访问的 范围,离开这个范围变量不能被访问
作用域 分为 全局和局部作用域
局部作用域分为 函数作用域 和 块作用域
函数作用域:在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
1:函数内部被访问,外部无法直接访问
2:函数的参数也是函数内部的局部变量
3:函数执行完毕后,函数内部的变量实际就被清空了
​
块作用域(letconst):  {} 包裹的代码称为代码块,代码块内部用let声明的变量  外部将无法被访问
1:let声明的变量会产生块作用域  var 不会产生块作用域  
2:不同代码块之间的变量无法互相访问
3:推荐使用 letconst
  
全局作用域:全局作用域中声明的变量,任何地方都可以被访问
1.window对象动态添加的属性默认也是全局的(var) 不推荐
2.函数中未使用任何关键字声明的变量为全局变量  不推荐
3.尽可能少的声明全局变量,防止全局变量被污染

数组扩展方法

Array.prototype.sum=function(){
     var num=0
     for(var i=0;i<this.length;i++){
            num+=this[i]
        }
        return num
     }
​
    var arr=[1,2,3,4]
    console.log(arr.sum());  //10

箭头函数和普通函数的区别

1.普通函数存在着变量的提升,箭头函数没有
2.普通函数的this指向,谁调用指向谁,箭头函数是在哪定义就指向谁
3.普通函数可以当成构造函数,而箭头函数是不可以的
4.箭头函数没有arguments, 要接受所有的参数用...rest 

let,var,const的区别

var : 有变量的提升,没有块级作用域,可以重复声明
​
let : 没有变量提升,有块级作用域,不能重复声明,暂时性死区
​
const:没有变量提升,声明常量,一旦定义,值不能改变,有块级作用域

es6的新特性

1. 新增了块级作用域(let,const) 
2. 提供了定义类的语法(class)
3. 新增了一种基本数据类型(Symbol)
4. 新增了变量的解构赋值
5. 新增了箭头函数
6. 数组新增了一些 API,如 isArray / from / of 方法
7. 对象和数组新增了扩展运算符...
8. ES6 新增了模块化(import/export)

async和await

async是异步的意思,用来声明一个函数是异步的。
await是等待的意思,它是用来等待某个动作的完成。
await不是用来调用async函数的,async函数的调用与普通函数调用没有区别。
await只能写在async函数中,不能写在别处。

return的作用

1return 后面的代码不会被执行
2return 只能返回一个值
3、 函数如果有return,则返回的是return后面的值;如果函数没有return,则返回undefined

对象的创建方式

字面量创建 : var obj={属性:属性值};
​
new的方式创建 : var obj1=new Object();
​
构造函数创建  :
                 function fn(name,age){
                        this.name=name
                        this.age=age
                    }
                    var obj=new fn('zs',20)
                    console.log(obj);
                
类(class)创建 : 
                 class fn{
                    constructor(name,age){
                        this.name=name
                        this.age=age
                    }
                }
​
                var o=new fn('ls',30)
                console.log(o);

new的作用

1.在内存中创建了一个对象
2.对象内部的__proto__指向构造函数的prototype属性
3.构造函数中的this指向这个创建的新对象
4.执行构造函数内部的代码,给新创建的对象添加属性和方法
5.如果构造函数有返回值,该返回值是非空对象,则返回该对象,否则返回新创建的对象

原型链

实例对象有__proto__属性,指向构造函数的原型
构造函数的原型也是一个对象,也有__proto__属性,指向Object的原型对象
Object的原型对象也有__proto__属性指向null,形成链式结构

原型

每一个构造函数都有一个prototype属性 指向原型,原型也是一个对象
每个构造函数实例都可以访问原型上的成员

构造函数存在的问题

每创建一个对象,都会创建方法,生成一个新的内存空间,方法的功能是一样的,就造成了资源的浪费

深拷贝、浅拷贝

浅拷贝 : 拷贝的是外层 内层拷贝的是引用的地址,如果属性是基本类型,拷⻉的就是基本类型的值。如果属性是引⽤类         型,拷⻉的就是内存地址
深拷贝 : 深拷⻉开辟⼀个新的栈,两个对象属性完成相同,但是对应两个不同的地址,修改⼀个对象的属性,不会
        改变另⼀个对象的属性

递归

函数自己调用自己,必须有结束条件

vue数据更新

- 生成新的虚拟dom结构
- 和旧的虚拟dom结构对比
- 利用diff算法,找不同,只更新变化的部分(重绘/回流)到页面---也叫作**打补丁**
​
好处:
​
- 提高了更新DOM的性能(不用把页面全部删除重新渲染)
- 虚拟dom只包含必要的属性(没有真实dom上的百个属性)
​
总结:
​
虚拟DOM保存在内存中,只记录dom关键信息,配合**diff算法**提高DOM更新性能
 
在内存中比较差异,给真实DOM打补丁

重绘/回流

重绘:不影响布局,只是标签页面发生变化,重新绘制
​
回流(重排):当浏览器必须重新处理和绘制部分或全部页面时,回流就会发生
​
注意:回流必引发重绘,重绘不一定引发回流

diff算法如何比较新旧虚拟DOM

同级比较
根元素变化 : 删除重新创建整个dom树
根元素未变 : 属性改变 DOM复用,只更新属性

v-for的更新检测

子元素或者内容改变diff分两种情况比较
无key 就地更新
有key 按照key比较 
​
key值得要求是?
唯一不重复得字符串或数字
​
key应该怎么用
有id用id 没有id用index下标
​
key 的好处
配合虚拟dom提高更新的性能

vue是如何提高更新性能的

采用虚拟DOM+diff算法

虚拟DOM

什么是虚拟DOM : 本质上是一个js对象,保存DOM关键信息
​
虚拟DOM的好处 : 1.提高DOM更新的性能,不频繁操作真实DOM
              2.在内存中找到变化的部分,再更新真实DOM(打补丁)   

mixins

混入(mixins)分发vue组件中的可复用的功能,一个混入对象可以包含任意组件选项(data,computed,methods)当组件使用混入对象时,所有混入对象的选项将被混合进入该组件本身的选项
​
多个组件都引入该混入,一个组件中改动了mixin中的数据,另一个引入了mixin的组件不会受到影响
data数据冲突 : mixin中的data数据和组件中的data数据冲突时,组件中的data数据会覆盖mixin中的数据
生命周期函数 : 这中不算冲突,声明周期函数的名称是固定的,先执行混入里面的代码,在执行组件中的代码
方法名冲突 : 在组件中调用该方法,调用的是组件中的方法

混入(mixins)优点缺点

优点 : 1.提高代码复用性2.只需要修改一个地方就可以
缺点 : 1.命名冲突 2.滥用的话后期很难维护,排查问题不方便

插槽

实现组件内容的分发,通过slot标签可以接收到写在组件标签内的内容 

匿名插槽

组件内使用slot占位
​
使用组件时  <组件名></组件名>  中间插入的标签替换slot

具名插槽

在slot上定义name属性
使用组件时 ,在template上通过v-slot:name属性值 给对应的slot传递标签
v-slot:  可以简写为#

作用域插槽

子组件里的变量,在给插槽赋值时在父组件的环境中使用
​
子组件,在slot上绑定属性和子组件内的值
使用组件,传入自定义标签,用tamplate和v-slot:name值="自定义变量名"scope"
scope上绑定slot上所有的属性和值
​
​
使用场景 : 需求:封装表格组件,在表格组件内循环产生单元格

获取DOM

通过id或ref属性获取原生DOM
通过ref可以获取组件实例 
它们的区别是: document没有组件之分,范围广,this.$refs找的是整个组件 只能在当前组件里获取 

生命周期

所谓的vue生命周期,就是vue实例从创建到销毁的整个过程,我们称之为vue的生命周期, 生命周期分为四个阶段 八个钩子,四个阶段是创建前后,挂载前后,更新前后和销毁前后, 八个钩子是beforeCreate创建前, created创建后, beforeMount 挂载前,mounted 挂载后, beforeUpdata更新前,updated 更新后, beforeDestory 销毁前, destroyed 销毁后, 页面刚开加载的时候会触发创建前后个挂载前后的钩子函数, 而更新的钩子函数是当我们改变data中的数据时就会触发,当组件进行切换的时候会触发销毁前后的钩子函数.

$nextTick

vue是异步修改DOM的,但是有时候数据更改完要立马操作DOM,这个时候DOM是获取不到的,要使用$nextTick(()->{})  还可以使用await 取代回调函数   await this.$nextTick()  dom操作

组件name的使用

可以使用组件的name属性,来注册组件的名字
组件名不可以随便写,我们封装组件的时候,可以自己定义name属性组件名,让使用者有个统一的前缀风格

响应式总结

1.所谓的响应式就是拦截对象属性的访问和设置,插入一些我们想要做的事情,
2.js中实现响应式拦截的方法Object.DefinePropertyProxy(劫持对象整体+惰性处理)对象代理
3.vue中data里的数据,不管层级多深,不管最终会不会用到这个数据都会进行递归响应式处理,如非必要,尽量不要添加太多的冗(rong)余数据在data中

垃圾回收机制

垃圾回收机制就是为了防止内存泄漏,当我们使用过的函数以及变量,垃圾回收机制会自动帮我们回收并释放内存,缓解内存压力,内存泄漏就是我们不需要的内存还存在没有被释放就会导致垃圾回收机制无法回收.

生命周期函数和执行时机

初始化阶段
1 .beforeCreate
实例在创建出来之前会执行它,在执行它时,data和methods中的数据都还未初始化。
2.created
此时data和methods已经初始化完成,vue开始编译模板,在内存中生成一个编译好的模板字符串,把模板字符串渲染为内存中的dom
​
挂载阶段
3.beforeMount
模板在内存中已经编译好了,但是并没有渲染到页面中.
4.Mounted
此时内存中的模板已经真实的挂载到了页面中,用户可以看到渲染好的页面
​
更新阶段
5.beforeUpdata
data中的数据已经被更新了,但是页面中的data还未被替换
6.updated
页面和data中的数据已经同步了
​
销毁阶段
7.beforeDestory
组件Dom被移除beforeDestory函数被执行
8.destoryed
销毁后内存和页面都没有东西

set和map

set本身是一个构造函数,用来生成set数据结构
set函数可以接受一个数组new set([1,2,3])
new set()有自动去重的功能
​
set实例的属性和方法: 
1 .constructor: 构造函数,默认就是set函数
2 .size: 返回set实例的成员总数,
3 .add: 添加某个值,返回set结构本身
4 .delete: 删除某个值,返回一个布尔值,表示删除是否成功。
5 .has: 返回一个布尔值,表示该值是否为Set的成员。
6 .clear() : 清除所有成员,没有返回值。
​
​
Map 是 ES6 中新增的数据结构,Map 类似于对象,但普通对象的 key 必须是字符串或者数字,而 Map
的 key 可以是任何数据类型…
size:获取成员的数量
set():设置成员 key 和 value
get():获取成员属性值
has():判断成员是否存在
delete():删除成员
clear():清空所有
​
Set和Map 实例的遍历方法有
.keys():返回键名的遍历器
.values():返回键值的遍历器
.entries():返回键值对的遍历器
.forEach():使用回调函数遍历每个成员

set和map得区别

set类似于数组 他的成员是唯一得 没有键值对 
map有键值对 它类似于对象 是键值对得集合

继承

继承就是子类继承父类的属性和方法
有四种继承 分别是  call继承 原型继承 混合继承和class继承
​
call继承就是在子类中调用call方法修改this的指向,让this指向父类的属性和方法,这样就可以在子类中访问父类的属性和方法.但call继承有个缺点就是不能继承原型上的属性和方法
​
原型继承就是将父类实例放在子类的原型上,这样就可以通过子类实例来获取继承的属性和方法,原型继承的缺点是原型是所有实例的公共祖先,原型改变 实例全部更改
​
混合继承就是call继承+原型继承 混合继承的缺点就是父类被调用了两次 浪费资源
​
class 继承的关键字是extends 子类可以通过extends来继承父类的属性和方法,子类使用super关键字 访问父类的属性方法,super必须在子类this之前调用,添加方法的话可以在constructor下面直接添加.