innerHTML和document.write()区别
dom元素属性
document方法,写入页面的内容流会导致页面重写
浏览器渲染网页的具体流程(关键渲染路径)
用户看到页面实际上可以分为两个阶段:页面内容加载完成和页面资源加载完成,分别对应于DOMContentLoaded和Load。
DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片等
load事件触发时,页面上所有的DOM,样式表,脚本,图片都已加载完成
1、HTML解析,构建DOM树
浏览器从网络或硬盘中获得HTML字节数据后会经过以下流程将字节解析为DOM树:
字符编码:先将HTML的原始字节数据转换为文件指定编码的字符。
令牌化:然后浏览器会根据HTML规范来将字符串转换成各种令牌(如<html>、<body>、<p>这样的标签以及标签中的字符串和属性等都会被转化为令牌,每个令牌具有特殊含义和规则)。
生成节点对象:接着每个令牌都会被转换成定义其属性和规则的对象,即节点对象。
构建DOM树:最后将节点对象构建成树形结构,即DOM树。HTML标签之间有复杂的父子关系,树形结构刚好可以诠释这样的关系。
2、CSS解析,构建CSSOM树
浏览器解析CSS文件并生成CSSOM,每个CSS文件都被分析成一个StyleSheet对象,每个对象都包含CSS规则。CSS规则对象包含对应于CSS语法的选择器和声明对象以及其他对象。
在这个过程需要注意的是:
CSS解析可以与DOM解析同时进行。
CSS解析与script的执行互斥 。
在Webkit内核中进行了script执行优化,只有在JS访问CSS时才会发生互斥。
3、Render Tree
在构建了DOM树和CSSOM树之后,浏览器只是拥有2个相互独立的对象集合,DOM树描述的文档结构和内容,CSSOM树描述了对应文档的样式规则,想要渲染出页面,就需要将DOM树、CSSOM树结合在一起,构建渲染树。
4、Layout
渲染树构建好后,浏览器得到了每个节点的内容与样式,下一步就是需要计算每个节点在浏览器窗口的确切位置与大小,即layout布局。
布局阶段,从渲染树的根节点开始遍历,采用盒子模型的模式来表示每个节点与其他元素之间的距离,从而确定每个元素在屏幕内的位置与大小。
布局阶段会从渲染树的根节点开始遍历,由于渲染树的每个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。所以浏览器就可以通过这些样式信息来确定每个节点对象在页面上的确切大小和位置,布局阶段的输出就是我们常说的盒子模型,它会精确地捕获每个元素在屏幕内的确切位置与大小。需要注意的是:
float元素,absoulte元素,fixed元素会发生位置偏移。
我们常说的脱离文档流,其实就是脱离Render Tree。
5、Paint绘制页面
当Layout布局完成后,浏览器会立即发出Paint事件,开始讲渲染树绘制成像素,绘制所需要的时间跟CSS样式的复杂度成正比,绘制完成后,用户才能看到页面在屏幕中的最终呈现效果。
渲染优化方案
1、优化关键渲染路径方案
通过优化关键渲染路径,可以优化页面渲染性能,减少页面白屏时间。
优化JS:JavaScript文件加载会阻塞DOM树的构建,可以给<script>标签添加异步属性async,这样浏览器的HTML解析就不会被js文件阻塞。
优化CSS:浏览器每次遇到<link>标签时,浏览器就需要向服务器发出请求获得CSS文件,然后才继续构建DOM树和CSSOM树,可以合并所有CSS成一个文件,减少HTTP请求,减少关键资源往返加载的时间,优化渲染速度。
2、其他优化方案
加载部分HTML
浏览器先加载主要HTML初始化静态部分,动态变化的HTML内容通过Ajax请求加载。这样可以减少浏览器构建DOM树的工作量,让用户感觉页面加载速度很快。
压缩
对HTML、CSS、JavaScript这些文件去除冗余字符(例如不必要的注释、空格符和换行符等),再进行压缩,减小文件数据大小,加快浏览器解析文件编码。
图片加载优化
1)小图标合并成雪碧图,进而减少img的HTTP请求次数;
2)图片加载较多时,采用懒加载的方案,用户滚动页面可视区时再加载渲染图片。
图片懒加载
优点:提升用户体验、减轻服务器压力
方法一:
clientHeight:浏览器视口的高度;
scrollTop:滚动轴滚动的距离;
offsetTop:图片的头部距离浏览器顶部的高度(注意不是距离视口顶部的高度);
offsetTop:直接通过img.offsetTop就可以获取;
scrollTop:通过document.documentElement.scrollTop获取;
clientHeight:通过document.documentElement.clientHeight获取;
当offsetTop <= 视口高度clientHeight + 滚动条的长度scrollTop加载图片
方法二:
getBoundClientRect介绍:
Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。我们可以取得它的top值,它的top值就是元素左上角到视口顶部的距离。
加载的条件
当Element.getBoundingClientRect().top< 视口高度时触发加载;
方法三:
IntersectionObserver介绍
IntersectionObserver可以异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。也就是说它可以帮助我们去判断一个元素是否出现在视口上。这里只介绍用到的两个属性:
IntersectionObserver.observe():使IntersectionObserver开始监听一个目标元素;
isIntersecting属性:可以判断该元素是否出现在视口内;
HTTP缓存
浏览器自带了HTTP缓存的功能,只需要确保每个服务器响应的头部都包含了以下的属性:
1)ETag: ETag是一个传递验证令牌,它对资源的更新进行检查,如果资源未发生变化时不会传送任何数据。当浏览器发送一个请求时,会把ETag一起发送到服务器,服务器会根据当前资源核对令牌(ETag通常是对内容进行Hash后得出的一个指纹),如果资源未发生变化,服务器将返回304 Not Modified响应,这时浏览器不必再次下载资源,而是继续复用缓存。
2)Cache-Control: Cache-Control定义了缓存的策略,它规定在什么条件下可以缓存响应以及可以缓存多久。
a、no-cache: no-cache表示必须先与服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续对同一网址的请求(每次都会根据ETag对服务器发送请求来确认变化,如果未发生变化,浏览器不会下载资源)。no-store直接禁止浏览器以及所有中间缓存存储任何版本的返回响应。简单的说,该策略会禁止任何缓存,每次发送请求时,都会完整地下载服务器的响应。
b、public&private:如果响应被标记为public,则即使它有关联的HTTP身份验证,甚至响应状态代码通常无法缓存,浏览器也可以缓存响应。如果响应被标记为private,那么这个响应通常只为单个用户缓存,因此不允许任何中间缓存(CDN)对其进行缓存,private一般用在缓存用户私人信息页面。
c、max-age: max-age定义了从请求时间开始,缓存的最长时间,单位为秒。
Promise 是做什么的,有哪些API
Promise用法
> Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
> resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise.prototype.then()
> Promise 实例具有 then 方法,也就是说,then 方法是定义在原型对象 Promise.prototype 上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then 方法的第一个参数是 resolved 状态的回调函数,第二个参数(可选)是 rejected 状态的回调函数。
> then 方法返回的是一个新的 Promise 实例**(注意,不是原来那个 Promise 实例)。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。
Promise.prototype.catch()
> Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
> 上面代码中,getJSON()方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。
Promise.all()
> Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
> 上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
> 另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
> p的状态由p1、p2、p3决定,分成两种情况:
> (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
> (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race()
> Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
> 上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
Promise.resolve()
> 有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
Promise.reject()
> Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
callback和Promise的区别是什么?
callback
回调函数本身是我们约定俗成的一种叫法。
优点:比较容易理解;
缺点:1.高耦合,维护困难,回调地狱;2.每个任务只能指定一个回调函数;3.如果几个异步操作之间并没有顺序之分,同样也要等待上一个操作执行结束再进行下一个操作。
Promise
ES6给我们提供了一个原生的构造函数Promise,Promise代表了一个异步操作,可以将异步对象和回调函数脱离开来,通过.then方法在这个异步操作上绑定回调函数,Promise可以让我们通过链式调用的方法去解决回调嵌套的问题,而且由于promise.all这样的方法存在,可以让同时执行多个操作变得简单。
promise对象存在三种状态:
1)Fulfilled:成功状态
2)Rejected:失败状态
3)Pending:既不是成功也不是失败状态,可以理解为进行中状态
Promise的缺点:
1.当处于未完成状态时,无法确定目前处于哪一阶段。
2.如果不设置回调函数,Promise内部的错误不会反映到外部。
3.无法取消Promise,一旦新建它就会立即执行,无法中途取消。
听说你还不知道Promise的allSettled()和all()的区别? - 云+社区 - 腾讯云 (tencent.com)
Event Loop 事件循环
js扩展运算符(...)
用于将一个数组或类数组对象转换为用逗号分隔的值序列。它的基本用法是拆解数组和字符串。
①代替apply()函数
②代替concat()函数合并数组
③转换Set,得到去重的数组
let arr=[1,2,4,6,2,7,4];
console.log([...newSet(arr)]);// [ 1, 2, 4, 6, 7 ]
④用于对象克隆
使用扩展运算符对数组或对象进行克隆时,如果数组的元素或者对象的属性是基本数据类型,则支持深克隆;
如果是引用数据类型,则不支持深克隆。归根结底是因为引用数据类型的克隆只是复制了引用的地址,克隆后的对象仍然共享同一个引用地址。
ES6 Set和Map
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。向Set加入值时,不会发送类型转换。
可用于去除数组重复成员或去除字符串中重复字符。
Set 结构的实例有以下属性。
Set.prototype.constructor:构造函数,默认就是Set函数。
Set.prototype.size:返回Set实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
操作方法
Set.prototype.add(value):添加某个值,返回 Set 结构本身。
Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear():清除所有成员,没有返回值。
遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员
如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
Ajax 基本流程
异步 JavaScript 和 XML,是指一种创建交互式网页应用的网页开发技术。是一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
创建一个 ajax 有这样几个步骤:
- 首先是创建一个 XMLHttpRequest 对象。
- 然后在这个对象上使用 open 方法创建一个 http 请求,open 方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息。
- 在发起请求前,我们可以为这个对象添加一些信息和监听函数。比如说我们可以通过 setRequestHeader 方法来为请求添加头信息。我们还可以为这个对象添加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发 onreadystatechange 事件,我们可以通过设置监听函数,来处理请求成功后的结果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成,这个时候我们可以通过判断请求的状态,如果状态是 2xx 或者 304 的话则代表返回正常。这个时候我们就可以通过 response 中的数据来对页面进行更新了。
- 当对象的属性和监听函数设置完成后,最后我们调用 sent 方法来向服务器发起请求,可以传入参数作为发送的数据体。
闭包
能够访问另一个函数作用域的变量的函数,参数和变量不会被垃圾回收机制回收。
创建私有变量
延长变量的生命周期
闭包的缺点:
- 常驻内存,增加内存使用量;
- 使用不当造成内存泄漏。
内存泄漏
由于疏忽或错误造成程序未能释放已经不再使用的内存
Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存
原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存
通常情况下有两种实现方式:
标记清除
引用计数
箭头函数与普通函数的区别
ES6 中箭头函数 VS 普通函数的 this 指向
普通函数中 this
1. 查看函数在哪被调用。
2. 点左侧有没有对象?如果有,它就是 “this” 的引用。如果没有,继续第 3 步。
3. 该函数是不是用 “call”、“apply” 或者 “bind” 调用的?如果是,它会显式地指明 “this” 的引用。如果不是,继续第 4 步。
4. 该函数是不是用 “new” 调用的?如果是,“this” 指向的就是 JavaScript 解释器新创建的对象。如果不是,继续第 5 步。
5. 是否在“严格模式”下?如果是,“this” 就是 undefined,如果不是,继续第 6 步。
6. JavaScript 很奇怪,“this” 会指向 “window” 对象。
ES6 箭头函数中 this
1. 默认指向定义它时,所处上下文的对象的 this 指向;
偶尔没有上下文对象,this 就指向 window
ES6 class 和 ES5 函数的区别
与ES5不同,ES6 类和模块的内部默认就是严格模式,不存在遍历提升
es5函数内的函数声明会提升到函数作用域起始处,但函数体本身只会提升到块级作用域起始处。
作用域链
当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域
如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错
作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合
全局作用域
任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问
函数作用域
函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问
JavaScript 遵循的就是词法作用域,变量被创建时就确定好了,而非执行阶段确定的。也就是说我们写好代码时它的作用域就确定了
- 判断数据类型的方式
1. typeof 缺陷:可以判断null以外的基础类型,复杂类型除funiton以外均判断为object
// typeof(null)===‘’object’;typeof (()=>{} ))===‘function’;
2. instanceof 可以准确判断复杂引用类型,但不能判断基础类型
作用: instanceof 运算符用于判断构造函数的 prototype 属性是否出现在某个实例对象的原型链中的任何位置。
3.Object.prototype.toString.call() ;可以判断所有类型,返回示例 ''[object Array]"
注意后面的类型一定是大写字母开头。
常用数组,字符串方法
增
下面前三种是对原数组产生影响的增添方法,第四种则不会对原数组产生影响
push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
unshift()在数组开头添加任意多个值,然后返回新的数组长度
splice()传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回空数组
concat()首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组
删
下面三种都会影响原数组,最后一项不影响原数组:
pop()方法用于删除数组的最后一项,同时减少数组的length 值,返回被删除的项
shift()方法用于删除数组的第一项,同时减少数组的length 值,返回被删除的项
splice()传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
slice() 用于创建一个包含原有数组中一个或多个元素的新数组,不会影响原始数组。传入两个参数,分别是开始位置和结束位置(不包含结束位置的元素)。
改 即修改原来数组的内容,常用splice
查
查找元素,返回元素坐标或者元素值
indexOf()返回要查找的元素在数组中的位置,如果没找到则返回 -1
includes()返回要查找的元素在数组中的位置,找到返回true,否则false
find()返回第一个匹配的元素
排序方法
数组有两个方法可以用来对元素重新排序:
reverse(),将数组元素方向反转
sort()方法接受一个比较函数,用于判断哪个值应该排在前面
转换方法
数组转字符串
join()方法接收一个参数,即字符串分隔符,返回包含所有项的字符串
toString()将数组转换成一个字符串
字符串转数组:
str.split(分隔符,留下的个数)
迭代方法
some()对数组每一项都运行传入的测试函数,如果至少有1个元素返回 true ,则这个方法返回 true
every()对数组每一项都运行传入的测试函数,如果所有元素都返回 true ,则这个方法返回 true
forEach()对数组每一项都运行传入的函数,没有返回值
filter()对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回
map()对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组
map和foreach区别
forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。
forEach()的执行速度 < map()的执行速度
JSON.parse(JSON.stringify(obj)) 实现深拷贝需要注意的问题
1. 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,只是字符串,不是时间对象;
2. 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
3. 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4. 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5. JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6. 如果对象中存在循环引用的情况也无法正确实现深拷贝;
js事件循环机制
js是一门单线程语言
1.所有任务都在主线程上执行,形成一个执行栈。
2.主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
3.一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。
4.主线程不断重复上面的第三步。
宏任务与微任务:
异步任务分为 宏任务(macrotask) 与 微任务 (microtask),不同的API注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。
宏任务(macrotask)::
script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
微任务(microtask):
Promise、 MutaionObserver、process.nextTick(Node.js环境)
Event Loop(事件循环):
Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:
选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
更新render(每一次事件循环,浏览器都可能会去更新渲染)
重复以上步骤
原型链
JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链
所有原型链的终点都是 Object 函数的 prototype 属性
Objec.prototype 指向的原型对象同样拥有原型,不过它的原型是 null ,而 null 则没有原型
每一个构造函数都拥有一个 prototype 属性,这个属性指向一个对象,也就是原型对象
原型对象默认拥有一个 constructor 属性,指向指向它的那个构造函数
每个对象都拥有一个隐藏的属性 __proto__,指向它的原型对象
实例可以共享原型上面的属性和方法
实例自身的属性会屏蔽原型上面的同名属性,实例上面没有的属性会去原型上面找
instanceof
最常用的确定原型指向关系的关键字,检测的是原型,但是只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型
通过使用 hasOwnProperty 可以确定访问的属性是来自于实例还是原型对象
对象继承方法
new做了什么操作
首先创建一个新的空对象
然后将空对象的__proto__指向构造函数的原型
它将新生成的对象的__proto__属性赋值为构造函数的prototype属性,使得通过构造函数创建的所有对象可以共享相同的原型。
这意味着同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的对象。
改变this的指向,指向空对象
对构造函数的返回值做判断,然后返回对应的值
一般是返回第一步创建的空对象;
但是当构造函数有返回值时则需要做判断再返回对应的值,是对象类型则返回该对象,是原始类型则返回第一步创建的空对象。
apply()、call()和 bind() 是做什么的,区别,手撕源码
相同点:三者都可以改变 this 的指向
不同点:
apply 方法传入两个参数:一个是作为上下文的对象,另一个是作为函数所组成的数组
call 方法第一个参数也是作为作为函数上下文的对象,但是后面传入的是一个参数列表,而不是单个数组
bind接受的参数有两个部分,第一个参数是作为函数上下文的对象,第二部分参数是一个列表,可以接受多个参数
apply、call 方法都会使函数立即执行,因此它们也可以用来调用函数
ajax原理,手撕源码
ES6 中箭头函数 VS 普通函数的 this 指向
普通函数中 this
1. 查看函数在哪被调用。
2. 点左侧有没有对象?如果有,它就是 “this” 的引用。如果没有,继续第 3 步。
3. 该函数是不是用 “call”、“apply” 或者 “bind” 调用的?如果是,它会显式地指明 “this” 的引用。如果不是,继续第 4 步。
4. 该函数是不是用 “new” 调用的?如果是,“this” 指向的就是 JavaScript 解释器新创建的对象。如果不是,继续第 5 步。
5. 是否在“严格模式”下?如果是,“this” 就是 undefined,如果不是,继续第 6 步。
6. JavaScript 很奇怪,“this” 会指向 “window” 对象。
ES6 箭头函数中 this
1. 默认指向定义它时,所处上下文的对象的 this 指向;
偶尔没有上下文对象,this 就指向 window
设计模式,应用场景,手撕源码
promise
Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
特点:
对象的状态不受外界影响
一旦状态改变,就不会再变,任何时候都可以得到这个结果
Promise 新建后就会立即执行
Promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。
Promise.prototype.catch 用于指定发生错误时的回调函数,具有冒泡性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获
then 方法返回的是一个新的Promise实例
promise.all,promise.race源码手撕(让你写一个请求,5秒内执行完就返回执行结果,否则返回超时)
垃圾回收
基本数据类型
==和===的区别
隐式转换(坑1)var a=?;console.log(a==1&&a==2&&a==3);//返回true 坑2)为什么[]==![])
如何判断数组
js动画和css动画区别
dom0级事件dom2级事件
模块化
防抖节流
深浅拷贝
事件流
浏览器渲染过程
generator
数组去重
数组展平
typeof 和instance of
为什么0.1+0.2!=0.3
用symbol.iterator实现对象遍历(清晰的记得是b站的问题,难搞哦)
javascript的三种引入方式_喜你已久丶不弃的博客-CSDN博客_js引入方式
DOM 和 BOM 区别 - HoneyCY - 博客园 (cnblogs.com)
吊打面试官前端系列(一): require和import的区别 - 云+社区 - 腾讯云 (tencent.com)
Javascript异步编程的4种方法 - 阮一峰的网络日志 (ruanyifeng.com)
web前端面试题@十二(数组去重,数组扁平化) - 简书 (jianshu.com)
5种方式实现数组扁平化 - _wind - 博客园 (cnblogs.com)
判断数据类型的5种方法 - 简书 (jianshu.com)
1.typeof
2.instanceof
3.object下的toString.call()
4.object的constructor
利用JS十分钟判断数组中存在元素的多种方式_javascript技巧_脚本之家 (jb51.net)
1.find()
2.indexOf()
3.filter()
4.some()
5.includes()
js去除字符串空格(空白符) - 一只看夕阳的猫 - 博客园 (cnblogs.com)
前端常见跨域解决方案(全) - SegmentFault 思否
彻底弄懂jsonp原理及实现方法 - xiaobe - 博客园 (cnblogs.com)
防抖(debounce)和节流(throttle)的原理以及实现_Seeker-Joseph的博客-CSDN博客
js事件委托,事件冒泡及捕获_么灬名字的博客-CSDN博客_js事件委托和事件冒泡
HTTP状态码
1xx:指示信息——表示请求已接收,继续处理
2xx:成功——表示请求已经被成功接收
3xx:重定向——要完成请求必须进行更进一步的操作
4xx:客户端错误——请求有语法错误或请求无法实现
5xx:服务器错误——服务器未能实现合法的请求
- 1XX 信息性状态码
- 100 继续
- 101 切换协议
- 2XX 成功状态码
- 200 OK 成功处理了请求
- 204 No Content 请求处理成功,但没有资源可返回
- 206 Partial Content 请求资源的某一部分
- 3XX 重定向状态码
- 301 永久性重定向,表示请求的资源已被分配了新的 URI
- 302 临时性重定向,资源的 URL 已临时定位到其他位置
- 303 告诉客户端应该用另一个 URL 获取资源
- 304 表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况
- 4XX 客户端错误状态码
- 400 表示请求报文中存在语法错误
- 401 未授权
- 403 服务器拒绝了请求
- 404 服务器无法找到所请求的 URL
- 5XX 服务器错误状态码
- 500 内部服务器错误
- 502 错误网关
- 503 服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
- 504 响应超时
浏览器缓存
浏览器将用户请求过的静态资源(html、css、js),存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载了,不需要再去服务端请求了
缓存的优点:
减少了冗余的数据传输,节省网费
减少服务器的负担,提升网站性能
加快了客户端加载网页的速度
arguments怎么转化成真数组
递归setTimeout()和setInterval()有何不同