JavaScript
浏览器
为什么浏览器可以解析javascript代码?
浏览器内置了解析is代码的解析引擎,DOM操作和BOM操作是浏览器本身提供的API方法
script标签的async和defer属性是什么?
当浏览器遇到没有任何属性的script标签时会暂停html的解析,会先请求js脚本中的网络请求然后再解析html
当script标签加上async属性后,js脚本中的网络请求就会变成异步的,不会阻塞html解析,但是网络请求完成之后如果html还没有解析完毕,浏览器就会暂停html的解析,先执行js代码
当script标签加上defer属性后,js脚本中的网络请求就会变成异步的,不会阻塞html解析,但是网络请求完成之后如果html还没有解析完毕,不会暂停html的解析,而是等html解析完毕后在执行js代码
ES6新特性
新增加了let 和const
var声明的变量/函数是全局的,并且有变量提升。let和const声明的变量/函数是局部的,如果写在{}内那就只能在那个{}内被使用,没有变量提升
var和let声明变量时可以不赋值,但const声明变量必须赋值
const声明的是常量,不能修改
let和const不能重复声明变量
新增了基本数据类型symbol
symbol的值是唯一的,通常用于解决命名冲突
模板字符串
用反引号包裹,区别于单双引号,它可以换行书写,如果用${}包裹可以直接解析变量
新增了Map和Set集合
Set本质上是一个对象,类似于数组,set中存储的值是唯一的,可以通过set轻松的对数组进行去重(const getUnique =(arr) => [...new Set(arr)] )
Map类似于对象,也是键值对的集合,但是键的类型不局限于字符串,可以是任何数据类型
新增了扩展运算符
… 可以展开或合并数组,ES7中展开运算符支持展开或合并对象
新增了reset参数
用于获取函数的实参,代替arguments
与arguments 的区别:
arguments返回的是一个对象,reset返回的是数组
arguments不需要写在形参内也可以直接获取,reset需要结合展开运算符写在形参内才能获取,并且当有其他形参时,reset只能放在最后,否则会报错
新增了箭头函数
箭头函数是函数的简写
与普通函数的区别:
箭头函数本身没有this, 只会从自己的作用域链的上一层继承 this,比如包裹它的函数或对象的this指向
箭头函数没有arguments,但是可以使用reset获取参数
箭头函数不能作为构造函数的实例化对象,否则会报错
解决了传统函数中this指向的问题
新增了函数形参默认值
新增了promise
promise是为了解决异步问题而引入的,如回调地狱。promise在语法上是一个构造函数,它有三种状态pending 、resolve、reject,分别代表进行中、成功、失败,只能由进行中转为成功或进行中转为失败,不可能由失败转为成功或者成功转为失败。
promise创建后的初始状态就是pending并且promise对象的状态不受外界影响
Promise中的实例方法
Promise.prototype.then
需要promise执行成功后才会执行,返回一个promise,也有resolve和reject两种状态,并且也可以调用then和catch方法,可以形成链式调用
Promise.prototype.catch
需要promise执行失败后才会执行,返回一个promise,也有resolve和reject两种状态,并且也可以调用then和catch方法,可以形成链式调用
Promise.prototype.finally
无论promise执行结果是成功还是失败都会返回一个promise,一般放在then或catch后面执行,用于执行结束后的操作
Promise中的静态方法
Promise.all 方法,将多个 Promise 实例,包装成一个新的Promise 实例
all 方法接收一个数组作为参数,数组中一般是多个promise,作用就是检测数组内部所有的promise是否执行成功,如果成功(resolve)就可以通过调用then方法拿到所有promise执行后的结果,也是以数组的形式存放,如果失败(reject)就可以通过调用reject拿到第一个执行失败的promise的结果
promise.all 方法参数是一个 promise 的数组,只有当所有的 promise 都完成并返回成功,才会调用 resolve,当有一个失败,都会进catch,被捕获错误,promise.all 调用成功返回的结果是每个 promise 单独调用成功之后返回的结果组成的数组,如果调用失败的话,返回的则是第一个 reject 的结果
Promise.race 方法 也是将多个 Promise 实例,包装成一个新的Promise 实例
有的时候,你已经预知了状态的结果为rejected,则可以用这种简写方式
Promise.allSettled
Promise.all有一个缺陷,就是当遇到一个rejected的状态,那么对于后面是resolve或者reject的结果我们是拿不到的
ES11 新增语法Promise.allSettled,无论状态是fulfilled/rejected都会把参数返回给我们
如果其中一个promise没有返回值,则什么结果都拿不到
async/await
是ES7新增的语法,也可以用来处理js中的异步操作,使用async/await可以让异步代码看起来更像同步代码,并且比promise的语法更简洁 async修饰符是用来修饰函数的,可以将函数声明为异步函数,并且会返回一个promise对象,可以通过.then()方法获取异步函数的返回值 await关键字会造成异步函数停止执行,只能在异步函数内使用,异步函数必须使用async声明,await一般与promise连用否则会导致语法错误,会等待promise执行成功后再继续执行异步函数并返回promise成功的值,相当于promise.then。如果promise执行失败则会抛出错误,可以使用try catch捕获
新增了模块化
作用是进行模块化开发,降低耦合性,方便后期维护。Vue和react 中都是进行了模块化的抽离,降低功能的耦合性,减少bug的产生
apply call bind 的区别
apply方法接受两个参数,第一个是this指向,第二个是函数的参数(参数列表),使用apply会立即执行函数,只会临时改变一次this指向.
call方法接受两个参数,第一个是this指向,第二个是函数的参数(以数组的形式传入),使用call也会立即执行函数,也只会临时改变一次this指向
bind方法接受两个参数,第一个是this指向,第二个是函数的参数,可以分多次传入参数(call只能一次性传完),使用bind不会立即执行函数而是返回一个永久改变this指向的函数
原型链
什么是原型?
在javascript中的每创建一个函数,都会自动产生一个属性prototype,而这个prototype指向顶级对象Object
什么是原型链?
原型链就是原型组成的链,对象的
__proto__指向它的原型,原型的__proto__指向原型的原型,逐级往上直到Object的__proto__,就是整个原型链
原型链的作用?
当我们需要在同一个原型的实例对象上添加共同的属性或方法时,如果一个一个添加就非常的耗时费力,此时就可以把属性或方法添加到它们共同的原型上面,从而实现一次添加共同访问
prototypeprototype是函数上特有的属性,最终指向的是对象,当函数作为构造函数使用时,那么它的实例对象的prototype就指向构造函数
Protoproto是用于查询原型链的,proto指向prototype(也就是构造函数的原型对象)
原型指向
实例对象的
__proto__指向对应构造函数的prototype构造函数的
__proto__指向构造函数的原型对象(Function.prototype)构造函数的原型对象的
__proto__指向Object.prototypeObject.prototype.__proto__指向null特殊情况:
构造函数. __proto__指向
Function.prototypeFunction.prototype.__proto__指向Object.prototype对象当作函数使用时
__proto__指向Function.prototype函数当作对象使用时
__proto__指向Function.prototype
深拷贝和浅拷贝的区别?
浅拷贝
假如对一个对象进行浅拷贝,如果对象中的属性值是基本数据类型,则直接拷贝原始值,修改不会影响原对象。如果属性值是引用数据类型,则拷贝的是引用地址,修改新对象中的引用类型的属性会影响原对象
实现方式:
Object.assignArray.slice展开运算符
...
深拷贝
深拷贝是直接在堆中开辟一个新的地址,将原对象的属性完全拷贝过来,与原对象是完全独立的,修改新对象不会影响原对象,一般用于引用数据类型
实现原理:
通过递归将属性全部递归为基本数据类型后再拷贝,就会得到一个与原对象互不影响的对象
实现方式:
JSON.stringify会忽略undefined
不能拷贝函数或多层嵌套的对象
手写递归循环
lodash库(cloneDeep方法)
闭包是什么?
闭包就是在一个函数里面再定义一个函数,并且内部函数一直保持对外部函数作用域的变量的访问 指的是有权访问另一个函数里的变量的函数
函数作用域:一个函数执行时就会产生一个函数作用域,执行完毕之后函数作用域销毁
内存回收机制:在不使用内存时,系统会自动回收,清理出内存空间
作用域继承:一个函数中再创建一个函数,此时就产生了父级作用域和子级作用域,子级可以获取父级作用域的变量,而父级不能获取子级作用域的内容
闭包的作用:
变量可以一直保存在内存中,延长变量的生命周期
可以让函数内部的变量转为全局变量(因为可以在全局获取)
注意点:
闭包会使函数作用域中的变量一直保持在内存中,内存消耗大,使用过多可能会导致内存泄漏(会损耗性能甚至系统崩溃),性能降低。在不使用闭包函数的时候将局部变量删除,释放内存
柯里化函数
优点:
可以灵活传参
保存固定参数
防抖和节流是什么?
防抖就是在一段时间内如果连续触发了100次那么只会执行最后一次 节流 在一段时间内如果连续触发了100次只执行第一次,当第一次执行结束后才会执行第二次,依此类推 防抖的应用场景,如:
轮播图的切换按钮
提交按钮
页面中某些开关
节流的应用场景,如:
瀑布流渲染
原型指向
每一个函数都有prototype,prototype上有一个constructor属性,指向函数本身 每一个实例对象都有__proto__属性,指向这个实例对象的构造函数的prototype 构造函数/函数的prototype作为对象,也有__proto__属性,指向object.prototype => Function.prototype.__proto__===Object.prototype Function.__proto__=== Function.prototype Instanceof
proto 是对象上面的一个隐式属性,指向自己的原型对象。 原型是一个对象,可以给其他对象提供共享属性或方法,对象的隐式引用(__proto__)指向原型或者null
原型链是什么?
原型的指向形成的链 每个对象都有自己的原型对象,而原型对象本身,也有自己的原型对象,从而形成了一条原型链条。 当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。 
数据类型检测
typeof
可以检测以下 7 种数据类型:(number、boolean、symbol、string、object、undefined、function)
instanceof
用于检测一个实例对象是否属于某个构造函数的实例
例如:A instanceof B。 instanceof 用于检测对象 A 是不是 B 的实例,而检测是基于原型链进行查找的,也就是说 B 的 prototype 有没有在对象 A 的__proto__ 原型链上,如果有就返回 true,否则返回 false
constructor
通过实例对象的
constructor属性判断是否为某个构造函数的实例
Object.prototype.toString.call(date)检验数据类型
闭包是什么? 闭包是指有权访问另一个函数里的变量的函数
如何创建闭包? 闭包是通过函数套函数,内层函数引用外层函数的变量创建的
闭包的作用? 创建私有变量 延长内部变量的生命周期,因为函数执行完返回结果后,内部变量没有被引用,浏览器会将没有引用的变量进行回收,释放内存。在闭包中内层函数一直在引用外层函数的私有变量,所以不会被回收
应用场景 防抖、节流 柯里化函数:保存参数 事件监听也可能有闭包
作用域是什么?
js的作用域是指变量、函数和对象的有效访问/使用范围,在ES6之前只有全局作用域和函数作用域,ES6提供了块级作用域
块级作用域是在{}内通过let和const创建
作用域链是什么?
作用域链指的是作用域与作用域之间形成的链条。访问/赋值会按照作用域链逐级查找
作用域的访问:访问某个变量时,首先会在自身作用域查找,如果没有找到,就会往上一级作用域查找,如果上一级也没有,就继续一层一层向上查找,如果到全局作用域还是没有找到,就会报错
作用域的赋值:对变量进行修改/运算操作时,首先会在自身作用域查找,如果没有找到,则往上一级查找,如果上一级也没有,会继续向上查找,直到全局作用域,如果全局作用域中没有该变量则会将该变量定义为全局变量并赋值
事件循环机制(宏任务、微任务)?
在js执行上下文的时候会区分同步代码、微任务和宏任务,它的执行顺序是从script标签的第一行开始向下执行,碰到同步代码会直接执行,碰到微任务和宏任务会先放到对应的栈里面,当同步代码执行完毕之后会先查看微任务栈里面是否有未执行的微任务,有则清空微任务,没有则查看是否有宏任务,依次轮询直到微任务和宏任务清空
常见的微任务包括: Promise 回调函数(promise.then, promise.catch, promise.finally) MutationObserver 的回调函数 queueMicrotask 函数注册的回调函数 常见的宏任务包括: setTimeout 和 setInterval 注册的回调函数 I/O 操作的回调函数(如文件读写、网络请求等) UI 渲染的回调函数(如requestAnimationFrame)
new操作符做了哪些事?
new关键字会创建一个用户自定义的对象类型的实例或者是js原有的内置对象的实例,如:Array/String/Date/Function
new的过程
首先会创建一个空的对象{}
将this指向这个创建出来的对象
把这个对象的__proto__指向了构造函数的prototype属性
继承是什么?
一个构造函数的实例 可以使用另一个构造函数的属性和方法(实例对象的属性和原型的方法) 继承有哪些:
原型链继承
实现方式:让子构造函数的原型对象 指向 父构造函数的实例对象(改变原型指向)
优点:可以继承实例的构造函数的属性、父类构造函数的属性、父类原型的属性
缺点:
只能继承属性
所有实例都会共享父类的属性,一个实例修改会影响其他实例
借用构造函数继承
实现方式:在子构造函数中 通过call/apply方法调用父类构造函数实现继承(改变this指向)
优点:只继承了父类构造函数的属性,不会继承父类原型的属性
缺点:
只能继承父类构造函数的属性
无法实现复用,每次用都要重新调用
每个实例中都有父类构造函数的副本,很臃肿
组合继承
实现方式:原型链继承 + 借用继承
优点:
可以继承父类原型上的属性,可以复用
每个实例中继承的父类构造函数的属性都是私有的
缺点:调用了两次父类构造函数,耗内存并且在子类构造函数的原型上会多出一套属性
寄生组合继承
实现方式:借助第三方构造函数寄生父类构造函数,再改变子构造函数的原型指向
优点:解决了组合继承的缺点
拷贝继承
实现方式:将父类构造函数的实例 在子构造函数中 通过
for-in遍历添加到子构造函数的原型中缺点:
只能继承父类构造函数的实例的属性
for-in只能遍历到Object.prototype
class继承
实现方式:创建子类时通过extends关键字继承父类的属性和方法,在子类构造器中通过super调用父类构造函数
事件流
事件流分为两种,捕获事件流和冒泡事件流 捕获事件流:从根节点开始执行,⼀ 直往子节点查找执行, 直到查找执行到目标节点 冒泡事件流:从目标节点开始执行,⼀ 直往父节点冒泡查找执行, 直到查到到根节点 事件流分为三个阶段:捕获阶段、目标阶段、冒泡阶段
严格模式后的限制
变量必须声明后再赋值
不能有重复的参数名,函数的参数也不能有同名属性
不能使用with语句
不能对只读属性赋值
不能使用前缀 0表示八进制数
不能删除不可删除的属性
禁止 this 指向全局对象
eval 不会在它的外层作用域引入变量。
eval和arguments不能被重新赋值
arguments 不会自动反应函数的变化
不能使用 arguments.callee
不能使用 arguments.caller
不能使用 fn.caller 和 fn.arguments 获取函数调用的堆栈
增加了保留字
DOM事件流
DOM事件流(DOM event flow)是指在浏览器中处理事件时,事件触发后传播到DOM树中的不同节点的过程。 DOM树是由HTML文档表示的文档对象模型,它类似于一棵树,由各个HTML元素组成,每个元素都有一个对应的节点。当用户与网页进行交互时(例如点击、滚动、键盘输入等),会触发相应的事件。事件从发生的地方开始传播,经过DOM树的各个节点,最终到达目标节点。
DOM的事件流指的是当事件触发时,事件会在DOM树中传播,从而影响到树中的其他节点。事件流分为三个阶段:捕获阶段、目标阶段和冒泡阶段。
捕获阶段(Capturing Phase):事件从根节点开始,向下传播到触发事件的最底层节点。
目标阶段(Target Phase):事件达到目标节点,触发目标节点上的事件处理函数。
冒泡阶段(Bubbling Phase):事件从目标节点开始向上冒泡,直到传播到根节点。 在捕获阶段和冒泡阶段,可以通过addEventListener()方法给节点添加事件监听器。这个方法接收三个参数:
事件类型:表示要监听的事件类型,如"click"、"mouseover"等。
事件处理函数:表示事件触发时要执行的处理函数。
是否在捕获阶段处理:一个可选的布尔值参数,默认为false,表示在冒泡阶段处理事件。
例如,下面的代码监听一个按钮的点击事件,并在冒泡阶段处理事件:
在捕获阶段的事件处理函数,可以通过event对象的stopPropagation()方法阻止事件继续传播。而在冒泡阶段的事件处理函数,可以通过event对象的stopImmediatePropagation()方法阻止事件继续冒泡,并且停止其他事件处理函数的执行。 总结起来,DOM的事件流可以通过捕获阶段和冒泡阶段来监听和处理事件,以实现交互和响应用户的操作。
最后更新于
这有帮助吗?