JS解析
次访问
JS解析
数据类型
基本数据类型
string 、number、 boolean、null、undefine、symbol、bigInt
引用数据类型
function、array、RegExp、Date、Math
this指向
(一)普通函数的调用,this指向的是Window
1 | var name = '卡卡'; |
(二)对象的方法,this指的是该对象
- 一层作用域时,this指的是该对象
1 | var name = '卡卡'; |
- 多层作用域时,this指的是距离方法最近的一层对象
1 | var name = '卡卡'; |
(三)构造函数的调用,this指的是实例化的新对象
1 | var name = '卡卡'; |
(四)apply和call调用时,this指向参数中的对象
1 | var name = '有鱼'; |
(五)匿名函数调用,指向的是全局对象
1 | var name = '卡卡'; |
(六)定时器中调用,指向的是全局变量
1 | var name = '卡卡'; |
(七)箭头函数调用
  如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this的值则被设置为全局对象
1 | var name = 'window'; |
注意
①普通函数的调用,this指向的是window
②对象方法的调用,this指的是该对象,且是最近的对象
③构造函数的调用,this指的是实例化的新对象
④apply和call调用,this指向参数中的对象
⑤匿名函数的调用,this指向的是全局对象window
⑥定时器中的调用,this指向的是全局变量window
⑦.箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this的值则被设置为全局对象
⑧.onclick和addEventerListener是指向绑定事件的元素(ev.currentTarget)
call、apply、bind的原理及实现
call
  改变当前函数this指向,并立即执行
1 | Function.prototype.mycall = function(context,...args){ |
apply
  将函数中的this指向传入的第一个参数,第二个为数组,和call方法作用相同,参数不同,立即执行
1 | Function.prototype.myapply = function(context,args){ |
bind
  bind() 方法创建一个新的函数,不自动执行,需要手动调用bind() 。这个新函数的 this 被指定为 bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
1 | Function.prototype.mybind = function(context,...args1){ |
变量提升
  变量提升是指在js代码执行过程中,js引擎把变量的声明部分和函数声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值undefined
  js在编译阶段的时候,会搜集所有的变量声明并且提前声明变量,而其他的语句都不会改变他们的顺序,因此,在编译阶段的时候,第一步就已经执行了,而第二步则是在执行阶段执行到该语句的时候才执行。
执行上下文
  执行上下文是js代码执行的一个环境,存放了代码执行所需的变量,变量查找的作用域链规则以及this指向等。在编译阶段创建
- 全局执行上下文:当进入全局代码时会进行编译,在编译中创建全局执行上下文,并生成可执行代码
- 函数执行上下文:执行代码的过程中,如果遇到函数调用,会编译函数内的代码和创建函数执行上下文,并创建可执行代码
- eval执行上下文::当使用eval函数的时候,eval的代码也会被编译,并创建执行上下文
作用域
- 全局作用域:不在函数内部作用域和块级内部作用域外的作用域
- 函数作用域:在函数内部的作用域
- 块级作用域:在花括号{}内部的作用域
注意:
  ①对象的{}不属于块级作用域,像for(){},if(){},else{},try{},cath(){}等等的花括号才是块级作用域
  ②对象的{}的作用域是什么作用域取决于对象所处的作用域,比如对象在全局作域 下定义的,那么对象的{}的作用域就是全局作用域
作用域链
  内部作用域访问外部作用域的变量,采取的是链式查找的方式来决定取哪个值,这种结构我们称为作用域链,采取就近原则的方式向上一级一级的作用域来查找变量值,最顶级是全局作用域,如果到全局作用域也没找值,那么就会报错。
闭包
概念
  如果一个函数访问了此函数的父级及父级以上的作用域变量,那么这个函数就是一个闭包。即函数不在当前语法作用域中执行,而是在当前语法作用域外执行,但仍然保持对当前语法作用域的引用,这种引用叫闭包
特点
- 被闭包函数访问的父级及以上的函数的局部变量(如范例中的局部变量 i )会一直存在于内存中,不会被JS的垃圾回收机制回收。
- 闭包函数实现了对其他函数内部变量的访问。(函数内部的变量对外是无法访问的,闭包通过这种变通的方法,实现了访问。)
用途
- 访问函数内部的变量
- 让变量始终保持在内存中
- 使用不当会导致内存泄漏
new 的原理
- 创建一个空对象作为返回实例
- 将这个空对象的原型,指向构造函数的prototype属性
- 将这个空对象赋值给构造函数内部的this关键字
- 开始执行构造函数内部的代码
- 返回这个对象
1
2
3
4
5function myNew(fn,...arg){
let obj = Object.create(fn.prototype)
let result = fn.apply(obj,arg)
return typeof result === 'object' ? result : obj;
}
原型与原型链
  在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数
1 | person1.constructor == Person |
注意: 原型对象(Person.prototype)是 构造函数(Person)的一个实例。
原型与原型链参考文章
事件处理机制
- 捕获阶段:事件从window对象自上而下向目标节点传播的阶段;
- 目标阶段:真正的目标节点正在处理事件的阶段;
- 冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。
模块化
  模块化开发是一种管理方式,一种生产方式,一种解决问题的方案。一个模块就是实现某个特定功能的文件,我们可以很方便的使用别人的代码,想要什么模块,就引入那个模块。
  防止出现命名冲突,污染作用域等一系列问题
Iterator迭代器
  迭代器对象本质上,就是一个指针对象。通过指针对象的next(), 用来移动指针。
1 | <!--JavaScript 原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for...of循环,允许遍历获得键值。--> |
Promise
promise共有三种状态,pending,fulfilled,rejected
1 | // 自定义实现promise |
Generator
什么是Generator
  是ES6提供的一种异步编程解决方案,语法不同于普通函数;简单的把Generator 理解为一个状态机,封装了多个内部状态。执行Generator 函数会返回一个迭代器对象,可以通过调用迭代器next依次遍历Generator函数内部的每一个状态。
Generator的特征
- function关键字与函数名之间有个星号;
- 函数内部使用yield表达式
Generator的使用
1 | function* generateSequence() { |
async/await
  声明异步函数,遇到await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,继续执行await后面的代码。
优缺点:
- 相对于promise,async/await处理 then 的调用链,代码要清晰很多,几乎和同步代码一样
- 滥用 await 可能会导致性能问题,因为 await 会阻塞代码
事件循环
定义
  事件循环,把异步任务的回调部分交给事件循环,等时机合适交还给 JS 线程执行。事件循环并不是 JavaScript 首创的,它是计算机的一种运行机制。
  所有任务都可以分成同步任务和异步任务,当任务进入执行栈,会判断任务是同步还是异步任务,如果是同步任务进行主线程,异步任务进入异步队列(event Queue),等待主线程中的任务执行完毕后,会在异步队列中读取相应的任务(先进先出),推入主线程执行,这个上述过程不断重复就是事件循环(Event Loop)
  事件循环是由一个队列组成的,异步任务的回调遵循先进先出,在 JS 引擎空闲时会一轮一轮地被取出,所以被叫做循环。
垃圾回收(GC)
  是清理程序不用的内存或者是之前用过了,以后不会再用的内存空间。
回收策略
  垃圾回收主要也是针对堆内存中的对象,对于栈中的非闭包变量,会被栈执行上下文切换时直接出栈,释放了内存空间
内存泄漏
- 内存泄漏:当不再用到的对象内存,没有及时被回收,想要避免就让无用数据不存在引用关系。
- 内存膨胀:即在短时间内内存占用极速上升到达一个峰值,想要避免需要使用技术手段减少对内存的占用。
- 频繁GC: GC执行的特别频繁,一般出现在频繁使用大的临时变量导致新生代空间被装满的速度极快,而每次新生代装满时就会触发 GC,频繁 GC 同样会导致页面卡顿,想要避免的话就不要搞太多的临时变量,因为临时变量不用了就会被回收。
泄露类型
- 闭包
- 隐式全局变量
- DOM引用
- 定时器
- 事件监听
- 事件监听发布
- Map、Set强引用
- 未清理的console
深浅拷贝
浅拷贝
  会在栈中开辟另一块空间,并将被拷贝对象的栈内存数据完全拷贝到该块空间中,即基本数据类型的值会被完全拷贝,而引用类型的值则是拷贝了“指向堆内存的地址”。
1 | function shallowCopy(target) { |
深拷贝
  不仅会在栈中开辟另一块空间,若被拷贝对象中有引用类型,则还会在堆内存中开辟另一块空间存储引用类型的真实数据。
1 | JSON.parse(JSON.stringify(obj)) |
1 | const obj3 = { |
深拷贝和浅拷贝的区分
  简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短;如果B没变,那就是深拷贝,自食其力。
节流和防抖
  在进行窗口的resize、scroll、输出框内容校验等操纵的时候,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常之差。那么为了前端性能的优化也为了用户更好的体验,就可以采用防抖(debounce)和节流(throttle)的方式来到达这种效果,减少调用的频率。
防抖
  一定在事件触发 n 秒后才执行,如果在一个事件触发的 n 秒内又触发了这个事件,以新的事件的时间为准,n 秒后才执行,等触发事件 n 秒内不再触发事件才执行。 即计最后一次触发
1 | function debounce(func, wait, immediate) { |
节流
  当持续触发事件时,保证一定时间段内只调用一次事件处理函数。即计第一次触发
1 | function throttle(func, wait) { |
Proxy代理
  Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
1 | // target:要使用 Proxy 包装的目标对象 |
  Proxy可以帮我们对数据的处理,对构造函数的处理,对数据的验证,说白了,就是在我们访问对象前添加了一层拦截,可以过滤很多操作,而这些过滤,由你来定义。
Ajax
定义
  Ajax 异步通信技术,是Asynchronous JavaScript+XML的简写,这种技术允许向服务器请求额外的数据而无需卸载(刷新)页面。
通信步骤
- 新建XMLHttpRequest对象
- 打开要发送的地址通道
- 给XMLHttpRequest对象增加侦听器,用来侦听各种情况
- 发送数据给打开的地址
数组方法
- join():用指定的分隔符将数组每一项拼接为字符串
- push():向数组的末尾添加新元素
- pop():删除数组的最后一项
- unshift():向数组首位添加新元素
- shift():删除数组的第一项
- slice():按照条件查找出其中的部分元素
- splice():对数组进行增删改
- filter():过滤功能
- concat():用于连接两个或多个数组
- indexOf():检测当前值在数组中第一次出现的位置索引
- lastIndexOf():检测当前值在数组中最后一次出现的位置索引
- every():判断数组中每一项都是否满足条件
- some():判断数组中是否存在满足条件的项
- includes():判断一个数组是否包含一个指定的值
- sort():对数组的元素进行排序
- reverse():对数组进行倒序
- forEach():es5及以下循环遍历数组每一项
- map():es6循环遍历数组每一项
- find():返回匹配的项
- findIndex():返回匹配位置的索引
- reduce():从数组的第一项开始遍历到最后一项,返回一个最终的值
- reduceRight():从数组的最后一项开始遍历到第一项,返回一个最终的值
- toLocaleString()、toString():将数组转换为字符串
- entries()、keys()、values():遍历数组
ES6新特性
let const
- let:使用let声明的变量具有块级作用域:,只能在当前声明的代码块中使用,必须要先声明再使用
- const:具有块级作用域的特点,只能在当前声明的代码块中使用,必须要先声明再使用。常量,声明时必须赋值,不能改
箭头函数
- 可以将箭头函数赋值给一个变量,变量名字就是函数的名字,通过变量名字调用函数。
- 如果函数体中只有一行代码,且代码的执行结果就是返回值,可以省略大括号。
- 如果形参只有一个,可以省略小括号(如果没有形参,小括号不能省略)
