JS杂烩:
1.垃圾回收机制
主要机制是标记并清除,部分老版ie还会用到计数清除。
2.判断括号的匹配
利用栈的特性。push()入栈和pop()出栈。
3.尾递归和尾调用及优化
看阮一峰大佬的文档:尾调用优化
4.数组与树结构的相互转换
5.宏任务和微任务
- 宏任务(macro-task):整体代码script、setTimeOut、setInterval
- 微任务(mincro-task):promise.then、promise.nextTick(node)
1.事件循环
当主线程内的任务执行完毕,主线程为空时,会检查微任务的Event Queue,如果有任务,就全部执行,如果没有就执行下一个宏任务;
6.ES6
深拷贝 JSON.parse(JSON.stringify(object))
8.数据扁平化
function fn(arr) {
return arr.reduce((prev,cur) =>{
return prev.concat(Array.isArray(cur) ? fn(cur) : cur)
},[])
}
// 增加dep 代表深度
function flat(arr, dep) {
if(dep === 0) {
return arr
}
return arr.reduce((prev, cur) => {
return prev.concat(Array.isArray(cur) ? flat(cur, --dep) : cur)
}, [])
}
注意:push返回的数组的长度,concat返回的是合并后的数组,所以要用concat
a++ 先用a,再加一 --a 先减一,再用a
9.数组去重
let arr = Array.from(new Set(arr))
10.数组求最大最小值
// es5
let max = Math.max.apply(null, arr)
let min = Math.min.apply(null, arr)
//es6
let max = Math.max(...arr)
11.两个数组取交集和差集
12.数组求和
var numbers = [65, 44, 12, 4]
var result = numbers.reduce((total, num) => { return total + num })
13.promise、async有什么区别
- promise是ES6为解决异步回调而生,async/await是基于Promise实现的,它不能用于普通的回调函数。建议使用async/await
- async更简洁,不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码
- Async/Await让try/catch可以同时处理同步和异步错误,Promise中要分别处理
- 根据返回数据决定的时候,async更简洁易读。
- promise不能在返回表达式的箭头函数中设置断点,async更容易调试
14.手写promise
15.input 中监听值的变化是在监听什么事件
keypress,keydown,keyup,input可以实时监控,onChange需要失去焦点才能触发。
16.函数科里化
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.from(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function () {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
console.log(add2(1, 2)(4)(2)) // 9
17. 变量提升
var a = 10
function test() {
console.log(a)
var a = 1
}
test()
输出 undefined
先找搜索局部变量,再搜索全局变量。
先声明变量,再赋值。
变量提升 相当于 var a;
var a = 10
function test() {
console.log(a)
a = 1
}
输出 10
原型链:
1.原型、构造函数、实例、原型链
2.instanceof 和 typeof 的区别
typeof 对于基本数据类型(null, undefined, string, number, boolean, symbol),除了 null 都会返回正确的类型。null 会返回 object。
typeof 对于对象类型,除了函数会返回 function,其他的都返回 object。
如果我们想获得一个变量的正确类型,可以通过Object.prototype.toString.call(xx)
。这样我们就可以获得类似[object Type]
的字符串。instanceof 用于判断一个引用类型是否属于某构造函数;还可以在继承关系中用来判断一个实例是否属于它的父类型。
instanceof 的原理是判断实例对象的__proto__
是否与构造函数的prototype
指向同一个引用。
3.判断是否等于undefined的方法
let a
a === void 0 // true
4.手写代码实现new,call方法
// new
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayName = function () {
console.log(this.name + this.age)
}
// 手写new
function _new(fn, ...args) {
// Object.create方法使用当前对象原型, 创建一个新对象
const obj = Object.create(fn.prototype)
// apply 改变 this
const rej = fn.call(obj, ...args)
// 优先返回新的构造函数
return rej instanceof Object ? rej : obj
}
const per = _new(Person, 'hello', 20)
per.sayName()
// call
Function.prototype._call = function (thisArg, ...args) {
if (typeof this !== 'function') {
throw new TypeError('err')
}
const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
thisArg = thisArg || window // 若没有传入this, 默认绑定window对象
thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数
const result = thisArg[fn](...args) // 执行当前函数
delete thisArg[fn] // 删除我们声明的fn属性
return result // 返回函数执行结果
}
// bind
Function.prototype.myBind = function(thisArg) {
if (typeof this !== 'function') {
return
}
var _self = this
var args = Array.prototype.slice.call(arguments, 1)
var fnNop = function () {} // 定义一个空函数
var fnBound = function () {
var _this = this instanceof _self ? this : thisArg
return _self.apply(_this, args.concat(Array.prototype.slice.call(arguments)))
}
// 维护原型关系
if (this.prototype) {
fnNop.prototype = this.prototype;
}
fnBound.prototype = new fnNop();
return fnBound;
}
5.继承
function Person (name) {
this.name = name
}
function Child(name, age) {
Person.call(this, name)
this.age = age
}
Child.prototype = Object.create(Person.prototype)
Child.prototype.constructor = Child
const p1 = new Child('wqs', 20)
网络服务相关:
1.浏览器输入 url 之后发生了什么
参考文档:在浏览器输入url后发生了什么
- 浏览器的地址栏输入URL并按下回车。
- 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
- DNS解析URL对应的IP。
- 根据IP建立TCP连接(三次握手)。
- HTTP发起请求。
- 服务器处理请求,浏览器接收HTTP响应。
- 渲染页面,构建DOM树。
- 关闭TCP连接(四次挥手)。
2.跨越解决方案
1、 通过jsonp跨域 // 只能实现get一种请求
2、 document.domain + iframe跨域 // 此方案仅限主域相同,子域不同的跨域应用场景
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)// 只服务端设置Access-Control-Allow-Origin即可 若要带cookie请求:前后端都需要设置。
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
安全
1.CSRF(Cross-site request forgery)跨站请求伪造
1)攻击原理
- 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
- 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
- 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
- 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
- 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
2)防御措施
- Token验证
- Referer 验证(简单易行,但 referer 可能被改变)
- 隐藏令牌(跟 Token验证差不多,把令牌存到 header 中)
2.XSS(cross-site scripting)跨域脚本攻击
1) 攻击原理
往 Web 页面里插入恶意Script代码
2) 防御措施
- HTML:对以下这些字符进行转义:&:& <:&alt; >:> ':' ":" /:/
- Javascript:把所有非字母、数字的字符都转义成小于256的ASCII字符;
- URL:使用Javascript的encodeURIComponent()方法对用户的输入进行编码,该方法会编码如下字符:, / ? : @ & = + $ #
3.https
证书验证阶段:
- 浏览器发起 HTTPS 请求
- 服务端返回 HTTPS 证书
- 客户端验证证书是否合法,如果不合法则提示告警
数据传输阶段:
- 当证书验证合法后,在本地生成随机数
- 通过公钥加密随机数,并把加密后的随机数传输到服务端
- 服务端通过私钥对随机数进行解密
- 服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输
性能优化
1.常用提升页面性能
- 资源压缩合并,减少 HTTP 请求
- 非核心代码异步加载(异步加载的方式,异步加载的区别)
- 利用浏览器缓存(缓存的分类,缓存原理)
- 使用 CDN
- 预解析 DNS
- 服务端使用gzip压缩,下载到客户端再解压,减少http传输时间
2.重排 Reflow和重绘 Repaint
1)触发重排 Reflow
- 增加、删除、修改 DOM 节点时,会导致 Reflow 或 Repaint
- 移动 DOM 位置,或搞个动画时
- 修改 CSS 样式时
- Resize 窗口(移动端没这个问题)或滚动的时候
- 修改网页默认字体时
2) 触发重绘 Repaint
- DOM 改动
- CSS 改动
3)如何优化
- 比如要添加多个 DOM 节点,一次性添加,而不要一个个添加。
- 更改样式,使用js添加类名
- 将动画效果应用到 position 属性为 absolute 或 fixed 的元素上
- 避免使用 CSS 表达式(例如:calc())
- 避免设置多层内联样式
2. 图片懒加载与节流
React
1.react生命周期
算法
1. 常用算法模板案例