【面试】前端JavaScript面试技巧

声明:慕课网《前端JavaScript面试技巧》的笔记,仅用于查阅复习,不得用于商业用途。javascript

BAT大牛带你横扫初级前端JavaScript面试

第1章 课程简介

1-1 课程简介

基础知识css

  • 原型、原型链
  • 做用域、闭包
  • 异步、单线程

JS APIhtml

  • DOM 操做
  • Ajax
  • 事件绑定

开发环境前端

  • 版本管理
  • 模块化
  • 打包工具

运行环境java

  • 页面渲染
  • 性能优化

1-2 前言

关于面试node

  • 基层工程师 - 基础知识
  • 高级工程师 - 项目经验
  • 架构师 - 解决方案

关于基础jquery

  • 工程师的自我修养 - 基础
  • 扎实的基础会让你高效学习新技术

1-3 几个面试题

先从几道面试题提及git

  • JS 中使用 typeof 能获得的哪些类型?
  • 什么时候使用 === ,什么时候使用 ==
  • window.onloadDOMContentLoaded 的区别?
  • 用 JS 建立 10 个 <a> 标签,点击的时候弹出对应的序号
  • 简述如何实现一个模块加载器,实现相似 require.js 的基本功能
  • 实现数组的随机排序

思考github

  • 拿到一个面试题,你第一时间看到的是什么?
  • 又如何看待网上搜出来的永远看不完的题海?
  • 如何对待接下来遇到的面试题?

1-4 如何搞定全部面试题

上一节思考问题的结论面试

  • 拿到一个面试题,你第一时间看到的是什么?- 考点
  • 又如何看待网上搜出来的永远看不完的题海?- 不变应万变
  • 如何对待接下来遇到的面试题?- 题目到知识再到题目

题目考察的知识点

  • JS 中使用 typeof 能获得的哪些类型? 考点:JS 变量类型
  • 什么时候使用 === ,什么时候使用 == ? 考点:强制类型转换
  • window.onloadDOMContentLoaded 的区别? 考点:浏览器渲染过程
  • 用 JS 建立 10 个 <a> 标签,点击的时候弹出来对应的序号 考点:做用域
  • 简述如何实现一个模块加载器,实现相似 require.js 的基本功能 考点:JS 模块化
  • 实现数组的随机排序 考点:JS 基础算法

第2章 JS 基础知识(上)

2-1 变量类型和计算 - 1

题目

  • JS 中使用 typeof 能获得的哪些类型?
  • 什么时候使用 === ,什么时候使用 ==
  • JS 中有哪些内置函数?
  • JS 变量按照存储方式区分为哪些类型,并描述其特色?
  • 如何理解 JSON ?

知识点

  • 变量类型
  • 变量计算

变量类型

  • 值类型、引用类型
  • typeof 运算符

值类型

let a = 100
let b = a
a = 200
console.log(b) // 100
复制代码

引用类型

let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21
复制代码

typeof 运算符

typeof undefined // "undefined"
typeof 'abc' // "string"
typeof 123 // "number"
typeof true // "boolean"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
typeof console.log // "function"
复制代码

变量计算 - 强制类型转换

字符串拼接

let a = 100 + 10 // 110
let b = 100 + '10' // "10010"
复制代码

== 运算符

100 == '100' // true
0 == '' // true
null = undefined // true
复制代码

if 语句

let a = true
if (a) {}
let b = 100
if (b) {}
let c = ''
if (c) {}
复制代码

逻辑运算符

console.log(10 && 0) // 0
console.log('' || 'abc') // "abc"
console.log(!window.abc) // true

// 判断一个变量会被当作 true 仍是 false
let a = 100
console.log(!!a) // true
复制代码

2-2 变量类型和计算 - 2

解答

JS 中使用 typeof 能获得的哪些类型?

答:undefinedstringnumberbooleanobjectfunctionsymbol

let sym = Symbol('commet')
console.log(typeof sym) // "symbol"
复制代码

什么时候使用 === ,什么时候使用 ==

答:判断对象的某个属性是否存在或为 null 时可使用 == ,由于 jQuery 源码这么使用

if (obj.a == null) {
  // 这里至关于 obj.a === null || obj.a === undefined 的简写形式
  // 这是 jQuery 源码中推荐的写法
}
复制代码

JS 中有哪些内置函数?

答:数据封装类对象,包括 StringNumberBooleanObjectArrayFunctionDateRegExpError

JS 变量按照存储方式区分为哪些类型,并描述其特色?

答:值类型和引用类型,一个存储值,一个存储引用地址,引用类型能够无限拓展属性

2-3 变量类型和计算 - 3

如何理解 JSON ?

答:JSON 只不过是一个 JS 对象而已,也是一种数据格式

JSON.stringify({ a: 10, b: 20 })
JSON.parse('{"a":10,"b":20}')
复制代码

2-4 变量类型和计算 - 代码演示

if 语句中条件为 false 的状况有哪些?

答:''0NaNfalsenullundefined

JS 中有哪些内置对象?

答:JSONMath

2-5 typeof symbol

let s = Symbol()
typeof s // "symbol"
let s1 = Symbol()
console.log(s === s1) // false
let s2 = Symbol('s2s2')
console.log(s2) // Symbol(s2s2)
let s3 = s2
console.log(s3 === s2) // true
let sym1 = Symbol('111')
let sym2 = Symbol('222')
let obj = { [sym1]: 'hello world' }
obj[sym2] = 123
console.log(obj) // {Symbol(111): "hello world", Symbol(222): 123}
console.log(obj[sym1]) // "hello world"
console.log(obj[sym2]) // 123
复制代码

2-6 原型和原型链

题目

  • 如何准确判断一个变量是数组类型
  • 写一个原型链继承的例子
  • 描述 new 一个对象的过程
  • zepto 或其余框架源码中如何使用原型链

知识点

  • 构造函数
  • 构造函数 - 扩展
  • 原型规则和示例
  • 原型链
  • instanceof

构造函数

function Foo(name, age) {
  this.name = name
  this.age = age
  this.class = 'class__1'
  // return this // 默认有这一行
}
let f = new Foo('negrochn', 18)
// let f2 = new Foo('lexiaodai', 17) // 建立多个对象
复制代码

构造函数 - 扩展

let a = {} // 实际上是 let a = new Object() 的语法糖
let b = [] // 实际上是 let b = new Array() 的语法糖
function Foo() {} // 实际上是 let Foo = new Function()
// 使用 instanceof 判断一个函数是不是一个变量的构造函数
console.log(b instanceof Array) // true
复制代码

2-7 原型和原型链 - 5 条原型规则

5 条原型规则

  1. 全部的引用类型(数组、对象、函数),都具备对象特性,便可自由扩展属性(除了 null 之外)
  2. 全部的引用类型(数组、对象、函数),都有一个 __proto__(隐式原型)属性,属性值是一个普通的对象
  3. 全部的函数,都有一个 prototype(显式原型)属性,属性值也是一个普通的对象
  4. 全部的引用类型(数组、对象、函数),__proto__ 属性值指向它的构造函数的 prototype 属性值
  5. 当试图获得一个对象的某个属性时,若是这个对象自己没有这个属性,那么会去它的 __proto__(即它的构造函数的 prototype)中寻找
// 原则 1
let obj = {}
obj.a = 100
let arr = []
arr.a = 100
function fn() {}
fn.a = 100

// 原则 2
console.log(obj.__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)

// 原则 3
console.log(fn.prototype)

// 原则 4
console.log(obj.__proto__ === Object.prototype) // true
复制代码
// 构造函数
function Foo(name) {
  this.name = name
}
Foo.prototype.alertName = function() {
  alert(this.name)
}

// 建立实例
let f = new Foo('negrochn')
f.printName = function() {
  console.log(this.name)
}
// 测试
f.printName()
f.alertName() // 原则 5
复制代码

2-8 原型和原型链 - 5 条原型规则 补充 2 点

for (let key in f) {
  // 高级浏览器已经在 for in 中屏蔽了来自原型的属性
  // 可是这里建议你们仍是加上这个判断,保证程序的健壮性
  if (f.hasOwnProperty(key)) {
    console.log(key)
  }
}
复制代码

2-9 原型和原型链 - 原型链

// 构造函数
function Foo(name) {
  this.name = name
}
Foo.prototype.alertName = function() {
  alert(this.name)
}

// 建立实例
let f = new Foo('negrochn')
f.printName = function() {
  console.log(this.name)
}
// 测试
f.printName()
f.alertName()
f.toString() // 要去 f.__proto__.__proto__ 中查找
复制代码

原型链

2-10 原型和原型链 - instanceof

// instanceof 用于判断引用类型属于哪一个构造函数的方法
console.log(f instanceof Foo) // true, f 的 __proto__ 一层一层往上,可否对应到 Foo.prototype
console.log(f instanceof Object) // true
复制代码

2-11 原型和原型链 - 解答 1

如何准确判断一个变量是数组类型

答:使用 instanceof Array

let arr = []
console.log(arr instanceof Array) // true
console.log(typeof arr) // "object" ,typeof 是没法判断是不是数组的
复制代码

写一个原型链继承的例子

// 动物
function Animal() {
  this.eat = function() {
    console.log('animal eat')
  }
}
// 狗
function Dog() {
  this.bark = function() {
    console.log('dog bark')
  }
}
Dog.prototype = new Animal()
// 哈士奇
let hashiqi = new Dog()

// 接下来代码演示时,会推荐更加贴近实战的原型继承示例
复制代码

描述 new 一个对象的过程

答:

  1. 建立一个新对象
  2. this 指向这个新对象
  3. 执行代码,即对 this 赋值
  4. 返回 this
function Foo(name, age) {
  this.name = name
  this.age = age
  this.class = 'class__1'
  // return this // 默认有这一行
}

let f = new Foo('negrochn', 18)
复制代码

zepto 或其余框架源码中如何使用原型链

答:

  • 阅读源码是高效提升技巧的方式
  • 但不能“埋头苦钻”,有技巧在其中
  • 慕课网搜索“zepto 设计和源码分析”

2-12 原型和原型链 - 解答 2

写一个封装 DOM 查询的例子

function Elem(id) {
  this.elem = document.getElementById(id)
}

Elem.prototype.html = function(val) {
  let elem = this.elem
  if (val) {
    elem.innerHTML = val
    return this // 为了链式操做
  } else {
    return elem.innerHTML
  }
}

Elem.prototype.on = function(type, fn) {
  let elem = this.elem
  elem.addEventListener(type, fn)
  return this // 为了链式操做
}

let div1 = new Elem('div1')
div1.html('<p>Hello World</p>').on('click', function() {
  alert('clicked')
})
复制代码

2-13 原型和原型链 - 代码演示

第3章 JS 基础知识(中)

3-1 做用域和闭包 - 执行上下文

题目

  • 说一下对变量提高的理解
  • 说明 this 几种不一样的使用场景
  • 建立 10 个 <a> 标签,点击的时候弹出对应的序号
  • 如何理解做用域
  • 实际开发中闭包的应用

知识点

  • 执行上下文
  • this
  • 做用域
  • 做用域链
  • 闭包

执行上下文

  • 范围:一段 <script> 或者一个函数
  • 全局上下文:执行前,会先把变量定义、函数声明拿出来(注意函数声明和函数表达式的区别)
  • 函数上下文:执行前,先把变量定义、函数声明、thisarguments 拿出来
console.log(a) // undefined
var a = 100

fn('negrochn') // "negrochn" 20
function fn(name) {
  age = 20
  console.log(name, age)
  var age
}
复制代码

3-2 做用域和闭包 - 执行上下文 代码演示

3-3 做用域和闭包 - this

this ,要在执行时才能确认值,定义时没法确认

  • 做为构造函数执行
  • 做为对象属性执行
  • 做为普通函数执行
  • callapplybind
var a = {
  name: 'A',
  fn: function() {
    console.log(this.name)
  }
}
a.fn() // this === a
a.fn.call({ name: 'B' }) // this 做为 { name: 'B' }
var fn1 = a.fn
fn1() // this === window
复制代码

3-4 做用域和闭包 - this 代码演示

// 做为构造函数执行
function Foo(name) {
  this.name = name
}
let f = new Foo('negrochn')
f.name // "negrochn"
复制代码
// 做为对象属性执行
let obj = {
  name: 'A',
  printName: function() {
    console.log(this.name)
  }
}
obj.printName() // "A"
复制代码
// 做为普通函数执行
function fn() {
  console.log(this)
}
fn() // window
复制代码
// call 、apply 、bind
function fn1(name, age) {
  console.log(name)
  console.log(this)
}
fn1.call({ x: 1 }, 'negrochn', 20) // "negrochn" { x: 1 }
fn1.apply({ x: 200 }, ['negrochn', 20]) // "negrochn" { x: 200 }

let fn2 = function(name, age) {
  console.log(name)
  console.log(this)
}.bind({ x: 300 })
fn2('negrochn', 20) // "negrochn" { x: 300 }
复制代码

3-5 做用域和闭包 - 做用域

做用域

  • 没有块级做用域
  • 只有函数和全局做用域
// 无块级做用域
if (true) {
  var name = 'negrochn'
}
console.log(name) // "negrochn"

// 只有函数和全局做用域
var a = 100
function fn() {
  var a = 200
  console.log('fn', a)
}
console.log('global', a) // "global" 100
fn() // "fn" 200
复制代码

做用域链

var a = 100
function fn() {
  var b = 200
  // 当前做用域没有定义的变量,即“自由变量”
  console.log(a)
  console.log(b)
}
fn()
复制代码
var a = 100
function f1() {
  var b = 200
  function f2() {
    var c = 300
    console.log(a) // a 是自由变量
    console.log(b) // b 是自由变量
    console.log(c)
  }
  f2()
}
f1()
复制代码

3-6 做用域和闭包 - 做用域 代码演示

3-7 补充 - ES6 块级做用域

JS 没有块级做用域,ES6 有块级做用域

3-8 做用域和闭包 - 闭包

闭包

function f1() {
  var a = 100
  // 返回一个函数(函数做为返回值)
  return function() {
    console.log(a)
  }
}
// f1 获得一个函数
var f = f1()
var a = 200
f() // 100
复制代码

闭包的使用场景

  • 函数做为返回值
  • 函数做为参数传递

3-9 做用域和闭包 - 闭包 代码演示

// 闭包 1 ,函数做为返回值
function f1() {
  var a = 100
  return function() {
    console.log(a) // a 是自由变量,向父级做用域去寻找,函数定义时的父做用域
  }
}
var f = f1()
var a = 200
f() // 100
复制代码
// 闭包 2 ,函数做为参数传递
function f1() {
  var a = 100
  return function() {
    console.log(a)
  }
}
var f = f1()
function f2(fn) {
  var a = 200
  fn()
}
f2(f) // 100
复制代码

3-10 做用域和闭包 - 解题

说一下对变量提高的理解

  • 变量定义
  • 函数声明(注意和函数表达式的区别)

说明 this 几种不一样的使用场景

  • 做为构造函数执行
  • 做为对象属性执行
  • 做为普通函数执行
  • callapplybind

建立 10 个 <a> 标签,点击的时候弹出对应的序号

// 这是一个错误的写法
var i, a
for(i = 0; i < 10; i++) {
  a = document.createElement('a')
  a.innerHTML = i + '<br>'
  a.addEventListener('click', function(e) {
    e.preventDefault()
    alert(i) // 自由变量,要去父做用域获取值
  })
  document.body.appendChild(a)
}
复制代码
// 这是正确的写法
var i
for(i = 0; i < 10; i++) {
  (function(i) {
    var a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function(e) {
      e.preventDefault()
      alert(i)
    })
    document.body.appendChild(a)
  })(i)
}
复制代码

若是理解做用域

  • 自由变量
  • 做用域链,即自由变量的查找
  • 闭包的两个场景

实际开发中闭包的应用

// 闭包实际应用中主要用于封装变量,收敛权限
function isFirstLoad() {
  var _list = []
  return function(id) {
    if (_list.indexOf(id) >= 0) {
      return false
    } else {
      _list.push(id)
      return true
    }
  }
}

// 使用
var firstLoad = isFirstLoad()
console.log(firstLoad(10)) // true
console.log(firstLoad(10)) // false
console.log(firstLoad(20)) // true
// 你在 isFirstLoad 函数外,根本不可能修改掉 _list 的值
复制代码

3-11 做用域和闭包 - 解题 代码演示

第4章 JS 基础知识(下)

4-1 异步和单线程 - 什么是异步

题目

  • 同步和异步的区别是什么?分别举一个同步和异步的例子
  • 一个关于 setTimeout 的笔试题
  • 前端使用异步的场景有哪些?

知识点

  • 什么是异步(对比同步)
  • 前端使用异步的场景
  • 异步和单线程

什么是异步

  • 同步阻塞后续代码执行
  • 异步不会阻塞程序的运行
console.log(100)
setTimeout(function() {
  console.log(200)
}, 1000)
console.log(300)
复制代码

对比同步

console.log(100)
alert(200) // 1 秒以后手动点击确认
console.log(300)
复制代码

什么时候须要异步

  • 在可能发生等待的状况
  • 等待过程当中不能像 alert 同样阻塞程序运行
  • 所以,全部的“等待的状况”都须要异步

前端使用异步的场景

  • 定时任务,setTimeoutsetInterval
  • 网络请求,Ajax 请求、动态 <img> 加载
  • 事件绑定
// Ajax 请求
console.log('start')
$.get('./data1.json', function(data1) {
  console.log(data1)
})
console.log('end')
复制代码
// 动态 <img> 加载
console.log('start')
var img = document.createElement('img')
img.onload = function() {
  console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
复制代码
// 事件绑定
console.log('start')
document.getElementById('btn1').addEventListener('click', function() {
  alert('clicked')
})
console.log('end')
复制代码

4-2 异步和单线程 - 什么是异步 代码演示

4-3 异步和单线程 - 单线程

console.log(100)
setTimeout(function() {
  console.log(200)
})
console.log(300)
复制代码
  1. 执行第 1 行,打印 100
  2. 执行 setTimeout 后,传入 setTimeout 的函数会被暂存起来,不会当即执行(单线程的特色,不能同时干两件事)
  3. 执行第 5 行,打印 300
  4. 待全部程序执行完,处于空闲状态时,会立马看有没有暂存起来要执行的
  5. 发现暂存起来的 setTimeout 中的函数,无须等待时间,就当即过来执行

单线程

  • JS 是单线程语言,即一次只能干一件事,若是同时有两件事,另外一件就先上一边排队去,我先干完这件事再说,若是没有异步,那就会出现阻塞现象
  • 因为 JS 是单线程,在代码执行的时候又不能由于执行须要等待的代码而形成阻塞,所以 JS 会首先将无需等待的(同步)代码执行完成后,再来处理异步代码,若是达到异步代码的执行条件的话,就会执行

4-4 异步和单线程 - 解答

同步和异步的区别是什么?分别举一个同步和异步的例子

  • 同步会阻塞代码执行,而异步不会
  • alert 是同步,setTimeout 是异步

一个关于 setTimeout 的笔试题

console.log(1)
setTimeout(function() {
  console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
  console.log(4)
}, 1000)
console.log(5)
// 1
// 3
// 5
// 2
// 4 ,一秒后
复制代码

前端使用异步的场景有哪些?

  • 定时任务,setTimeoutsetInterval
  • 网络请求,Ajax 请求、动态 <img> 加载
  • 事件绑定

重点总结

  • 异步和同步的区别
  • 异步和单线程的关系
  • 异步在前端的使用场景

4-5 其余知识点 - Date 和 Math

题目

  • 获取 2020-02-24 格式的日期
  • 获取随机数,要求长度一致的字符串格式
  • 写一个能遍历对象和数组的通用 forEach 函数

知识点

  • Date
  • Math
  • 数组 API
  • 对象 API

Date

Date.now() // 获取当前时间毫秒数
var dt = new Date()
dt.getTime() // 获取毫秒数
dt.getFullYear() // 年
dt.getMonth() // 月(0-11)
dt.getDate() // 日(1-31)
dt.getHours() // 时(0-23)
dt.getMinutes() // 分(0-59)
dt.getSeconds() // 秒(0-59)
复制代码

Math

Math.random() // 获取随机数
复制代码

4-6 其余知识点 - 数组和对象的 API

数组 API

  • forEach ,遍历全部元素
  • every ,判断全部元素是否都符合条件
  • some ,判断是否有至少一个元素符合条件
  • sort ,排序
  • map ,对元素从新组装,生成新数组
  • filter ,过滤符合条件的元素
// forEach
var arr = [1, 2, 3]
arr.forEach(function(item, index) {
  // 遍历数组的全部元素
  console.log(index, item)
})
// 0 1
// 1 2
// 2 3
复制代码
// every
var arr = [1, 2, 3]
var result = arr.every(function(item, index) {
  // 用来判断全部的数组元素,都知足条件
  if (item < 4) {
    return true
  }
})
console.log(result) // true
复制代码
// some
var arr = [1, 2, 3]
var result = arr.some(function(item, index) {
  // 用来判断只要有一个数组元素知足条件
  if (item < 2) {
    return true
  }
})
console.log(result) // true
复制代码
// sort
var arr = [1, 4, 2, 3, 5]
var result = arr.sort(function(a, b) {
  // 从小到大排序
  return a - b // 从大到小排序 return b - a
})
console.log(result) // [1, 2, 3, 4, 5]
复制代码
// map
var arr = [1, 2, 3]
var result = arr.map(function(item, index) {
  // 将元素从新组装并返回
  return '<b>' + item + '</b>'
})
console.log(result) //  ["<b>1</b>", "<b>2</b>", "<b>3</b>"]
复制代码
// filter
var arr = [1, 2, 3]
var result = arr.filter(function(item, index) {
  // 经过某一个条件过滤数组
  if (item >= 2) {
    return true
  }
})
console.log(result) // [2, 3]
复制代码

对象 API

var obj = {
  x: 100,
  y: 200,
  z: 300
}
var key
for (key in obj) {
  // 注意这里的 hasOwnProperty ,在将原型链的时候讲过了
  if (obj.hasOwnProperty(key)) {
    console.log(key, obj[key])
  }
}
// x 100
// y 200
// z 300
复制代码

4-7 其余知识点 - 知识点 代码演示

4-8 其余知识点 - 解答

获取 2020-02-24 格式的日期

function formatDate(dt) {
  if (!dt) {
    dt = new Date()
  }
  var year = dt.getFullYear()
  var month = dt.getMonth() + 1
  var date = dt.getDate()
  return year + '-' + month.toString().padStart(2, '0') + '-' + date.toString().padStart(2, '0')
}

formatDate(new Date()) // "2021-02-24"
复制代码

获取随机数,要求长度一致的字符串格式

var random = Math.random()
random = random + '0000000000'
random = random.slice(0, 10)
console.log(random)
复制代码

写一个能遍历对象和数组的通用 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(key, value) {
  console.log(key, value)
})
// 0 1
// 1 2
// 2 3

var obj = {
  x: 100,
  y: 200
}
forEach(obj, function(key, value) {
  console.log(key, value)
})
// x 100
// y 200
复制代码

重点总结

  • Date
  • Math
  • 数组 API
  • 对象 API

4-9 其余知识点 - 代码演示

第5章 JS Web API(上)

5-1 从基础知识到 JS Web API

回顾 JS 基础知识

  • 变量类型和计算
  • 原型和原型链
  • 闭包和做用域
  • 异步和单线程
  • 其余(如 Date 、Math 、各类经常使用 API)
  • 特色:表面看起来并不能用于工做中开发代码
  • 内置函数:Object 、Array 、Boolean 、String ...
  • 内置对象:Math 、JSON ...
  • 咱们连在网页弹出一句 Hello World 都不能实现

JS Web API

  • JS 基础知识:ECMA262 标准
  • JS Web API:W3C 标准

W3C 标准中关于 JS 的规定有

  • DOM 操做
  • BOM 操做
  • 事件绑定
  • Ajax 请求(包括 http 协议)
  • 存储

页面弹框是 window.alert(123) ,浏览器须要作

  • 定义一个 window 全局变量,对象类型
  • 给他定义一个 alert 属性,属性值是一个函数

获取元素 document.getElementById(id) ,浏览器须要

  • 定义一个 document 全局变量,对象类型
  • 给它定义一个 getElementById 的属性,属性值是一个函数

可是 W3C 标准没有规定任何 JS 基础相关的东西

  • 无论什么变量类型、原型、做用域和异步
  • 只管定义用于浏览器中 JS 操做页面的 API 和全局变量

全面考虑,JS 内置的全局函数和对象有哪些?

  • 以前讲过的 Object 、Array 、Boolean 、String 、Math 、JSON 等
  • 刚刚提到的 window 、document
  • 接下来还有继续讲到的全部未定义的全局变量,如 navigator.userAgent

总结

常说的 JS(浏览器执行的 JS)包含两部分

  • JS 基础知识(ECMA262 标准)
  • JS Web API(W3C 标准)

5-2 DOM 本质

DOM ,全称 Document Object Model

题目

  • DOM 是哪一种基本的数据结构?
  • DOM 操做的经常使用 API 有哪些?
  • DOM 节点的 Attribute 和 Property 有何区别?

知识点

  • DOM 本质
  • DOM 节点操做
  • DOM 结构操做

DOM 本质

<?xml version="1.0" encoding="utf-8"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend</body>
  <other>
    <a></a>
    <b></b>
  </other>
</note>
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <p>this is p</p>
  </div>
</body>
</html>
复制代码

DOM 本质:浏览器拿到 HTML 代码后,DOM 把 HTML 代码结构化成浏览器可识别以及 JS 可识别的东西。

HTML 代码就是一个字符串,可是浏览器已经把字符串结构化成树形结构了。

5-3 DOM 节点操做

DOM 能够理解为:浏览器把拿到的 HTML 代码,结构化成一个浏览器能识别而且 JS 可操做的一个模型而已。

DOM 节点操做

  • 获取 DOM 节点
  • Property ,JS 对象属性
  • Attribute ,HTML 标签属性

获取 DOM 节点

var div1 = document.getElementById('div1') // 元素
var divList = document.getElementsByTagName('div') // 集合
console.log(divList.length)
console.log(divList[0])

var containerList = document.getElementsByClassName('container') // 集合
var pList = document.querySelectorAll('p') // 集合
复制代码

Property ,JS 对象属性

var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width) // 获取样式
p.style.width = '100px' // 修改样式
console.log(p.className) // 获取 class
p.className = 'p1' // 修改 class

// 获取 nodeName 和 nodeType
console.log(p.nodeName) // "P"
console.log(p.nodeType) // 1
复制代码

Attribute ,HTML 标签属性

var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'imooc')
p.getAttribute('style')
p.setAttribute('style', 'font-size: 30px;')
复制代码

5-4 DOM 节点操做 - 代码演示

5-5 DOM 结构操做

DOM 结构操做

  • 新增节点
  • 获取父元素
  • 获取子元素
  • 删除节点

新增节点

var div1 = document.getElementById('div1')
// 添加新节点
var p = document.createElement('p')
p.innerHTML = 'new p'
div1.appendChild(p) // 添加新建立的元素
// 移动已有节点
var p4 = document.getElementById('p4')
div1.appendChild(p4)
复制代码

获取父元素和子元素

var div1 = document.getElementById('div1')
var parent = div1.parentNode

var children = div1.childNodes
复制代码

删除节点

div1.removeChild(children[0])
复制代码

5-6 DOM 结构操做 - 代码演示

5-7 DOM 结构操做 - 解答

DOM 是哪一种基本的数据结构?

答:树

DOM 操做的经常使用 API 有哪些?

答:

  • 获取 DOM 节点,以及节点的 Property 和 Attribute
  • 获取父节点,获取子节点
  • 新增节点,删除节点

DOM 节点的 Attribute 和 Property 有何区别?

答:

  • Property 只是一个 JS 对象的属性的修改
  • Attribute 是对 HTML 标签属性的修改

重点总结

  • DOM 本质
  • DOM 节点操做
  • DOM 结构操做

5-8 BOM 操做

题目

  • 如何检测浏览器的类型
  • 拆解 URL 的各部分

知识点

  • navigator
  • srceen
  • location
  • history

navigator & screen

// navigator
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome) // 81

// screen
console.log(screen.width) // 1920
console.log(screen.height) // 1080
复制代码

location & history

// location ,以 http://localhost:8080/login?username=negrochn&password=123456#mid=1 为例
console.log(location.href) // http://localhost:8080/login?username=negrochn&password=123456#mid=1
console.log(location.protocol) // http:
console.log(location.host) // localhost:8080
console.log(location.hostname) // localhost
console.log(location.port) // 8080
console.log(location.pathname) // /login
console.log(location.search) // ?username=negrochn&password=123456
console.log(location.hash) // #mid=1

// history
history.back()
history.forward()
复制代码

5-9 BOM 操做 - 代码演示

第6章 JS Web API(下)

6-1 事件

题目

  • 编写一个通用的事件监听函数
  • 描述事件冒泡流程
  • 对一个无限下拉加载图片的页面,如何给每一个图片绑定事件

知识点

  • 通用事件绑定
  • 事件冒泡
  • 代理

通用事件绑定

var btn = document.getElementById('btn1')
btn.addEventListener('click', function(e) {
  console.log('clicked')
})

function bindEvent(elem, type, fn) {
  elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', function(e) {
  e.preventDefault() // 阻止默认行为
  alert('clicked')
})
复制代码

关于 IE 低版本的兼容性

  • IE 低版本使用 attachEvent 绑定事件,和 W3C 标准不同
  • IE 低版本使用量已很是少,不少网站都早已不支持
  • 建议对 IE 低版本的兼容性:了解便可,无需深究
  • 若是遇到对 IE 低版本要求苛刻的面试,果断放弃

事件冒泡

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>事件冒泡</title>
</head>
<body>
  <div id="div1">
    <p id="p1">激活</p>
    <p id="p2">取消</p>
    <p id="p3">取消</p>
    <p id="p4">取消</p>
  </div>
  <div id="div2">
    <p id="p5">取消</p>
    <p id="p6">取消</p>
  </div>
  <script> function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } var p1 = document.getElementById('p1') var body = document.body bindEvent(p1, 'click', function(e) { e.stopPropagation() alert('激活') }) bindEvent(body, 'click', function(e) { alert('取消') }) </script>
</body>
</html>
复制代码

代理

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>事件冒泡</title>
</head>
<body>
  <div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    <a href="#">a3</a>
    <a href="#">a4</a>
    <!-- 会随时新增更多 a 标签 -->
  </div>
  <script> var div1 = document.getElementById('div1') div1.addEventListener('click', function(e) { var target = e.target if (target.nodeName === 'A') { alert(target.innerHTML) } }) </script>
</body>
</html>
复制代码

完善通用绑定事件的函数

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)
    }
  })
}

// 使用代理
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', 'a', function(e) {
  console.log(this.innerHTML)
})

// 不使用代理
var a = document.getElementById('a1')
bindEvent(div1, 'click', function(e) {
  console.log(a.innerHTML)
})
复制代码

代理的好处

  • 代码简洁
  • 减小浏览器内存占用

6-2 事件 - 代码演示

6-3 事件 - 解答

编写一个通用的事件监听函数

答:

function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector
    selector = null
  }
  elem.addEventListener(type, function(e) {
    let target
    if (selector) {
      target = e.target
      if (target.matches(selector)) {
        fn.call(target, e)
      }
    } else {
      fn(e)
    }
  })
}
复制代码

描述事件冒泡流程

答:

  1. DOM 树形结构
  2. 事件冒泡
  3. 阻止冒泡
  4. 冒泡的应用

对一个无限下拉加载图片的页面,如何给每一个图片绑定事件

答:

  • 使用代理
  • 代理的两个优势(代码简洁、减小浏览器内存占用)

重点总结

  • 通用事件绑定
  • 事件冒泡
  • 事件代理

6-4 Ajax - XMLHttpRequest

题目

  • 手写一个 Ajax ,不依赖第三方库
  • 跨域的几种实现方式

知识点

  • XMLHttpRequest
  • 状态码说明
  • 跨域

XMLHttpRequest

var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
  // 这里的函数异步执行
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      alert(xhr.responseText)
    }
  }
}
xhr.send(null)
复制代码

IE 兼容性问题

  • IE 低版本使用 ActiveXObject ,和 W3C 标准不同
  • IE 低版本使用量已很是少,不少网站都早已不支持
  • 建议对 IE 低版本的兼容性:了解便可,无需深究
  • 若是遇到对 IE 低版本要求苛刻的面试,果断放弃

readyState

  • 0 - 未初始化,尚未调用 send() 方法
  • 1 - 载入,已调用 send() 方法,正在发送请求
  • 2 - 载入完成,send() 方法执行完成,已经接收到所有响应内容
  • 3 - 交互,正在解析响应内容
  • 4 - 完成,响应内容解析完成,能够在客户端调用了

status

  • 1XX - 信息,服务器收到请求,须要请求者继续执行操做
  • 2XX - 成功,操做被成功接收并处理
  • 3XX - 重定向,须要进一步的操做以完成请求
  • 4XX - 客户端错误,请求包含语法错误或没法完成请求
  • 5XX - 服务器错误,服务器在处理请求的过程当中发生了错误
状态码 英文描述 中文描述
100 Continue 继续。客户端应继续其请求。
200 OK 请求成功。
204 No Content 无内容。服务器成功处理,但未返回内容。
206 Partial Content 部份内容。服务器成功处理了部分 GET 请求。
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新 URI ,返回信息会包括新的 URI ,浏览器会自动定向到新 URI 。
302 Found 临时移动。客户端应继续使用原有 URI 。
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。
307 Temporary Redirect 临时重定向。使用 GET 请求重定向。
400 Bad Request 客户端请求的语法错误,服务器没法理解。
401 Unauthorized 请求要求用户的身份认证。
403 Forbidden 服务器理解请求客户端的请求,可是拒绝执行此请求。
404 Not Found 服务器没法根据客户端的请求找到资源(网页)。
500 Internal Server Error 服务器内部错误,没法完成请求。
502 Bad Gateway 做为网关或代理工做的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应。
503 Service Unavailable 因为超载或系统维护,服务器暂时的没法处理客户端的请求。

6-5 Ajax - 跨域

什么是跨域

  • 浏览器有同源策略,不容许 Ajax 访问其余域接口
  • 跨域条件:协议、域名、端口,有一个不一样就算跨域
  • 有三个标签容许跨域加载资源
    • <img src=xxx> ,用于打点统计,统计网站多是其余域
    • <link href=xxx> ,可使用 CDN
    • <script src=xxx> ,可使用 CDN ,用于 JSONP

跨域注意事项

  • 全部的跨域请求都必须通过信息提供方容许
  • 若是未经容许便可获取,那是浏览器同源策略出现漏洞

JSONP 实现原理

  • 加载 coding.imooc.com/lesson/115.…
  • 不必定服务器端真正有一个 115.html 文件
  • 服务器能够根据请求,动态生成一个文件返回
  • 同理,<script src="http://coding.imooc.com/api.js">
  • 例如你的网站要跨域访问慕课网的一个接口
  • 慕课给你一个地址 coding.imooc.com/api.js
  • 返回内容格式如 callback({ x: 100, y: 200 })(可动态生成)
<script> window.callback = function(data) { // 这是咱们跨域获得的信息 console.log(data) } </script>
<script src="http://coding.imooc.com/api.js"></script>
<!-- 以上将返回 callback({ x: 100, y: 200 }) -->
复制代码

服务器设置 http header

  • 另一个解决跨域的简洁方法,须要服务器来作
  • 可是做为交互方,咱们必须知道这个方法
  • 是未来解决跨域问题的一个趋势
// 注意:不一样后端语言的写法可能不同

// 第二个参数填写容许跨域的域名称,不建议直接写 *
response.setHeader('Access-Control-Allow-Origin', 'http://a.com, http://b.com')
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With')
response.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')

// 接收跨域的 Cookie
response.setHeader('Access-Control-Allow-Credentials', 'true')
复制代码

手写一个 Ajax ,不依赖第三方库

var xhr = new XMLHttpRequest()
xhr.open('GET', '/api', false)
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      alert(xhr.responseText)
    }
  }
}
xhr.send(null)
复制代码

跨域的几种实现方式

  • JSONP
  • 服务器端设置 http header

重点总结

  • XMLHttpRequest
  • 状态码
  • 跨域

6-6 存储

题目

  • 请描述一下 Cookie 、sessionStorage 和 localStorage 的区别?

知识点

  • Cookie
  • sessionStorage 和 localStorage

Cookie

  • 自己用于客户端和服务器端通讯
  • 可是它有本地存储的功能,因而就被“借用”
  • 使用 document.cookie 获取和修改

Cookie 用于存储的缺点

  • 存储量过小,只有 4KB
  • 全部 http 请求都带着,会影响获取资源的效率
  • API 简单,须要封装才能用 document.cookie

localStorage 和 sessionStorage

  • HTML5 专门为存储而设计,最大容量 5MB
  • API 简单易用,getItem(key)setItem(key, value)
  • iOS Safari 隐藏模式下,localStorage.getItem 会报错,建议统一使用 try-catch 封装

请描述一下 Cookie 、sessionStorage 和 localStorage 的区别?

  • 容量
  • 是否携带到请求中
  • API 易用性

第7章 开发环境

7-1 介绍

关于开发环境

  • 面试官想经过开发环境了解面试者的经验
  • 开发环境,最能体现工做产出的效率
  • 会以聊天的形式为主,而不是出具体的问题

开发环境包含

  • IDE(写代码的效率)
  • Git(代码版本管理,多人协做开发)
  • JS 模块化
  • 打包工具
  • 上线回滚的流程

7-2 IDE

IDE

  • WebStorm
  • Sublime
  • VS Code(推荐)
  • Atom

7-3 Git - 经常使用命令

Git

  • 正式项目都须要代码版本管理
  • 大型项目须要多人协做开发
  • Git 和 Linux 是一个做者
  • 网络 Git 服务器,如 github
  • 通常公司代码非开源,都有本身的 Git 服务器
  • 搭建 Git 服务器无需你了解太多
  • Git 的基本操做必须很熟练

经常使用 Git 命令

  • git add .
  • git checkout xxx ,还原文件
  • git commit -m 'xxx' ,提交到本地仓库
  • git push origin master ,提交到远程仓库
  • git pull origin master ,下载远程仓库的文件
  • git branch ,建立分支
  • git checkout -b xxx/git checkout xxx ,新建一个分支/切换到另外一个分支
  • git merge xxx ,把以前的分支拷贝到这里

7-4 Git - 代码演示

7-5 Git - 代码演示 多人协做

多人开发

  • 为了编写属于本身的功能,能够建立本身的分支,而不要在主分支上改动,最后进行合并便可

步骤

  1. git checkout -b dev ,建立一个 dev 分支
  2. vi a.js修改内容(vi 属于 Linux 命令,git status 查看是否被修改,git diff 查看修改的内容)
  3. git add .
  4. git commit -m 'update dev'
  5. git push origin dev ,提交到远程仓库的 dev 分支
  6. 半个月以后,合并到 master 分支
  7. git checkout master ,切换到 master 分支
  8. git pull origin master ,下载远程仓库的 master 分支的文件
  9. git merge dev ,把 dev 分支的修改内容拷贝到 master 分支
  10. git push origin master ,提交到远程仓库的 master 分支

附加命令

  • cat 文件名,查看文件内容
  • git diff ,查看修改的内容
  • git branch ,查看分支和当前在哪条分支上
  • git status ,查看文件是否被修改的状态
  • vi 文件 ,新建文件并打开文件
  • esc + :wq ,退出并保存文件

7-6 模块化 - AMD

不使用模块化

// util.js
function getFormatDate(date, type) {
  // type === 1 返回 2021-03-06
  // type === 2 返回 2021年3月6日
  // ...
}
复制代码
// a-util.js
function aGetFormatDate(date) {
  // 要求返回 2021年3月6日格式
  return getFormatDate(date, 2)
}
复制代码
// a.js
var dt = new Date()
console.log(aGetFormatDate(dt))
复制代码
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
<!-- 1. 这些代码中的函数必须是全局变量,才能暴露给使用方。全局变量污染。 -->
<!-- 2. a.js 知道要引用 a-util.js ,可是他知道还须要依赖于 util.js 吗 -->
复制代码

AMD

  • require.js
  • 全局 define 函数
  • 全局 require 函数
  • 依赖 JS 会自动、异步加载

使用 require.js

// util.js
define(function() {
  return {
    getFormatDate: function(date, type) {
      if (type === 1) {
        return '2021-03-06'
      } else if (type === 2) {
        return '2021年3月6日'
      }
    }
  }
})
复制代码
// a-util.js
define(['./util.js'], function(util) {
  return {
    aGetFormatDate: function(date) {
      return util.getFormatDate(date, 2)
    }
  }
})
复制代码
// a.js
define(['./a-util.js'], function(aUtil) {
  return {
    printDate: function(date) {
      console.log(aUtil.aGetFormatDate(date))
    }
  }
})
复制代码
// main.js
require(['./a.js'], function(a) {
  var date = new Date()
  a.printDate(date)
})
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AMD</title>
</head>
<body>
  <script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.min.js" data-main="./main.js"></script>
</body>
</html>
复制代码

7-7 模块化 - AMD 代码演示

7-8 模块化 - CommonJS

CommonJS

  • Node.js 模块化规范,如今被前端大量使用,缘由:
  • 前端开发依赖的插件和库,均可以从 npm 中获取
  • 构建工具的高度自动化,使得使用 npm 的成本很是低
  • CommonJS 不会异步加载 JS ,而是同步一次性加载出来

使用 CommonJS

// util.js
module.exports = {
  getFormatDate: function(date, type) {
    if (type === 1) {
      return '2021-03-06'
    } else if (type === 2) {
      return '2021年3月6日'
    }
  }
}
复制代码
// a-util.js
var util = require('util.js')
module.exports = {
  aGetFormatDate: function(date) {
    return util.getFormatDate(date, 2)
  }
}
复制代码

AMD 和 CommonJS 的使用场景

  • 须要异步加载 JS ,使用 AMD
  • 使用了 npm 以后建议使用 CommonJS

重点总结

  • AMD
  • CommonJS
  • 二者的区别

7-9 构建工具 - 安装 Node.js

7-10 构建工具 - 安装 Webpack

7-11 构建工具 - 配置 Webpack

7-12 构建工具 - 使用 jQuery

7-13 构建工具 - 压缩 JS

7-14 上线回滚 - 上线回滚流程

7-15 上线回滚 - Linux 基础命令

8章 运行环境

8-1 介绍

运行环境

  • 浏览器就能够经过访问连接来获得页面的内容
  • 经过绘制和渲染,显示出页面的最终的样子
  • 整个过程当中,咱们须要考虑什么问题?

知识点

  • 页面加载过程
  • 性能优化
  • 安全性

8-2 页面加载 - 渲染过程

题目

  • 从输入 URL 到获得 HTML 的详细过程
  • window.onloadDOMContentLoaded 的区别

知识点

  • 加载资源的形式
  • 加载一个资源的过程
  • 浏览器渲染页面的过程

加载资源的形式

  • 输入 URL(或跳转页面)加载 HTML
  • 加载 HTML 中的静态资源

加载一个资源的过程

  • 浏览器根据 DNS 服务器获得域名的 IP 地址
  • 向这个 IP 的机器发送 HTTP 请求
  • 服务器收到、处理并返回 HTTP 请求
  • 浏览器获得返回内容

浏览器渲染页面的过程

  • 根据 HTML 结构生成 DOM Tree
  • 根据 CSS 生成 CSSOM
  • 将 DOM 和 CSSOM 整合造成 Render Tree
  • 根据 Render Tree 开始渲染和展现
  • 遇到 <script> 时,会执行并阻塞渲染

8-3 页面加载 - 几个示例

为什么要把 CSS 放在 head 中?

答:保证先加载 CSS ,接着渲染,否则要渲染两次,用户体验差。

为什么要把 JS 放在 body 最下面?

答:不会阻塞渲染过程,提升性能。

window.onloadDOMContentLoaded

window.addEventListener('load', function() {
  // 页面的所有资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function() {
  // DOM 渲染完便可执行,此时图片、视频还可能没有加载完
})
复制代码

8-4 页面加载 - 解答

从输入 URL 到获得 HTML 的详细过程

  • 浏览器根据 DNS 服务器获得域名的 IP 地址
  • 向这个 IP 的机器发送 HTTP 请求
  • 服务器收到、处理并返回 HTTP 请求
  • 浏览器获得返回内容

window.onloadDOMContentLoaded 的区别

  • 页面的所有资源加载完才会执行,包括图片、视频等
  • DOM 渲染完便可执行,此时图片、视频尚未加载完

8-5 性能优化 - 优化策略

性能优化

  • 这自己就是一个综合性的问题
  • 没有标准答案,若是要很是全面,能写一本书
  • 只关注核心店,针对面试

原则

  • 多使用内存、缓存或者其余方法
  • 减小 CPU 计算、减小网络

从哪里入手

  • 加载页面和静态资源
  • 页面渲染

加载资源优化

  • 静态资源的压缩合并
  • 静态资源缓存
  • 使用 CDN 让资源加载更快
  • 使用 SSR 后端渲染,数据直接输出到 HTML 中

渲染优化

  • CSS 放前面,JS 放后面
  • 懒加载(图片懒加载、下拉加载更多)
  • 减小 DOM 操做,对 DOM 查询作缓存,多个操做尽可能合并在一块儿执行
  • 事件节流
  • 尽早执行操做,如 DOMContentLoaded

8-6 性能优化 - 几个示例

资源合并

<script src=a.js></script>
<script src=b.js></script>
<script src=c.js></script>
复制代码
<script src="abc.js"></script>
复制代码

缓存

  • 经过连接名称控制缓存
  • 只有内容改变的时候,连接名称才会改变

CDN

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
复制代码

使用 SSR 后端渲染

  • 如今 Vue/React 提出了这样的概念
  • 其实 JSP/PHP/ASP 都属于后端渲染

懒加载

<img id="img1" src="preview.png" data-realsrc="abc.png" />
<script> var img1 = document.getElementById('img1') img1.src = img1.getAttribute('data-realsrc') </script>
复制代码

缓存 DOM 查询

// 未缓存 DOM 查询
var i
for (i = 0; i < document.getElementByTagName('p').length; i++) {
  
}

// 缓存了 DOM 查询
var pList = document.getElementByTagName('p')
var i
for (i = 0; i < pList.length; i++) {
  
}
复制代码

合并 DOM 插入

var listNode = document.getElementById('list')

// 要插入 10 个 li 标签
var frag = document.createDocumentFragment()
var x, li
for (x = 0; x < 10; x++) {
  li = document.createElement('li')
  li.innerHTML = 'List item ' + x
  frag.appendChild(li)
}
listNode.appendChild(frag)
复制代码

事件节流

var textarea = document.getElementById('text')
var timeoutid
textarea.addEventListener('keyup', function() {
  if (timeoutid) {
    clearTimeout(timeoutid)
  }
  timeoutid = setTimeout(function() {
    // 触发 change 事件
  }, 100)
})
复制代码

尽早操做

window.addEventListener('load', function() {
  // 页面的所有资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function() {
  // DOM 渲染完便可执行,此时图片、视频还可能没有加载完
})
复制代码

8-7 安全性 - XSS

知识点

  • XSS 跨站请求攻击
  • XSRF 跨站请求伪造

XSS

  • 在新浪博客写一篇文章,同时偷偷插入一段 <script>
  • 攻击代码中,获取 Cookie ,发送本身的服务器
  • 发布博客,有人查看博客内容
  • 会把查看者的 Cookie 发送到攻击者的服务器
  • 前端替换关键字,例如替换 <&lt;>&gt;
  • 后端替换

8-8 安全性 - XSRF

XSRF

  • 你已登陆一个购物网站,正在浏览商品
  • 该网站付费接口是 xxx.com/pay?id=100 ,可是没有任何验证
  • 而后你收到一封邮件,隐藏着 <img src="xxx.com/pay?id=100" />
  • 你查看邮件的时候,就已经悄悄的付费购买了
  • 增长验证流程,如输入指纹、密码、短信验证码

8-9 面试技巧

简历

  • 简洁明了,重点突出项目经理和解决方案
  • 把我的博客放在简历中,而且按期维护更新博客
  • 把我的的开源项目放在简历中,并维护开源项目
  • 简历千万不要造假,要保持能力和经历上的真实性

面试过程当中

  • 如何看待加班?加班就像借钱,救急不救穷
  • 千万不要挑战面试官,不要反考面试官
  • 学会给面试官惊喜,但不要太多
  • 遇到不会回答的问题,说出你知道的也能够
  • 谈谈你的缺点——说一下你最近正在学什么就能够了

第9章 真题模拟

  1. varletconst 的区别

    答:

    • var 是 ES5 语法,letconst 是ES6 语法,var 有变量提高
    • varlet 是变量,可修改,const 是常量,不可修改
    • letconst 有块级做用域,var 没有
  2. typeof 返回哪些类型

    答:

    • undefined 、string 、number 、boolean 、symbol
    • object(注意,typeof null === 'object'
    • function
  3. 列举强制类型转换和隐式类型转换

    答:

    • 强制:parseIntparseFloattoString
    • 隐式:if 、逻辑运算、== 、+ 拼接字符串
  4. 手写深度比较,模拟 lodash 的 isEqual

    答:

    // 判断是不是对象或数组
    function isObject(obj) {
      return typeof obj === 'object' && obj !== null
    }
    function isEqual(obj1, obj2) {
      if (!isObject(obj1) || !isObject(obj2)) {
        // 值类型(注意,参与 equal 的通常不会是函数)
        return obj1 === obj2
      }
      if (obj1 === obj2) {
        return true
      }
      // 两个都是对象或数组,并且不相等
      // 1. 先取出 obj1 和 obj2 的 keys ,比较个数
      const obj1Keys = Object.keys(obj1)
      const obj2Keys = Object.keys(obj2)
      if (obj1Keys.length !== obj2Keys.length) {
        return false
      }
      // 2. 以 obj1 为基准,和 obj2 一次递归比较
      for (let key in obj1) {
        if (!isEqual(obj1[key], obj2[key])) {
          return false
        }
      }
      // 3. 全相等
      return true
    }
    
    const obj1 = {
      a: 100,
      b: {
        x: 100,
        y: 200
      }
    }
    const obj2 = {
      a: 100,
      b: {
        x: 100,
        y: 200
      }
    }
    console.log(obj1 === obj2) // false
    console.log(isEqual(obj1, obj2)) // true
    复制代码
  5. splitjoin 的区别

    答:

    '1-2-3'.split('-') // [1, 2, 3]
    [1, 2, 3].join('-') /// '1-2-3'
    复制代码
  6. 数组的 poppushunshiftshift 分别作什么

    答:

    • 功能是什么?
    • 返回值是什么?
    • 是否会对原数组形成影响?
    const arr = [10, 20, 30, 40]
    const result = arr.pop()
    console.log(result, arr) // 40, [10, 20, 30]
    复制代码
    const arr = [10, 20, 30, 40]
    const result = arr.push(50) // 返回 length
    console.log(result, arr) // 5, [10, 20, 30, 40, 50]
    复制代码
    const arr = [10, 20, 30, 40]
    const result = arr.unshift(5) // 返回 length
    console.log(result, arr) // 5, [5, 10, 20, 30, 40]
    复制代码
    const arr = [10, 20, 30, 40]
    const result = arr.shift()
    console.log(result, arr) // 10, [20, 30, 40]
    复制代码

    纯函数:1. 不改变源数组;2. 返回一个数组

    // 纯函数
    const arr = [10, 20, 30, 40]
    
    const cArr = arr.concat([50, 60, 70]) // [10, 20, 30, 40, 50, 60, 70]
    const mArr = arr.map(item => item * 10) // [100, 200, 300, 400]
    const fArr = arr.filter(item => item > 20) // [30, 40]
    const sArr = arr.slice() // [10, 20, 30, 40]
    
    
    // 非纯函数
    // push 、pop 、shift 、unshift
    // forEach
    // some every reduce
    复制代码
  7. 数组 slicesplice 的区别

    答:

    • 功能区别,slice 是切片,splice 是剪接
    • 参数和返回值
    • 是否纯函数?
    const arr = [10, 20, 30, 40, 50]
    
    // slice 是纯函数
    const sliceArr = arr.slice(1, 4)
    
    // splice 不是纯函数
    const spliceArr = arr.splice(1, 2, 'a', 'b', 'c')
    console.log(arr, spliceArr) // [10, "a", "b", "c", 40, 50],[20, 30]
    复制代码
  8. [10, 20, 30].map(parseInt) 返回结果是什么?

    答:

    • map 的参数和返回值
    • parseInt 的参数和返回值
    [10, 20, 30].map(parseInt)
    // 至关于
    [10, 20, 30].map((item, index) => {
      return parseInt(item, index)
    })
    // 分解为
    parseInt(10, 0) // 10
    parseInt(20, 1) // NaN
    parseInt(30, 2) // NaN
    复制代码
  9. Ajax 请求 get 和 post 的区别?

    答:

    • get 通常用于查询操做,post 通常用于提交操做
    • get 参数拼接在 url 上,post 放在请求体内(数据体积可更大)
    • 安全性,post 易于防止 CSRF
  10. 函数 call 和 apply 的区别?

    答:

    fn.call(this, p1, p2, p3)
    fn.apply(this, arguments)
    复制代码
  11. 事件代理(委托)是什么?

    答:

    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)
        }
      })
    }
    
    // 使用代理
    var div1 = document.getElementById('div1')
    bindEvent(div1, 'click', 'a', function(e) {
      console.log(this.innerHTML)
    })
    
    // 不使用代理
    var a = document.getElementById('a1')
    bindEvent(div1, 'click', function(e) {
      console.log(a.innerHTML)
    })
    复制代码
  12. 闭包是什么,有什么特性?有什么负面影响?

    答:

    • 回顾做用域和自由变量
    • 回顾闭包应用场景:做为参数被传入,做为返回值被返回
    • 回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
    • 影响:变量会常驻内存,得不到释放。闭包不要乱用
  13. 如何阻止事件冒泡和默认行为?

    答:

    • event.stopPropagation()
    • event.preventDefault()
  14. 查找、添加、删除、移动 DOM 节点的方法?

    答:

    • getElementByIdgetElementsByTagNamegetElementsByClassNamequerySelectorAll
    • appendChild(添加和移动)
    • removeChild
    • parentNodechildNodes
  15. 如何减小 DOM 操做?

    答:

    • 缓存 DOM 查询结果
    • 屡次 DOM 操做,合并到一次插入
  16. 解释 JSONP 的原理,为什么它不是真正的 Ajax ?

    答:

    • 浏览器的同源策略(服务端没有同源策略)和跨域
    • 哪些 HTML 标签能绕过跨域?
    • JSONP 的原理
  17. document 的 load 和 ready 的区别

    答:

    window.addEventListener('load', function() {
      // 页面的所有资源加载完才会执行,包括图片、视频等
    })
    document.addEventListener('DOMContentLoaded', function() {
      // DOM 渲染完便可执行,此时图片、视频还可能没有加载完
    })
    复制代码
  18. ===== 的不一样

    答:

    • == 会尝试类型转换
    • === 严格相等
    • 哪些场景才会用 ==
  19. 函数声明和函数表达式的区别

    答:

    • 函数声明 function fn() {}
    • 函数表达式 const fn = function() {}
    • 函数声明会在代码执行前预加载,而函数表达式不会
  20. new Object()Object.create() 的区别

    答:

    • {} 等同于 new Object() ,原型是 Object.prototype
    • Object.create(null) 没有原型
    • Object.crate({...}) 可指定原型
    const obj1 = {
      a: 10,
      b: 20,
      sum() {
        return this.a + this.b
      }
    }
    const obj2 = new Object({
      a: 10,
      b: 20,
      sum() {
        return this.a + this.b
      }
    })
    const obj3 = new Object(obj1)
    console.log(obj1 === obj2) // false
    console.log(obj1 === obj3) // true
    
    const obj4 = Object.create(null) // {} ,但没有原型
    const obj5 = new Object() // {}
    const obj6 = Object.create(obj1) // 建立一个空对象,把空对象的原型指向 obj1
    console.log(obj1 === obj6) // false
    console.log(obj1 === obj6.__proto__) // true
    复制代码
  21. 关于 this 的场景题

    const User = {
      count: 1,
      getCount: function() {
        return this.count
      }
    }
    console.log(User.getCount()) // 1
    const func = User.getCount
    console.log(func()) // undefined
    复制代码
  22. 关于做用域和自由变量的场景题(1)

    let i
    for (i = 1; i <= 3; i++) {
      setTimeout(function() {
        console.log(i)
      }, 0)
    }
    // 4
    // 4
    // 4
    复制代码
  23. 判断字符串以字母开头,后面字母数字下划线,长度 6-30

    答:

    const reg = /^[a-zA-Z]\w{5,29}$/
    复制代码
  24. 关于做用域和自由变量的场景题(2)

    let a = 100
    function test() {
      alert(a) // 100
      a = 10
      alert(a) // 10
    }
    test()
    alert(a) // 10
    复制代码
  25. 手写字符串 trim 方法,保证浏览器兼容性

    答:

    String.prototype.trim = function() {
      return this.replace(/^\s+/, '').replace(/\s+$/, '')
    }
    // (原型、this 、正则表达式)
    复制代码
  26. 如何获取多个数字中的最大值

    答:

    function max() {
      const nums = Array.prototype.slice.call(arguments) // 变为数组
      let max = -Infinity
      nums.forEach(n => {
        if (n > max) {
          max = n
        }
      })
      return max
    }
    // 或者使用 Math.max()
    复制代码
  27. 如何用 JS 实现继承?

    答:

    • class 继承
    • prototype 继承
  28. 如何捕获 JS 程序中的异常?

    答:

    // 第一种方式
    try {
      // TODO
    } catch(error) {
      console.error(error) // 手动捕获
    } finally {
      // TODO
    }
    
    // 第二种方式
    // 自动捕获
    window.onerror = function(message, source, lineNum, colNum, error) {
      // 第一,对跨域的 JS ,如 CDN 的不会有详细的报错信息
      // 第二,对于压缩的 JS ,还要配合 SourceMap 反查到未压缩代码的行、列
    }
    复制代码
  29. 什么是 JSON ?

    答:

    • JSON 是一种数据格式,本质是一段字符串
    • JSON 格式和 JS 对象结构一致,对 JS 语言更友好
    • window.JSON 是一个全局对象,JSON.stringifyJSON.parse
  30. 获取当前页面 URL 参数

    答:

    • 传统方式,查找 location.search
    • 新 API ,URLSearchParams
    // 传统方式
    function query(name) {
      const search = location.search.substr(1)
      const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
      const res = search.match(reg)
      if (res === null) {
        return null
      }
      return res[2]
    }
    复制代码
    // URLSearchParams
    function query(name) {
      const search = location.search
      const p = new URLSearchParams(search)
      return p.get(name)
    }
    复制代码
  31. 将 URL 参数解析为 JS 对象

    答:

    // 传统方式,分析 search
    function query2Obj() {
      const res = {}
      const search = location.search.substr(1) // 去掉前面的 ?
      search.split('&').forEach(paramStr => {
        const arr = paramStr.split('=')
        const [key, val] = arr
        res[key] = val
      })
      return res
    }
    复制代码
    // 使用 URLSearchParams
    function query2Obj() {
      const res = {}
      const pList = new URLSearchParams(location.search)
      pList.forEach((val, key) => {
        res[key] = val
      })
      return res
    }
    复制代码
  32. 手写数组 faltern ,考虑多层级

    答:

    function flat(arr) {
      // 验证 arr 中,还有没有深层数组
      const isDeep = arr.some(item => item instanceof Array)
      if (!isDeep) {
        return arr
      }
      return flat(Array.prototype.concat.apply([], arr))
    }
    
    const res = flat([1, 2, [3, 4], [5, [6, 7]]])
    console.log(res) // [1, 2, 3, 4, 5, 6, 7]
    复制代码
  33. 数组去重

    答:

    • 传统方式,遍历元素挨个比较、去重
    • 使用 Set
    • 考虑计算效率
    // 传统方式
    function unique(arr) {
      const res = []
      arr.forEach(item => {
        if (res.indexOf(item) < 0) {
          res.push(item)
        }
      })
      return res
    }
    console.log(unique([1, 2, 3, 1, 2, 3, 4])) // 1 2 3 4
    复制代码
    // 使用 Set(无序、不重复)
    function unique(arr) {
      const set = new Set(arr)
      return [...set]
    }
    console.log(unique([1, 2, 3, 1, 2, 3, 4])) // 1 2 3 4
    复制代码
  34. 手写深拷贝

    答:

    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
    }
    
    // 注意 Object.assign 不是深拷贝
    复制代码
  35. 介绍一下 RAF(requestAnimationFrame

    答:

    • 要想动画流畅,更新频率要 60 帧/秒,即 16.67ms 更新一次视图
    • setTimeout 要手动更新频率,而 RAF 浏览器会自动控制
    • 后台标签或隐藏 iframe 中,RAF 会暂停,而 setTimeout 依然执行
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>setTimeout</title>
      <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
      <style> #div1, #div2 { width: 100px; height: 50px; margin-bottom: 20px; background-color: red; } </style>
    </head>
    <body>
      <div id="div1">setTimeout</div>
      <div id="div2">requestAnimateFrame</div>
      <script> const $div1 = $('#div1') let curWidth = 100 const maxWidth = 640 function animate() { curWidth += 3 $div1.css('width', curWidth) if (curWidth < maxWidth) { setTimeout(animate, 16.7) } } animate() const $div2 = $('#div2') let curWidth2 = 100 function animate2() { curWidth2 += 3 $div2.css('width', curWidth2) if (curWidth2 < maxWidth) { window.requestAnimationFrame(animate2) } } animate2() </script>
    </body>
    </html>
    复制代码
  36. 前端性能如何优化?通常从哪几个方面考虑?

    答:

    • 原则:多使用内存、缓存,减小计算、减小网络请求
    • 方向:加载页面,页面渲染,页面操做流畅度
相关文章
相关标签/搜索