JavaScript技术

前端知识点

2021-06-11  本文已影响0人  林思念

1、从输入url到浏览器呈现页面中间经历了什么?

相关链接:
输入url至呈现页面过程
三次握手
四次挥手

2、js基本数据类型

Number、String、Boolean、undefined、object、symbol、bigInt、Null
js基本数据类型

3、typeof返回类型

number、 string、boolean、 undefined、object、symbol、bigint、function
typeof NaN == 'number'

4、最大安全值和最大值

Number.MAX_SAFE_INTEGER 进行运算不会丢失精度
Numebr.MAX_VALUE js能表示的最大值

5、this的绑定规则

显示绑定 call、apply、bind
隐式绑定 obj对象的属性为函数时,内部指向obj
默认绑定 函数内部的this默认指向window
new绑定
this的绑定规则

6、call、bind、apply

改变this指向的方法
call参数为列表、apply为数组、立即执行
bind为等待状态

Math.max.call(null, 1,3,5,2,4)  
Math.max.apply(null, [1,3,5,2,4])

call、apply、bind

7、promise

创建对象

let p1 = function(){
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve('成功1!')
    },1000)
  })
}
let p2 = function(){
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve('成功2!')
    },1000)
  })
}
let p3 = function(){
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve('成功3!')
    },1000)
  })
}

链式操作

p1().then((data)=>{
  console.log(data)
  return p2()
}).then((data)=>{
  console.log(data)
  return p3()
}).then((data)=>{
  console.log(data)
})

race函数

Promise.race([p1(),p2(),p3()]).then((data)=>{
  console.log(data)  //[p1,p2,p3]
}).catch((err)=>{
  console.log(err)
})

all函数

Promise.all([p1(),p2(),p3()]).then((data)=>{
  console.log(data)    //p1
})

promise原理

8、观察者模式

class Event{
  constructor(){
    this.list = []
  }
  subscrible(type, handler){
    if(this.list[type]){
      this.list[type].push(handler)
    }else{
      this.list[type] = [handler]
    } 
  }
  unSubscrible(type, handler){
    if(this.list[type]){
      let index = this.list[type].indexOf(handler)
      this.list[type].splice(index, 1)
    }
  }
  publish(type){
    if(this.list[type]){
      this.list[type].forEach((item)=>{
        item()
      })
    }
  } 
}
let event = new Event()
function load(index){
  console.log('load')
}
function click1(index){
  console.log('click')
}
function click2(index){
  console.log('click')
}
event.subscrible('load', load)
event.subscrible('click', click1)
event.subscrible('click', click2)
event.publish('load')
event.publish('click')

9、发布订阅模式

class Dog{
  call(){
    console.log('汪汪')
  }  
}
class  Pubsub{
  constructor(){
    this.list = []
  }
  subscrible(call){
    this.list.push(call)
  }
  publish(){
    this.list.forEach((item)=>{
      item()
    })
  }
}
let pubsub = new Pubsub()
class  Thief{
  constructor(){
    this.list = []
  } 
  action(){
    pubsub .publish()
  }
}
let thief = new Thief()
let dog1 = new Dog()
let dog2 = new Dog()
pubsub .subscrible(dog1.call)
pubsub .subscrible(dog2.call)
thief.action()

链接

10、eventLoop (堆、栈、队列)

1、先执行主线程(包含执行栈和堆)
2、遇到宏队列(macrotask)放到宏队列(macrotask)
3、遇到微队列(microtask)放到微队列(microtask)
4、主线程执行完毕(执行栈Stack被清空) (栈内存出栈后自动销毁)
5、执行微队列(microtask),微队列(microtask)执行完毕
6、执行一次宏队列(macrotask)中的一个任务,执行完毕 (宏任务有可能产生微任务)
7、执行微队列(microtask),执行完毕
8、依次循环...

11、宏任务和微任务

macroTask

setTimeout、setInterval 、setImmediate、I/O、UI Rendering

microTask

Process.nextTick(Node独有)、Promise、Object.observe(废弃)、MutationObserver

执行顺序

console.log(1);

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});

new Promise((resolve, reject) => {
  console.log(4)
  resolve(5)
}).then((data) => {
  console.log(data);
})

setTimeout(() => {
  console.log(6);
})

console.log(7);

带你彻底弄懂eventLoop
一次弄懂eventLoop

12、闭包

  1. 模拟私有变量,在函数外部间接访问函数内部的变量
  2. 私有变量一直保存在内存中,不被销毁
  3. 内部变量不会污染全局命名空间
  4. 闭包在处理速度和内存消耗方面对脚本性能具有负面影响
let count = (function(){
  let sum = 10
  function change(x){
    return sum += x
  }
  return change
})();
console.log(count(10)) //20

13、js 事件冒泡和事件捕获

事件传递有两种方式:冒泡与捕获。
事件传递顺序:捕获阶段=>目标阶段=>冒泡阶段
匿名函数事件绑定无法删除

//事件绑定
dom.addEventListener('click', fn, false)  // 默认为 false(冒泡) true(捕获)
dom.removeEventListener('click', fn, false)
dom.attachEvent('onclick', fn)        //IE
dom.detachEvent('onclick', fn)      //IE
//阻止冒泡和捕获
event.cancelBubble=true;
event.stopPropagation()
//阻止默认事件传播
event.preventDefault()
window.event.returnValue = false;

14、addEventListener 和 attachEvent 区别

  1. attachEvent 为IE下的事件绑定函数,不支持捕获
  2. attachEvent 指向 window, 有内存泄漏问题,全局作用域中运行 addEventListener 指向obj,依附于元素的作用域中运行
  3. 都可以绑定多个事件处理函数,同一个事件处理函数可以绑定多次

15、事件委托

利用冒泡的原理,把事件加到父级上,触发执行效果。
1、提高性能,不需要为每个子元素添加事件处理函数
2、新添加的元素也可以通过之前的事件进行触发
3、事件基于冒泡,对于不冒泡的事件不触发

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div>
            <ul>
                <li class="li1" data-url="1111">11</li>
                <li class="li2" data-url="2222">22</li>
                <li class="li3" data-url="3333">33</li>
                <li class="li4" data-url="4444">44</li>
            </ul>
        </div>
    </body>
    <script type="text/javascript"> 
        document.getElementsByTagName('ul')[0].addEventListener('click', function(e){
            console.log(e) 
            if(e.stopPropagation){
                e.stopPropagation()
            }else{
                e.cancelBubble = true
            }
            console.log(e.toElement.dataset.url)  ///1111 2222
        }, false)
    </script> 
</html>

16、(本地缓存)请描述一下 cookies,sessionStorage 和 localStorage 的区别?

Web Storage有两种形式:

LocalStorage(本地存储)和sessionStorage(会话存储)。

这两种方式都允许开发者使用js设置的键值对进行操作,在在重新加载不同的页面的时候读出它们。这一点与cookie类似。

  1. 与cookie不同的是:Web Storage数据完全存储在客户端,不需要通过浏览器的请求将数据传给服务器,因此相比cookie来说能够存储更多的数据,大概5M左右。
  2. LocalStorage和sessionStorage功能上是一样的,但是存储持久时间不一样。LocalStorage:浏览器关闭了数据仍然可以保存下来,并可用于所有同源(相同的域名、协议和端口)窗口(或标签页);sessionStorage:数据存储在窗口对象中,窗口关闭后对应的窗口对象消失,存储的数据也会丢失。
    注意:sessionStorage 都可以用localStorage 来代替,但需要记住的是,在窗口或者标签页关闭时,使用sessionStorage 存储的数据会丢失。
  3. 使用 local storage和session storage主要通过在js中操作这两个对象来实现,分别为window.localStorage和window.sessionStorage. 这两个对象均是Storage类的两个实例,自然也具有Storage类的属性和方法
  4. localstorage是浏览器多个标签共用的存储空间,所以可以用来实现多标签之间的通信(ps:session是会话级的存储空间,每个标签页都是单独的)。

17、简述一下你对HTML语义化的理解。

  1. HTML语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;

  2. 即使在没有样式CSS的情况下也能以一种文档格式显示,并且是容易阅读的;

  3. 搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,有利于SEO;

  4. 使阅读源代码的人更容易将网站分块,便于阅读、维护和理解。

18、Html语义化标签

<ul> <li> <title> <mark> <h1> <small> <strong>

新增 <header> <nav> <aside> <footer> <section> <article> <main>

19、你能描述一下渐进增强和优雅降级之间的不同吗?

  1. 渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

(一开始保证最基本的功能,再改进和追加功能)

  1. 优雅降级 graceful degradation:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

(一开始就构建完整的功能,再针对低版本浏览器进行兼容。)

20、前端需要注意哪些 SEO

  1. 使用title、description、keywords属性及合理的描述

  2. 语义化标签让搜索引擎更好的理解文章结构

  3. img标签添加alt属性

  4. 不使用iframe标签

  5. 服务器渲染,页面内容尽量不要使用js输出

  6. 页面内容尽可能靠前,搜索引擎抓取有长度限制,保证抓取到内容

21、html5 有哪些新特性

  1. 拖拽释放(Drag and drop) API

  2. 语义化更好的内容标签(header,nav,footer,aside,article,section)

  3. 音频、视频 API(audio,video)

  4. 画布(Canvas) API

  5. 地理(Geolocation) API

  6. 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;

  7. sessionStorage 的数据在浏览器关闭后自动删除

  8. 表单控件,calendar、date、time、email、url、search

  9. 新的技术 webworker, websocket, Geolocation

22、link和@import的区别:

  1. 从属关系区别
    @import是 CSS 提供的语法规则,只有导入样式表的作用;link是HTML提供的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel 连接属性等。
  2. 加载顺序区别
    加载页面时,link标签引入的 CSS 被同时加载;@import引入的 CSS 将在页面加载完毕后被加载。
  3. DOM可控性区别
    可以通过 JS 操作 DOM ,插入link标签来改变样式;由于 DOM 方法是基于文档的,无法使用@import的方式插入样式。

23、防抖函数

//非立即执行版本
function debounce(func, wait) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, wait);
    }
}
//立即执行版本
function debounce(func,wait) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);

        let callNow = !timeout;
        timeout = setTimeout(() => {
            timeout = null;
        }, wait)

        if (callNow) func.apply(context, args)
    }
}

24、节流函数

//时间戳   立即执行版本
function throttle(func, wait) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}
//定时器 非立即执行版本
function throttle(func, wait) {
    let timeout;
    return function() {
        let context = this;
        let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context, args)
            }, wait)
        }

    }
}

25、常见状态码

100 Continue 继续
200 OK      正常返回信息
201 Created  请求成功并且服务器创建了新的资源
202 Accepted 服务器已接受请求,但尚未处理
301 Moved Permanently 请求资源被永久移动到新位置
302 Found    请求资源临时性重定向
303 See Other 临时性重定向,且总是使用GET请求新的url
304 Not Modified 自从上次请求后,请求的网页未修改过
307 Temporary Redirect  临时重定向。与302类似。使用GET请求重定向
400 Bad Request  服务器无法理解的请求格式,客户端不应当尝试使用相同的内容发起请求
401 Unauthorized 请求未授权
403 Forbidden    禁止访问
404 Not Found    找不到与URL相匹配的资源
500 Internal Server Error  服务器端错误
502 Bad Gateway
503 Service Unavailable   服务器暂时无法处理请求

26、let var const 区别

  1. var可以声明提前、let不能
  2. var可以重复定义、let不能
  3. let 是块级作用域 和 var函数作用域
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function() {
    console.log(i);
  };
}
console.log(a[1]())    //1
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function() {
    console.log(i);
  };
}
console.log(a[1]())    //10
//var 声明变量是全局的只有一个i,一直在发生改变,所有最终数组函数打印的都是同一个数 
  1. 特别的for循环中,循环体和内部 i 不是同一个作用域
for(let i = 0; i<3;i++){
  let i =1
  console.log(i)   //  1  1  1
}
  1. const 定义的变量并非全部不能修改
    定义的变量包含基本变量和引用变量,引用变量不能修改地址 ,但是可以修改内容
  2. let在声明 变量之前 不能使用,成为暂时性死区
var tmp = 123;
if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
} 
  1. typeof 也不是 百分百安全的操作
typeof x; // ReferenceError
let x;
console.log(typeof x) //undefiend

27、堆内存和栈内存

  1. 变量分为基本类型和引用类型
  2. 基本类型包括 Undefined、Null、Boolean、Number 和String, 引用类型包括Object
  3. 基本类型是存放在栈内存中,引用类型是存在于堆内存中
  4. 基本类型在当前执行环境结束时销毁(出栈即销毁),而引用类型不会随执行环境结束而销毁,只有当所有引用它的变量不存在时这个对象才被垃圾回收机制回收(V8 垃圾回收)。
  5. 对于引用数据类型是,将变量保存在栈内存中,指针指向堆内存地址,多以在使用的时候是先在栈内存中找到,然后寻找相应的堆地址
  6. 变量全局在整个应用的生命周期中从一打开就处于整个执行栈的最底层,所以难以释放对于垃圾回收的不利,也是少用全局变量的重要原因。
  7. 栈内存和栈数据结构 与 堆内存和堆数据结构是两种东西,但是机制类似

28、深拷贝、浅拷贝

如果B复制了A,当我们改变A的值时,如果B也发生了变化那就说明是浅拷贝,反之就是深拷贝。

var a = 1;
var b = a;
b = 2;
console.log(a); // a = 1
var obj1 = { a: 1, b: 2 };
var obj2 = obj1;
obj2.a = 3;
console.log(obj1.a); // obj1.a = 3
// 变量保存引用类型,只能保存到一个引用地址,变量与堆内存不直接绑定!
// obj1 和 obj2 都保存了同一个引用地址,指向同一个堆内存,所以堆内存改变会一起改变 
var obj1 = { a: 1 };
var obj2 = obj1;
obj1 = null;
console.log(obj2); // obj2 = { a: 1 }
// 虽然前面 obj1、obj2 都保存了堆内存地址,但后面只有 obj1 把保存的值改成null,所以并不影响 obj2 保存的引用地址指向堆内存。 
//序列化和反序列化
function deepClone(obj){
  let _obj = JSON.stringify(obj),
      objClone = JSON.parse(_obj);
  return objClone
}
let a=[0, 1, [2, 3], 4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a);   // [1, 1, [1, 3], 4]
console.log(b);   // [0, 1, [2, 3], 4]
//迭代递归实现深拷贝
function deepClone(oldobj){
  let newObj = oldObj instanceof Array ? [] : {};
  for(let key in oldobj){
    if(typeof oldobj[key] === 'object'){
      newObj[key] = deepClone(oldObj[key])
    }else{
      newObj[key] = oldObj[key]
    } 
  }
  return newObj
}
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);

29、垃圾回收

JS的垃圾回收机制是为了防止内存泄漏,就是间歇的不定期的寻找到不再使用的变量,并释放掉他们所指向的内存。

  1. 基础类型基本用完就回收
  2. 引用类型 当所有的引用对象都被回收,才被销毁

30、类型判断

  1. typeof
typeof 1 // "number" 
typeof 'a'  // "string"
typeof true  // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof 42n // "bigint"

可以识别基本数据类型,不能识别引用类型

  1. instanceof
    不能识别基本数据类型,但是可以识别引用数据类型object(除了null)、array、function
console.log(bool instanceof Boolean);// false
console.log(num instanceof Number);// false
console.log(str instanceof String);// false
console.log(und instanceof Object);// false
console.log(nul instanceof Object);// false
console.log(arr instanceof Array);// true
console.log(obj instanceof Object);// true
console.log(fun instanceof Function);// true
console.log(s1 instanceof Symbol);// false
  1. constructor
    null、undefined没有construstor方法,因此constructor不能判断undefined和null。
console.log(bool.constructor === Boolean);// true
console.log(num.constructor === Number);// true
console.log(str.constructor === String);// true
console.log(arr.constructor === Array);// true
console.log(obj.constructor === Object);// true
console.log(fun.constructor === Function);// true
console.log(s1.constructor === Symbol);//true
  1. object.property.toString.call
Object.prototype.toString.call(999) // "[object Number]"
Object.prototype.toString.call('') // "[object String]"
Object.prototype.toString.call(Symbol()) // "[object Symbol]"
Object.prototype.toString.call(42n) // "[object BigInt]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(true) // "[object Boolean]
Object.prototype.toString.call({a:1}) // "[object Object]"
Object.prototype.toString.call([1,2]) // "[object Array]"
Object.prototype.toString.call(new Date) // "[object Date]"
Object.prototype.toString.call(function(){}) // "[object Function]"

31、hasOwnProperty 和 in

hasOwnProperty 只能获取对象自身属性、in可以获得原型链上的属性

function Fn() {
  this.age = 'wm'
}
Fn.prototype.sex = '女'
var obj = new Fn()
//  实例化对象会继承构造函数里的属性和方法
console.log(obj.hasOwnProperty('age')) // true
console.log(Fn.prototype.hasOwnProperty('age')) //false
console.log(obj.hasOwnProperty('sex')) // false 

32、前端有哪些安全问题以及解决办法

  1. XSS(Cross Site Scripting, 跨站脚本攻击)
    XSS攻击全称跨站脚本攻击(Cross-Site Scripting),为区别CSS,在安全领域叫做XSS。攻击者通过在目标网站上注入恶意脚本并运行,获取用户的敏感信息如 Cookie、SessionID等,影响网站与用户数据安全。
    例如:留言板提交页面跳转代码,恶意劫持流量
    办法:
  2. CSRF(Cross-site request forgery, 跨站请求伪造)
    CSRF(Cross-site request forgery)跨站请求伪造,攻击者盗用受害人身份,以受害人的名义发送恶意请求。 其本质是重要操作的所有参数都是可以被攻击者猜测到的,攻击者只有预测出URL的所有参数与参数值,才能成功地构造一个伪造的请求;反之,攻击者将无法攻击成功。
    例如:登录网站以后,同时登陆了第三方危险网站,token信息被窃取,并伪造请求。
    设置同源检测
  3. SQL注入
    通过Web应用接口注入SQL语法,破坏原有SQL结构,达到攻击行为。 如果网站存在注入漏洞,相当于将数据库直接暴露在攻击者面前 根据注入位置及方式不同分分为POST注入、GET注入、cookie注入、盲注、延时注入、搜索注入、base64注入等
    对数据进行校验,敏感字符进行转义
  4. 暴力破解
    增加密码复杂度
    限制尝试次数

33、弹性盒模型

  1. display:flex;设置弹性布局
  2. flex-direction属性 决定主轴的方向(即项目的排列方向)
  3. flex-wrap 属性,定义子元素是否换行显示
  4. flex-flow是属性flex-direction和flex-wrap的简写方式,默认值为row nowrap;
  5. justify-content属性定义了项目在主轴上的对齐方式。
  6. align-items属性定义项目在交叉轴(侧轴)上如何对齐。
  7. align-content属性定义了多根轴线的对齐方式。对于单行子元素,该属性不起作用。
  8. order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
  9. flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
  10. flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
  11. align-self属性定义子项,允许和其他子项以不同的方式进行排列

34、面试问题

  1. 项目的背景是什么;
  2. 当前项目的目的是什么;
  3. 在开发过程中,你的角色是什么;
  4. 在开发过程中有遇到过什么样的难题;
  5. 遇到这些问题,你都是如何进行解决的;
  6. 项目完成之后,取得了哪些成果;

35、vue2.0响应式原理

原理
vue.js 采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 getter进行依赖收集和setter进行派发更新,在数据变动时发布消息给订阅者,触发响应的监听回调
缺点

  1. 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
  2. Object.defineProperty 无法检测到对象属性的添加和删除 。
  3. 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
  4. 深度监听需要一次性递归,对性能影响比较大。

36、vue3.0响应式原理

改进

  1. 基于Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除。
  2. 不需要一次性遍历data的属性,可以显著提高性能。
  3. 因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11 。
  4. Vue3.0基于Proxy来做数据的劫持代理,可以原生支持到数组的响应式,不需要重写数组的原型,还可以直接支持新增和删除属性, 比Vue2.x的Object.defineProperty更加的清晰明了。
let data = {
    name: "zhangsan",
    age: 20
  };
const handler = {
  get: function (target, key, receive) {
    // 只处理本身(非原型)的属性
    const ownKeys = Reflect.ownKeys(target)
    if (ownKeys.includes(key)) {
      console.log('get', key) // 监听
    }
    const result = Reflect.get(target, key, receive)
    return result
  },
  set: function (target, key, val, reveive) {
    // 重复的数据,不处理
    const oldVal = target[key]
    if (val == oldVal) {
      return true
    }
    const result = Reflect.set(target, key, val, reveive)
    console.log(result)
    return result
  },
  // 删除属性
  deleteProperty: function (target, key) {
    const result = Reflect.deleteProperty(target, key)
    return result
  } 
}; 
data = new Proxy(data, handler); 
data.name = 'lisi'
console.log(data.name) 

37、MVC和MVVM

  1. mvc 中 Controller演变成 mvvm 中的 viewModel
  2. mvvm 通过数据来驱动视图层的显示而不是节点操作。
  3. mvc中Model和View是可以直接打交道的,造成Model层和View层之间的耦合度高。而mvvm中Model和View不直接交互,而是通过中间桥梁ViewModel来同步
  4. mvvm主要解决了:mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。

38、vue特点

  1. 入门难度低
  2. 虚拟dom
  3. 双向绑定
  4. 插件化
  5. 轻量级
  6. 数据结构分离
  7. 数据驱动
  8. vue2.0不支持IE8及以下 和 vue3.0不支持IE11及以下

39、vue3.0有哪些改进

  1. 采用了 Proxy,抛弃了object. defineProperty
    Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对 data 中的属性做了遍历 + 递归,为每个属性设置了 getter、setter。
    这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因,在Vue中使用下标的方式直接修改属性的值或者添加一个预先不存在的对象属性是无法做到setter监听的,这是defineProperty的局限性。对于数组添加对七种数组操作的方法进行了监听,分别是: push、pop、shift、unshift、sort、reverse、splice函数
    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
  2. 虚拟dom新增了静态节点和动态节点
    Vue2.x 中的虚拟dom是进行全量的对比。
    Vue3.0 中新增了静态标记(PatchFlag):在与上次虚拟结点进行对比的时候,值对比带有patch flag的节点,并且可以通过flag 的信息得知当前节点要对比的具体内容化。
  3. 静态提升
    Vue2.x : 无论元素是否参与更新,每次都会重新创建。
    Vue3.0 : 对不参与更新的元素,只会被创建一次,之后会在每次渲染时候被不停的复用。

40、生命周期函数

  1. beforeCreate:是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
  2. created:在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
  3. beforeMount:发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
  4. mounted:在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
  5. beforeUpdate:发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
  6. updated:发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
  7. beforeDestroy:发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
  8. destroyed:发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
  9. activated: 包含在keep-alive中的组件在进行调用的时候,会被存放在缓存队列中,再次调用的时候通过activated函数进行更新。
  10. deactivated: 与activated组件相反,在调用其他tab组件时当前会调用当前组件的deactivated函数
  11. errorCaptured:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

41、为什么组件的data必须是一个函数

一个组件可能在很多地方使用,也就是会创建很多个实例,如果data是一个对象的话,对象是引用类型,一个实例修改了data会影响到其他实例,所以data必须使用函数,为每一个实例创建一个属于自己的data,使其同一个组件的不同实例互不影响。

42、组件之间是怎么通信的

  1. 父子组件通信
    父组件 -> 子组件:prop
    子组件 -> 父组件:on / emit
  2. 兄弟组件通信
    Event Bus:每一个Vue实例都是一个Event Bus,都支持on/emit,可以为兄弟组件的实例之间new一个Vue实例,作为Event Bus进行通信。
  3. Vuex:将状态和方法提取到Vuex,完成共享

43、说一下什么是Virtual DOM

Virtual DOM 是 DOM 节点在 JavaScript 中的一种抽象数据结构,之所以需要虚拟DOM,是因为浏览器中操作DOM的代价比较昂贵,频繁操作DOM会产生性能问题。虚拟DOM的作用是在每一次响应式数据发生变化引起页面重渲染时,Vue对比更新前后的虚拟DOM,匹配找出尽可能少的需要更新的真实DOM,从而达到提升性能的目的。

44、diff算法介绍

  1. 在新老虚拟DOM对比时
  2. 首先,对比节点本身,判断是否为同一节点,如果不为相同节点,则删除该节点重新创建节点进行替换
  3. 如果为相同节点,进行patchVnode,判断如何对该节点的子节点进行处理,先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)
  4. 比较如果都有子节点,则进行updateChildren,判断如何对这些新老节点的子节点进行操作(diff核心)。
  5. 匹配时,找到相同的子节点,递归比较子节点
  6. 在diff中,只对同层的子节点进行比较,放弃跨级的节点比较,使得时间复杂从O(n^3)降低值O(n),也就是说,只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。

45、computed 和 watch 有什么区别及运用场景?

  1. computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
  2. watch 侦听器 : 更多的是「观察」的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

46、for循环中key的作用是什么?

在对节点进行diff的过程中,判断是否为相同节点的一个很重要的条件是key是否相等,如果是相同节点,则会尽可能的复用原有的DOM节点。所以key属性是提供给框架在diff的时候使用的,而非开发者。

47、vue渲染过程

  1. parse 函数解析 template,生成语法树
  2. optimize 函数优化静态节点,diff 算法会直接跳过静态节点,从而减少比较的过程
  3. generate 函数生成 render 函数字符串
  4. 调用 new Watcher 函数,监听数据的变化,Render 函数执行生成 vnode 对象
  5. 调用 patch 方法,对比新旧 vnode 对象,通过 DOM diff 算法,添加、修改、删除真正的 DOM 元素

48、聊聊 keep-alive 的实现原理和缓存策略

keep-alive包含的组件在使用的时候,会根据组件 ID 和 tag 生成缓存 Key,并在缓存对象中,再次使用的时候会直接取出并更新key值,
在 this.cache 对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否超过 max 的设置值,超过则shift第一个
LRU 缓存淘汰算法
LRU(Least recently used)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

49、SSR有了解吗?原理是什么?

在客户端请求服务器的时候,服务器到数据库中获取到相关的数据,并且在服务器内部将Vue组件渲染成HTML,并且将数据、HTML一并返回给客户端,这个在服务器将数据和组件转化为HTML的过程,叫做服务端渲染SSR。

50、使用SSR的好处:

  1. 有利于SEO:其实就是有利于爬虫来爬你的页面,因为部分页面爬虫是不支持执行JavaScript的,这种不支持执行JavaScript的爬虫抓取到的非SSR的页面会是一个空的HTML页面,而有了SSR以后,这些爬虫就可以获取到完整的HTML结构的数据,进而收录到搜索引擎中。
  2. 白屏时间更短:相对于客户端渲染,服务端渲染在浏览器请求URL之后已经得到了一个带有数据的HTML文本,浏览器只需要解析HTML,直接构建DOM树就可以。而客户端渲染,需要先得到一个空的HTML页面,这个时候页面已经进入白屏,之后还需要经过加载并执行 JavaScript、请求后端服务器获取数据、JavaScript 渲染页面几个过程才可以看到最后的页面。特别是在复杂应用中,由于需要加载 JavaScript 脚本,越是复杂的应用,需要加载的 JavaScript 脚本就越多、越大,这会导致应用的首屏加载时间非常长,进而降低了体验感。

51、盒子模型和怪异盒子

box-sizing:border-box; 怪异盒模型
box-sizing:content-box; 标准盒模型

52、清除浮动

  1. 父级元素设置overflow:hidden;
    元素溢出无法显示
  2. 在最后一个浮动标签后,新加一个标签,给其设置clear:both;
    添加无意义标签,语意化差
  3. 使用after伪元素清除浮动(推荐使用)
.clearfix:after{
  /* 设置添加子元素的内容是空 */ 
  content: '';
  /* 设置添加子元素为块级元素 */ 
  display: block;
  /* 设置添加的子元素的高度0 */ 
  height: 0;
  /* 设置添加子元素看不见 */ 
  visibility: hidden;
  /* 设置clear:both */ 
  clear: both;
}

53、es6

  1. promise
  2. acync、await
  3. 箭头函数
    没有this指向,没有arguments参数,可以使用rest(...)剩余运算符,不能作为构造函数,没有原型属性,不能作为Generator函数,不能使用yield关键字
  4. 解构赋值
let [a, b, c] = [1, 2, 3];
let [a, ...b] = [1, 2, 3];
let { type, name, value = true } = node;
  1. var、let、const
  2. map和set
    map和set是es6新定义的一种数据结构,都可以用forEach和for...of遍历
    map类似于字典的一种数据结构,任何类型的数据都可以为键
var map = new Map([['XiaoMing', 99], ['XiaoHong', 66]]);
map.set("XiaoHong",66);
map.get("XiaoHong");
map.has("XiaoHong");
map.delete("XiaoHong")

set集合无重复

let set = new Set([1,2,3,4,5,4,3,2,1])
console.lg(set)  //  Set(5) {1, 2, 3, 4, 5}
set.add(6)  
set.clear()  
set.delete(2)  
set.has(2)
  1. symbol

54、移动端适配方案

  1. viewport(scale=1/dpr)
  2. rem
  3. flex
  4. vm/vh
    1、vw:1vw等于视口宽度的1%。
    2、vh:1vh等于视口高度的1%。

55、history和hash实现原理

  1. hash路由在地址栏URL上有#
  2. hash路由支持低版本的浏览器,而history路由是HTML5新增的API
  3. hash的特点在于它虽然出现在了URL中,但是不包括在http请求中,所以对于后端是没有一点影响的,所以改变hash不会重新加载页面,所以这也是单页面应用的必备。
  4. history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()方法,它们提供了对历史记录进行修改的功能
  5. history的这种模式需要后台配置支持。比如:当我们进行项目的主页的时候,一切正常,可以访问,但是当我们刷新页面或者直接访问路径的时候就会返回404,那是因为在history模式下,只是动态的通过js操作window.history来改变浏览器地址栏里的路径,并没有发起http请求,但是当我直接在浏览器里输入这个地址的时候,就一定要对服务器发起http请求,但是这个目标在服务器上又不存在,所以会返回404
  6. 解决报错需要nginx配置
location / { 
  try_files $uri $uri/ /index.html; 
}

56、new一个函数,都会发生什么?

  1. 创建一个新对象;
  2. 将构造函数的作用域赋给新对象;
  3. 执行构造函数中的代码
  4. 返回新对象

57、实现new方法

function _new(fn, ...arg) { 
  const obj = Object.create(fn.prototype); 
  const result = fn.apply(obj, arg); 
  return result instanceof Object ? result : obj;
}
function Student(name){
  this.name = name
}
let obj = new Student('sgr')
console.log(obj)
let student = _new(Student, 'sgr')
console.log(student)

58、vue双向绑定

let obj = {}
let val = ''
Object.defineProperty(obj, 'name', {
  get(){
    console.log('get')
    return val
  },
  set(newValue){
    console.log('set')
    val = newValue
    document.getElementById('input').value=val
    return val
  }
})
obj.name
obj.name = 22 
function change(){
  val = document.getElementById('input').value
  console.log(val)
} 

59、同源

相同协议、域名、端口

60、跨域

  1. JSONP跨域
    客户端和服务端约定好一个函数名,当我请求文件的时候,服务端返回一段 JavaScript。这段 JavaScript 调用了我们约定好的函数,并且将数据当做参数传入
function dosomething(response){
    console.log('The responsed data is: '+response.data);
    //处理获得的Json数据
}
<script src="http://www.example.com/data/?callback=dosomething"></script>
  1. window.name+iframe来进行跨域
    window的name属性特征:name 值在不同的页面(甚至不同域名)加载后依旧存在,所以可以通过iframe标签将数据页面加载到当前页面获取,数据页面的window.name
<script>
  function getData(){
    //iframe载入data.html页面会执行此函数
    var ifr = document.getElementById("iframe");
    ifr.onload = function(){
      //这个时候iframe和a.html已经处于同一源,可以互相访问
      var data = ifr.contentWindow.name;
      //获取iframe中的window.name,也就是data.html中给它设置的数据
      alert(data);
    }
      ifr.src = 'b.html';//这里的b.html为随便一个页面,只要与a.html同源就行,目的是让a.html能够访问到iframe中的东西,否则访问不到
  }
</script>
<iframe id = "iframe" src = "cnblogs.com/data.html" style = "display:none" onload = "getData()"></iframe>
  1. 通过document.domain+iframe来跨子域
    不设置主域是访问不到的
// a.html
<iframe src = "http://script.a.com/dir/b.html" id="iframe" onload = "loLoad()"></iframe>
<script>
  document.domain = "a.com";//设置成主域
  function loLoad(){
    var iframe = document.getElementById("iframe"); 
    var win = iframe.contentWindow;
    //在这里就可以操作b.html
    var doc = win.document;//这里获取iframe中的document对象
    var name = win.name;//这里获取window对象的name属性
  }
</script>
// b.html
<script>
  document.domain = "a.com";
</script>
  1. cors跨域
    服务器在响应的header中设置Access-Control-Allow-origin为*,即可允许所有域名的跨域访问。

61、性能优化

  1. 减少 HTTP 请求
  2. 使用服务端渲染
  3. 静态资源使用 CDN
  4. 将 CSS 放在文件头部,JavaScript 文件放在底部
  5. 使用字体图标 iconfont 代替图片图标
  6. 压缩文件
  7. 图片优化
    1)图片延迟加载
    2)响应式图片
    3)图片压缩
    4)CSS3 效果代替图片
    5)精灵图
  8. 使用事件委托
  9. 降低 CSS 选择器的复杂性
    浏览器读取选择器,遵循的原则是从选择器的右边到左边读取
  10. 使用 flexbox
  11. 减少内存泄漏
    闭包的使用,全局变量的使用
  12. 减少重排重绘
    当改变 DOM 元素位置或大小时,会导致浏览器重新生成渲染树,这个过程叫重排。
    当重新生成渲染树后,就要将渲染树每个节点绘制到屏幕,这个过程叫重绘。
    重排会导致重绘,重绘不会导致重排 。
  13. link代替import
  14. 减少dom操作
  15. 避免css表达式
  16. 防抖和节流函数
  17. 数据扁平化管理
  18. 列表的滚动窗口
  19. 本地缓存
  20. 按需加载资源
  21. 使用负载均衡方案

62、webpack性能优化

  1. 路由懒加载
  2. image-webpack-loader
  3. vender.js过于大 ,进行拆分
    由于webpack打包后的js过大,以至于在加载资源时间过长。所以将文件打包成多个js文件,在需要的时候按需加载。
  4. 引入 三方组件库过大的话,可以进行按需加载
  5. web前端项目,静态资源放在cdn上比较多,gzip的压缩是非常必要的,它直接改变了js文件的大小,减少两到三倍。
    nginx: 开启gzip和缓存
  6. 服务器缓存

63、继承

  1. 原型链继承
    核心:父类实例作为子类原型
    优点:1:复用了父类构造函数方法
    缺点:1:不能向父类构造函数方法传参, 2:子类实例共享父类构造函数引用属性
function Parent(name){
  this.name = name || 'parent'
  this.arr = [1] 
}
Parent.prototype.say = function(){
  console.log('hello')
}
function Child(like){
  this.like = like
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
        
let boy1 = new Child()
let boy2 = new Child()
//优点1:复用了父类构造函数方法
console.log(boy1.say === boy2.say)  //true
//缺点1:不能向父类构造函数方法传参
console.log(boy1.name, boy2.name) // parent parent
//缺点2:子类实例共享父类构造函数引用属性
boy1.arr.push(2)
console.log(boy2.arr)           //[1,2]
  1. 构造函数继承
    核心:借用父类构造函数方法来增强子类实例
    优点:1:子类实例可以强父类传参,2:子类实例不共享父类引用属性,3:可实现多继承(通过多个call或者apply继承多个父类)
    缺点:1:父类方法不能复用(相当于每次创建实例都重新创建了一次方法), 2:不能继承子类原型上的方法
function Parent(name){
  this.name = name
  this.arr = [1] 
  this.say = function(){
    console.log('hello')
  }
} 
function Child(name, like){
  Parent.call(this,name)
  this.like = like
}
        
let boy1 = new Child('Jhon', 'Apple')
let boy2 = new Child('Mary', 'Orange')
//优点1:可以向父类构造函数传参
console.log(boy1.name, boy2.name)   //Jhon Mary
//优点2:子类不共享父类构造函数的引用属性 
boy1.arr.push(2)
console.log(boy2.arr)           //[1]
//缺点1:方法不能复用
console.log(boy1.say === boy2.say)  //false
//缺点2:不能继承父类原型上的方法
Parent.prototype.walk = function(){
  console.log('walk')
}
console.log(boy1.walk)  //undefined
  1. 组合继承
    核心:通过调用父类构造方法继承父类属性并保存传参的优点,通过父类的实例作为子类的原型复用方法
    优点:1:创建子类实例,可以向父类传参,2:原型方法实现复用,3:不共享父类构造函数的引用属性
    缺点:1:调用了两次父类构造函数的方法
//组合继承
function Parent(name){
  this.name = name
  this.arr = [1]
}
Parent.prototype.say = function(){
  console.log('hello')
}
function Child(name, like){
  Parent.call(this, name)       //第二次调用父类构造函数方法
  this.like = like
}
Child.prototype = new Parent()   //第一次调用父类构造函数方法
Child.prototype.constructor = Child
let boy1 = new Child('John', 'Apple')
let boy2 = new Child('Mary', 'Orange')
//优点1:可以向父类构造函数传参
console.log(boy1.name, boy2.name) // John   Mary
//优点2:可以复用父类原型上的方法
console.log(boy1.say === boy2.say)  //true
//优点3:不共享父类构造函数引用属性
boy1.arr.push(2)
console.log(boy2.arr)   //[1]
//缺点1:调用了两次父类构造函数
  1. 组合继承优化
function Parent(name){
  this.name = name
  this.arr = [1]
}
Parent.prototype.say = function(){
  console.log('hello')
}
function Child(name, like){
  Parent.call(this, name)                
  this.like = like
}
//通过Object.create把Parent.prototype进行复制一份,1.避免调用 2.深度拷贝不会共享属性
Child.prototype = Object.create(Parent.prototype)        
Child.prototype.constructor = Child
let boy1 = new Child('John', 'Apple')
let boy2 = new Child('Mary', 'Orange')
let parent = new Parent('Label')
//优点1:可以向父类构造函数传参
console.log(boy1.name, boy2.name) // John   Mary
//优点2:可以复用父类原型上的方法
console.log(boy1.say === boy2.say)  //true
//优点3:不共享父类构造函数引用属性
boy1.arr.push(2)
console.log(boy2.arr)   //[1] 
//父类实例指向父类原型
console.log(boy1.constructor, boy2.constructor, parent.constructor)

64、原型链

在js中万物皆对象,任何对象都会有一个__proto__属性,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的proto隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的proto中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。

65、0.1 + 0.2 === 0.3 嘛?为什么?

10进制整数转化为2进制,除2取余
10进制小数转化为2进制,乘2取整
0.1和0.2都是无限循环,截取52位,0舍1入(类似4舍5入)
0.2+0.7 = 0.899999999999

66、v-show和v-if指令的共同点和不同点?

答: 共同点:都能控制元素的显示和隐藏;
不同点:实现本质方法不同,v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译一次;v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。而且v-if不停的销毁和创建比较消耗性能。

67、如何获取dom?

答:ref="domName" 用法:this.$refs.domName

68、单页面应用和多页面应用区别及优缺点

答:单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
单页面的优点:
用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。
单页面缺点:
不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多。

69、vue常用修饰符

  1. stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡;
  2. prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);
  3. capture:与事件冒泡的方向相反,事件捕获由外到内;
  4. self:只会触发自己范围内的事件,不包含子元素;
  5. once:只会触发一次。
  6. passive:滚动时候触发
  7. sync

70、get和post的区别

  1. GET与POST都有自己的语义,不能随便混用。
  2. 对参数的数据类型,GET只接受ASCII字符,而POST支持多种编码方式。
  3. 参数传输:GET请求的参数在URL中,POST请求是在请求的body里。

71、vuex

  1. state 全局状态
  2. getters 缓存属性
  3. mutations 同步函数,修改状态,commit
  4. actions 异步函数,更新 mutations,dispatch
  5. modules 模块分割

72、async、await、generator

// promise版本 版本自执行函数
function run (Generator) {
  var hw = Generator();
 const  next = (query)=>{
   var aa = hw.next(query)
   if(!aa.done){
    aa.value.then(next) 
   }else{
     return true
   }
 }
 next()
}
function* fib(max) {
  var t,
  a = 0,
   b = 1,
  n = 0;
  while (n < max) {
    yield a;
    [a, b] = [b, a + b];
    n ++;
  }
  return;
}
var f = fib(5);
console.log(f.next()); // {value: 0, done: false}
console.log(f.next()); // {value: 1, done: false}
console.log(f.next()); // {value: 1, done: false}
console.log(f.next()); // {value: 2, done: false}
console.log(f.next()); // {value: 3, done: false}
console.log(f.next()); // {value: undefined, done: true}
  1. await 必须放在async function中
  2. await 是 async wait的简写,await会将代码放入promise中,执行完毕之后,返回resolve(data), 并将之后的代码放入then函数中
  3. 转换 await 为 yield 、转换 async 为 Generator
  4. async await 实际上是由 generator + yield 控制流程 + promise 实现回调
  5. 利用generator遍历器生成器函数的分段执行 ,只有在遍历器对象 执行next方法之后交出了控制权 ,在完成后 callback || promise.then()里面调用下一次next的时候又继续恢复控制权这个功能来实现的

73、webpack常用的一些模块以及功能

  1. style-loader:将CSS添加到DOM的内联样式标签style里
  2. css-loader:允许将CSS文件通过require的方式引入,并返回CSS代码
  3. less-loader:处理less
  4. sass-loader:处理sass
  5. postcss-loader:用postcss来处理CSS
  6. file-loader:分发文件到output目录并返回相对路径
  7. url-loader 和 file-loader 类似,但是当文件小于设定的limit时可以返回一个Data Url
  8. html-minify-loader:压缩HTML文件
  9. babel-loader:把ES6文件转换成ES5文件
  10. webpack-dev-server: 开发时使用,静态服务器,并且有热更新等功能。
  11. compression-webpack-plugin: 生产环境时可选择让代码压缩gzip.
  12. html-webpack-plugin : 生成index.html 并自动注入打包后的js css 等
  13. definePlugin: 可以生成配置常量。编译时就有的常量。
  14. extract-text-webpack-plugin: 提取使用文件的css 组成一个或多个css 文件。
  15. uglifyjs-webpack-plugin: 删除警告,压缩代码等。

74、常用的设计模式有哪些

  1. 单例模式
  2. 工厂模式
  3. 观察者模式
  4. 发布订阅模式
  5. 代理人模式

75、不使用比较运算符,计算出较大值

max = (a+b+Math.abs(a-b))/2
min = (a+b-Math.abs(a-b))/2

76、如果上一层就是根root了,em和rem等价么?

等价

77、websocket

const socket = new WebSocket('ws://localhost:8080');
//方法
socket .send("Hello server!");      //发送数据
socket .close();              //关闭链接
//事件函数     open   message error close
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});
socket.addEventListener('error', function (event) {
    console.log('Message from server ', event.data);
});
socket.addEventListener('close', function (event) {
    console.log('Message from server ', event.data);
});
//属性
事件绑定等价于属性函数执行 操作
socket.onclose = function (event) {
    console.log('Message from server ', event.data);
}

78、重排重绘

  1. 重绘不一定需要重排,重排必然会导致重绘
  2. 重绘是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。
  3. 导致重排,增加、删除节点,改变元素位置、尺寸
  4. 批量修改dom可以先让父级元素脱离文档流,然后进行添加批量dom元素,然后在进行显示,脱离文档流增删元素,不会引起回流
  5. 文档片段是一个轻量级的document对象,它设计的目的就是用于更新,移动节点之类的任务,而且文档片段还有一个好处就是,当向一个节点添加文档片段时,添加的是文档片段的子节点群,自身不会被添加进去。

不同于第一种方法,这个方法并不会使元素短暂消失造成逻辑问题。

79、HTML5的离线储存怎么使用

Web App是通过浏览器来访问的,其中web app中的一些资源并不经常改变,不需要每次都向服务器发送请求。通过把需要离线缓存储的文件列在一个manifest配置文件中。这样在离线情况下也可以使用app。

<html manifest="sample.appcache"> 
在服务器添加mime-type text/cache-manifest

80、原生ajax过程

var xhr = new XMLHttpRequest();
xhr.open(‘get’,’http://www.example.com’); 
xhr.send();
xhr.onload = function () {
  //xhr.responseText 获取服务器端的响应数据 
  console.log(xhr.responseText);  
} 

81、如何实现浏览器内多个标签页之间的通信? (阿里)

  1. localStorage
    在一个标签页调用localStorage.setItem(name,val)保存数据localStorage.removeItem(name)删除数据的时候会触发 'storage'事件。
    在另外一个标签页监听document对象的storage事件,在事件event对象属性中获取信息
window.onstorage = (e) => {console.log(e)} 
window.addEventListener('storage', (e) => console.log(e)) 
  1. websocket
    多标签页面链接同一个服务器,当发送数据给服务器的时候,服务器作为转接将数据发送给其他标签页面。
  2. sharedWorker

82、线程,进程

  1. 线程是可以进行运算调度的最小单元,进程是拥有独立的内存单元
  2. 一个进程可以包含多个线程,不同进程间数据不能共享
  3. 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

83、href与src的区别

  1. href 标识超文本引用,用在 link 和 a 等元素上,href 是引用和页面关联,是在当前元素和引用资源之间建立联系。
  2. src 表示引用资源,表示替换当前元素,用在img,script,iframe 上,src 是页面内容不可缺少的一部分。

84、断点续传原理

一个大文件,可以将其切割成适当的小文件,提交给后台,再由后台合并成大文件。

  1. 化整为零,可以防止文件上传中断而导致整个文件都需要重新上传的杯具问题。
  2. 分成小文件,可以利用ajax的多线程,同时提交多个小文件,从而提升上传速度。

85、怎么实现两个大整数的相乘,说下思路

一般涉及到大整数,都需要考虑通过数组或者字符串来模拟算术运算。我们通过数组来表示两个数字a、b(这里从左往右需要从个位数到最高位),这里的相乘只需要理解一点:a的每一位a[i]乘以b的每一位b[j],我们可以先将其放在结果中的result[i+j]中。这是模拟运算的过程。剩下的只需要将result中每一位大于9的进行进位即可。

function dazhenghsuAdd(str1,str2){
    str1=str1.split('').reverse();
    str2=str2.split('').reverse();
    let result=[];
    for(let i=0;i<str1.length;i++){
        for(let j=0;j<str2.length;j++){
            result[i+j]=result[i+j]||0;//如果result[i+j]是undefined则将其变为0
            result[i+j]+=parseInt(str1[i])*parseInt(str2[j]);
        }
    }
    let temp;
    for(let k=0;k<result.length;k++){
        if(result[k]>9){
            temp=Math.floor(result[k]/10);
            result[k]=result[k]%10;
            result[k+1]=result[k+1]||0;
            result[k+1]+=temp;
        }
    }
    return result.reverse().join('');
} 

86、cookie与session

  1. 数据存放位置不同:
    cookie数据存放在客户的浏览器上,session数据放在服务器上。
  2. 安全程度不同:
    cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。
  3. 性能使用程度不同:
    session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
  4. 数据存储大小不同:
    单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie,而session则存储与服务端,浏览器对其没有限制。

87、undefined 和 null 区别

  1. 都是基本的数据类型
  2. 语义上:未定义,空对象
  3. boolean:false, false
  4. Number:NaN, 0
  5. typeof:undefined, object

88、eval

  1. eval() 是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function 就不容易被攻击。
  2. eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。

89、iframe有那些缺点?

  1. 优点:
    1)iframe能够把嵌入的网页原样展现出来;
    2)跨域
  2. 缺点:
    1)iframes阻塞页面加载,影响网页加载速度
    2)嵌入其他页面、增加资源请求
    3)爬虫无法解读页面,影响seo

91、nexttick

在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
先更新数据,后更新视图节点,用于解决数据已经赋值,但是进行dom操作的时候拿到的还是之前的值

92、require.js

  1. 首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;
  2. 其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
//IE不识别async属性,需要添加 defer
<script src="js/require.js" defer async="true"  data-main="js/main"></script>
// math.js
define(function (){
  var add = function (x,y){
    return x+y;
  };
  return {
    add: add
  };
});
//main.js
require(['math'], function (math){
  alert(math.add(1,1));
}); 

93、CommonJs

一个单独文件就是一个模块,通过require方法来同步加载要依赖的模块,然后通过extports或则module.exports来导出需要暴露的接口。

  1. 优点:服务器端模块重用,NPM中模块包多,有将近20万个。
  2. 缺点:加载模块是同步的,只有加载完成后才能执行后面的操作,也就是当要用到该模块了,现加载现用,不仅加载速度慢,而且还会导致性能、可用性、调试和跨域访问等问题。Node.js主要用于服务器编程,加载的模块文件一般都存在本地硬盘,加载起来比较快,不用考虑异步加载的方式,因此,CommonJS规范比较适用。然而,这并不适合在浏览器环境,同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
  3. 实现:服务器端的 Node.js;Browserify,浏览器端的 CommonJS 实现,可以使用 NPM 的模块,但是编译打包后的 文件体积可能很大;modules-webmake,类似Browserify,还不如 Browserify 灵活;wreq,Browserify 的前身;

94、AMD

  1. 优点:在浏览器环境中异步加载模块;并行加载多个模块;
  2. 缺点:开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅;不符合通用的模块化思维方式,是一种妥协的实现;
  3. 实现:RequireJS; curl;

95、CMD

  1. 优点:依赖就近,延迟执行 可以很容易在 Node.js 中运行;
  2. 缺点:依赖 SPM 打包,模块的加载逻辑偏重;
  3. 实现:Sea.js ;coolie

96、calc, support, media各自的含义及用法?

  1. @support主要是用于检测浏览器是否支持CSS的某个属性,其实就是条件判断,如果支持某个属性,你可以写一套样式,如果不支持某个属性,你也可以提供另外一套样式作为替补。
@supports (display: flex) { div { display: flex; }}
  1. calc() 函数用于动态计算长度值。 calc()函数支持 "+", "-", "*", "/" 运算;
width: calc(100% - 100px);
  1. @media 查询,你可以针对不同的媒体类型定义不同的样式。
@media screen and (max-width: 300px) {
    body {
        background-color:lightblue;
    }
}

97、页面渲染html的过程?

  1. 浏览器解析html源码,然后创建一个 DOM树。在DOM树中并行请求 css/image/js。
  2. 浏览器解析CSS代码,计算出最终的样式数据。构建CSSOM树。
  3. DOM Tree + CSSOM--> 渲染树(rendering tree) --> layout树(重排) --> painting树(重绘) 。渲染树和DOM树有点像,但是是有区别的。
    DOM树完全和html标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如head、display:none的元素等。
  4. 一旦渲染树创建好了,浏览器就可以根据渲染树直接把页面绘制到屏幕上。

98、如何中断ajax请求?

一种是设置超时时间让ajax自动断开,另一种是手动停止ajax请求,其核心是调用XML对象的abort方法,ajax.abort()

99、解构赋值是深拷贝还是浅拷贝

基础类型深拷贝,引用类型浅拷贝

100、css3新增

transition:all 2s ;
transform:translate(100px, 100px);
transform:rotate(30deg, 30deg);
transform:skew(100px, 100px);
transform:scale(1.2, 1.2);
animation:myfirst 3s;
@keyframes myfirst
{
    from {background: red;}
    to {background: yellow;}
}

101、点击穿透

手机端判断是否是双击行为,浏览器会进行300ms延迟操作,如果没有第二次点击,则视为点击操作
touchStart=>touchMove=>touchEnd=>click
因为300ms延迟的原因,如果点击已经关闭弹窗,延迟执行了click,就会多执行一次click事件,这个过程就叫 点击穿透
preventDefault()

102、Function Object

Function instanceof Object;//true
Object instanceof Function;//true
Object.prototype = null
Function.prototype.proto===Object.prototype
Function.constructor==Function
Object.constructor==Function

103、vue文档

vue文档

104、vuex文档

vuex文档

105、sass文档

(sass官方文档)[https://www.sass.hk/docs/]

106、字符串、数组、map、集合函数

107、算法与数据结构

排序算法
常见算法题

108、git

git

109、restful

110、webpack调试 、打包、测试

111、同步、异步和阻塞、非阻塞

A煮开水

  1. 水壶放到火上,等水烧开(同步阻塞)
  2. 水壶放到火上,看电视 ,时不时(轮询)查看水是否烧开,(同步非阻塞)
  3. 响水壶放到火上,等着水烧开,水烧开发出响声(异步阻塞)
  4. 响水壶放到火上,看电视,水烧开的响声,得到开水(异步非阻塞)

同步/异步主要针对C端
阻塞/非阻塞主要针对S端

同步IO和异步IO的区别就在于:数据访问的时候进程是否阻塞!
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!

112、浏览器线程

  1. 浏览器中有三个常驻的线程,分别是JS引擎,界面渲染,事件响应。由于这三个线程同时要访问DOM树,所以为了线程安全,浏览器内部需要做互斥:当JS引擎在执行代码的时候,界面渲染和事件响应两个线程是被暂停的。所以当JS出现死循环,浏览器无法响应点击,也无法更新界面。
  2. http请求线程不能操作Dom
  3. 定时器线程

113、worker对象

执行http请求线程,不与js主线程

//index.js
var worker = new Worker("data.js");
//接受数据
worker.onmessage = function(){
  console.log(event.data)
};
document.getElementById('ale').addEventListener('click',()=>{
  alert(1)
},false)
//用于关闭worker线程
worker.terminate();
//data.js 
var i = 0
setInterval(()=>{
  i++
  postMessage(i)
},1000)

114、异步处理机制

console.log(1);
setTimeout(e => {
  console.log(2);
}, 0)
setTimeout(e => {
  console.log(3);
}, 0)
new Promise((resolve, reject) => {
  console.log(4);
  resolve();
}).then(e => {
  console.log(5);
})
setTimeout(e => {
  console.log(6);
  new Promise((resolve, reject) => {
    console.log(7);
    resolve();
  })
  .then(e => {
    console.log(8);
  })
})
//1  4  5  2  3  6  7  8  

115、nodejs中的异步处理机制

阶段总览

  1. timers : setTimeout、setInterval
  2. I/O callbacks : 执行几乎所有异常的close回调,由timer和setImmediate执行的回调。
  3. idle,prepare: 只用于内部
  4. poll : 获取新的I/O事件,node在该阶段会适当的阻塞
  5. check : setImmediate的回调被调用
  6. close callbacks: e.g socket.on(‘close’,…);
  7. 每个阶段执行完毕都会执行nexttick和promise

116、实现一个add函数

add(1,2,3)(4)(5,6)
function add(...args){
  let sum = args.reduce((prev, cur)=>{return prev + cur}, 0)
  function add2(...args2){
    return add(sum, ...args2)
  }
  add2.toString = function(){
    return sum
  }
  return add2
}
//console.log(add(1,2,3)(4)(5,6))    //21

117、强缓存与协商缓存

  1. 直接使用本地的缓存,不用跟服务器进行通信(200)
缺点:
Last-Modified/If-Modified-Since 精确到秒,1s内多次更改无法识别
部分文件有可能是定期(或周期)生成,不需要重新进行获取,但是Last-Modified/If-Modified-Since会认为发生了改变
  1. 至少与服务器通信一次,判断浏览器是否能直接使用本地缓存(304)
与 Last-Modified/If-Modified-Since 不同的是,返回 304 时,
ETag 还是会重新生成返回至浏览器。

118、浏览器缓存有哪些

  1. http缓存是基于HTTP协议的浏览器文件级缓存机制。
  2. websql这种方式只有较新的chrome浏览器支持,并以一个独立规范形式出现
  3. indexDB 是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API
  4. Cookie一般网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)
  5. Localstoragehtml5的一种新的本地缓存方案,目前用的比较多,一般用来存储ajax返回的数据,加快下次页面打开时的渲染速度
  6. Sessionstorage和localstorage类似,但是浏览器关闭则会全部删除,api和localstorage相同,实际项目中使用较少。
  7. application cache 是将大部分图片资源、js、css等静态资源放在manifest文件配置中
  8. cacheStorage是在ServiceWorker的规范中定义的,可以保存每个serverWorker申明的cache对象
  9. flash缓存 这种方式基本不用,这一方法主要基于flash有读写浏览器端本地目录的功能

119、http和https的区别

120、vue中观察者模式的体现

data发生变化触发监听函数执行
发布者 监听者
事件点击dom触发事件绑定函数执行回调
发布者 监听者

121、1px问题

由于不同的手机有不同的像素密度导致的。如果移动显示屏的分辨率始终是普通屏幕的2倍,1px的边框在devicePixelRatio=2的移动显示屏下会显示成2px,所以在高清瓶下看着1px总是感觉变胖了
许多博客都是这么解释的,实际上不是的
设计稿如果是750px像素的1px,在375px的逻辑像素设备上显示1px的时候只需要0.5px,然而css本身最小像素是1px,所以感觉变宽了

  1. 在ios8+中当devicePixelRatio=2的时候使用0.5px
p{ 
    border:1px solid #000; 
} 
@media (-webkit-min-device-pixel-ratio: 2) { 
     p{ 
         border:0.5px solid #000; 
     } 
} 
  1. transform
transform: scaleY(0.5);
  1. 图片填充背景
  2. box-shadow模拟边框
  3. viewport + rem 实现

122、面向对象的静态方法

  1. 面向对象静态方法是直接在class中进行定义的,可以通过类型直接进行调用,不能通过实例调用,不能继承。普遍来说都是一些工具类方法(Array.isArray())或者一些原始值(Date.now())
  2. 实例方法是通过实例进行调用的,可继承 ,通过原型队形进行定义。
  3. 在java中class是一等公民,在一些需要调用函数,但是又不需要实例化的情况下,引入了静态方法的概念,在js中函数是一等公民,所以实际上根本不需要引入静态方法,静态方法更像定义了一些全局方法,唯一的效果个人理解就是有了前缀,更利于理解和记忆,和尽可能的避免全局变量环境的一个污染

123、es6一些概念性的东西

作用域
上下文

124、垃圾回收的一些实例

125、import、export、require、defined

126、深拷贝和浅拷贝的实现

127、单行文本居中、多行文本居左

128、弹性盒子container中有三个元素,前两个居左,第三个居右

129、inject、provide函数

130、vue传值方式?

props emit()on() parentchildren listenerattr

131、eventBus

132、vue初始化页面闪动问题?

webpack、vue-router
v-cloak css:[v-cloak]:display:none

133、字符串数组转为数字数组

['1','2','3'].map(Number) //  [1, 2, 3]
['1','2','3'],map(parseInt) // [1, NaN, NaN]
parseInt(str, radix) 
//函数接收两个参数, 默认传递item,index参数,radix为基数,转换按照radix进制,所以str每一项不能大于或等于radix否则NaN,当index为0时,按照字符串0开头以十进制返回

134、for...of 和 for...in 区别

  1. for-of 遍历的是值,for-in遍历的是key
  2. for-of 无法遍历 不可迭代对象(obj)
    可迭代对象包括: Array,Map,Set,String,TypedArray,arguments等等
    可通过return、break,进行中断和关键字continue;
  3. for...in...遍历对象也会遍历原型对象上的属性,需要hasOwnProperty进行过滤

135、Object对象

136、v-deep

如果不使用scoped属性改公共组件的样式会被污染到全局,但设置scoped属性的话,直接覆盖样式会不生效。
vue组件中使用第三方组件库,需要在这个组件中定制样式又不想影响其他地方使用此第三方组件的样式。
用deep选择器,可以穿透scoped但又不会污染全局样式。

137、触发冒泡和不能触发冒泡的方法有哪些

138、Proxy

139、 mixin 的缺陷

140、数字不能直接当作对象使用

10.toString(2)   // 出错:SyntaxError
2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
(2).toString(); // 2先被计算

let a = new Map();
a[1] = 2
console.log(a.1)  // SyntaxError
console.log(a[1])  // 2 

141、http各个版本的区别

  1. 每次TCP连接只能发送一个请求,当服务器响应后就会关闭这次连接,不支持 keep-alive,只有get方法,只能发送纯文本
  1. 引入了持久链接,TCP连接默认不关闭,可以被多个请求复用;
  2. 加入了管道机制,允许多个请求同时发送,增加了并发性;
  3. 一个TCP连接可以传送多个回应,势必区分数据包是哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度;
  4. 新增了请求方式PUT、PATCH、OPTIONS、DELETE等;
  5. 客户端请求的头信息新增了Host字段,用来指定服务器的域名;
  6. 使用Content-Length字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。对于一些很耗时的动态操作来说,服务器要等到所有操作完成,才能发送数据,更好的处理方法是,产生一块数据,就发送一块,分块传输编码;
  7. 支持文件断点续传,RANGE:bytes,HTTP/1.0每次传送文件都是从文件头开始,即0字节处开始。RANGE:bytes=XXXX表示要求服务器从文件XXXX字节处开始传送,断点续传。即返回码是206(Partial Content)
  1. 二进制协议
    一个彻底的二进制协议,头信息和数据体都是二进制
  2. 多工
    一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,避免了"队头堵塞",采用多路复用的技术,数量级的一个提升
  3. 头信息压缩
    HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。
    一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
  4. 服务器推送
    HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送,适合加载静态资源

142、react 和 vue 对比

  1. 都使用了虚拟dom
  2. 提供了响应式和组件化的视图组件
  3. 提供了路由和全局状态管理库
  1. react 当组件的状态发生变化时,会以该组件为根,重新渲染整个组件子树。避免不必要的子组件的重渲染,需要在所有可能的地方使用PureComponent,或是手动实现shouldComponentUpdate 方法。同时你可能会需要使用不可变的数据结构immutable来使得你的组件更容易被优化。Vue 中组件的依赖是在渲染过程中自动追踪的
  2. react 所有的组件的渲染功能都依靠 JSX。JSX 是使用 XML 语法编写 JavaScript 的一种语法糖。
    vue是在 .vue文件中将 html、css、js分别划分了一块区域,组合到一起,用各自的处理方式。
  3. react元素都是immutable不可变的。当元素被创建之后,你是无法改变其内容或属性的。一个元素就好像是动画里的一帧,它代表应用界面在某一时间点的样子。更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render() 方法。
    vue 数据直接修改,响应式。
  4. react是单向数据流。
    vue的依赖追踪是通过 Object.defineProperty 把data对象的属性全部转为 getter/setter来实现的;当改变数据的某个属性值时,会触发set函数,获取该属性值的时候会触发get函数,通过这个特性来实现改变数据时改变视图;也就是说只有当数据改变时才会触发视图的改变,反过来在操作视图时,只能通过DOM事件来改变数据,再由此来改变视图,以此来实现双向绑定。
  5. react本身,是严格的view层,MVC模式
    vue 是MVVM模式的一种方式实现
    虽然没有完全遵循 MVVM 模型,Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的简称) 这个变量名表示 Vue 实例。

143、tcp 和 udp

144、bebal 原理

145、tcp 握手和挥手

146、promise.all方法

疑问1:如果第一个报错,后面的会执行吗,返回什么?

static all (list) {
  return new MyPromise((resolve, reject) => {
    /**
     * 返回值的集合
     */
    let values = []
    let count = 0
    for (let [i, p] of list.entries()) {
      // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
      this.resolve(p).then(res => {
        values[i] = res
        count++
        // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
        if (count === list.length) resolve(values)
      }, err => {
        // 有一个被rejected时返回的MyPromise状态就变成rejected
        reject(err)
      })
    }
  })
}

回答:会返回报错信息,后面的不会执行。代码可以看出来,全部成功执行完,才会执行resolve函数,只要有一个报错就会执行reject函数

别走,还有后续呐······
本文只是我个人的知识点整理,日后还会不断的进行补充,工作中遇到的知识点如果不及时记录过后很有可能会被遗忘。如果小伙伴们还能想到其他vue前端面试题,欢迎在评论区留言,分享是种美德,谢谢你的贡献。

0k

上一篇下一篇

猜你喜欢

热点阅读