手写方法案例:链接
import 和 require
import操作符是静态的,import函数式动态的,require是动态的,静态是指编译时加载,动态是指执行时加载。
import既然转码后还是require,为啥还要import???
递归
数据类型
- 有哪些类型?
- 类型转换
- 类型判断
- 数据在内存中怎么存储?
变量
- 声明?
- 变量的作用域
作用域
- 什么是作用域?
- 什么是作用域链?
- 浏览器js解释器对js代码解释执行过程,都做了哪些事情?
- 参考:
1、预编译–执行期上下文
闭包
- 是什么?
- 作用
- 缺点
阮一峰
数组、对象、字符串内置方法
-
Object
- 创建对象方法???
使用自定义构造函数创建对象时,发生了什么?
1、申请内存空间
2、把this设置成当前对象
3、设置对象的属性和方法
4、把this这个对象返回 - 克隆一个对象
Object.assign() - 劫持变量get、set方法
Object.defineProperty() - 获取可枚举属性
Object.keys() - 获取所有属性
Object.getOwnPropertyNames() - 检查对象是否包含某个属性
Object.getPrototypeOf()
- 创建对象方法???
-
Array
- join()
数组元素组成一个字符串,只有以参数为分隔符,默认 逗号 - push()、pop()
- unshift()、shift()
- sort()
–升序排列数组
–对数组每个成员调用toString()转型,因此是字符串之间比较,数值比较只会比较第一位。
–解决上述问题方法,提供一个函数:sort((v1, v2) => v1 - v2) - reverse()
反转数组项 - concat()
1、会创建一个当前数组的副本;
2、没有参数:返回副本;
3、有参数:
–参数不是数组:将参数添加到副本末尾,返回副本;
–参数是数组:将数组中成员一次添加到副本末尾,返回副本; - slice()
-截取数组,返回新数组,不会改变原数组。
-两个参数,起始位置到结束位置,不包含结束 - splice()
可以实现删除、插入和替换。
最终都会返回删除项组成的数组,没删除则返回空数组。
1、删除:两个参数,起始位置,删除个数
2、插入:三个参数,起始位置,0(删除个数),插入项(可以多项)
3、替换:三个参数,起始位置,删除个数,插入项(可以多项) - indexOf()
一个参数时,遍历数组,找到与参数全等(===)的第一个项结束,返回下标,否则返回-1;
两个参数时,第二个参数表示从哪个下标开始查找,包括这个下标。 - forEach()
遍历数组,成员执行函数,永远返回undefined - some()
循环数组成员,执行指定函数,满足条件则终止循环,返回Boolean - every()
循环数组成员,执行指定函数,所有项满足条件则返回true,否则false - filter()
循环数组成员,执行指定函数,满足条件的项返回,组成新数组。 - map()
循环数组成员,执行指定函数返回的结果,组成新数组。 - find()
循环数组,执行函数,返回第一个满足条件的数组成员。 - reduce()
基本使用 - Arrar.of()
- Array.from()
几种遍历方法比较
- 案例
1、删除数组中多个成员
倒叙遍历删除
filter
2、数组去重
最优的方法应该是 利用对象属性key的唯一性去重,可以同时过滤掉NaN,object
参考几种方法
3、手写indexOf
- join()
-
Set
- 用于什么场景
数组去重,交集,并集。。。
- 用于什么场景
函数
- 函数调用模式
- 函数模式
- 方法模式
- 构造器模式
- 上下文模式
- bind() 模式
箭头函数
- 箭头函数有什么好处?
使用简单 - 怎么做到的这种效果?
这就要说箭头函数的特点了:
1. 箭头函数没有prototype(原型),所以箭头函数本身没有this
2. this指向在定义的时候继承自外层第一个普通函数的this,所继承的函数this发生改变,随着改变
3. arguments不能使用,可以用rest
4. 不能new,因为没 constructor
函数柯里化
- 柯里化:一个函数有多个参数,只传入一个参数,生成一个新函数,新函数接收剩下的参数运行的到结构
- 偏函数:一个函数有多个参数,只传入一部分参数,生成一个新函数,新函数接收剩下的参数运行得到结构
- 高阶函数:一个函数的参数是一个函数,该函数对这个传入的函数进行加工得到一个函数,这个加工的函数就是高阶函数
事件循环
- js为什么需要事件循环(event loop)?
js是单线程语言,实现多线程和异步需要借助 event loop机制 - event loop是如何运作的?
1、首先由三部分组成:调用栈(call stack),消息队列(Message Queue),微任务队列(Microtask Queue)
2、event loop开始时会从 全局栈一行一行执行js语句
3、遇到函数,会将函数的调用压入调用栈中,被压入的函数叫帧(Frame),函数返回时会从调用栈中弹出
4、执行函数中的代码:
同步代码直接执行,执行完,从调用栈弹出;
异步代码,分为宏任务和微任务:
宏任务:回调函数压入消息队列
微任务:回调函数压入微任务队列
5、全局栈代码执行完,调用栈为空时,会立即执行微任务队列中的任务,并且新加入的微任务也会一起执行,微任务队列为空时,再去执行宏任务队列的任务。依次循环,直到微任务队列,消息队列,调用栈都为空,结束。 - 宏任务
setTimeout、 setInterval、 Ajax、 DOM事件 - 微任务
Promise async/await - 案例:
1、事件循环在 vue 中应用???
JSON
- 案例:
- 手写JSON.parse、JSON.stringify
- 不使用JSON.stringify将对象转换为JOSN格式字符串???
原型、原型链、继承
-
原型带了什么?
1、在js中,都是对象,作者想把对象关联起来,所以引入了 new 这个关键字,通过 new 一个原型对象得到一个实例对象。但是js中又没有 类(class) ,因此 new的是构造函数(constructor)
2、用 构造函数实例对象 又有另外一个问题:无法共享属性和方法,即 得到的对象都是独立的,会在内存中开辟新的空间,浪费内存资源
3、解决问题:
作者设计了 prototype 属性,是一个对象,里面存的是实例对象需要共享的 属性和方法,不需要共享的放在构造函数里 -
案例
1、原型方法重写
2、手动实现 instanceof 方法function mockInstanceof(obj,constructorFn){ let pro = obj.__proto__ while(pro) { if(!pro) { return false; } else if(pro === constructorFn.prototype) { return true; } else { pro = pro.__proto__ } } } mockInstanceof([], Array)
继承
js8中继承方法
1. 原型链继承
缺点:
多个实例数据公用,不独立,会相互污染;
实例化子类没办法传参给父类
2. 使用构造函数继承
不使用原型
缺点:
只能继承父类实例的属性和方法,不能继承原型的属性和方法;
不能复用,因为每一个实例都会有父类的实例的副本,影响性能;
3. 组合继承(原型链+构造函数)
原型链实现原型属性和方法的继承,构造函数实现实例的属性和方法的继承
缺点:
实例中的属性会屏蔽原型对象上的属性;
4. 原型式继承
缺点:
原型链继承多个实例的属性,引用类型属性指向相同,存在数据污染可能;
没办法传参数;
5. 寄生式继承
实际就是原型式继承增强,扩展了给构造函数新增属性和方法的功能
缺点:同原型式继承
6. 寄生式组合继承(寄生式+构造函数)
最优的解决方案
7. 混入方式继承多个对象
8. extends关键字
ES6继承方式,实际就是 寄生式继承的封装。
ES5 和 ES6 区别:
ES5是先创建子类实例对象,将父类属性和方法通过this添加到自类中;
ES6是先创建父类实例对象 this,再用子类的构造函数修改this指向自己。因此必须先调用super方法。
Promise
new Promise((resolve, reject) => {
reject('err')
})
.then(
res => {
console.log('fulfilled', res)
},
err => {
console.log('rejected', err)
}
)
.catch(err => {
console.log('rejected catch', err)
})
.finally(() => {
console.log('finally', 1)
})
// 结果:
// rejected err
// finally 1
- 案例:
1、实现一个promise?
三个状态、三个回调函数、两个函数入参
参考手动实现
2、promise是怎么实现异步的???
3、promise中断调用链?
在then/catch 的最后一行返回一个永远 pending 的 promise 即可:
4、中断promise?return new Promise(() => {})
promise无法终止,中断后依然会执行返回结果,只是这时候我们不再关心结果。
使用 ***Promise.race([…])***,只要有一个返回就结束不论then/catch。
async\await
-
async
async 定义的函数默认返回一个Promise对象resolve的值,因此有一个then方法,then的入参即为函数返回值。async function fn() { return 'fn 1' } fn().then(res => { console.log('async', res) //async fn 1 })
-
await
await 关键字 只能放在 async 函数内部,作用是获取Promise函数中resolve或者reject的值
如果不是一个Promise的返回值,则会按照同步程序返回值处理 -
是如何实现异步请求的?
async/await 是一个语法糖,是基于Promise 、Generator 封装实现的。
利用 Generator 这个生成器的特点:yieId 这个关键字可以将函数执行流挂起,然后通过一个 next方法 主动切换状态,获取结果。 封装的关键点是让这个next方法自动执行。next执行返回的结果为{value:value, done: true/false},done 代表的是所有 Generator 中的语句执行完毕,因此我们可以提供一个方法,递归执行 next方法 ,返回最终的结果。
Promise/async/Generator实现原理解析
setTimeout、setInterval
-
setTimeout
只在指定时间后执行一次
清除 clearTimeout() -
setInterval
以指定时间为周期循环执行
clearInterval()
⚠️:少用,有很多缺陷
1、无视代码错误function a() { try { cnosole.log('拼写错误 console') } catch (e) { console.log('err') } } setInterval(a, 1000) // 每隔1s输出 err
2、无视网络延迟
function testSetInterval() { let i = 0 const start = Date.now() const timer = setInterval(() => { i++; i === 2 && clearInterval(timer) console.log(`第${i}次开始`, Date.now() - start) for(let i = 0; i < 900000000; i++) {} console.log(`第${i}次结束`, Date.now() - start) }, 100) } testSetInterval() // 第1次开始 105 // 第1次结束 694 // 第2次开始 695 // 第2次结束 1263 // 问题:为什么第一次结束 和第二次开始只隔了1ms // 原因:无视网络延迟,不会管上一次还行结果,定时将要执行的函数放入 异步队列
处理可能的阻塞:
function foo(){ setTimeout(foo, 1000) } foo()
关于这两定时器清除问题:
返回的都是一个id,代表顺序,从1开始,清除之后id不会被清除。
是否有必要清除?
答案是看需求,如果只有一个,不清除没什么影响,如果是用setTimeout模拟setinterval,则需要根据实际情况清除。
DOM方法
BOM方法
BOM即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window
window常见事件
- setTimeout、setInterval
- location
用于获取或设置窗体的 URL: protocol://host[:port]/path/[?query]#fragment 。
location.href ------ 获取或者设置整个URL
location.host ------ 返回主机(域名)www.itheima.com
location.port ------ 返回端口号 如果未写返回空字符串
location.pathname ------ 返回路径
location.search ------ 返回参数
location.hash ------ 返回片段 #后面内容常见于锚点链接
location.replace ------ 设置整个URL,不记录历史 - navigator
userAgent属性,返回 客户机信息。主要用于判断客户机是什么终端。 - history
back() 返回上一页
forward() 前进一页
go(n) n :正数前进n页,负数返回n页
pushState(state, title, url)
replaceState(state, title, url)
state:一个与指定历史记录相关联的状态对象,当popstate事件触发时,会把该对象传入回调函数。如果不需要用到,可以传null。
title:页面的标题。但当前大多数浏览器都不支持或忽略这个值。可以传null。
url:添加或修改的history的网址。为了安全性,必须保持与当前URL同一个域。
简而言之,两个方法的区别只是pushState添加一个最新的历史记录,而replaceState则是把当前的页面的历史记录替换掉。他们最大的特点是添加或替换历史记录后,浏览器地址栏会变成你传的地址,而页面并不会重新载入或跳转。
函数式编程、面向对象
- 函数式编程 用来描述数据(函数)之间的映射,相同的输入始终要得到相同的输出(纯函数)。
- ***面向对象***核心是对象,通过 封装、继承、多态 来演示事物事件的联系。
缓存
localStorage、sessionStorage 存储大小都是5M左右,不同浏览器无法共享其中的信息。
- localStorage
生命周期是永久,需要手动清除。 - sessionStorage
生命周期是页面或浏览器,关闭了就会清除。
cookie、session比较
跨域
http
-
是什么
超文本传输协议,是互联网中应用最为广泛的网络协议,是基于 TCP/IP 协议簇来传递数据。 -
原理?
1、http建立连接
其实就是建立 TCP连接 ,???
因为http这个协议传输层使用的是 TCP协议。既然 http 是基于 TCP/IP 协议簇传递数据,那么我们必须了解这个 TCP/IP协议簇
四层模型: 传输层、网络层、链路层、物理层
工作原理:
数据发送端是一层一层封装数据,数据接收端一层一层拆封,最后应用层获得数据。TCP连接过程:就是我们常说的 3次握手:
客户端发送位码要求连接服务端;
服务端收到并确认联机信息后同意客户端来连接;
客户端收到通知并校验再次发送请求;
服务端收到请求确认并建立连接成功。2、客户端发送请求
就是http请求,请求报文实际就是TCP数据体。
2.1 http请求报文结构: 请求行、请求头、空行(表示请求体开始)、请求体、空行(表示请求体结束)
2.2 请求实例3、服务端响应请求
3.1 响应报文结构: 响应行、响应头、空行(表示响应体开始)、响应体、空行(表示响应体结束)
3.2 响应实例
3.3 响应状态码:4、断开连接
在服务器响应完毕后,http一次会话就结束。
4.1 短连接: TCP断开连接,再请求需要重新建立TCP连接。
4.2 长连接: TCP不断开连接,允许http多次会话,http永远都是一次请求响应会话就结束,不存在长连接之说。
请求头设置:Connection:keep-alive 表示建立TCP长连接。服务端也可以设置 close 拒绝长连接。
长连接优缺点:看http请求次数,以及网站需要的资源量。
4.3 断开连接过程: 四次挥手 -
http缺点
明文且不能保证完整性,被HTTPS代替。 -
https和http区别
https
- 问题?
从url输入到返回请求的过程?
数据请求 Ajax
-
原理
通过 XmlHttpRequest 对象来向服务器发异步请求。对该对象方法和属性的扩展,实现Ajax。 -
案例:
实现一个Ajax请求?
XmlHttpRequest
- 方法
1、open()
创建一个XmlHttpRequest 对象,需要几个参数:
method:指定用来发送请求的http方法,常用GET,POST,还有,PUT,DELETE或HEAD;
uri:请求服务器地址;
async:异步还是同步请求,默认 异步(ture);
XmlHttpRequest 有个属性 readyState,值为4,将对responseText、responseXML、status和statusText 这些属性复位至初始值,值为 1,则复位完成,同时我们准备好了一个请求。
2、send()
readyState 值为 1时,才可以调用send方法,将请求发送至服务器。
当async参数为true时,send()方法立即返回,从而允许其它客户端脚本处理继续。在调用send()方法后,XMLHttpRequest对象把readyState的值设置为2(发送)。当服务器响应时,在接收消息体之前,如果存在任何消息体的话,XMLHttpRequest对象将把readyState设置为3(正在接收中)。当请求完成加载时,它把readyState设置为4(已加载)。对于一个HEAD类型的请求,它将在把readyState值设置为3后再立即把它设置为4。
3、setRequestHeader()
该setRequestHeader(DOMString header,DOMString value)方法用来设置请求的头部信息,Content-Type。当readyState值为1时,你可以在调用open()方法后调用这个方法;否则,你将得到一个异常。
4、getResponseHeader()
getResponseHeader(DOMString header,value)方法用于检索响应的头部值。仅当readyState值是3或4(换句话说,在响应头部可用以后)时,才可以调用这个方法;否则,该方法返回一个空字符串。
5、getAllResponseHeaders()
该getAllResponseHeaders()方法以一个字符串形式返回所有的响应头部(每一个头部占单独的一行)。如果readyState的值不是3或4,则该方法返回null。