JS 进阶
作用域分为局部作用域与全局作用域
- 函数作用域、块级作用域是局部作用域
- script标签、js文件是全局作用域
作用域链本质上是底层的变量查找机制
作用域链的查找规则:优先查找当前函数作用域,查找不到则依次逐级查找父级作用域直到全局作用域
垃圾回收机制
Section titled “垃圾回收机制”- 引用计数法:被引用的次数,存在嵌套引用问题
- 标记清除法:从根部扫描对象,能查找到的就是使用的,查找不到的就要回收
闭包 = 内层函数 + 外层函数的变量
闭包的作用:封闭数据,提供操作,外部也可以访问函数内部的变量
闭包可能引起内存泄漏
变量提升:用var声明变量会有变量提升,允许变量在声明之前被访问
不建议用var声明变量
变量提升的流程:
- 先把var变量提升到当前作用域的最前面
- 只提升变量声明,不提升变量赋值
- 然后依次执行代码
函数提升类似于变量提升,函数表达式不存在提升
- 动态参数:在函数内部用
arguments,伪数组 - 剩余参数:函数最后一个形参前加
...,获取多余的实参,真数组
(x, y) => x + y返回对象字面量时,需要加括号
用于批量赋值
const [max, min, avg] = arr数组开头时,要加分号
;[b, a] = [a, b]变量名要与属性名一致
可以重新改名,新变量名写在冒号后面
函数参数也可以用对象解构
const { uname: username, age } = { uname: '喜多', age: 16 }forEach方法
Section titled “forEach方法”用于遍历数组,索引号是可选的
被遍历的数组.forEach(function (element, index) { // 函数体})filter方法
Section titled “filter方法”用于筛选数组符合条件的元素,返回新数组
被遍历的数组.filter(function (element, index) { return 筛选条件})- 用对象字面量创建
- 用
new Object()创建 - 用构造函数创建
构造函数首字母大写
function Pig(name) { this.name = name}const p = new Pig('佩奇')实例化:使用new关键字调用函数
实例化没有参数时可以省略括号
构造函数内部不用写return
实例化执行过程:
- 创建新对象
- 构造函数this指向新对象
- 执行构造函数代码,修改this,添加新的属性
- 返回新对象
- 实例成员:实例对象的属性和方法,当前实例对象使用
- 静态成员:构造函数的属性和方法,只能构造函数访问
内置构造函数
Section titled “内置构造函数”Object
Section titled “Object”Object.keys()获取所有的属性名Object.values()获取所有的属性值Object.assign()对象拷贝
forEach()遍历数组,不返回数组filter()过滤数组,返回数组map()迭代数组,返回数组reduce()累计器,返回累计处理的结果join()数组元素拼接为字符串find()查找元素every()检测数组所有元素是否满足条件Array.from()伪数组转真数组
String
Section titled “String”length属性 获取字符串长度split()将字符串拆分成数组,与join()相反substring()字符串截取startsWith()检测字符串开头includes()判断一个字符串是否包含另一个字符串
深入面向对象
Section titled “深入面向对象”面向对象的特性:封装性、继承性、多态性
JS面向对象通过构造函数实现封装
构造函数存在浪费内存的问题
每个构造函数都有一个prototype属性,指向一个对象,也称为原型对象
可以把那些不变的方法,直接定义在prototype对象上,实现方法共享
构造函数和原型对象中的this指向实例化的对象
constructor属性
Section titled “constructor属性”每个原型对象都有一个constructor属性,指向该原型对象的构造函数
给原型对象用对象形式赋值时,可以添加一个constructor属性指向原来的构造函数
对象都会有一个__proto__指向构造函数的prototype原型对象
__proto__是非标准的,且已被弃用,访问对象原型的标准方法是Object.getPrototypeOf()
[[Prototype]]与__proto__等价
JS通过原型对象实现继承:
子类.prototype = new 父类()子类.prototype.constructor = 子类原型链查找规则:
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型
- 如果还没有就查找原型对象的原型
- 依此类推一直找到Object为止
- __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
- 可以用
instanceof检测构造函数的prototype属性是否出现在某个实例对象的原型链上
浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址
常见方法:
- 拷贝对象:
Object.assign()或展开运算符{...obj} - 拷贝数组:
Array.prototype.concat()或[...arr]
深拷贝:拷贝的是对象
常用方法:
- 递归函数
- js库lodash的
_.cloneDeep() JSON.stringify()
通过递归函数实现深拷贝:
- 需要用函数递归
- 基本类型直接赋值,数组用递归
- 对象用递归
- 先数组后对象
function deepCopy(newObj, oldObj) { for (let k in oldObj) { if (oldObj[k] instanceof Array) { newObj[k] = [] deepCopy(newObj[k], oldObj[k]) } else if (oldObj[k] instanceof Object) { newObj[k] = {} deepCopy(newObj[k], oldObj[k]) } else { newObj[k] = oldObj[k] } }}throw抛异常,与Error对象配合使用try...catch...finally捕获异常,当try代码段中出现错误后,会执行catch代码段,finally代码段不管是否有错误都会执行debugger调试代码
处理this
Section titled “处理this”this指向
Section titled “this指向”普通函数this指向:谁调用我,我就指向谁
箭头函数this指向:函数内不存在this,沿用上一级的
改变this
Section titled “改变this”call()调用函数apply()调用函数,必须以数组形式传递bind()不调用函数,返回函数
防抖 debounce
Section titled “防抖 debounce”防抖:单位时间内,频繁触发事件,只执行最后一次
防抖的使用场景:搜索框搜索输入,手机号、邮箱验证输入检测
核心思路:
- 声明一个定时器变量
- 当鼠标每次滑动都先判断是否有定时器了,如果有定时器先清除以前的定时器
- 如果没有定时器则开启定时器,记得存到变量里面
- 在定时器里面调用要执行的函数
function debounce(fn, t) { let timer
return function () { if (timer) clearTimeout(timer) timer = setTimeout(fn, t) }}节流 throttle
Section titled “节流 throttle”节流:单位时间内,频繁触发事件,只执行一次
节流的使用场景:高频事件,如鼠标移动、页面尺寸缩放、滚动条滚动等
核心思路:
- 声明一个定时器变量
- 当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
- 如果没有定时器则开启定时器,记得存到变量里面
- 定时器里面调用执行的函数,定时器里面要把定时器清空
function throttle(fn, t) { let timer = null
return function () { if (!timer) { timer = setTimeout(function () { fn() timer = null }, t) } }}