Javascript进阶
作用域
局部作用域
局部作用域分为函数作用域和块作用域。
块作用域:
在 JavaScript 中使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。
全局作用域
<script> 标签 和 .js 文件 的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问
作用域链
作用域链本质上是底层的变量查找机制。
在函数被执行时,会优先查找当前函数作用域中查找变量
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
JS垃圾回收机制
垃圾回收机制(Garbage Collection) 简称 GCJS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。正因为垃圾回收器的存在,许多人认为JS不用太关心内存管理的问题但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况不再用到的内存,没有及时释放,就叫做 内存泄漏
JS环境中分配的内存, 一般有如下生命周期:1. 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存2. 内存使用:即读写内存,也就是使用变量、函数等3. 内存回收:使用完毕,由垃圾回收自动回收不再使用的内存4. 说明:全局变量一般不会回收(关闭页面回收);一般情况下局部变量的值, 不用了, 会被自动回收掉
堆栈空间分配区别:1. 栈(操作系统): 由 操作系统自动分配释放 函数的参数值、局部变量等,基本数据类型放到栈里面。2. 堆(操作系统): 一般由程序员分配释放,若程序员不释放,由 垃圾回收机制 回收。 复杂数据类型 放到堆里面。
引用计数法
IE采用的引用计数算法, 定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
核心:1. 标记清除算法将“不再使用的对象”定义为“ 无法达到的对象 ”。2. 就是从 根部 (在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从 根部到达 的对象,都是还 需要使用 的。3. 那些 无法 由根部出发触及到的 对象被标记 为不再使用,稍后进行 回收
标记清除法
从根部扫描对象,能查找到的就是使用的,查找不到的就要回收
闭包
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域简单理解: 闭包 = 内层函数 + 外层函数的变量闭包作用: 封闭数据,提供操作,外部也可以访问函数内部的变量
变量提升
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
变量在未声明即被访问时会报语法错误,变量在var声明之前即被访问,变量的值为 undefined
let/const 声明的变量不存在变量提升
函数进阶
函数参数
1. 动态参数
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
2. 剩余参数
1. ... 是语法符号,置于最末函数形参之前,用于获取多余的实参
2. 借助 ... 获取的剩余实参,是个真数组
展开运算符
展开运算符(…),将一个数组进行展开
箭头函数
‘语法1:基本写法
箭头函数不会创建自己的this ,它只会从自己的作用域链的上一层沿用this。
解构赋值
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值。
数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
变量多 单元值少的情况
变量的数量大于单元值数量时,多余的变量将被赋值为 undefined
变量少 单元值多的情况
利用剩余参数解决变量少 单元值多的情况
对象解构
基本语法
1. 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量2. 对象属性的值将被赋值给与属性名 相同的 变量3. 注意解构的变量名不要和外面的变量名冲突否则报错4.对象中找不到与变量名一致的属性时变量值为 undefined
2.给新的变量名赋值
冒号表示“什么值:赋值给谁
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数主要使用场景: 遍历数组的每个元素
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素主要使用场景: 筛选数组符合条件的元素 ,并返回筛选之后元素的新数组
对象
创建对象三种方式
构造函数
构造函数 : 是一种特殊的函数,主要用来初始化对象使用场景: 常规的 {...} 语法允许创建一个对象。比如我们创建了佩奇的对象,继续创建乔治的对象还需要重新写一遍,此时可以通过 构造函数 来 快速创建多个类似的对象 。
实例成员&静态成员
静态成员:构造函数的属性和方法被称为静态成员
实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
内置构造函数
Object
Object 是内置的构造函数,用于创建普通对象。
常用静态方法作用: Object.keys 静态方法获取对象中所有属性(键)作用: Object.values 静态方法获取对象中所有属性值作用: Object. assign 静态方法常用于对象拷贝
Array
String
Number
编程思想
面向过程介绍
面向过程 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了
面向对象介绍
面向对象 是把事务分解成为一个个对象,然后由对象之间分工与合作在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。面向对象的特性: 封装性 继承性 多态性
构造函数
封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装。同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之 间是彼此不影响的
原型
构造函数通过原型分配的函数是所有对象所 共享的。
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象
这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。构造函数和原型对象中的this 都指向 实例化的对象
原型- this指向
构造函数和原型对象中的this 都指向 实例化的对象
constructor 属性
每个原型对象里面都有个constructor 属性( constructor 构造函数 )作用: 该属性 指向 该原型对象的 构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子
对象原型
对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在
原型继承
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承的特性。龙生龙、凤生凤、老鼠的儿子会打洞描述的正是继承的含义继承与原型链 - JavaScript | MDN (mozilla.org)
原型链.
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对 象的链状结构关系称为原型链
原型链-查找规则
当访问一个对象的属性(包括方法)时,首先查找这个 对象自身 有没有该属性。② 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象 )③ 如果还没有就查找原型对象的原型( Object的原型对象 )④ 依此类推一直找到 Object 为止( null )⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
深浅拷贝
浅拷贝
首先浅拷贝和深拷贝只针对引用类型浅拷贝:拷贝的是地址常见方法:1. 拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象2.拷贝数组:Array.prototype.concat() 或者 [...arr]
深拷贝
异常处理
throw 抛异常
try /catch 捕获异常
debugger
处理this
改变this
JavaScript 中还允许指定函数中 this 的指向,有 3 个方法可以动态指定普通函数中 this 的指向 call() apply() bind()
性能优化
节流
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
防抖
防抖(debounce)所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间开发使用场景- 搜索框防抖假设输入就可以发送请求,但是不能每次输入都去发送请求,输入比较快发送请求会比较多我们设定一个时间,假如300ms, 当输入第一个字符时候,300ms后发送请求,但是在200ms的时候又输入了一个字符,则需要再等300ms 后发送请求