1.性能优化
1.1优化原则和方向
原则
- 多使用内存、缓存或者其他方法
- 减少 CPU 计算、较少网络
方向
- **加载页面和静态资源**
- **页面渲染**
1.2方法:
## 加载资源优化
- 静态资源的压缩合并(JS代码压缩合并、CSS代码压缩合并、雪碧图)
- 静态资源缓存(资源名称加 MD5 戳)
- 使用 CND 让资源加载更快
- 使用 SSR 后端渲染,数据直接突出到 HTML 中
## 渲染优化
- CSS 放前面 JS 放后面
- 懒加载(图片懒加载、下拉加载更多)
- 减少DOM 查询,对 DOM 查询做缓存
- 减少DOM 操作,多个操作尽量合并在一起执行(`DocumentFragment`)
- 事件节流
- 尽早执行操作(`DOMContentLoaded`)
1.3 事件节流
例如要在文字改变时触发一个 change 事件,通过 keyup 来监听。使用节流。
var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(function () {
// 触发 change 事件
}, 100)
})
1.4 尽早执行操作
window.addEventListener('load', function () {
// 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function () {
// DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})
2.常见的 web 攻击方式有哪些,简述原理?如何预防?
2.1前端端最常见的攻击就是 XSS(Cross Site Scripting,跨站脚本攻击):很多大型网站(例如 FaceBook 都被 XSS 攻击过)。举一个例子,我在一个博客网站正常发表一篇文章,输入汉字、英文和图片,完全没有问题。但是如果我写的是恶意的 js 脚本,例如获取到`document.cookie`然后传输到自己的服务器上,那我这篇博客的每一次浏览,都会执行这个脚本,都会把自己的 cookie 中的信息偷偷传递到我的服务器上来。
预防 XSS 攻击就得对输入的内容进行过滤,过滤掉一切可以执行的脚本和脚本链接。大家可以参考[xss.js](https://github.com/leizongmin/js-xss)这个开源工具。
简单总结一下,XSS 其实就是攻击者事先在一个页面埋下攻击代码,让登录用户去访问这个页面,然后偷偷执行代码,拿到当前用户的信息。
2.2还有一个比较常见的攻击就是 CSRF/XSRF(Cross-site request forgery,跨站请求伪造):它是借用了当前操作者的权限来偷偷的完成某个操作,而不是拿到用户的信息。例如,一个购物网站,购物付费的接口是`http://buy.com/pay?id=100`,而这个接口在使用时没有任何密码或者 token 的验证,只要打开访问就付费购买了。一个用户已经登录了`http://buy.com`在选择商品时,突然收到一封邮件,而这封邮件正文有这么一行代码`<img src="http://buy.com/pay?id=100"/>`,他访问了邮件之后,其实就已经完成了购买。
预防 CSRF 就是加入各个层级的权限验证,例如现在的购物网站,只要涉及到现金交易,肯定输入密码或者指纹才行。
3.JS中使用`typeof`能得到的哪些类型
typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {} // object
typeof [] // object
typeof null // object
typeof console.log // function
4.说明 this 几种不同的使用场景
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
- call apply bind
5.前端使用异步的场景有哪些
setTimeout setInterval
网络请求
6.构造函数
function DomElement(selector) {
var result = document.querySelectorAll(selector)
var length = result.length
var i
for (i = 0; i < length; i++) {
this[i] = selectorResult[i]
}
this.length = length
}
// 修改原型
DomElement.prototype = {
constructor: DomElement,
get: function (index) {
return this[index]
},
forEach: function (fn) {
var i
for (i = 0; i < this.length; i++) {
const elem = this[i]
const result = fn.call(elem, elem, i)
if (result === false) {
break
}
}
return this
},
on: function (type, fn) {
return this.forEach(elem => {
elem.addEventListener(type, fn, false)
})
}
}
// 使用
var $div = new DomElement('div')
$div.on('click', function() {
console.log('click')
})
7.用于`replace`的示例
function trim(str) {
return str.replace(/(^\s+)|(\s+$)/g, '')
}
8.获取`2017-06-10`格式的日期
function formatDate(dt) {
if (!dt) { dt = new Date() }
var year = dt.getFullYear()
var month = dt.getMonth() + 1
var date = dt.getDate()
if (month < 10) {
// 强制类型转换
month = '0' + month
}
if (date < 10) {
// 强制类型转换
date = '0' + date
}
// 强制类型转换
return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)
9.获取随机数,要求是长度一直的字符串格式
var random = Math.random()
var random = random + '0000000000' // 后面加上 10 个零
var random = random.slice(0, 10)
console.log(random)
10.写一个能遍历对象和数组的`forEach`函数
function forEach(obj, fn) {
var key
if (obj instanceof Array) {
// 准确判断是不是数组
obj.forEach(function (item, index) {
fn(index, item)
})
} else {
// 不是数组就是对象
for (key in obj) {
fn(key, obj[key])
}
}
}
var arr = [1,2,3]
// 注意,这里参数的顺序换了,为了和对象的遍历格式一致
forEach(arr, function (index, item) {console.log(index, item)})
var obj = {x: 100, y: 200}
forEach(obj, function (key, value) {console.log(key, value)})
11.如何检测浏览器的类型
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
12.拆解url的各部分
console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.pathname) // '/learn/199'
console.log(location.search)
console.log(location.hash)
13.编写一个通用的事件监听函数
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, function (e) {
var target
if (selector) {
target = e.target
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
14.手动编写一个 ajax,不依赖第三方库
var xhr = new XMLHttpRequest()
xhr.open("GET", "/api", false)
xhr.onreadystatechange = function () {
// 这里的函数异步执行,可参考之前 JS 基础中的异步模块
if (xhr.readyState == 4) {
if (xhr.status == 200) { alert(xhr.responseText) }
}
}
xhr.send(null)
xhr.readyState 的状态吗说明
- 0 - (未初始化)还没有调用send()方法
- 1 -(载入)已调用send()方法,正在发送请求
- 2 -(载入完成)send()方法执行完成,已经接收到全部响应内容
- 3 -(交互)正在解析响应内容
- 4 -(完成)响应内容解析完成,可以在客户端调用了
### status
http 状态吗有 `2xx` `3xx` `4xx` `5xx` 这几种,比较常用的有以下几种
- 200 正常
- 404 找不到资源
- 5xx 服务器端出错了
15.跨域的几种实现方式
①JSONP;②服务器端设置 http header
16.请描述一下 cookie,sessionStorage 和 localStorage 的区别?
## cookie 有它致命的缺点:
- 存储量太小,只有 4KB
- 所有 http 请求都带着,会影响获取资源的效率
- API 简单,需要封装才能用
## locationStorage 和 sessionStorage
后来,HTML5标准就带来了`sessionStorage`和`localStorage`,先拿`localStorage`来说,它是专门为了浏览器端缓存而设计的。其优点有:
- 存储量增大到 5M
- 不会带到 http 请求中
- API 适用于数据存储 `localStorage.setItem(key, value)` `localStorage.getItem(key)`
17.写出一些常用的 git 命令
- git add .
- git checkout xxx
- git commit -m "xxx"
- git push origin master
- git pull origin master
- git stash / git stash pop
18.简述多人使用 git 协作开发的基本流程
- git branch
- git checkout -b xxx / git checkout xxx
- git merge xxx
19.上线和回归
### 上线原理
- 将测试完成的代码提交到git版本库的master分支
- 将当前服务器的代码全部打包并记录版本号,备份
- 将master分支的代码提交覆盖到线上服务器,生成新版本号
### 回滚原理
- 将当前服务器的代码打包并记录版本号,备份
- 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号
20.从输入url到得到html的详细过程
- 浏览器根据 DNS 服务器得到域名的 IP 地址
- 向这个 IP 的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容
21.浏览器渲染页面的过程
- 根据 HTML 结构生成 DOM Tree
- 根据 CSS 生成 CSS Rule
- 将 DOM 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree 开始渲染和展示
- 遇到`<script>`时,会执行并阻塞渲染
22.window.onload 和 DOMContentLoaded 的区别
- 页面的全部资源加载完才会执行,包括图片、视频等
- DOM 渲染完即可执行,此时图片、视频还没有加载完
23.ES6
- 搭建 ES6 编译环境(使用 babel 编译,使用 webpack 做模块化处理)
- modules:关键字`import` `export`
- 琐碎的功能:let/const,多行字符串/模板变量,解构赋值,块级作用域,函数默认参数,箭头函数
- class:用法、和原型的关系、继承
- Promise:用法
// 加载一张图片
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject()
}
img.src = src
})
return promise
}
var src = 'http://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
console.log(img.width)
}, function () {
console.log('failed')
}).then(function (img) {
console.log(img.height)
})
24.event-loop
将 js 中要执行的每个任务都做一个划分,同步执行的放在“执行栈”,而异步执行的**将**放在(不是马上就放进去)“异步队列”。然后,将“执行栈”放在主线程中执行,挨个任务排队执行,执行到最后就立马去看“异步队列”是否有数据,有数据就拿到主线程中执行,执行完再去看“异步队列”是否有数据。
25.异步的变同步的解决方案(异步本身没问题,但callback hell是有问题的,所以需要处理)
25.1 Promise (then的方式,参考23)
25.2 asyc-await(注意,async/await 并不是取代了 promise ,而是利用 promise 之后的另外一种写法,取代了 then 函数,写起来更加像同步代码,看着从上到下书写,也从上到下执行)
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () { resolve(img) }
img.onerror = function () { reject() }
img.src = src
})
return promise
}
const src1 = 'http://www.imooc.com/static/img/index/logo_new.png'
const src2 = 'https://avatars3.githubusercontent.com/u/9583120'
const load = async function () {
const result1 = await loadImg(src1)
console.log(result1)
const result2 = await loadImg(src2)
console.log(result2)
}
load()
26.数组对象深拷贝(重点在递归调用,例如在对象层级好几层(特别深的时候))
function deepClone(obj = {}) {
//如果不是对象直接返回
if(typeof obj !== 'object' || obj == null) { return obj }
let result;
if(obj instanceof Array) {
result = []
} else {
result = {}
}
for(let key in obj) {
// 保证key不是原型属性
if(obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
27.手写jQuery
class Jquery{
constructor(selector){
const result = document.querySelectorAll(selector)
const length = result.length;
for(let i=0;i<result.length;i++){
this[i]=result[i]
}
this.length = result.length
this.selector = selector
}
get(index){
return this[index]
}
each(fn){
for (let i=0;i<this.length;i++){
const elem = this[i]
fn(elem)
}
}
on(type,fn){
return this.each(elem=>{
elem.addEventListener(type,fn,false)
})
}
}
28.手写bind函数
Function.prototype.bind1 = function(){
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
// 获取this(数组第一项)
const t = args.shift()
// fn1.bind(...) 中的fn1
const self = this
return function(){
return self.apply(t,args)
}
}
29.防抖(输入框输入keyword,做搜索)
function debounce(fn,delay=500){
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
timer = null
},delay)
}
}
30.节流(拖拽根据位置做一些逻辑操作)
function throttle(fn, delay = 100) {
let timer = null
return function() {
if(timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
31.事件绑定(普通事件和事件委托)
function bindEvent(elem,type,selector,fn){
if(fn==null){
fn=selector
selector = null
}
elem.addEventListener(type,event=>{
let target = event.target
if( selector ){
if(target.matches(selector)){
fn.call(target,event)
}
}else{
fn.call(target,event)
}
})
}