为21届学弟学妹秋招护驾(前端复习总结)

双学位两个专业答辩全都通过了,也拿到了心仪的offer。把自己在春招期间整理的1万字资料供21届学弟学妹作为大纲性内容自查哈,希望你们在特殊时期也能和20届一起奋斗。以下几个是我感觉非常不错的复习资料,这些题目看完后找工作都挺稳的了。希望你们在秋招能获得心仪的offer
2019前端面试系列——JS高频手写代码题
2019前端面试系列——CSS面试题
2019前端面试系列——JS面试题
2019前端面试系列——Vue面试题
2019前端面试系列——HTTP、浏览器面试题

仅作大纲性参考,有错误请自行翻阅文献哈

实现居中

水平居中

  • text-align:center;

  • margin: 0 auto;

  • display:flex; justify-content: center;

  • position:absolute;left: 50%; transform: translateX(-50%);

垂直居中:

  • line-height等于高度
  • position:absolute; top: 50%; transform: translateY(-50%);
  • display:flex; align-items:center;
  • 父元素display:table; 子元素display:table-cell;vertical-align:middle;

https://juejin.im/post/58f818bbb123db006233ab2a

闭包的概念

概念:闭包就是能读取其他函数内部变量的函数

优点:1、避免全局变量的污染;2、希望一个变量长期存储在内存(缓存变量);

缺点:1、容易造成内存泄露

内存泄露例子:

let getMoney = function() {
    let money = 10
    return function() {
        return money
    }
}
let f = getMoney()
// f函数存在的话,money会一直得不到释放

深拷贝和浅拷贝

浅拷贝:

Object.assign()
Array.prototype.slice()

深拷贝:

JSON.parse(JSON.stringify()) // 无法序列化函数

递归函数:

function cloneObject(obj) {
  var newObj = {} //如果不是引用类型,直接返回
  if (typeof obj !== 'object') {
    return obj
  }
  //如果是引用类型,遍历属性
  else {
    for (var attr in obj) {
      //如果某个属性还是引用类型,递归调用
      newObj[attr] = cloneObject(obj[attr])
    }
  }
  return newObj
}
// 无法解决循环引用问题

数组去重

ES6中的set:

let arr = [1,2,3,1,2,3]
let arr2 = [...new Set(arr)]

新数组去重:

let newArr = []
let arr = [1,2,3,1,2,3]
arr.forEach(item => {
    if (newArr.indexOf(item) === -1) {
        newArr.push(item)
    }
})

JS执行机制、事件循环

事件循环:同步任务进入主线程,异步任务进入EventTable中并注册回调函数中。当所有同步任务执行完毕时,主线程从事件队列中读取回调函数并执行。上述过程不断重复,称为事件循环

宏任务和微任务

异步任务会分成宏任务与微任务

微任务是在宏任务执行完后立即执行的任务

宏任务:主代码块,setTimeout,setInterval等

微任务:Promise,process.nextTick等

当宏任务执行完时,如果有可执行的微任务,则执行所有微任务后再执行新的宏任务。如果没有可执行的微任务,那么直接开始执行新的宏任务

https://www.jianshu.com/p/184988903562

函数提升与变量提升

在作用域中,函数声明与变量声明都会提升到顶部。首先声明变量整体,然后再声明函数整体。

function v() {
  console.log(a)
  var a = 1;
  function a() {}
}
v()
// 输出函数a
// 上面函数相当于:
function v() {
  var a;
  function a() {}
  console.log(a)
  a = 1;
}
v()
function v() {
  var a = 1;
  function a() {}
  console.log(a)
}
v()
// 输出1
// 相当于:
function v() {
  var a;
  function a() {}
  a = 1;
  console.log(a)
}
v()

注意:函数表达式不会被提升

常见内存泄露

1、全局变量滥用

当不用var声明变量的时候,比如b=1,相当于挂载到全局变量。

解决方法:使用严格模式

2、没有清理DOM元素的引用

3、闭包

4、定时器被遗忘

性能优化

1、减少请求数量

2、事件委托:减少DOM操作,每个新增的元素都能拥有该事件

3、压缩资源大小

4、减少重绘回流

5、innerHTML代替dom操作

6、图片预加载

Vue性能优化

1、路由懒加载

2、v-if与v-show正确使用

3、v-for中设置唯一key

4、图片懒加载

实现EventBus

function EventEmitter() {
  this.events = new Map()
  this.addListener = function (name, fn) {
    this.events.set(name, fn)
  }
  this.emit = function (name) {
    let fn = this.events.get(name)
    fn(...[...arguments].slice(1))
  }
}
var bus = new EventEmitter()
bus.addListener('age', (age, time) => {
  console.log(age, time)
})
bus.emit('age', 18, '2019')

引用类型有哪几种

  • Object
  • Array
  • Date
  • RegExp
  • Function

圣杯布局、双飞翼布局的实现

这个请自查哈

CSS盒模型

Margin、Border、Padding、Content

IE定义的盒模型中元素的宽高包括了padding和border

W3C盒模型元素宽高为Content

box-sizing: content-box; // W3C盒模型

box-sizing: border-box; // IE盒模型

伪类和伪元素的区别

它们之间的根本区别在于是否创造了新的元素

伪类不会创造新的元素,比如:hover :active

伪元素会创造新的元素,比如:after :before

伪类:表示已存在的某个元素处于某种状态

伪元素:用于将特殊的效果添加到某元素

实现快速排序

function quickSort(arr) {
  if (arr.length < 1) {
    return arr
  }
  var left = []
  var right = []
  var pivot = arr[0]
  for (var i = 1; i < arr.length; i++) {
    if (arr[i] > pivot) {
      right.push(arr[i])
    } else {
      left.push(arr[i])
    }
  }
  return [...quickSort(left), pivot, ...quickSort(right)]
}
var arr = [0, 3, 4, 2, 6, 1, -1, 123]
console.log(quickSort(arr))
// ----------------------
console.log('------------')
// ----------------------
// 单指针循环法
function quickSort2(arr) {
  if (arr.length < 1) {
    return arr
  }
  var pivot = arr[0]
  var mark = 0
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      mark++
      let temp = arr[i]
      arr[i] = arr[mark]
      arr[mark] = temp
    }
  }
  let temp = arr[0]
  arr[0] = arr[mark]
  arr[mark] = temp
  return [...quickSort2(arr.slice(0, mark)), arr[mark], ...quickSort2(arr.slice(mark+1, arr.length))]
}
var arr = [0, 3, 4, 2, 6, 1, -1, 3]
console.log(quickSort2(arr))

堆排序

// 最小堆,父节点小于两个子节点

function downAjust(arr, parentIndex, length) {
  let childIndex = 2 * parentIndex + 1
  while (childIndex < length) {
    if (childIndex + 1 < length && arr[childIndex + 1] < arr[childIndex]) {
      childIndex ++
    }
    if (arr[parentIndex] < arr[childIndex]) {
      return
    }
    let temp = arr[parentIndex]
    arr[parentIndex] = arr[childIndex]
    arr[childIndex] = temp
    parentIndex = childIndex
    childIndex = 2 * parentIndex + 1
  }
}
function buildHeap(arr) {
  for (let i = parseInt((arr.length -1) / 2); i >= 0; i--) {
    downAjust(arr, i, arr.length)
  }
  console.log(arr)
}
var arr = [7, 1, 3, 10, 5, 2, 8, 9, 6]
buildHeap(arr)
heapSort(arr)
function heapSort(arr) {
  for (let i = arr.length - 1; i >= 0; i--) {
    let temp = arr[i]
    arr[i] = arr[0]
    arr[0] = temp
    downAjust(arr, 0, i)
  }
  console.log(arr)
}

归并排序

function merge(left, right) {
  let arr = []
  while (left.length && right.length) {
    if (left[0] < right[0]) {
      arr.push(left.shift())
    } else {
      arr.push(right.shift())
    }
  }
  arr.push(...left, ...right)
  return arr
}
function mergeSort(arr) {
  if (arr.length > 1) {
    let mid = parseInt(arr.length / 2)
    let left = arr.slice(0, mid)
    let right = arr.slice(mid)
    return merge(mergeSort(left), mergeSort(right))
  }
  return arr
}
var arr = [8, 2, 4, 1, 3, 9, 0, -1, -2]
console.log(mergeSort(arr))

实现二分查找

function binarySearch(arr, target) {
  let low = 0
  let high = arr.length - 1
  while (low <= high) {
    let mid = parseInt((low + high) / 2)
    if (arr[mid] > target) {
      high = mid - 1
    } else if (arr[mid] < target) {
      low = mid + 1
    } else {
      return mid
    }
  }
  return -1
}
var arr = [0, 1, 2, 3, 3, 4, 5]
console.log(binarySearch(arr, 5))

数组和链表的区别

数组

在内存中连续存储

优点是具有高效的随机访问能力,常量时间内可以找到对应元素

缺点是在插入和删除元素时,可能会导致大量元素被迫移动,影响效率

数组适合在读操作多,写操作少的场景

链表

在内存中是随机存储

优点是能够灵活地进行插入删除操作

缺点是查找元素的性能较差

URI和URL

URI就是统一资源标识符,URL就是统一资源定位符

URI有两种形式,URL和URN

URL由协议、主机、端口、路径四个部分组成,唯一标识资源位置

URN是统一资源名,它使得资源与位置无关,只使用资源名就能够获取资源,这种形式处于试验阶段,尚未被大范围使用

TCP与UDP

TCP是一种可靠的传输方式,能够将数据按序无差错传输。HTTP使用TCP来传输其报文数据。

TCP的建立需要经过三次握手

UDP是一种不可靠的传输方式,它不保证数据能准确传输。在允许丢包的情况下使用UDP传输效率会更高,比如视频、音频,个别丢包不影响整体画面

访问一个网址时浏览器经历的步骤

1、浏览器从URL中解析主机名和端口

2、DNS解析出IP地址

3、建立TCP连接

4、浏览器发送HTTP请求报文

5、服务器返回HTTP响应报文

6、关闭连接,浏览器开始解析文档

7、浏览器首先解析出DOM树

8、再次解析出CSSOM树

9、根据DOM树和CSSOM树来构造render tree渲染树

10、浏览器重排、重绘,绘制每个节点

重排、重绘

浏览器默认采用流式布局模型

重排也叫回流

重排:根据渲染树中每个对象的信息,计算渲染对象各自的几何信息(位置、尺寸大小),并将其安置在界面正确位置。

因此呢,如果发生改变了位置、尺寸大小,会引发回流,重新计算全局布局(从根节点重新布局)或者局部布局。例如,改变窗口大小会引发全局布局的重新计算;

引起重排的操作

  • 页面首次渲染
  • 窗口大小改变
  • 元素尺寸或位置改变
  • 字体大小改变
  • 添加或删除DOM元素
  • 获取offsetTop、offsetLeft等属性触发重排,应缓存起来

重绘:当页面某元素样式的改变并不影响其在文档流中的位置。

例如改变背景颜色、字体颜色等操作

强缓存与协商缓存

强缓存:服务器通过设置response header中的cache-control字段来控制浏览器对资源缓存,max-age用于设置缓存的过期时间。如果是cache-control: no-cache,代表跳过设置强缓存,会走协商缓存。

协商缓存:当强缓存过期后,客户端每次访问资源时会先看下缓存有无过期,如果过期了,则询问服务器资源是否已更改,如果已更改,服务器会返回资源以及对应的etag和last-modified;如果没有更改,则服务器返回一样的etag和last-modified,状态码为304(资源无变更),这时客户端之后访问缓存都要走一次协商缓存。

etag:每个文件的hash

last-modified:资源最后更改时间

https://www.jianshu.com/p/9c95db596df5

  • 强制缓存是我们在第一次请求资源时在 http 响应头设置一个过期时间,在时效内都将直接从浏览器进行获取,常见的 http 响应头字段如 Cache-Control 和 Expires
  • 协商缓存是我们通过 http 响应头字段 etag 或者 Last-Modified 等判断服务器上资源是否修改,如果修改则从服务器重新获取,如果未修改则 304 指向浏览器缓存中进行获取

call、apply、bind

这三个函数都是改变this的指向

call、apply第一个参数都是this所指向的对象,它们之间的区别是传参的方式不同。

apply的传参方式是以数组的方式传参,而call是以逐个传参的方式

bind函数的第一个参数也是this所指向的对象,它和上面call、apply不一样,它返回一个新的函数。

完全二叉树与满二叉树

满二叉树:深度为k,并且有2^k-1个节点的二叉树

完全二叉树:完全二叉树的n个节点的编号都能对应相同深度满二叉树的1~n个节点一一对应

Promise对象

Promise有三个状态:

1、进行中pending;2、成功态fulfilled;3、拒绝rejected;

Promise构造函数接受一个参数为resolve和reject的函数

resolve、reject能改变promise的状态。

通过then方法为promise注册完成时的处理程序

Vue生命周期

beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestory、destoryed

created周期在模板渲染成HTML前调用,能够获取到实例的data,通常初始化某些属性值

mounted周期在模板渲染成HTML后调用,通常是初始化页面完成后对DOM进行一些操作。

生命周期 是否获取dom节点 是否可以获取data 是否获取methods
beforeCreate
created
beforeMount
mounted

web worker

JavaScript是单线程语言,WebWorker是为了创造多线程环境,允许主线程创建Worker线程,这样worker线程完成任务后会自动返回结果给主线程。

需要注意以下几点:

1、主线程与worker线程必须同源

2、worker无法读取主线程的Dom对象

3、不能直接通信,需要通过消息postMessage来完成

封装ajax

function ajax(url, type, data, success) {
    let aj = new XMLHttpRequest()
    if (type === 'get') {
        if (data) {
            url += '?'
            let arr = []
            Object.keys(data).forEach(key => {
                arr.push(key + '=' + data[key])
            })
            url += arr.join('&')
        }
        aj.open(type, url)
        aj.send(data)
    } else if (type === 'post') {
        aj.open(type, url)
        aj.setRequestHeader('Content-type', 'x-www-form-urlencoded')
        if (data) {
            aj.send(data)
        } else {
            aj.send()
        }
    }
    aj.onreadystatechange = function() {
        if (aj.readyState === 4 && aj.status === 200) {
            success(aj.responseText)
            console.log(aj.responseText)
            return aj.responseText
        }
    }
    }

for..in与for..of

for..in循环用于获取键名

for..of循环用于获取键值

例如

var arr = ['a','b','c']
for (var i in arr) {
    console.log(i)
}
// 0, 1, 2 //遍历数组就是获取下标
for (var i of arr) {
    console.log(i)
}
// a b c //遍历数组就是获取值

var obj = {
    a: 1,
    b: 2,
    c: 3
}
for (var i in obj) {
    console.log(i)
}
// a b c  //遍历对象就是获取键名
for (var i of arr) {
    console.log(i)
}
// 用在对象会报错

防篡改对象

主要使用三个方法Object.preventExtensions、Object.seal、Object.freeze

分别对应为不可扩展、密封、冻结

不可扩展 ✖️ ✔️ ✔️
密封 ✖️ ✖️ ✔️
冻结 ✖️ ✖️ ✖️

通过 Object.isExtensible([Object]) 可以确定对象是否可以扩展。 true 可以扩展, false 不可以扩展。

通过 Object.isSealed([Object]) 可以确定对象是否被密封了。

通过 Object.isFrozen([Object]) 来检测对象是否被冻结。

数组扁平化

var arr = [1, 2, [3,4], [1, [2, 3, [3, 4]]]]
function fn(arr) {
  let newArr = []
  for (var i of arr) {
    if (i instanceof Array) {
      newArr.push(...fn(i))
    } else {
      newArr.push(i)
    }
  }
  return newArr
}
console.log(fn(arr))

判断数据类型

判断数组:Array.isArray()

typeof:能判断的数据类型有String、number、objcet、function、boolean、undefined。不能判断null和array

typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效

instanceof:判断是否某个原型的实例,不能检测null和undefined

[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true
null instanceof Null//报错
undefined instanceof undefined//报错

Object.prototype.toString.call():这个较为准确,不能检测自定义类型

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]

BFC

块格式化上下文,BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素

如何创建BFC:

1、display为inline-block table-cell flex

2、浮动元素float不是none

3、定位元素:absolute、fixed

4、overflow除了visible以外的值

BFC的作用:

1、避免margin重叠(放在不同的BFC)

2、避免高度塌陷

3、包裹浮动元素

BFC布局规则:

1、在BFC中,盒子(块级元素)会在垂直方向排列

2、box垂直方向的距离有margin决定,同一个BFC的margin会重叠(以最大值为准)

https://blog.csdn.net/sinat_36422236/article/details/88763187

https://www.cnblogs.com/chen-cong/p/7862832.html

清除浮动

1、clear:both(通过after伪元素清除浮动)

2、父级div设置overflow:hidden(触发BFC)

async和await

async函数返回一个Promise对象

await用于等待一个异步函数执行结果

这两个配合使用能使得代码看起来更像同步代码

await处理错误

把await的代码放在try..catch块中或者使用catch注册函数

async function MyAsync() {
    try{
        await doSomething()
    } catch(err) {
        console.log(err)
    }

    await doAnotherThing().catch((err) => {
        console.log(err)
    })
}
// 以上两种方法皆可以

在数组中找到两数之和等于n的下标

利用二分查找

// 
var arr = [0, 5, 4, 1, 3, 2, 3]
function findN(arr, n) {
  arr = arr.sort()
  for (let i = 0; i <=  parseInt(arr.length / 2); i++ ) {
    let result = binarySearch(arr, n - i)
    if (result !== -1) {
      console.log(`下标${i}值${arr[i]}, 下标${result}值${arr[result]}`)
    }
  }
}
findN(arr, 6)

实现Promise.all

function PromiseAll(arr) {
  let result = []
  return new Promise((res, rej) => {
    for (let i = 0; i < arr.length; i++) {
      let p = arr[i]
      p.then(r => {
        result.push(r)
        if (result.length === arr.length) {
          res(result)
        }
      }, rej) // 有一个失败就reject
    }
  })
}
let p1 = new Promise((res, rej) => {
  res(3)
})
let p2 = new Promise((res, rej) => {
  res(2)
})

PromiseAll([p1, p2]).then(res => {
  console.log(res)
})

主要是需要为每个promise注册then回调函数,在每个回调函数里判断下结果数组的长度是否和promise对象数组的长度相同,如果相同的时候就可以resolve了,注意Promise.all返回的是一个promise

JS事件循环

img

同步任务先进入主线程,异步任务进入EventTable并注册回调函数进入事件队列,当主线程中的任务执行完毕后,主线程会进入事件队列中取出回调函数执行

XSS和CSRF

CSRF:跨站请求伪造,攻击者诱导用户访问危险网站,在危险网站伪造用户请求,服务器以为是合理请求;

XSS:跨站脚本攻击。攻击者将恶意代码植入到页面中,用户访问页面时会自动执行恶意代码

大数求和

function addNum(a, b) {
  let n = a.split('').reverse()
  let m = b.split('').reverse()

  let maxLength = Math.max(n.length, m.length)

  let p = 0 // 进位
  let result = []
  for (let i = 0; i < maxLength; i++) {
    let curN = n[i] || 0
    let curM = m[i] || 0

    let temp = Number(curN) + Number(curM)

    if (p) {
      temp += p
    }
    p = temp >= 10 ? 1 : 0
    result.push(temp % 10)
    if (i === maxLength) {
      if (p) {
        result.push(1)
      }
    }
  }
  return result.reverse().join('')
}
console.log(addNum('122665522626555223', '9728728727827827'))
console.log(addNum('555223', '11111111111927827'))

Cookie的弊端

1、每个特定的域名下最多生成20个cookie

2、最大存储空间为4K

3、安全性问题。被恶意获取到时他人只需原样转发cookie便可以达到目的

CSS中 link 和@import 的区别是?

link是HTML标签,无兼容性问题

@import是CSS提供的,需要IE5以上才能识别

页面加载时link会同时被加载,而import会在页面被加载后再加载

CSS 选择符有哪些?哪些属性可以继承?优先级算法如何计算?

css选择器:

  • id选择器#id
  • 类选择器.class
  • 属性选择器a[href="bb"]
  • 后代选择器h2 p
  • 子代选择器div > p
  • 相邻选择器div + h1
  • 伪类选择器a:hover
  • 标签选择器div,p

可继承样式:

  • font-size
  • font-family
  • color

不可继承样式:

  • margin
  • padding
  • border
  • width
  • height

优先级:

  • !important > 内联 > id > class > tag
  • !important比内联优先级高,内联优先级比id高

HTML语义化

1、当样式丢失时,页面能够呈现出清晰 的结构

2、有利于SEO搜索爬取有效信息

3、方便其他设备解析(屏幕阅读器、盲人阅读器)

new操作符的作用

1、创建空对象,并将this指向该对象

2、属性和方法被加入到this指向的对象中

3、最后隐式返回this

深拷贝与浅拷贝

区别:

浅拷贝只拷贝基本类型的值,如果对象中属性值为引用类型,也只是复制指针,而深拷贝能另外创造一个一模一样的对象。以下代码就是浅拷贝

var obj = {
    p: {
        name: 1
    },
    q: 2
}
var newObj = Object.assign({}, obj)
newObj.p.name = 2
console.log(obj.p.name) // 变为2

浅拷贝:

let copy = {...{x:1}}

let copy2 = Object.assign({}, {x:1})

深拷贝代码:

let copy = JSON.parse(JSON.stringify({x:1}))

// 递归拷贝
function cloneDeep(obj) {
  let newobj = obj instanceof Array ? [] : {}
  for (let key in obj) {
    newobj[key] = typeof obj[key] === 'object' ? cloneDeep(obj[key]) : obj[key]
  }
  return newobj
}
var a = {b: 1, c: [1,2], d: {dd: 1}, e: [{ee:1}]}
console.log(cloneDeep(a))

创建对象几种方式

  • 工厂模式:不能解决对象类型识别问题

    function Person(name) {
        var o = new Object()
        o.name = name
        return o
    }
  • 构造函数模式:缺点是每个方法都要在实例中创建一遍

  • 原型模式:缺点是所有实例只能共享属性值

  • 组合使用构造函数与原型模式

  • 动态原型模式:在构造函数当中去初始化原型、

function Person(name) {
    this.name = name
    // 在构造函数中初始化原型的某些属性
    if (typeof this.showName !== 'function') {
        Person.prototype.showName = function() {
            return name
        }
    }
}

JS继承

原型链继承:通过改写prototype属性实现继承。该方法的缺点是无法向超类型构造函数传递参数

借用构造函数:在构造函数中,改变超类型的this指向,实现向超类型构造函数传递参数。缺点是超类型的原型中的方法不可复用

组合继承:结合了原型链继承和借用构造函数的优点,缺点是调用了两次构造函数

function superType(name) {
  this.name = name
}
superType.prototype.sayName = function() {
  console.log(this.name)
}
function subType() {
  superType.call(this, 'haha') // 继承属性
}
subType.prototype = new superType() // 继承方法
var a = new subType()
console.log(a)
a.sayName()
a.name = '123'
a.sayName()

原型式继承:借住已有对象创建新对象,它不需要为新对象额外创建自定义类型

实际上改写prototype时执行了一次浅复制。缺点是会共享引用类型值的属性

function create(obj){
    function F()
    F.prototype = obj
    return new F()
}

类型转换比较

undefined
null true
[] false
{} false
'' false
0 false
false false
null
undefined true
'' false
[] false
P false
0 false
[]
{} false
'' true
0 true
false true
''
0 true
{} false
false true
false
0 true
{} {} ==false报错,而false=={}为false
{}
0

相等操作符的比较

==操作符会将操作数进行数据类型的强制转换

1、如果有操作数为布尔值,则布尔值转为数字,false转0,true转1

2、如果有操作数为字符串,则字符串转为数字

3、如果有操作数为对象,则调用valueOf, 没有valueOf就有toString,都没有就返回NaN

反正就是都转为数字来进行比较

对象转字符串或数字

var a = {
    toString: () => '1',
    valueOf: () => 0
}
a == '1' // false
a == 0 // true
var b = {
    toString: () => '1'
}
b == '1' // true

对象转字符串:

1、判断对象是否有toString,有则调用

2、没有toString,判断有无valueOf,有则调用

3、都没有,报错

对象转数字:

1、判断有无valueOf,有则调用

2、没有valueOf,判断有无toString,有则调用

3、都没有。报错

HTTP各版本

HTTP/1.1:

1、持久连接:只要任意一端没有提出断开连接则保持TCP连接状态,这样不用每次发请求都建立并断开TCP连接

2、管道化连接:不需要等待前一个事务(HTTP请求与响应)完成,就可以发送多个请求

Http1.1:利用Connection保持连接

Connection:Keep-Alive

Keep-Alive:max=5,timeout=120

以上两行,connection指明该Http1.0需要保持持久连接,keep-alive选项则 规定了最多max个事务保持连接,空闲timeout秒后断开

在HTTP1.1中改进了keep-alive,用了更好的设计,称之为持久连接,他是默认的。当客户端和服务器可以在报文中添加connection:close对连接进行关闭

Cookies机制

cookies分为会话cookies和持久cookies,当没有设置max-age或者expires来扩展过期时间的时候,那么它就是会话cookies。

会话cookies:当用户退出浏览器的时候,会自动删除

持久cookies:存储在硬盘里

工作机制

服务器在Http响应头中添加set-cookie选项,如set-cookie: id="123"; domain:"joes-hardware.com",键值对之间用分号隔开。

浏览器收到set-cookies选项,会进行存储,并在每次请求中自动添加cookies选项

Domain属性

Set-cookie中添加domain属性,指定哪些域可以查看这些cookies。

例如:domain=".airtravel.com" 指明以.airtravel.com结尾的站点都可以查看

域名相同,端口不同的站点都可以共享cookies

缓存机制

因为某些资源变动较少,服务器可以允许客户端对资源缓存到硬盘中,以此减少网络资源流量的传输,提高响应速度

1、通过Cache-Control:max-age=100 来设置100秒后缓存过期,或者Expires设置绝对过期日期例如2020年3月5日18:00时资源过期。

2、当资源过期后,客户端要询问服务器资源是否已修改,如果服务器再验证发现资源已修改,则返回已修改的资源给客户端。服务器再验证发现没有修改的话,那么在Http响应中更新缓存过期时间即可

通过两个条件首部进行条件验证:

1、If-Modified-Since:\:这个首部通常配个Last-Modified一起使用,它指明了资源最后修改时间,如果时间对不上,说明资源已经修改了

2、If-None-Match::这个首部通常配合ETag进行使用,它指明了资源的序列号,如果序列号对不上,说明资源已经修改了

当服务器再验证发现资源没修改时,返回304状态码(Not Modified)资源未修改

no-store和no-cache

在Cache-Control选项里可以选这两个值

no-store:彻底禁用缓存,浏览器将不缓存资源

no-cache:不缓存过期资源。使用缓存前强制向服务器发送请求进行再验证对比ETag,只有服务器返回304未修改才使用缓存

虚拟DOM

虚拟DOM其实就是用JavaScript去模拟真实的DOM,当DOM变化的时候,先在虚拟DOM上进行新旧对比,然后将差异部分更新到真实的DOM上。

Vue中key值的作用

key值主要出现在使用v-for的时候,需要为每个渲染元素指定key值。当v-for更新列表时,默认采用就地复用的策略。如果顺序被打乱,vue不会移动DOM元素来匹配,而是用就地复用每个元素。key的作用是为了更高效的更新虚拟DOM

为每个元素指定key值,以便vue能跟踪节点的身份

https://www.jianshu.com/p/4bd5e745ce95

Vue组件通信

1、props和emit向父组件派发事件来进行传值。

2、vuex:这是vue配套的状态管理插件,通过抽离部分状态为全局状态,使整个数据流变得清晰可追踪。

Vue的prop单向数据流

父级prop的更新将向下流动到子组件中,反过来不行。但是如果传入的prop是引用类型比如数组或者对象,那么子组件对prop的改动会影响父组件的状态

vuex有哪几种属性

state、getter、mutation、action、module

vue中的v-model

v-model实际上是默认利用了名为value的prop属性和名为input的事件,通过model选项来指定prop和事件的名称

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

vue的插槽

使用插槽可以将组件内的slot替换为任何模板代码

<navigation-link url="/profile">
  Your Profile
</navigation-link>

然后你在 <navigation-link> 的模板中可能会写为:

<a
  v-bind:href="url"
  class="nav-link"
>

注意:如果组件内没有slot元素,那么组件起始标签和结束标签之间的任何内容都会被抛弃

当含有多个插槽时,为每个插槽指定一个name,这是就可以指定希望往哪个具名插槽内添加模板代码了

各种跨域技术

JSONP:利用script没有跨域限制,来对服务器发起请求。

function jsonp(url, params, callback) {
    return new Promise((res, rej) => {
        let script = document.createElement('script')
        window[callback] = function(data) { // 注册callback函数
            res(data)
            document.body.removeChild(script)
        }
        let arrs = []
        for (let key in params) {
            arrs.push(`${key}=${params[key]}`)
        }
        arrs.push('callback='+callback)
        script.src = `${url}?${arrs.join('&')}`
        document.body.appendChild(script)
    })
}
jsonp('http://localhost:3000/api', {
    num: 123
}, 'show').then(data => {
    console.log(data)
})
// http://localhost:3000/api?num=123&callback=show
// callback名为show

CORS:跨域资源共享

后端需要设置Access-Control-Allow-Origin来设置允许跨域的源。

前端发起请求需要自动携带Origin字段

postMessage:跨文档消息传递。允许多窗口间的消息传递

例如父页面与iframe的通信

Websocket:这是一个全双工的通信,是持久化的协议。HTTP返回101状态码来更改为upgrade选项中指明的协议

Node代理服务器:同源策略只针对浏览器,而服务器与服务器之间没有同源限制。开启一个Node服务器作为代理,设置CORS跨域资源共享,接受到前端请求后转发给服务器,并将服务器返回的数据转发给前端。webpack中就提供了后台转发的功能

判断宿主对象

宿主对象:由宿主环境提供的对象,在浏览器中是window对象及其子对象(location、document、navigator、history、screen),在nodejs中是global对象及其子对象

try {
  if (window) {
    console.log('window') // 在浏览器
  }
} catch (e) {}

try {
  if (global) {
    console.log('global') // 在nodejs
  }
} catch(e) {}

移动端原生事件

orientationchange:设备纵横变换事件

触摸事件:

  • touchstart:触摸屏幕时触发
  • touchmove:滑动时连续触发
  • touchend:触摸结束
  • touchcancel:停止跟踪触摸

手势事件:

  • gesturestart:手势开始
  • gesturechange
  • gestureend

箭头函数

箭头函数没有this、arguments、new.target等属性

他只能通过作用域查找来寻找this

如果被包含在非箭头函数时,自动绑定到最近一层非箭头函数。否则自动绑定到全局对象

class和构造函数的区别

1、class不能被提升

new Foo(); // 报错
class Foo{}

2、class中所有方法默认是不可枚举的

3、class必须使用new调用否则会报错

in和hasOwnProperty

in:能够检测对象是否含有某个属性。它能够检测到继承的属性

hasOwnProperty:检测对象自身是否含有某个属性。它不能检测到继承的属性

进程和线程

进程:是程序的一次执行过程。是系统分配资源的基本单位。

线程:是任务调度和执行的基本单位,线程之间可以共享内存。

线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程。

进程切换的开销比较大,线程之间切换开销比较小。

检测是否通过new调用

new.target元属性

该属性是来自ES6中,旨在检测一个构造函数是否通过new调用

如果不是通过new调用,new.target为undefined

如果是new调用,new.target为该构造函数

function Person(name) {
    if (new.target === Person) {
        console.log('new 调用')
    } else if (new.target === undefined) {
        console.log('请通过new调用')
    }
}

通过this instanceof [[constructor]]判断

当使用new的时候,new会创建新的对象,并将this指向该对象

function Person(name) {
    if (this instanceof Person) {
        console.log('new 调用')
    } else  {
        console.log('请通过new调用')
    }
}

this指向问题

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数被多个对象包含,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

this永远指向的是最后调用它的对象

https://www.cnblogs.com/pssp/p/5216085.html

一句话:如果一个函数没有被一个对象所调用,那么它指向window。只有被对象所调用,才会指向对象

HTTP常见状态码

302:临时重定向。展示最新的网页,但网址仍然是旧网址

301:永久重定向。网页将永久转移到另一个网址上

https://www.cnblogs.com/unixcs/p/10668960.html

304:未修改

401:未验证登录

403:拒绝访问

500:服务器错误

五层网络模型

自查哈

#笔试题目#
全部评论
真的非常感谢
1 回复 分享
发布于 2021-04-03 10:50

相关推荐

评论
20
153
分享
牛客网
牛客企业服务