跳至主要內容

常见考点

Yang大约 5 分钟JavaScript

问题考点

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-07open in new window 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
预防
  • 替换特殊字符,如 < 变为 &lt;> 变为 &gt;
  • <script> 变为 &lt;script&gt;,直接显示,而不会作为脚本执行
  • 前端要替换,后端也要替换,都做总不会有错
工具
XSRF 跨站请求伪造
攻击方式
预防
  • 使用 POST 接口
  • 增加验证,例如密码、短信验证码、指纹等
上次编辑于:
贡献者: sunzhenyang