常见考点
2022年10月14日大约 5 分钟
问题考点
typeof 能判断哪些类型
- 考点:JS 变量类型
- 返回判断:
- 所有值类型
- 函数
- 引用类型(不可细分)
何时使用 === 何时使用 ==
- 考点:强制类型装换
- 使用:除了
== null
之外,其他一律用===
if (obj.a == null) {
}
// 相当于
// if( obj.a === null || obj.a === undefined ) {}
window.onload 和 DOMContentLoaded 的区别
- 考点:页面渲染过程
JS 创建 10 个 a 标签,点击时候弹出对应的序号
- 考点:JS 作用域
手写节流 throttle 、防抖 debounce
- 考点:性能、体验优化
Promise 解决了什么问题
- 考点:JS 异步
知识体系
基础语法
变量类型和计算
JS-Web-API
/**
* 深拷贝
* @param {*} obj
*/
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) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key])
}
}
return result
}
let obj1 = {
name: 'sunzhenyang',
age: 26,
address: {
city: 'beijing'
},
arr: ['a', 'b', 'c', 'd']
}
let obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'e'
console.log(obj1.address.city, obj2.address.city)
console.log(obj1.arr[0], obj2.arr[0])
原型关系
- 每个 class 都有显式原型 prototype
- 每个实例都有隐式原型 proto
- 实例的隐式原型指向对应 class 的 prototype
- 每个 class 的显式原型的隐式原型指向其父类的隐式原型
- Object 的隐式原型指向 null
- instanceof:沿着隐式原型往上找到对应的显式原型
class 本质
- 原型和原型链的图示
- 属性和方法的执行规则
![](https://cdn.jsdelivr.net/gh/sunzhenyang/blog-vuepress-img/img/iShot2021-11-07 20.21.38.png)
闭包
作用域应用的特殊情况
- 函数作为参数被传递
- 函数作为返回值被返回
应用
隐藏数据
自由变量的查找,是在函数定义的地方,向上级作用于查找,不是在执行的地方!!!
// 函数作为返回值
function create() {
let a = 100
return function () {
console.log(a)
}
}
let fn = create()
let a = 200
fn() // 100
// 函数作为参数
function print(fn) {
let a = 200
fn()
}
let a = 100
function fn() {
console.log(a)
}
print(fn) // 100
this
调用场景
- 作为普通函数
- 使用 call apply bind
- 作为对象方法被调用
- class 方法中被调用
- 箭头函数
*this 取值是在函数执行时被确定的,不是在函数定义时确定的!!!
// 数组参数转换为数组
function fn() {
const args = Array.prototype.slice.call(arguments)
}
Function.prototype.myBind = function (context, ...args) {
const self = this
return function () {
return self.apply(context, args)
}
}
Function.prototype.myCall = function (context, ...args) {
context = context || window
context.fn = this
const result = context.fn(...args)
delete context.fn
return result
}
Function.prototype.myApply = function (context, args) {
context = context || window
context.fn = this
const result = context.fn(args)
delete context.fn
return result
}
function fn1(a, b, c) {
console.log(this, a, b, c)
}
function fn2([a, b, c]) {
console.log(this, a, b, c)
}
const f2 = fn1.myBind({ x: 10 }, 20, 30, 40)
f2()
fn1.myCall({ x: 10 }, 20, 30, 40)
fn2.myApply({ x: 10 }, [20, 30, 40])
作业
- 研究 call bind apply
- 手写 call bind apply
- 闭包隐藏数据
运行环境
加载过程
- DNS 解析:域名 -> IP 地址
- 浏览器根据 IP 地址向服务器发起 http 请求(三次握手等等)
- 服务器处理 http 请求,并返回给浏览器
渲染过程
- 根据 HTML 生成 DOM Tree
- 根据 CSS 生成 CSSOM
- 将 DOM Tree 和 CSSOM 整合形成 Render Tree
- 遇到
<script>
则暂停渲染,优先加载并执行 JS 代码,完成再继续 - 直至 Render Tree 渲染完成
性能优化
- 空间换时间(适用于所有变成的性能优化)
- 多使用内存、缓存或其他方法
- 减少 CPU 计算量,介绍网络加载耗时
- 从何入手
- 让加载更快
- 减少资源体积,压缩代码
- 减少访问次数:合并代码,SSR 服务器端渲染
- 缓存
- 静态资源加 hash 后缀,根据文件内容计算 hash
- 文件内容不变,则 hash 不变,则 url 不变
- url 和 文件不变,则会自动触发 http 缓存机制,返回 304
- 让渲染更快
- CSS 放在 head,JS 放在 body 最下面
- 尽早开始执行 JS,用 DOMContentLoaded 触发
- 懒加载( 图片懒加载,上滑加载更多 )
- 对 DOM 查询进行缓存
- 频繁 DOM 操作,合并到一起插入 DOM 结构 ( createDocumentFragment )
- 让渲染更加流程
- 节流 throttle
- 防抖 debounce
- 使用更快的网络:CDN
- 让加载更快
防抖 debounce
场景
- 监听输入框文字变化后触发 change 事件
- 直接用 keyup 事件,则会频繁触发 change 事件
- 防抖:用户结束或暂停时,才会触发 change 事件
// 防抖
const inputEl = document.getElementById('input')
const timer = null
inputEl.addEventListener('keyup', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 模拟触发change时间
console.log(inputEl.value)
timer = null
}, 500)
})
// 防抖封装
function debounce(fn, delay = 1000) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.call(this)
timer = null
}, delay)
}
}
const inputEl = document.getElementById('input')
inputEl.addEventListener(
'keyup',
debounce(function () {
console.log(this.value, arguments)
}, 500)
)
节流 throttle
场景
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置
- 直接用 drag 事件,则会频繁触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每个 100ms 触发一次
// 节流
const divEL = document.getElementById('div')
let timer = null
divEL.addEventListener('drag', function (e) {
if (timer) {
return
}
timer = setTimeout(() => {
console.log(e.offsetX, e.offsetY)
timer = null
}, 500)
})
// 节流封装
function throttle(fn, delay = 100) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
const divEL = document.getElementById('div')
divEL.addEventListener(
'drag',
throttle(e => {
console.log(e.offsetX, e.offsetY)
}, 100)
)
安全
常见的 web 前端攻击方式
XSS 跨站请求攻击
攻击方式
- 一个博客网站,我发表一篇博客,其中嵌入
<script>
脚本 - 脚本内容:获取 cookie,发送到我的服务器( 服务器配合跨域 )
- 发布之篇博客,有人查看它,轻松收割访问者 cookie
预防
- 替换特殊字符,如
<
变为<
,>
变为>
<script>
变为<script>
,直接显示,而不会作为脚本执行- 前端要替换,后端也要替换,都做总不会有错
工具
XSRF 跨站请求伪造
攻击方式
- 你正在购物,看中了某个商品,商品 id 是 100
- 付费接口是 xxx.com/pay?id=100,但没有任何验证
- 我是攻击者,我看中了一个商品,id 是 200
- 我向你发送一封电子邮件,邮件标题很吸引人
- 但邮件正文隐藏着 <img src=xxx.com/pay?id=200 />
- 你已查看右键,就帮我购买了 id 是 200 的商品
预防
- 使用 POST 接口
- 增加验证,例如密码、短信验证码、指纹等