# 1. 变量声明与类型

# 1.1 var let const 区别

  1. var 是 ES5 语法,let、const 是 ES6 的语法
  2. var 有变量提升
  3. var、let 是变量,可修改;const 是常量,不可修改
  4. let、const 块级作用域;var 函数作用域

【ES6】变量声明 - var-let-const - 区别与联系 - 总结

# 1.2 数据类型

值类型(7 个):Undefined、Null、Number、String、Boolean、Symbol (ES6)、BigInt (ES10)
引用类型:Object:Array、Function

【JS】JavaScript-ES5 数据类型 - 基本数据类型 - 引用数据类型 - 类型之间的转换 - 数据类型的判断

# 1.3 值类型与引用类型的区别

值类型 存在内存中,变量拿到的就是它的值
引用类型 存在内存中,变量拿到的只是它的一个引用,是它的地址

【JS】JavaScript - 对象 - Object - 内建对象 - 宿主对象 - 自定义对象 - 操作对象 - 基本数据类型与引用数据类型区别

# 1.4 typeof 能判断哪些类型

  1. undefined、string、number、boolean、symbol、bigint【除了 null 的基本类型】
  2. function 【函数】
  3. object (typeof null === ‘object’) 【所有引用类型只能到 object 这里】

# 1.5 判断数据类型的方式

  1. typeof 【除了 null 的基本类型 + function】
  2. instanceof 【引用类型】【从子类到父类直到 object】【顺着原型链】
  3. toString () 【任意类型】
  4. Array.isArray () 【数组】

# 1.6 =====

=== 严格的比较是否相等

== 会进行类型转换,再进行比较

以下都是成立的

100 == '100'
0 == ''
0 == false
fase == ''
null == undefined

有一个情况可以用下 ==

if(a == null) {}
// 等价于
if(a === null || a === undefined)()

# 1.7 truly 变量与 falsely 变量

truly 变量: !!a === true 的变量
falsely 变量: !!b === false 的变量

以下是 falsey 变量,除了这六种情况,其余都是 truely 变量

!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false

# 1.8 强制类型转换和隐式类型转换

强制: parseIntparseFloattoString
隐式: if 、逻辑运算、 ==+ 拼接字符串

一定要看这个,狠详细
【JS】JavaScript-ES5 数据类型 - 基本数据类型 - 引用数据类型 - 类型之间的转换 - 数据类型的判断

# 1.9 语句与表达式

表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方

a
a+b
demo(1)
x===y? 'a': 'b'

语句

if(){}
for(){}

# 2. 数组字符串相关

# 2.1 手写深拷贝

function deepClone(obj){
	if (typeof obj !== 'object' || obj === null){
		return obj
	}
	let result = Array.isArray(obj) ? []: {}
	for (let key in obj) {
		if(obj.hasOwnProperty(key)) {
			result[key] = deepClone(obj[key])
		}
	}
	return result
}

【JS】自定义 JS 工具函数库 - 自定义对象方法 - new-instanceof-mergeObject - 实现数组与对象的深拷贝与浅拷贝 - 封装字符串相关函数

# 2.2 手写深度比较

`// 判断是否是对象或数组
function isObject(obj) {
  return typeof obj === object && obj !== null;
}

// 深度比较
function isEqual(obj1, obj2) {
  if (!isObject(obj1) || !isObject(obj2)) {
    // 值类型,直接判断【一般不会传函数,不考虑函数】
    return obj1 === obj2;
  }
  if (obj1 === obj2) {
    return true;
  }
  // 两个都是对象或数组,而且不相等
  // 1. 先判断键的个数是否相等,不相等一定返回false
  const obj1Keys = Object.keys(obj1);
  const obj2Keys = Objext.keys(obj2);
  if (obj1Keys.length !== obj2Keys.length) {
    return false;
  }
  // 2. 以obj1为基准,和obj2依次递归比较
  for (let key in obj1) {
    // 递归比较
    const res = isEqual(obj1[key], obj2[key]);
    if (!res) {
      return false;
    }
  }
  // 3. 全相等
  return true;
}` 

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png)

*   1
*   2
*   3
*   4
*   5
*   6
*   7
*   8
*   9
*   10
*   11
*   12
*   13
*   14
*   15
*   16
*   17
*   18
*   19
*   20
*   21
*   22
*   23
*   24
*   25
*   26
*   27
*   28
*   29
*   30
*   31
*   32

# 2.3 数组的 API 有哪些是纯函数

纯函数:①不改变原数组 (没有副作用) ②返回一个新数组
concat、map、filter、slice

非纯函数:push、pop、shift、unshift、forEach、some、every、reduce

【JS】你不得不知道的 JavaScript 数组相关知识【全面总结】复习专用

# 2.4 split()join() 的区别

split() 是字符串的方法
join() 是数组的方法

'1-2-3'.split('-') // [1,2,3]
[1,2,3].join('-') // 1-2-3

# 2.5 数组 slicesplice 区别

slice 切片
splice 剪接

【JS】JavaScript 数组 - 操作方法 - concat - 数组强制打平 - slice-splice 方法使用

# 2.6 手写字符串 trim

String.prototype.trim = function() {
	return this.replace(/^\s+/, '').replace(/\s+$/, '')
}

# 3. 函数相关

# 3.1 函数声明与函数表达式

函数声明式

function fn(a, b) {
  return a + b;
}

函数表达式

let fun = function(a, b){
  return a + b;
}

# 3.2 什么是 JSON

  • JSON 是一种数据格式,本质是一段字符串
  • JSON 格式与 JS 对象结构一致,对 JS 语言更友好
  • window.JSON 是一个全局对象,常用的两个方法 JSON.stringifyJSON.parse

# 3.3 将 URL 参数解析成 JS 对象

传统方法,分析 search

function queryToObj() {
	const res = {}
	const search = location.search.substr(1)
	search.split('&').forEach(paramStr => {
		const arr = paramStr.split('=')
		const key = arr[0]
		const val = arr[1]
		res[key] = val
	})
	return res
}

使用 URLSearchParams

function queryToObj() {
	const res = {}
	const pList = new URLSearchParams(location.search)
	pList.forEach((val, key) => {
		res[key] = val
	})
	return res
}

# 4. 原型与原型链

# 4.1 解释一下原型与原型链

每个函数对象都有显式原型 prototype
每个实例对象都有隐式原型 __proto__
实例对象的 __proto__ 指向函数对象的 prototype

(之前博文中的图)

原型链:实例对象在获取对象上的属性和方法时,先在自身找,找不到就去隐式原型上面找

(之前博文中的图)

# 4.2 class 的原型本质

class 是 ES6 语法规范,由 ECMA 委员会发布【构造函数、继承】
ECMA 只规定语法规则,不规定如何实现

下面博文具体介绍了 class 语法,以及具体的原生实现【构造函数、继承】

【ES6】JavaScript 面向对象 - 面向对象与面向过程的对比 - 类 class - 继承 extends - 构造函数 - super

【JS】JavaScript 创建对象 - 工厂模式 - 构造函数模式 - 原型模式 - 原型链 - 组合模式

【JS】JavaScript 继承 - 原型链 - 盗用构造函数 - 组合继承 - 原型式继承 - 寄生式继承 - 寄生式组合继承

# 4.3 new Object () 与 Object.create () 的区别

  • {} 等同于 new Object() ,原型为 Object.prototype
  • Object.create(null) 没有原型
  • Object.create({...}) 可以指定原型

# 4.4 用 class 语法写一个简单的 jQuery

`class jQuery {
  constructor(selector) {
    const result = document.querySelectorAll(selector);
    const length = result.length;
    for (let i = 0; i < length; i++) {
      this[i] = result[i];
    }
    this.length = 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);
    });
  }
}

// 插件
jQuery.prototype.dialog = function(info){
	console.log(info);
}

// 拓展性
class myjQuery extends jQuery{
	constructor(selector){
		super(selector)
	}
	// 扩展自己的方法
	addClass(className){}
	addStyle(data){}
}` 

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png)

*   1
*   2
*   3
*   4
*   5
*   6
*   7
*   8
*   9
*   10
*   11
*   12
*   13
*   14
*   15
*   16
*   17
*   18
*   19
*   20
*   21
*   22
*   23
*   24
*   25
*   26
*   27
*   28
*   29
*   30
*   31
*   32
*   33
*   34
*   35
*   36
*   37
*   38
*   39
*   40
*   41
*   42
*   43

# 5. 作用域与闭包

这篇博文写的很详细,推荐阅读
【JS】你不知道的 JavaScript 笔记(一)—— 作用域与闭包 - 编译原理 - LHS - RHS - 循环与闭包 - 模块 - 词法作用域 - 动态作用域

【JS】函数定义与调用方式 - 函数 this 指向问题 - call-apply-bind 方法使用与自定义

【JS】你不知道的 JavaScript 笔记(二)- this - 四种绑定规则 - 绑定优先级 - 绑定例外 - 箭头函数

# 5.1 作用域

一个变量合法的使用范围,JS 中采用的是词法作用域(静态作用域)
【变量的查找,取决于在哪里定义,而不是在哪里执行】

全局作用域 - 函数作用域 - 块级作用域

自由变量:一个变量在当前作用域没有定义,但是被使用了。这时就向上级作用域一层一层依次查找,直到找到为止,最后在全局作用域都没找到就报错 ReferenceError:xxx is not defiend

# 5.2 this 不同场景下如何取值

this 查找采用的是动态作用域
【this 的指向,取决于在哪里执行,而不是在哪里定义】

例题

const User = {
	count: 1,
	getCount: function() {
		return this.count
	}
}
console.log(User.getCount()) // 1
const func = User.getCount
console.log( func() ) // undefined

【JS】你不知道的 JavaScript 笔记(二)- this - 四种绑定规则 - 绑定优先级 - 绑定例外 - 箭头函数

# 5.3 手写 bind

【JS】函数定义与调用方式 - 函数 this 指向问题 - call-apply-bind 方法使用与自定义

Function.prototype.myBind = 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)
	}
}

使用

function fn1(a, b, c) {
	console.log('this', this)
	console.log(a, b, c)
	return 'this is fn1'
}
const fn2 = fn1.myBind({x: 100}, 10, 20, 30)
const result = fn2() 
console.log(result)

# 5.4 闭包

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包

① 函数作为参数被传递 ② 函数作为返回值被返回

【JS】你不知道的 JavaScript 笔记(一)—— 作用域与闭包 - 编译原理 - LHS - RHS - 循环与闭包 - 模块 - 词法作用域 - 动态作用域

# 5.5 闭包的应用场景

隐藏数据,只提供 API

function createCache() {
	const data = {}  // 闭包中的数据被隐藏,不被外界访问
	return {
		set: function(key, value) {
			data[key] = value
		},
		get: function(key){
			return data[key]
		}
	}
}

const cache = createCache()
c.set('a', 100)
console.log(c.get('a'))

# 6. ES6 新特性

【ES6】变量声明 - var-let-const - 区别与联系 - 总结

【ES6】Symbol 基本使用及常用内置符号

【ES6】JavaScript 函数 - 箭头函数 - this 指向 - 简写

【ES6】JavaScript - 变量的解构赋值 - 数组解构 - 对象解构 - 对象的属性 - 对象的方法

【ES6】JavaScript 对象 - 增强的对象语法 - 属性值简写 - 可计算属性 - 简写方法名

【ES6】JavaScript 函数 - 参数的默认值 - 与解构赋值的结合使用 - 对 arguments 的影响 - 默认参数作用域与暂时性死区

【ES6】JavaScript 数组 - 数组的创建 - 构造函数 - 字面量 - Array.from ()-Array.of () 静态方法

【ES6】JavaScript 数组 - 迭代器方法 - keys ()-values ()-entries ()- 迭代方法 - every ()-some ()-filter ()-map ()-forEach ()

【ES6】JavaScript 面向对象 - 面向对象与面向过程的对比 - 类 class - 继承 extends - 构造函数 - super

【ES6】迭代器与生成器

【Promise】入门 - 同步回调 - 异步回调 - JS 中的异常 error 处理 - Promis 的理解和使用 - 基本使用 - 链式调用 - 七个关键问题

【ES8】异步代码终极解决方案 async 和 await

【ES6 模块化】import - export - 按需引入 - 项目中使用 babel - ES6 模块化引入 npm 包

# 7. 异步相关

因为单线程,所以异步【同步会阻塞代码执行】

JS 单线程, 和 DOM 渲染共用一个线程【因为 JS 可以修改 DOM 结构】
浏览器和 node.js 已经支持 JavaScript 启动进程,如 Web Worker

# 7.1 同步与异步的区别

【JavaScript】同步与异步 - 异步与并行 - 异步运行机制 - 为什么要异步编程 - 异步与回调 - 回调地狱 - JavaScript 中的异步操作

# 7.2 前端使用异步的场景

网络请求,如 ajax
定时任务

# 7.3 Promise 的三种状态

  1. pending 等待中 不会触发 thencatch
  2. resolved 成功了 会触发后续的 then 回调函数
  3. rejected 失败了 会触发后续的 catch 回调函数

then 正常返回 resolved,里面有报错则返回 rejected
catch 正常返回 resolved,里面有报错则返回 rejected

【Promise】入门 - 同步回调 - 异步回调 - JS 中的异常 error 处理 - Promis 的理解和使用 - 基本使用 - 链式调用 - 七个关键问题

# 7.4 promise 的 then 和 catch

Promise.resolve().then(()=>{
	console.log(1) // 执行
}).catch(()=>{
	console.log(2) // 不执行
}).then(()=>{
	console.log(3) // 执行
})
Promise.resolve().then(()=>{
	console.log(1) // 执行
	throw new Error('err1')
}).catch(()=>{
	console.log(2) // 执行
}).then(()=>{
	console.log(3) // 执行
})
Promise.resolve().then(()=>{
	console.log(1) // 执行
	throw new Error('err1')
}).catch(()=>{
	console.log(2) // 执行
}).catch(()=>{
	console.log(3) // 不执行
})

# 7.5 手写 promise 加载图片

``function loadImg(src){
	return new Promise((resolve, reject)=>{
		const img = document.createElement('img')
		img.onload = () =>{
			resolve(img)
		}
		img.onerror = () => {
			reject(new Error(`图片加载失败 ${src}`))
		}
		img.src = src
	})
}

// 使用
const url = ''
loadImg(url).then(img => {
	console.log(img.width)
	return img
}).then(img => {
	console.log(img.height)
}.catch(err => console.error(err))

// 使用加载多张图片
url1 = ''
url2 = ''
loadImg(url1).then(img1 => {
	console.log(img1.width)
	return img1
}).then(img1 => {
	console.log(img1.height)
	return loadImg(url2)	
}).then(img2 => {
	console.log(img2.width)
	return img2
}).then(img2 => {
	console.log(img2.height)
}).catch(err => console.error(err))`` 

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png)

*   1
*   2
*   3
*   4
*   5
*   6
*   7
*   8
*   9
*   10
*   11
*   12
*   13
*   14
*   15
*   16
*   17
*   18
*   19
*   20
*   21
*   22
*   23
*   24
*   25
*   26
*   27
*   28
*   29
*   30
*   31
*   32
*   33
*   34
*   35
*   36
*   37

# 7.6 async/await 与 Promise

  1. 执行 async 函数,返回的是 Promise 对象
  2. await 相当于 Promise 的 then
  3. try/catch 可以捕获异常,代替了 Promise 的 catch

【ES8】异步代码终极解决方案 async 和 await

# 7.7 字节面试看代码题

async function async1() { // 函数定义
  console.log("async1 start"); // 2
  await async2(); // 函数执行
  // await后面的内容,可以看作是回调里的内容,即异步执行
  console.log("async1 end"); // 6
}

async function async2() { // 函数定义
  console.log("async2"); // 3
}

console.log("script start"); // 1
async1(); // 函数执行

new Promise((resolve)=>{
	console.log('promise1'); // 4
	resolve();
}).then(()=>{
	console.log('promise2'); // 7
})

console.log("script end"); // 5 同步代码执行完毕

# 7.8 for-of 的应用场景 【异步】

for、forEach、for-in 是常规的【同步】遍历
for-of 常用于【异步】的遍历

function muti(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(num * num);
    }, 1000);
  });
}

const nums = [1, 2, 3];

nums.forEach(async (i) => {
  const res = await muti(i);
  console.log(res); // 会同时打印出三个结果
});

(async function () {
  for (let i of nums) {
    const res = await muti(i);
    console.log(res); // 会每隔一秒打印出一个结果
  }
})();

# 8. Event Loop

# 8.1 宏任务 macroTask 与微任务 microTask

  • 宏任务:setTimeout、setInterval、Ajax、DOM 事件【W3C 规范】
  • 微任务:Promise、async/await【ES 规范】

微任务执行时机比宏任务早


  • 微任务在 DOM 渲染触发 【Promise】
  • 宏任务在 DOM 渲染触发 【setTimeout】

# 8.2 Event Loop 机制

  • 回调栈 Call Stack
  • 事件循环 event loop
  • 回调队列 Callback Queue
  • 微任务队列 micro task queue
  1. 同步代码,一行一行放在回调栈中执行,执行完了就出栈
  2. 遇到异步,记录下来,等待时机;时机到了,就移动到回调队列中
  3. 当回调栈为空,(微任务 [微任务队列])【尝试 DOM 渲染】 事件循环开始工作:轮询查找(宏任务)回调队列,有则移动到回调栈中执行
  4. 继续轮询 loop

【同步任务 —— 微任务 ——DOM 渲染 —— 宏任务】—— 同步任务 —— 微任务 ——DOM 渲染 —— 宏任务…


实际上真正的 event loop 是这样的

  1. 一开始整个脚本作为一个宏任务执行
  2. 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  3. 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
  4. 执行浏览器 UI 线程的渲染工作
  5. 检查是否有 Web Worker 任务,有则执行
  6. 执行完本轮的宏任务,回到 2,依此循环,直到宏任务和微任务队列都为空

所以过程是

【宏任务(代码整体)—— 同步任务 —— 微任务 ——DOM 渲染】—— 宏任务 —— 同步任务 —— 微任务 ——DOM 渲染…

# 8.3 event loop 练习

`console.log('1'); // ①同步任务 

setTimeout(function() { // ① 宏任务
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})

process.nextTick(function() { // ① 微任务
    console.log('6');
})

new Promise(function(resolve) {
    console.log('7'); // ① 同步任务 
    resolve();
}).then(function() { // ① 微任务
    console.log('8')
})

setTimeout(function() { // ① 宏任务
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
    
    console.log('13');
    
    process.nextTick(function() {
        console.log('14'); // 微任务 process.nextTick 比 promse.then优先级要高
    })
})` 

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png)

*   1
*   2
*   3
*   4
*   5
*   6
*   7
*   8
*   9
*   10
*   11
*   12
*   13
*   14
*   15
*   16
*   17
*   18
*   19
*   20
*   21
*   22
*   23
*   24
*   25
*   26
*   27
*   28
*   29
*   30
*   31
*   32
*   33
*   34
*   35
*   36
*   37
*   38
*   39
*   40
*   41
*   42
*   43
*   44
*   45

1 7 6 8 2 4 3 5 9 11 13 10 14 12

# 9. DOM

# 9.1 获取节点操作

document.getElementById('yk') // 元素
document.getElementsByTagName('div') // 集合

document.getElementsByClassName('container') // 集合
document.querySelectorAll('p') // 集合

# 9.2 标签属性 attribute

修改的是标签属性【内联样式】
修改 html 属性,会改变 html 结构

const pList = document.querySelectorAll('p')
const p = pList[0]

p.getAttribute('data-name')
p.setAttribute('data-name','ykjun')
p.getAttribute('style')
p.setAttribute('style', 'font-size: 10px')

# 9.3 对象属性 property

用 JS 的属性操作 DOM 元素
修改对象属性,不会 体现到 html 结构中

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

console.log(p.className) // 获取class
p.className = 'p1' // 修改class

console.log(p.nodeName) // 获取nodeName节点名称
console.log(p.nodeType) // 获取nodeType节点类型

# 9.4 DOM 结构操作

`const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')

// 新建节点
const newP = document.createElement('p')
newP.innerHTML = 'this is new p'

// 插入节点
div1.appendChild(newP)

// 移动节点
const p1 = document.getElementsByTagName('p')[0]
div2.appendChild(p1)

// 获取父元素
console.log(p1.parentNode)

// 获取子元素列表
const div1ChildNodes = div1.childNodes
console.log('div1ChildNodes', div1ChildNodes)

const div1ChildNodesP = Array.from(div1ChildNodes).filter(child => {
	if(child.nodeType === 1) {
		return true;
	}
	return false;
}
console.log('div1ChildNodesP', div1ChildNodesP)

// 删除节点
div1.removeChild(div1ChildNodesP[0])` 

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png)

*   1
*   2
*   3
*   4
*   5
*   6
*   7
*   8
*   9
*   10
*   11
*   12
*   13
*   14
*   15
*   16
*   17
*   18
*   19
*   20
*   21
*   22
*   23
*   24
*   25
*   26
*   27
*   28
*   29
*   30
*   31

# 9.5 优化 DOM 性能

# 9.5.1 对 DOM 查询进行缓存

不使用缓存 DOM 查询结果

for(let i = 0; i < document.getELementsByTagName('p').length; i++){
	//	每次循环都会计算length,频繁进行DOM查询
}

缓存 DOM 查询结果

const pList = document.getELementsByTagName('p')
const length = pList.length
for(let i = 0; i < length; i++){
	// 缓存length, 只进行一次DOM查询
}

# 9.5.2 将频繁操作改为一次性操作

const listNode = document.getElementById('list')
// 创建一个文档片段,此时还没有插入到DOM树中
const frag = document.createDocumentFragment()
// 执行插入操作
for(let i = 0; i < 10; i++){
	const li = document.createElement('li')
	li.innerHTML = "Iist Item " + i
	frag.appendChild(li)
}
// 都完成后,再插入DOM树中
listNode.appendChild(frag)

# 10. BOM

  • navigator
  • screen
  • location
  • history

【BOM】JavaScript - 定时器 - 执行机制 - location-navigator-history

# 10.1 检查浏览器类型

const ua = navigator.userAgent
const isChorme = ua.indexOf('Chrome')
console.log(isChorme)

# 10.2 拆解 URL 各个部分

location

# 11. 事件

【DOM】JavaScript - 事件高级 - 注册事件 - 事件流 - 事件对象 - 事件冒泡 - 委派 - 鼠标键盘事件

# 11.1 事件绑定、冒泡、代理

const btn = document.getElementById('btn1')
btn.addEventListener('click', event => {
	console.log('clicked')
})

# 11.2 写一个通用的事件绑定函数

function bindEvent(elem, type, fn){
	elem.addEventListener(type, fn)
}

const btn1 = document.getELementById('btn1')
bindEvent(btn1,  'click', event => {
	console.log(event.target) // 获取触发的元素
	event.preventDefault() // 阻止默认行为
	alert('clicked')
})

升级版【支持代理】

`function bindEvent(elem, type, selector, fn){
	if(fn == null){
		fn = selector
		selector = null
	}
	elem.addEventListener(type, event => {
		if(selector){
			// 代理绑定 
			if (target.matches(selector)){
				fn.call(target, event)
			}
		}else {
			// 普通绑定
			fn.call(target, event)
		}
	})
}

// 普通绑定
const btn1 = document.getELementById('btn1')
bindEvent(btn1, 'click', function(event) {
	console.log(event.target) // 获取触发的元素
	event.preventDefault() // 阻止默认行为
	alert(this.innerHTML)
})

// 代理绑定 [在a父节点div上绑定事件]
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function(event) {
	event.preventDefault()
	alert(this.innerHTML)
})` 

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png)

*   1
*   2
*   3
*   4
*   5
*   6
*   7
*   8
*   9
*   10
*   11
*   12
*   13
*   14
*   15
*   16
*   17
*   18
*   19
*   20
*   21
*   22
*   23
*   24
*   25
*   26
*   27
*   28
*   29
*   30
*   31
*   32
*   33

# 11.3 描述事件冒泡过程

  • 基于 DOM 树形结构
  • 事件会顺着触发元素往上冒泡
  • 应用场景:事件代理

# 11.4 无限下拉的图片列表,如何监听每个图片的点击

  • 事件代理
  • event.target 获取触发元素
  • matches 来判断是否是触发元素

# 12. AJAX

【Ajax】HTTP 相关问题 - GET-POST-XHR 使用 - jQuery 中的 ajax - 跨域 - 同源 - jsonp-cors

【axios】使用 json-server 搭建 REST API - 使用 axios - 自定义 axios - 取消请求 - 拦截器

# 12.1 手写一个简单的 ajax

function ajax(url) {
	const p = new Promise((resolve, reject) => {
		const xhr = new XMLHttpRequest()
		xhr.open('GET', url, true)
		xhr.onreadystatechange = function () {
			if(xhr.readyState === 4) {
				if(xhr.status === 200) {
					resolve(
						JSON.parse(xhr.responseText)
					)
				} else if (xhr.status === 404) {
					reject(new Error('404 not found'))
				}
			}
		}
		xhr.send(null)
	})
	return p
}

// 使用
const url = '/data/test.json'
ajax(url)
.then(res => console.log(res))
.catch(err => console.log(err))

# 12.2 跨域解决方案

浏览器中加载图片、css、js 可以无视同源策略

  • <img /> 可用于统计打点,可使用第三方服务
  • <link /> <script> 可以使用 CDN
  • <script> 可以实现 JSONP

所有跨域,都必须经过 server 端允许和配合
未经 server 允许就实现跨域,说明浏览器有漏洞

  • JSONP
  • CORS
  • 代理

【Ajax】HTTP 相关问题 - GET-POST-XHR 使用 - jQuery 中的 ajax - 跨域 - 同源 - jsonp-cors

# 13. 浏览器存储

【浏览器】浏览器存储 & 缓存 - Cookie - localStorage - sessionStorage - IndexDB - Cache Storage - Application Cache

cookie

  • 本身用于浏览器和服务器通讯
  • 存储大小最大 4KB
  • http 请求时需要发送到服务端,增加请求数据量
  • 是能用 document.cookie='...' 来修改,太过简陋

localStorage 和 sessionStorage

  • HTML5 专门为存储设计,最大可存 5M
  • API 简单易用 setItem getItem
  • 不会随着 HTTP 请求被发出去

# 14. 页面加载

# 14.1 资源的形式

  • HTML 代码
  • 媒体文件,如图片、视频
  • JavaScript 代码 css 代码

# 14.2 从输入 url 到渲染出页面的整个过程

① 获取资源
DNS 域名解析:域名 ——> IP 地址
TCP 三次握手建立连接
浏览器根据 IP 地址向服务器发起 HTTP 请求
服务器处理 HTTP 请求,并资源返回给浏览器

② 渲染页面
浏览器根据 HTML 代码生成 DOM Tree
根据 CSS 代码生成 CSSOM
将 DOM Tree 和 CSSOM 整合成 渲染树 Render Tree
根据 Render Tree 渲染页面
遇到 scrpit 标签 则暂停渲染,优先加载并执行 JS 代码,完成再继续
直至把 Render Tree 渲染完成

# 14.3 window.onload 与 DOMContentLoaded 的区别

window.onload 资源全部加载完成才能执行,包括图片
DOMContentLoaded DOM 渲染完成即可,图片可能尚未下载

const img1 = document.getElementById('img1')
img1.onload = function() {
	console.log('img loaded') // 2 
}
window.addEventListener('load', function() {
	console.log('window loaded') // 3
})
document.addEventListener('DOMContentLoaded', function() {
	console.log('dom content loaded') // 1
})

# 14.4 重绘与回流

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)

# 15. 性能优化

# 15.1 前端常见性能优化方案

  1. 思路
  • 多使用内存、缓存或其他方法
  • 减少 CPU 计算量,减少网络加载耗时
  • 空间换时间
  1. 加载优化
  • 减少资源体积:压缩代码
  • 减少访问次数:合并代码,SSR 服务端渲染、缓存
  • 使用更快的网络:CDN
  1. 渲染优化
  • CSS 放在 head,JS 放在 body 最下面
  • 尽早开始执行 JS,用 DOMContentLoaded 触发
  • 懒加载
  • 对 DOM 查询进行缓存
  • 频繁 DOM 操作,合并到一起插入 DOM 结构
  • 节流与防抖

# 15.2 缓存

静态资源加 hash 后缀,根据文件内容计算 hash
文件内容不变,则 hash 不变,则 url 不变
url 和文件不变,就会自动触发 HTTP 缓存机制,返回 304

网络方面的缓存分为三块:DNS 缓存、HTTP 缓存、CDN 缓存, HTTP 缓存也称为浏览器缓存

(建议收藏) 为什么第二次打开页面快?五步吃透前端缓存,让页面飞起

# 15.3 SSR 服务渲染

将网页和数据一起加载,一起渲染
非 SSR(前后端分离):先加载网页,再加载数据 (ajax),再渲染数据

# 15.4 图片懒加载

先给一个小的预览图,判断到用户访问到当前位置,再加载高清

<img id="img1" src="preview.png" data-realsrc="real.png" />
<script>
	let img1 = document.getElementById('img1')
	img1.src = img1.getAttribute('data-realsrc')
<script>

# 15.5 节流与防抖

【JS】函数节流与函数防抖 - 自定义 JS 工具类

# 16. 前端安全

# 16.1 XSS 跨站请求攻击

前端安全系列(一):如何防止 XSS 攻击?

# 16.2 XSRF 跨站请求伪造

前端安全系列之二:如何防止 CSRF 攻击?

# 17. 算法刷题

【算法】经典排序算法总结 - JavaScript 描述 - 图解 - 复杂度分析

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结

【LeetCode】经典题分类(链表 )精选 - JavaScript - ES6 - 技巧总结

[【LeetCode】经典题分类(树 & 图 )精选 - JavaScript - ES6 - 技巧总结](