promise

一、基础应用

  1. let promise = new Promise((resolve,reject)=>{
  2. console.log('Promise')
  3. })
  4. //then方法返回一个新的promise实例,可以链式调用then
  5. //Promise.prototype.then()
  6. //Promise.prototype.catch();
  7. promise.then((res)=>{
  8. console.log("resolve成功后执行的函数")
  9. },(err)=>{
  10. console.log("reject失败后执行的函数")
  11. })
  12. .then(()=>{},()=>{})
  13. .catch(()=>{ //捕获rejected/then方法指定的回调函数 的异常
  14. })
  15. //Promise.prototype.finally() //不管Promise对象最后状态如何,都会执行,一般用于释放一些资源
  16. let requst = (time,id)=>{
  17. return new Promise((resolve,reject)=>{
  18. setTimeout(()=>{
  19. resolve(`第${id}个请求${time/1000}秒`)
  20. },time)
  21. })
  22. }
  23. let p1 = requst(3000,1)
  24. let p2 = requst(2000,2)
  25. Promise.all([p1,p2].then((res)=>{//整合数据
  26. console.log(res)
  27. }).catch(err)=>{
  28. console.log(err)
  29. })
  30. //Promise.race(),哪个结果获取的快,就返回哪个结果,不管成功还是失败
  31. Promise.race([p1,p2].then(()=>{
  32. }))
  33. //超时取消
  34. //2020.6.11补充
  35. function test(bool){
  36. if(bool){
  37. return new Promise((resolve,reject)=>{
  38. //操作
  39. resolve(30) //将返回的数字转换为一个promise对象,以便调用then方法
  40. })
  41. }else{
  42. return Promise.reject(new Error('ss'))
  43. //也可以返回一个值,resolve和reject 属于Promise上的静态方法
  44. //return Promise.resolve(30)
  45. }
  46. }
  47. test(0).then((val)=>{
  48. console.log(val)
  49. },(err)=>{
  50. console.log(err)
  51. })
  52. //用 catch 来捕获 reject改变promise状态的错误
  53. //不要用 throw Error去捕获

2019.09.15补充
Promise解决回调地狱问题得例子

  1. function getData(path){
  2. return new Promise((resolve,reject)=>{
  3. var xhr = new XMLHttpRequest();
  4. xhr.open('get','http://localhost:3000/' + path);
  5. xhr.send(null);
  6. xhr.onreadystatechange = function() {
  7. if(xhr.readyState != 4) return;
  8. if(xhr.readyState == 4 && xhr.status == 200) {
  9. // 获取后台数据
  10. var ret = xhr.responseText;
  11. // 成功的情况
  12. resolve(ret);
  13. } else {
  14. // 失败的情况
  15. reject('服务器错误');
  16. }
  17. }
  18. })
  19. }
  20. getData(path1)
  21. .then(res=>{
  22. console.log(res);
  23. return getData(path2)
  24. })
  25. .then(res=>{
  26. console.log(res);
  27. return getData(path3)
  28. })
  29. .then(res=>{
  30. console.log(res);
  31. })
  32. .catch(err=>{
  33. console.log(err)
  34. })
  35. .finally(res=>{
  36. console.log('完成')
  37. })

async 和 await

  1. //伪代码
  2. async function test() {
  3. // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
  4. // 如果有依赖性的话,其实就是解决回调地狱的例子了
  5. await fetch(url)
  6. await fetch(url1)
  7. await fetch(url2)
  8. }
  9. //例子
  10. let a = 0
  11. let b = async () => {
  12. a = a + await 10
  13. console.log('2', a) // -> '2' 10 ,因为b()比a++先执行,所以这里a还是0
  14. }
  15. b()
  16. a++
  17. console.log('1', a) //-> '1' 1。这里是同步代码,先执行

二、规范(promiseA+)与实现

规范

1、3种状态:
1.1 pending:可改变,可以通过resolve(value),reject(reason)改变成最终态
1.2 fulfilled:最终态,不可改变
1.3 rejected:最终态,不可改变
2、then

  1. promise.then(onFulfilled,onRejected)

2.1 参数规范:只能是函数,如果不是函数,应该被忽略
2.2 promise的状态变成fulfilled时,应该调用onFulfilled,参数是value,并且只能调用一次
2.3 onRejected同理
2.4 onFulfilled和onRejected应该是在微任务阶段执行
2.4.1 queueMicrotack实现微任务的调用
2.5 then方法可以被链式调用(返回一个promise),并且所有的onFulfilled和onRejected回调需要按照.then的顺序执行,用一个数组存
2.6 返回值。then应该返回一个promise

  1. const promise2 = promise.then(onFulfilled,onRejected)

2.6.1 onFulfilled或者onRejected执行结果为x,调用resolvePromise(作用是递归解析x)
2.6.2 onFulfilled或者onRejected执行过程种抛出了异常e,promise2需要被reject
2.6.3 如果onFulfilled不是一个函数,promise2以promise1的value触发fulfilled,值穿透
2.6.4 如果onRejected不是一个函数,promise2以promise1的then触发fulfilled,值穿透
2.7 resolvePromise

  1. resolvePromise(promise2,x,resolve,reject)

2.7.1 如果promise2和x相等, reject typeError
2.7.2 如果x是一个promise
如果x是pending,promise必须要在pending状态,直到x状态变更
如果x是fulfilled, value -> fulfilled
如果x是rejected,reason -> rejected
2.7.3 如果x是一个Object\Function
去获取 const then = x.then,
如果获取这个then报错,直接reject reason
如果then是一个函数,then.call(x,resolvePromiseFn,rejectPromiseFn): 用上面的方式获取后,可能指针已经改变了,所以要重新绑定指针
resolvePromiseFn 的入参是y , 执行resolvePromise(promise2,y,resolvePromiseFn,rejectPromiseFn)???
then 如果跑出异常,resolvePromiseFn,rejectPromiseFn 还未执行完,就reject(reason)

实现

1、是一个构造函数或者class
2、定义3种状态
3、初始化状态pending
4、resolve 和 reject 方法
4.1 改变状态
4.2 入参分别是value,reason
5、实例化时入参的处理
5.1 入参是一个函数,接受resolve,reject两个参数
5.2 初始化实例的时候就要同步去执行这个函数,如果有报错,要reject,利用try catch
6、then 方法
6.1 接收两个参数,onFulfilled,onRejected
6.2 两个参数应该是函数,如果不是函数,就忽略
6.3 根据当前promise的状态,调用不同的函数
6.4 首先要拿到所有的回调,新建2个数组,存储成功和失败的回调,如果还是pending,就存入数组
6.5 在status发生变化的时候,执行回调,用到getter sertter,监听status变化,然后做对应的操作,对应遍历执行成功和失败回调,(value要在status改变之前改变)
7、then的返回值
7.1

  1. 里面有 resolve方法,reject方法,
  2. 多个回调的时候怎么处理?用一个数组存储,为了解决 以下调用方式
  3. let p1 = Promise.resolve()
  4. p1.then();
  5. p1.then();
  6. p1.then();
  7. p1.then();
  8. const PENDING = 'pending'
  9. const FULFILLED = 'fulfilled'
  10. const REJECTED = 'rejected'
  11. class MPromise{
  12. RESOLVECALLBACL = [];
  13. REJECTEDCALLBACK = [];
  14. _status = PENDING
  15. constructor(fn){
  16. this.status = PENDING; //初始状态
  17. this.value = null;
  18. this.reason = null;
  19. try{
  20. fn(this.resolve.bind(this),this.reject.bind(this))
  21. //为什么要绑定?如果传入的不是箭头函数,this会丢失
  22. }catch(e){
  23. console.log('出错拉~~~~~~~~~~~~~~~~~~~')
  24. this.reject(e)
  25. }
  26. }
  27. get status(){
  28. //定义一个私有变量,禁止套娃
  29. return this._status
  30. }
  31. set status(newStatus){
  32. this._status = newStatus
  33. //、 执行回调
  34. switch(newStatus){
  35. case FULFILLED:
  36. this.RESOLVECALLBACL.forEach(callback=>{
  37. callback(this.value)
  38. })
  39. break
  40. case REJECTED:
  41. this.REJECTEDCALLBACK.forEach(callback=>{
  42. callback(this.reason)
  43. })
  44. break
  45. }
  46. }
  47. resolve(value){
  48. if(this.status === PENDING){
  49. //这里一定是先改变值,再去改变状态,因为 set 拦截里需要用到值
  50. this.value = value
  51. this.status = FULFILLED
  52. }
  53. }
  54. reject(reason){
  55. if(this.status === PENDING){
  56. this.reason = reason;
  57. this.status = REJECTED
  58. }
  59. }
  60. then(onFulfilled,onRejected){ //判断是否是函数,如果不是,需要直接返回value(值透传)
  61. const realOnFuifilled = this.isFunction(onFulfilled)?onFulfilled:value=>value
  62. const realOnRejected = this.isFunction(onRejected)?onRejected:reason=>{
  63. throw reason
  64. }
  65. //返回一个promise
  66. //定义微任务
  67. //数组存回调
  68. const promise2 = new MPromise((resolve,reject)=>{
  69. const fulfilledMicrotask = ()=>{
  70. queueMicrotask(()=>{
  71. try{
  72. const x = realOnFuifilled(this.value)
  73. this.resolvePromise(promise2,x,resolve,reject)
  74. }catch(e){
  75. reject(e)
  76. }
  77. })
  78. }
  79. const rejectedMicrotask = ()=>{
  80. queueMicrotask(()=>{
  81. try{
  82. const x = realOnRejected(this.reason)
  83. this.resolvePromise(promise2,x,resolve,reject)
  84. }catch(e){
  85. reject(e)
  86. }
  87. })
  88. }
  89. switch(this.status){
  90. case FULFILLED:{
  91. fulfilledMicrotask()
  92. break
  93. }
  94. case REJECTED:{
  95. rejectedMicrotask()
  96. break
  97. }
  98. case PENDING:{
  99. this.RESOLVECALLBACL.push(fulfilledMicrotask)
  100. this.REJECTEDCALLBACK.push(rejectedMicrotask)
  101. break
  102. }
  103. }
  104. })
  105. return promise2
  106. }
  107. isFunction(fn){
  108. return typeof fn ==="function"
  109. }
  110. resolvePromise(promise2,x,resolve,reject){
  111. if(promise2 === x){
  112. return reject(new TypeError('The promise and the return value are the same'))
  113. }
  114. if(x instanceof MPromise){
  115. //如果x是一个promise,需要再次解析y,(可以理解为递归)
  116. queueMicrotask(()=>{
  117. x.then(y=>{
  118. this.resolvePromise(promise2,y,resolve,reject)
  119. },reject)
  120. })
  121. }else if(typeof x ==="object" || this.isFunction(x)){
  122. if(x=== null){
  123. return resolve(x)
  124. }
  125. let then = null
  126. //这里规范里定义要捕获这个错误
  127. try{
  128. then = x.then
  129. }catch(e){
  130. reject(e)
  131. }
  132. if(this.isFunction(then)){
  133. let called = false //设置一个标志位,只调用一次
  134. try{
  135. then.call(x,
  136. y=>{
  137. if(called) return;
  138. called = true
  139. this.resolvePromise(promise2,y,resolve,reject)
  140. },r=>{
  141. if(called) return
  142. called = true
  143. reject(r)
  144. })
  145. }catch(e){
  146. if(called) return;
  147. reject(e)
  148. }
  149. }else{
  150. resolve(x)
  151. }
  152. }else {
  153. resolve(x)
  154. }
  155. }
  156. catch(onRejected){
  157. return this.then(null,onRejected)
  158. }
  159. static resolve(value){
  160. if(value instanceof MPromise){
  161. return value
  162. }
  163. return new MPromise(resolve=>{
  164. resolve(value)
  165. })
  166. }
  167. static reject(reason){
  168. return new MPromise((resolve,reject)=>{
  169. reject(reason)
  170. })
  171. }
  172. static race(promiseList){ //哪个执行的快就返回哪个,其他的执行完但是不会返回了
  173. return new MPromise((resolve,reject)=>{
  174. let len = promiseList.length;
  175. for(var i = 0;i<len;i++){
  176. MPromise.resolve(promiseList[i]) //先转成promise,防止传入的不是promise导致报错
  177. .then(value=>{
  178. return resolve(value)
  179. },reason=>{
  180. return reject(reason)
  181. })
  182. }
  183. })
  184. }
  185. }
  186. MPromise.resolve().then(() => {
  187. console.log(0);
  188. return MPromise.resolve(4); //返回值是promise ,会创建2次微任务
  189. }).then((res) => {
  190. console.log(res)
  191. })
  192. MPromise.resolve().then(() => {
  193. console.log(1);
  194. }).then(() => {
  195. console.log(2);
  196. }).then(() => {
  197. console.log(3);
  198. }).then(() => {
  199. console.log(5);
  200. }).then(() =>{
  201. console.log(6);
  202. })
  203. //备注 :::
  204. static all(promiseList){ //一个失败全部都失败
  205. return new MPromise((resolve,reject)=>{
  206. let len = promiseList.length;
  207. let res = []
  208. let counter = 0;
  209. if(len === 0) return resolve()
  210. for(var i = 0; i <len; i++){
  211. MPromise.resolve(promiseList[i]).then(
  212. (value)=>{
  213. res[i] = value
  214. counter++
  215. if (counter === len) { //所有的promise执行完毕
  216. return resolve(res)
  217. }
  218. },
  219. (reason)=>{
  220. return reject(reason)
  221. }
  222. )
  223. }
  224. })
  225. }
  226. }
  227. //Promise.allSettled的实现
  228. function PromiseAllSettled(promiseArray){
  229. return new Promise((resolve,reject)=>{
  230. if(!Array.isArray(promiseArray)){
  231. return reject(new TypeError('参数必须是一个数组'))
  232. }
  233. let counter = 0;
  234. const promiseNum = promiseArray.length;
  235. const resultArray = []
  236. for(let i=0; i<promiseNum;i++){
  237. Promise.resolve(promiseArray[i])
  238. .then((value)=>{
  239. resultArray[i] = {
  240. status:'fulfilled',
  241. value
  242. }
  243. })
  244. .catch((reason)=>{
  245. resultArray[i] = {
  246. status:'rejected',
  247. reason
  248. }
  249. })
  250. .finally(()=>{
  251. counter++;
  252. if(counter===promiseNum){
  253. resolve(resultArray)
  254. }
  255. })
  256. }
  257. })
  258. }

参考链接 :https://zhuanlan.zhihu.com/p/264944183
https://juejin.cn/post/6953452438300917790#heading-0

三、promise与微任务、宏任务,vue的 nextTikc原理,以及浏览器V8串联

四、promise,async await对于错误的处理以及缺点

定时器相关知识

一、requestAnimationFrame 实现setInterval

  1. function setInterval(callback, interval) {
  2. let timer
  3. const now = Date.now
  4. let startTime = now()
  5. let endTime = startTime
  6. const loop = () => {
  7. timer = window.requestAnimationFrame(loop)
  8. endTime = now()
  9. if (endTime - startTime >= interval) {
  10. startTime = endTime = now()
  11. callback(timer)
  12. }
  13. }
  14. timer = window.requestAnimationFrame(loop)
  15. return timer
  16. }
  17. let a = 0;
  18. setInterval(timer => {
  19. console.log(1)
  20. a++
  21. if (a === 3) cancelAnimationFrame(timer)
  22. }, 1000)

二、定时器应用(防抖和节流)

再学js之 es6-es10

一、环境准备

1、安装node,推荐12.2.0
2、npx命令 npm版本>5.6
3、初始化项目:npx es10-cli create projectName
4、启动项目 npm start
如安装失败
npm install es10-cli -g
es10-cli create projectName

二、es6

—-全局作用域,函数作用域,块级作用域,动态作用域this,根据调用方式不同,this指向也不同(详见https://www.yuque.com/tudouxianbaozi/lvh04f/uz26r7,顺便复习一下)

1、let const

  1. function test(){
  2. var a = 3;
  3. //会提升成这样:var b
  4. if(a === 3){
  5. var b = 4;
  6. //这里如果换成 es6 的let ,就不会有变量提升,b只是在这个{}块里访问得到,下面的b就不能访问成功
  7. console.log('abc')
  8. }else{
  9. console.log('abcd')
  10. }
  11. console.log(b) // 这里能打印成功,因为es5没有块级作用域,而且还会变量提升
  12. return a + 4 //2、闭包(实现外部拿到内部的变量)
  13. }
  14. console.log(test())
  15. //利用闭包
  16. function test(){
  17. var a = 3;
  18. function test2(){
  19. var b = 4;
  20. return a+b;
  21. }
  22. return test2
  23. }
  24. var t = test();
  25. t();

1、let 声明的变量具有块级作用域
2、let声明的变量在全局作用域|window上找不到
3、let 声明的变量不能被重复定义
4、不会变量提升
5、const定义的是常量,不允许再修改
6、const不能先声明后赋值

  1. //会不会报错??
  2. //es5
  3. Object.defineProperty() 声明常量

2、Array

1、for in ,array.every,for,forEach,数组对象的遍历方式,es5
2、for of,遍历自定义的数据,es6的

  1. //伪数组到数组的转换,如arguments,nodeList
  2. let args = [].slice.call(arguments)
  3. //es6已经废弃了arguments
  4. //es6的做法
  5. let args = Array.from(arguments)
  6. {0:'a',1:'b',length:2} //伪数组
  7. let arr = Array.from({length:5},()=>1)
  8. //es6生成数组的方法
  9. ler arr1 = Array.of(6)
  10. let arr2 = Array(9).fill(7)
  11. //fill方法可以用来改变数组的某一项值,达到 splice的效果
  12. //如何给数据结构自定义遍历
  13. //手写实现array 的flat方法,flat方法的参数表示打平的深度
  14. function flatDeep(arr,d = 1){
  15. if(d>0){
  16. arr.reduce((res,val)=>{ //reduce(回调参数(累计结果,当前处理的值),初始值)
  17. if(Array.isArray(val)){
  18. res = res.concat(flatDeep(val,d-1))
  19. }esle{
  20. res = res.concat(val)
  21. }
  22. return res
  23. },[])
  24. }else{
  25. return arr.slice()
  26. }
  27. }
  28. Array.incluedes()
  29. Array.from(伪数组\真数组,回调函数map)
  30. //一个伪数组转成真数组的方式
  31. 1、[...arguments]
  32. 2Array.from()
  33. 3Array.prototype.slice.call(arguments)

3、类 class

  1. monkey.constructor.protptype.eat = Animal.prototype.eat
  2. //es5是怎么做到属性保护(get 和set)
  3. let Animal = function(type){
  4. this.type = type;
  5. }
  6. Animal.prototype.eat = function(){
  7. Animal.walk();//调用静态方法
  8. console.log('eat food')
  9. }
  10. Animal.walk = function(){
  11. console.log('walking')
  12. }
  13. let dog = new Animal('dog');
  14. dog.eat()
  15. //es6
  16. class Animal{
  17. constructor(type){
  18. this.type = type;
  19. }
  20. eat(){
  21. Animal.walk()
  22. console.log('eat')
  23. }
  24. static walk(){ //静态
  25. console.log('walk')
  26. }
  27. }
  28. let dog = new Animal('dog')
  29. dog.eat()

4、继承

  1. //es5的继承,请复习
  2. //es6的继承

5、函数不确定参数,函数默认值

  1. //es5处理
  2. //arguments,call,apply,Array.prototype
  3. function sum(...nums){
  4. let num = 0;
  5. }
  6. function sum(base,...nums){
  7. }
  8. function sum(x,y=3,z=x+y){
  9. }
  10. let data = [1,2,3]
  11. sum(...data)

6、箭头函数

  1. let sum =(x,y,z)=>({
  2. x:x,
  3. y:y,
  4. z:z
  5. })
  6. console.log(sum(1,2,3))
  7. //箭头函数的this
  8. //如何用箭头函数来实现一个数组排序的问题?
  9. //箭头函数对this的处理还有什么妙用?
  10. const fn = (value) => ({})
  11. //不能用作构造函数
  12. 1、会改变this指向,指向new出来的那个实例,但是箭头函数的this是在定义的时候就决定了的

7、Set

  1. let s = new Set()
  2. //add ,delete,has,clear,size
  3. //keys,values,entries
  4. //s.forEach(item=>console.log(item))
  5. //for of

8、Map

  1. let map = new Map([[1,2],[3,4]]);
  2. map.set(1,3)
  3. map.set(3,4)
  4. map.set(1,2)
  5. map.delete(1)//删除某一个
  6. map.clear() //全部删除
  7. map.size
  8. map.has(1) //查的是索引值
  9. map.get()
  10. map.keys();
  11. map.values
  12. map.entries()
  13. map.forEach((v,k)=>console.log('值':v +','+'键':k))
  14. for (let [key,value] of map){
  15. console.log(key ,value)
  16. }
  17. //map 的key值可以是任意的,包括函数
  18. //map 性能优于object

9、object

  1. //1.属性简写
  2. //object拷贝
  3. Object.assign()//浅拷贝
  4. 实现浅拷贝shallowCopy for in
  5. {...a,...b}
  6. 实现 Object.keys()
  7. 实现 Object.values()
  8. Object.entries()
  9. Object.getOwnPropertyNames
  10. Object.getOwnPropertyDescriptors
  11. Object.freeze(obj) //引用类型的常量属性不能被修改,会报错

10、正则

  1. const s = 'aaa_aa_a';
  2. const r1 = /a+/g;; //^ $
  3. const r2 = /a+/y //y修饰符,粘连,每次匹配从上一次的结束位置开始,连续匹配
  4. //es6种处理中文字符的方法
  5. //加上U修饰符,es6都习惯加上u修饰符
  6. console.log(/\u{61}/u.test('a'))

10、字符串

  1. function Price(string,type){
  2. console.log(string[0])
  3. console.log(type)
  4. }
  5. let shoTxt = Price`您此次的${'retail'}`//还能这样用!!!!
  6. //面试题:如何实现模板字符串的render函数
  7. const year = '2021'
  8. const month = '10'
  9. const day = '24'
  10. const template = '${year}-${month}-${day}'
  11. const context = {
  12. year,month,day
  13. }
  14. const str = render(template)(context)
  15. console.log(str) //2021-10-24
  16. //解答, 高阶函数+正则
  17. function render(template){
  18. return function(context){
  19. return template.replace(/\$\{(.*?)\}/g,(match,key)=>context[key])
  20. }
  21. }
  22. //正则真的很重要!!!!

11、解构赋值

a、右边是一个可遍历的对象,左边 [],如 let [ f, t ] = [1,2]

  1. //数组的解构赋值
  2. let user= {name:'s',surname:'t'};
  3. [user.name,user.surname] = [1,2];
  4. for(let [key,val] of Object.entries(user)){
  5. console.log(`${key}:${val}`)
  6. }
  7. //Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组
  8. //对象的解构赋值
  9. let options = {
  10. title:'rt',
  11. size:{
  12. width:100,
  13. height:200
  14. },
  15. items:['cake','dount'],
  16. extra:true
  17. }
  18. //let {size,...last} = options
  19. let {title,size:{width,height},items:[,item2]} = options
  20. //原理:针对可迭代对象Iterator,通过遍历按顺序获取对应的值进行赋值
  21. symbol.iterator
  22. Iterator是?
  23. Iterator是一种接口,interface,为各种不一样的数据解构提供统一的访问机制
  24. 任何数据解构,只要有这个Symbol.iterator,都可以进行遍历,然后按照顺序解构出数据,表明是一个可迭代对象
  25. for of 相当于一个遍历器,遍历的时候,去寻找Iterator
  26. 有什么作用?
  27. 为各种不同的数据解构提供统一的访问接口
  28. 数据解构按照顺序处理
  29. for of 可以进行遍历
  30. 可迭代对象是什么?
  31. 可迭代对象存在两种协议:可迭代协议 和迭代器协议
  32. 可迭代协议:对象必须实现interator方法,对象或者原型上必须有个Smybol.interator:()=>迭代器协议
  33. 迭代器协议:必须实现一个next方法,next方法返回一个对象,包含done,value
  34. function generateInterator(array){
  35. let nextIndex = 0;
  36. return {
  37. next:()=>{
  38. return nextIndex<array.length?{
  39. done:false,
  40. value:array[nextIndex++]
  41. }:{
  42. done:true,
  43. value:undefined
  44. }
  45. }
  46. }
  47. }
  48. //调用
  49. const interator = generateInterator([1,2,3])
  50. console.log(interator.next())
  51. console.log(interator.next())
  52. console.log(interator.next())
  53. console.log(interator.next())
  54. for of for in 的区别
  55. 1.使用上,for in输出下标(对象输出key),for of 输出的item
  56. 2.for in 还会遍历原型上面的属性,需要用hasOwnProperty判断一下,可以被break中断,
  57. ,不适合遍历数组
  58. 3.for of 可以break中断,
  59. forEach 没有break ,可以throw error,然后try catch
  60. 应用场景
  61. 1、数组传参
  62. 2、结合函数参数的初始值
  63. const c =({a,b,c=1})=>{}
  64. 3、返回值取值
  65. 4、变量交换
  66. let a = 1,b =2
  67. [b,a] = [a,b]
  68. 5json处理
  69. 解构取JSON.perse(json)
  70. 6ajax
  71. 返回值通过解构取

12、promise

  1. //补充手写 promise源码
  2. //返回所有promise的状态和结果
  3. Promise.allSettled

13、Reflect

Reflect对象不能使用new

  1. Math.floor.apply(null,[3.76])
  2. Reflect.apply(Math.floor,null,[3.76])
  3. let price = 101.9
  4. console.log(Reflect.apply(price>100?Math.ceil:Math.floor,null,[price]))
  5. let d = Reflect.construct(Date,[]) //注意:这里第二个参数为空数组,等于以下代码
  6. let d1 = new Date()
  7. let student = {};
  8. let s = Reflect.defineProperty(student,'name',{value:'rt'});
  9. let s1 = Object.defineProperty(student,'name',{value:'rt'});
  10. Reflect.deleteProperty(obj,'属性名')
  11. Reflect.get(obj,'属性名')
  12. Reflect.get([1,23,4],'索引')
  13. //尽量使用Reflect,以后挂载到Object上的方法会逐渐废除?
  14. 优化了 js中一些不规范的行为
  15. 比如说命令式的删除某一个对象上的属性 delete obj.属性名

14、proxy

  1. //场景一,只读 用 Proxy
  2. //场景二,校验
  3. //场景三,还原后端的数据
  4. //场景四,生成随机Id,不能被修改,每次实例ID生成的不一样
  5. class Component {
  6. constructor (){
  7. this.proxy = new Proxy({
  8. id: Math.random().toString(36).slice(-8)
  9. },{})
  10. }
  11. get id (){
  12. return this.proxy.id
  13. }
  14. }
  15. //每次实例随机生成的ID,不一样
  16. let com = new Component()
  17. let com2 = new Component()
  18. for(let = 0; i< 10,i++){
  19. console.log(com.id,com2.id)
  20. }
  21. com.id = 'abc' //不生效,不能被修改
  22. //撤销代理
  23. let 0 = {
  24. price:190,
  25. name:'34dffgb'
  26. }
  27. let d = Proxy.revocable(o,{
  28. get(target,key){
  29. if(key ==="price"){
  30. return target[key]+=20
  31. }else{
  32. return target[key]
  33. }
  34. }
  35. })
  36. setTimeout(()=>{
  37. d.revoke();//撤销
  38. setTimeout(()=>{
  39. console.log(d.proxy.price)
  40. })
  41. },1000)

15、generator

  • 迭代器 Iterator的概念:es6引入的一种新的遍历机制,同时也是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口
  • 每个迭代器对象都有一个next()方法,每次调用都返回一个当前结果对象。当前结果对象有两个属性

value:当前属性的值
done:用于判断是否遍历结束,当没有更多可返回的数据时,返回true

  1. //一、可以自定义遍历器
  2. let authors = {
  3. allAuthors:{
  4. fiction:['qwe','weer','rrtt','rtyy','yui','xcd'],
  5. scienceFiction:['sdvv','axd','ss','hj','','dff','cdd','mjh','cvv','bnn'],
  6. fantasy:['hj','dd','gh','jk','rt','sbnv']
  7. },
  8. adress:[]
  9. }
  10. //如果是 es5遍历
  11. let r = [];
  12. for(let [k,v] of Object.entries(author.allAuthors)){
  13. r = r.concat(v)
  14. }
  15. //定义遍历器,,还是和 业务 耦合呀????
  16. authors[Symbol.iterator] = function(){ //此函数没有入参
  17. let allAuthors = this.allAuthors;
  18. let keys = Reflect.ownKeys(allAuthors);
  19. let values = [];
  20. return {
  21. next(){ //必须有
  22. if(!values.length){
  23. if(keys.length){
  24. values = allAuthors[keys[0]]
  25. keys.shift()
  26. }
  27. }
  28. return { //必须有
  29. done:!values.length,
  30. value:values.shift()
  31. }
  32. }
  33. }
  34. }
  35. //这个迭代器用generator来替代
  36. authors[Symbol.iterator] = function * (){
  37. let allAuthors = this.allAuthors;
  38. let keys = Reflect.ownKeys(allAuthors);
  39. let values = [];
  40. while(1){
  41. if(!values.length){
  42. if(keys.length){
  43. values = allAuthors[keys[0]];
  44. keys.shift()
  45. yield values.shift()
  46. }else{
  47. return false
  48. }
  49. }else {
  50. yield values.shilft()
  51. }
  52. }
  53. }
  54. let r1 = []
  55. for(let r of authors){
  56. r1= r1.concat(r)
  57. }
  58. //1、可迭代协议,如果一个对象上包含以Symbol.iterator为key的方法,说明这个对象可迭代
  59. //2、迭代器协议,必须返回一个对象,对象必须包含next方法,next方法必须返回一个包含done和value的对象
  60. function * gen(){
  61. let val
  62. val = yield [1,2,3]
  63. //yield 后面可以跟一个可遍历的对象,也可以跟一个generotor对象
  64. //val = yield * [1,2,3]
  65. console.log(val)
  66. }
  67. let l = gen()
  68. //next可以传值,用于修改内部的数据
  69. console.log(l.next())
  70. //提前终止
  71. console.log(l.return(100))
  72. //抛出异常,可以通过try catch捕获
  73. l.throw(new Error('ss'))
  74. console.log(l.next())
  75. //年会抽奖案例
  76. //可以控制抽奖的节奏
  77. function * getPrize(first=1,second=3,third=5){
  78. let allPepole = ['qw','we','er','rt','ty','yu','ui','io','op','pa','sa','sd','df','fg','gh','hj','jk','kl','la','lz']
  79. let count = 0
  80. let random
  81. while(1){
  82. if(count < first+second+third){
  83. random = Math.floor(Math.random()*allPepole.length);
  84. yield allPepole[random]
  85. count ++
  86. allPepole.splice(random,1)
  87. }else{
  88. return false
  89. }
  90. }
  91. }
  92. let p = getPrize();
  93. console.log(p.next().value)
  94. console.log(p.next().value)
  95. console.log(p.next().value)
  96. console.log(p.next().value)
  97. console.log(p.next().value)
  98. console.log(p.next().value)
  99. function * count(x=1){
  100. while(1){
  101. if(x % 3 === 0){
  102. yield x
  103. }
  104. x++
  105. }
  106. }
  107. let n = count()
  108. console.log(n.next().value)
  109. //用generator实现一个裴波那数列
  • 实现自动执行的generator ,Co库
  • 特征 1、每当执行完一条yield语句后函数就会自动停止执行,直到再次调用next()

2、yield关键字只可以在生成器内部使用
3、可以通过函数 表达式来创建生成器,但是不能使用箭头函数

  1. function longTimeFn(time){
  2. return new Promise(resolve=>{
  3. setTimeout(()=>{
  4. resolve(time)
  5. },time)
  6. })
  7. }
  8. function asyncFunc(generator){
  9. const iterator = generator();
  10. let n = iterator.next
  11. const next = (data)=>{
  12. const {
  13. value,
  14. done
  15. } = iterator.next(data) //感觉就像递归调用next方法
  16. if(done) return;
  17. value.then(data=>{
  18. next(data)
  19. })
  20. }
  21. next()
  22. }
  23. asyncFunc(function* (){
  24. let data = yield longTimeFn(2000)
  25. console.log(data)
  26. data = yield longTimeFn(3000)
  27. console.log(data)
  28. })

三、es7

1、es7中判断数组中一个元素是否存在

  1. let arr = [1,2,3,4,5,6,7]
  2. console.log(arr.includes(7))

2、数学乘方的简写

  1. console.log(Math.pow(2,5))
  2. //es7
  3. console.log(2 ** 5)

四、es8

1、比promise更优雅的处理异步的方式

  1. //async关键字会自动返回一个promise对象
  2. async function test(){
  3. let promise = new Promise((resolve,reject)=>{
  4. setTimeout(()=>{
  5. resolve('now is done')
  6. },1000)
  7. })
  8. console.log(await promise) //控制宏任务先执行
  9. console.log(await Promise.resolve(40)) //先执行
  10. console.log(20)
  11. return Promise.resolve(4)
  12. }
  13. test().then((val)=>{
  14. console.log(val)
  15. })

2、object快速遍历的方法

  1. let grade = {
  2. 'lilei' : 96,
  3. 'han' : 99
  4. }
  5. //遍历keys
  6. console.log(Object.keys(grade))
  7. console.log(Object.keys(grade).filter(item => item === 'lilei'))
  8. //遍历values
  9. console.log(Object.values(grade))
  10. console.log(Object.values(grade).filter(item => item > 96))
  11. //object.entries:把对象变成可遍历的对象;(方法返回一个给定对象自身可枚举属性的键值对数组)
  12. let result = []
  13. for (let [k, v] of Object.entries(grade)) {
  14. console.log(k, v)
  15. result.push(k)
  16. }
  17. console.log(result)

3、String补白的方式

  1. for (let i=1;i<4522;i+=1000){
  2. console.log(i.toString().padStart(4,'*&')) //前面补全
  3. }
  4. for (let i=1;i<4522;i+=1000){
  5. console.log(i.toString().padEnd(4,'*&'))//后面补全
  6. }

4、获取对象的描述符

  1. const data = {
  2. age:'23',
  3. name:'hailei',
  4. legs:4,
  5. }
  6. //等同于 Reflcet.defineProperty
  7. Object.defineProperty(data,'legs',{
  8. enumerable:false
  9. })
  10. console.log(Object.keys(data))
  11. //获取所有属性的描述符
  12. //Reflect.getOwnPropertyDescriptor(target, propertyKey)===Object.getOwnPropertyDescriptor(target, propertyKey)

五、es9

1、for await of ,对异步集合的遍历,,可以用来实现网约车的异步流程

  1. function Gen(time){
  2. return new Promise((resolve,reject)=>{
  3. setTimeout(()=>{
  4. resolve(time)
  5. ,time})
  6. })
  7. }
  8. //老的语法可以这样控制,但是不完美
  9. async function test(){
  10. let arr = [Gen(2000),Gen(100),Gen(3000)]
  11. for(let item of arr){
  12. console.log(Date.now(),await item.then(console.log))
  13. }
  14. }
  15. //使用 for await of
  16. async function test(){
  17. let arr = [Gen(2000),Gen(100),Gen(3000)]
  18. for await (let item of arr){
  19. console.log(Date.now,item)
  20. }
  21. }
  22. //自定义数据结构的异步遍历器
  23. let obj = {
  24. count:0,
  25. Gen(time){
  26. return new Promise((resolve,reject)=>{
  27. setTimeout(()=>{
  28. resolve({done:false,value:time}) //要遵循迭代器协议
  29. },time)
  30. })
  31. },
  32. [Symbol.asyncIterator](){
  33. let self = this;
  34. return {
  35. next(){
  36. self.count++
  37. if(self.count < 4){
  38. return self.Gen(Math.radom()*1000)
  39. }else{
  40. return Promise.resolve({
  41. done:true,
  42. value:''
  43. })
  44. }
  45. }
  46. }
  47. }
  48. }
  49. async function test(){
  50. for await (let item of obj){
  51. console.log(Date.now(),item)
  52. }
  53. }

2、升级版的promise

  1. function Gen(time){
  2. return new Promise((resolve,reject)=>{
  3. setTimeout(()=>{
  4. if(time < 500){
  5. reject(time)
  6. }else{
  7. resolve(time)
  8. }
  9. },time)
  10. })
  11. }
  12. Gen(Math.random()*1000)
  13. .then(val=>console.log(val,'resolve'))
  14. .catch(err=>console.log(err,'reject'))
  15. .finally(()=>{console.log('finish')})

3、Object 的 Rest 和Spread

  1. //可以用来合并对象
  2. const obj1 = {
  3. a:1,
  4. b:2
  5. }
  6. const obj2 = {
  7. c:3,
  8. d:4
  9. }
  10. const obj3 = {
  11. e:5,
  12. f:8
  13. }
  14. const obj = {...obj1,...obj2,...obj3,n:7}
  15. console.log(obj)
  16. obj1.a = 34;
  17. console.log(obj1,obj)
  18. const input = {
  19. a:1,b:2,c:3,d:4
  20. }
  21. let {a,b,...rest} = input;
  22. console.log(a,b,rest)

4、dotAll,命名分组捕获、后行断言

  1. //需要匹配 . 以及行终止符,就需要加 s 的修饰符,需要匹配
  2. //匹配4个字节的utf16字符,需要加U修饰符
  3. console.log(/foo.bar/us.test('foo\nbar'))
  4. //判断是否启用dotAll模式
  5. const re = /foo.bar/s
  6. console.log(re.dotAll)
  7. console.log(re.flags)
  8. (2)命名分组捕获
  9. const t = '2019-04-09'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)
  10. console.log(t)
  11. console.log(t.groups.year)
  12. (3)后行断言
  13. let test = 'hello world'
  14. console.log(test.match(/hello(?=\sworld)/))
  15. console.log(test.match(/(?<=hello\s)world/))
  16. console.log(test.match(/(?<!hellr\s)world/)) // !表示不等于

六、es10

1、JSON.stringify,修正0xD800-0xDFFF之间字符显示的bug
2、新增扁平化数组,参数为深度,指定深度的打平

  1. let arr = [1,[2,[4,3,[6,7,[8]]]]];
  2. console.log(arr.flat(4))

3、数组增加flatMap
4、字符串增加的API

  1. //去掉字符串首尾空格
  2. let s = ' foo '
  3. console.log(s.trimStart()) //trimStart = trimLeft
  4. console.log(s.trimEnd()) //trimEnd = trimRight
  5. console.log(s.trim()) //去掉全部
  6. s.match(正则)
  7. s.matchAll(正则)

5、新增object 的API,fromEntries

  1. const arr = [['a',1],['b',2]]
  2. const obj = Object.fromEntries(arr)
  3. console.log(obj)
  4. const test = {
  5. 'a':1,
  6. 'b':2,
  7. 'c':3,
  8. 'sd':5
  9. }
  10. let res = Object.fromEntries(Object.entries(test).filter(([key,val])=>key.length===1));
  11. console.log(res)
  12. //应用一、提取url中的参数
  13. let url3 = "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&rsv_pq=acb2887700009bd5&rsv_t=4b0dalrwbS8EVGgEzFz57Bv%2BtOH4PY3P6AoZRV6xYfwao8HSYZrTuaXYNRc&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_btype=t"
  14. let r = url3.split('?')[1]
  15. let query = Object.fromEntries(new URLSearchParams(r))
  16. console.log(query)
  17. //应用二、交换属性和值
  18. function foo(obj) {
  19. return Object.fromEntries(Object.entries(obj)
  20. .map(([key, value]) => [value, key])
  21. )
  22. }
  23. console.table({ name: 'oli', age: '12' })
  24. console.table(foo({ name: 'oli', age: '12' }))
  25. //应用三、过滤属性
  26. function foo(obj, ...keys) {
  27. return Object.fromEntries(Object.entries(obj)
  28. .filter(([key]) => keys.includes(key))
  29. )
  30. }
  31. console.table(foo({ name: 'oli', age: '12' }, 'name'))
  32. //应用四、处理表格数据
  33. let arr = [{
  34. name: 'oli',
  35. age: 12
  36. }, {
  37. name: 'troy',
  38. age: 14
  39. }]
  40. obj = Object.fromEntries(
  41. arr.map(({name, age}) => [name, age])
  42. )
  43. console.table(obj)
  44. //try catch能省略(e)
  45. try{
  46. }catch{
  47. }

6、新增的一种数据类型 bigint

  1. const a = 11n
  2. console.log(typeof a)

七、Vue 结合新语法的实战

知识点1、vue自定义指令
2、Mock 数据,利用http-server启动本地服务
3、async\await异步处理
4、箭头函数,解构赋值等
5、利用proxy保存原始接口请求回来的原始数据(排序后还原)
6、自定义遍历器处理接口返回数据

八、环境构建

1、webpack配置,常用的loader,插件
2、babel工作原理,es6 的code => AST =》AST(es5的) =》 es5 的code
3、eslint 使用,自动修复需要编辑器的配置:安装linter 和 linter-eslint ,设置里开启 lint-onchange/Fix errors on save

es6实战

class 写tab 和newList 组件

  1. //节流 :丢弃
  2. //防抖 : 等待
  3. //装饰器的节流,,时间戳,定时器

九、前端知识结构整理

一、思维导图

前端知识结构整理(新).xmind
前端知识架构整理.xmind

二、面试重点知识点导图

十、模块化(AMD,CMD,CommonJS ,ES6)

一、模块化演进过程:

1.文件划分的方式 污染全局作用域 命名冲突 无法管理模块依赖关系
2.命名空间方式 在第一个阶段的基础上 将每个模块只暴露一个全局对象 所有的变量都挂载到这个全局对象上
3.IIFE 立即执行函数 为模块提供私有空间,闭包0
4.esm

问题

commonJs怎么解决循环引用问题?
如果a 依赖了b,同步执行到b,如果b又引用了a,就只会拿到a的执行结果,如果没有就undefind,不会再回到a了;待执行完b,再继续执行 a后面的

try catch 的 catch可以实现块级作用域,面试居然被问到过 let ,{} ,?????

AMD异步加载(require.js) CMD(sea.js) CommonJS,服务端规范,nodejs采用 es6
使用:













特点
1、异步,允许指定回调函数
2、
define([加载的资源],回调函数)
3、使用起来比较复杂
4、模块的js文件请求频繁
5、先加载依赖

实现原理:运行require.js后,会动态在body元素尾部添加需要的js文件,

插入sciprt节点,并且加载+运行其js文件


1. // 建一个node节点, script标签
1. var node = document.createElement(‘script’)
1. node.type = ‘text/javascript’
1. node.src = ‘3.js’
1.

  1. // 将script节点插入dom中
    1. document.body.appendChild(node)
    | 1、按需加载
    2、define(function(){
    var a = require(‘2.js’)
    console.log()
    })
    3、碰到require(‘2.js’) 就立即执行2.js | 1、同步的,所以不适用浏览器,会阻塞
    2、一个文件就是一个模块,有单独的作用域
    3、通过module.exports导出成员
    4、通过require函数载入模块
    5、CommonJS 模块输出的是一个值的拷贝,一旦输出一个值,模块内部的变化就影响不到这个值
    6、CommonJS 模块加载的顺序,按照其在代码中出现的顺序
    7、由于 CommonJS 是同步加载模块的,在服务器端,文件都是保存在硬盘上,所以同步加载没有问题,但是对于浏览器端,需要将文件从服务器端请求过来,那么同步加载就不适用了,所以,CommonJS 是不适用于浏览器端的。
    8、ommonJS 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存
    9、const exports= module.exports; exports只是module.exports的快捷方式
    导出方式:module.export ={
    a:1,b:2,test:’100’
    }
    或者 export.a = 1
    export.b = 2
    export.test = ‘100’
    不能直接给你export 赋值,不然断了和module.export的联系,,调用模块就访问不到exports对象及其属性 | 1、自动采用严格模式,忽略use strict
    2、每个ESM模块都是单独的私有作用域
    3、ESM是通过CORS去请求外部JS模块的:

    4、ESM中的script标签会延迟执行脚本,在普通的js后面执行

    5、ES6 模块是动态引用,引用类型属性被改变会相互影响
    6、导出的并不是成员的值 而是内存地址 内部发生改变外部也会改变,外部导入的是只读成员不能修改
    7、import不是解构导出对象
    用法:export const name=”aa”
    export funciton foo(){}
    export class Person{}
    或者导出多个变量
    export {
    name,foo,Person
    }
    import{name}from’./modules.js’
    as 关键字
    export {name as alias}
    import{ name as alias}from’./modules.js’
    default
    export default name

    导入关键字
    import
    as param from’./modules.js’
    param 是个对象
    同时接受 default 和普通变量
    // 默认值在前,普通变量在后,用逗号隔开,语法可以正常使用import defaultParam,{normalParam …}from’./modules.js’
    或者
    import defaultParam,*as nomalParam from’./modules.js’
    动态导入
    import(‘path’).then((param)=>{// param 是导入的成员变量}) |

二、代码实现补充

  • 如何知道依赖的哪些模块。解析require关键字 ```javascript //AMD大致原理 * 依赖前置 提前加载并读取 var node = document.createElement(‘script’) node.type = ‘text/javascript’ node.src = ‘1.js’

// 给该节点添加onload事件,标签上onload,这里是load,见事件那里的知识点 // 1.js 加载完后onload的事件 node.addEventListener(‘load’, function(evt) { // 开始加载 2.js var node2 = document.createElement(‘script’) node2.type = ‘text/javascript’ node2.src = ‘2.js’ // 插入 2.js script 节点,所以2.js先执行 document.body.appendChild(node2) }) // 插入 1.js script 节点 document.body.appendChild(node) //如何判断 所有的依赖都加载完了,递归

  1. CMD大致原理
  2. ```javascript
  3. // sea.js *** 依赖就近 **提前读取文件,但在需要的时候再加载
  4. const modules = {};
  5. const exports = {};
  6. sj = {};
  7. const toUrl = (dep) => {
  8. const p = location.pathname;
  9. return p.slice(0, p.lastIndexOf('/')) + '/' + dep + '.js';
  10. }
  11. const getDepsFromFn = (fn) => {
  12. let matches = [];
  13. // require('a ')
  14. //1. (?:require\() -> require( -> (?:) 非捕获性分组
  15. //2. (?:['"]) -> require('
  16. //3. ([^'"]+) -> a -> 避免回溯 -> 回溯 状态机
  17. let reg = /(?:require\()(?:['"])([^'"]+)/g; // todo
  18. let r = null;
  19. while((r = reg.exec(fn.toString())) !== null) {
  20. reg.lastIndex
  21. matches.push(r[1])
  22. }
  23. return matches
  24. }
  25. const __load = (url) => {
  26. return new Promise((resolve, reject) => {
  27. const head = document.getElementsByTagName('head')[0];
  28. const node = document.createElement('script');
  29. node.type = 'text/javascript';
  30. node.src = url;
  31. node.async = true;
  32. node.onload = resolve;
  33. node.onerror = reject;
  34. head.appendChild(node)
  35. })
  36. }
  37. // 依赖呢?
  38. // 提取依赖: 1. 正则表达式 2. 状态机
  39. define = (id, factory) => {
  40. const url = toUrl(id);
  41. const deps = getDepsFromFn(factory); //此函数是从define(id,callback) 的 callback 中提取require的文件路径,转成一个数组, 如 ['a','b']
  42. if (!modules[id]) { //添加到modules 中
  43. debugger
  44. modules[id] = { url, id, factory, deps } //deps :依赖, factory === callback回调, id :define('main',()=>{}) 中的'main' ***路径*** ,url :拼全的路径
  45. }
  46. }
  47. const __exports = (id) => exports[id] || (exports[id] = {});
  48. const __module = this;
  49. // 这里面才是加载模块的地方
  50. const __require = (id) => {
  51. debugger
  52. return __load(toUrl(id)).then(() => { //加载依赖的模块a
  53. debugger
  54. // 加载之后
  55. const { factory, deps } = modules[id];
  56. debugger
  57. if (!deps || deps.length === 0) {
  58. factory(__require, __exports(id), __module);
  59. return __exports(id);
  60. }
  61. return sj.use(deps, factory);
  62. })
  63. }
  64. sj.use = (mods, callback) => { //入口,比如 sj.use('main') ,
  65. mods = Array.isArray(mods) ? mods : [mods];
  66. return new Promise((resolve, reject) => {
  67. Promise.all(mods.map(mod => { //就去加载main ,main 里又有了 define ,就先去执行define
  68. debugger
  69. return __load(toUrl(mod)).then(() => { //
  70. debugger
  71. const { factory } = modules[mod];
  72. debugger
  73. return factory(__require, __exports(mod), __module) //拿到 main 中的factory === callback,正式去加载main
  74. })
  75. })).then(resolve, reject)
  76. }).then(instances => callback && callback(...instances))
  77. }

JSONP原理

利用了 script标签 src 加载 js 文件不受跨域限制的特性

  • 动态的创建了一个script 标签,加载一个js文件,src = “服务端请求地址+callback=callback”
  • 服务端获取这个callback,然后再把需要获取的数据装在里面
  • 前端请求完这个js后,自动执行callback回调,在回调里就能拿到数据

    1. function callback(data){
    2. console.log(data) //这里就是后台 装入的数据
    3. }
  • JQ 的jsonp实现

    1. window.onload = function(){
    2. jsonp({
    3. url:"",
    4. jsonp:'callback',
    5. data:{
    6. a:1,
    7. b:2,
    8. c:3
    9. },
    10. success:function(res){
    11. //成功的回调
    12. },
    13. fail:function(){},
    14. timeout:3000
    15. })
    16. }
    17. (function(global){
    18. function jsonp(options){
    19. let url = options.url
    20. let cbName = options.jsonp //设置传递给后台的函数名
    21. let data = options.data
    22. let success = options.success
    23. let fail = options.fail
    24. let timeout = options.timeout || 3000
    25. let head = document.getElementsByTagName('head')[0]
    26. let script = document.createElement('script')
    27. data['callback'] = cbName
    28. let str = ""
    29. for(let i in data){
    30. str+= i+'='+ data[i] +"&"
    31. }
    32. console.log(str)
    33. str = str.slice(0,str.length-1)
    34. script.src = url.indexOf('?')>-1?url +'&'+ str:url+'?'+str
    35. script.onerror = function(){
    36. fail && fail({msg:'出错啦'})
    37. }
    38. global[cbName] = function(res){
    39. head.removeChild(script)
    40. global[cbName] = null
    41. //todo 清除超时定时器
    42. success &&success(res)
    43. script.timer&&clearTimeout(script.timer)
    44. }
    45. head.appendChild(script)
    46. script.timer = setTimeout(()=>{
    47. head.removeChild(script)
    48. global[cbName] = null
    49. fail && fail({msg:'超市啦'})
    50. },timeout)
    51. }
    52. })(this)

    十一、浏览器事件模型

    1、stopProgation :阻止事件传播(不论是捕获还是冒泡)。
    2、阻止默认行为:e.preventDefault
    封装一个没有兼容问题的addEvent
    事件委托+ 伪数组(获取的node节点数组)
    3、浏览器原生请求
    xhr.upload.onprogress //原生的上传的进度
    xhr.timeout = 3000

fentch:默认不带cookie
错误不会reject出去
不支持设置超时
可以中止fentch,signal:new Abort
封装一个ajax工具函数,处理对于异步函数的超时处理

  1. function test(asyncFn,options){}
  2. function asyncFn(){
  3. return new Promise((resolve,reject)=>{});
  4. }
  5. const asyncFnWithTimeout = test(asyncFn)
  6. asyncFnWithTimeout()

代码补充

十二、js对象及原型链

1.1对象创建方式

对象字面量创建
工厂模式 :无法正确识别对象的类型(实例.constructor)
构造函数:缺点:浪费内存,每个实例中都会存一份属性和对象
原型:将方法挂在到原型上面去,内存中只会存一份
静态属性:绑定再构造函数的属性 Fn.xx

1.2new 关键字做了什么

1、创建一个继承至构造函数的新对象
2、把这个新对象的proto指向构造函数的prototype
3、把this指向该对象
4、返回该对象
4.1如果构造函数没有返回值或者返回值是基础类型,那就返回this
4.2如果构造函数的返回值是引用类型,就返回该对象

  1. function Player(name){
  2. this.name = name
  3. }
  4. function objectFactory(){
  5. let o = new Object();
  6. let FunctionConstructor = [].shift.call(arguments);
  7. o.__proto__ = FunctionConstructor.prototype
  8. let resObj = FunctionConstructor.apply(o,arguments)
  9. return typeof resObj ==='object'?resObj:o
  10. }
  11. let p1 = objectFactory(Player,'rt')

1.3原型链

一条由proto和prototype组成的链条,表述对象关系的链条

  1. function DogFactory(){
  2. }
  3. console.log(DogFactory.__proto__ === Function.prototype)
  4. console.log(DogFactory.prototype.__proto__ === Object.prototype)
  5. //demo 二
  6. let O = {
  7. a:1,b:2
  8. }
  9. let b = {}
  10. b.__proto__ = O
  • 最好不要直接操作proto,因为1、是隐藏属性,不是标准定义的,2、会造成性能问题:v8源码中对原型的实现做了很多的优化,比如通过隐藏类优化了很多原有的对象结构,所以直接修改proto会直接破坏已经优化的结构,造成性能问题
  • 可以使用Object.getPrototypeOf ,Object.setPrototypeOf ,或则Object.create ,或者 es6的extends

1.4继承

1.4.1原型继承

  1. function Parent(){
  2. this.name = 'parentName'
  3. }
  4. Parent.prototype.getName = function(){
  5. console.log(this.name)
  6. }
  7. function Child(){}
  8. Child.prototype = new Parent();
  9. //因为Child.prototype被覆盖,constructor指向不正确了,指向了Parent
  10. Child.prototype.constructor = Child;
  11. //问题1、如果属性是引用类型,那么new 出来多个实例会共享,修改 了一个就会修改全部
  12. 2new Child的时候,无法传参

1.4.2 构造函数继承

  1. function Parent(name,age){
  2. this.name = name;
  3. this.age = age
  4. this.say = function(){
  5. consle.log('eat------'+ name)
  6. }
  7. }
  8. function Child(id){
  9. this.id = id
  10. Parent.apply(this,Array.from(arguments).slice(1))
  11. }
  12. //问题,实例之间各自创建了方法,造成了内存浪费

1.4.3 组合继承

  1. function Parent(name,age){
  2. this.name = name;
  3. this.age = age
  4. }
  5. Parent.prototype.say = function(){
  6. console.log('say')
  7. }
  8. function Child(id){
  9. this.id = id
  10. Parent.apply(this,Array.from(arguments).slice(1))
  11. //等价于
  12. //Parent.apply(this,[].slice.call(arguments,1))
  13. //Parent.apply(this,Array.prototype.slice.call(arguments,1))
  14. //Parent.apply(this,[...arguments].slice(1))
  15. }
  16. Child.prototype = new Parent()
  17. child.prototype.constructor = Child
  18. //缺点 :调用了2次Parent构造函数

1.4.4 寄生组合继承

  1. function Parent(name,age){
  2. this.name = name;
  3. this.age = age
  4. }
  5. Parent.prototype.say = function(){
  6. console.log('say')
  7. }
  8. function Child(id){
  9. this.id = id
  10. Parent.apply(this,Array.from(arguments).slice(1))
  11. }
  12. //Child.prototype = new Parent()
  13. Child.prototype = Object.create(Parent.prototype)
  14. //等价于
  15. let MidFunction = function (){}
  16. MidFuntion.prototype = Parent.prototype
  17. Child.prototype = new MidFunction()
  18. child.prototype.constructor = Child

1.4.5 class 继承

  1. class Parent{
  2. constructor(name,age){
  3. this.name = name;
  4. this.age = age;
  5. }
  6. say(){
  7. console.log('say-----')
  8. }
  9. }
  10. class Child extends Parent{
  11. constructor() {
  12. super();
  13. }
  14. }

十三、babel

  1. //插件实现

十四、this

函数调用方式和this

默认绑定(函数直接调用)

  • let 定义的变量不会被绑定到全局,它有自己的块级作用域
  • var 定义的变量在非严格模式下会被绑定到window上
  • setTimeout/setInterval 调用的代码运行在与所在函数完全分离的执行环境上,默认指向window
    1. let obj = {
    2. fun:function(){}
    3. }
    4. setTimeout(obj.fun,2000)
    5. //此时this 指向的是window

    隐式绑定(对象属性访问调用)

    隐式绑定的this指向的是调用堆栈的上一级(.前面一个) ```javascript //案列
  1. <a name="o1Eoh"></a>
  2. #### 显示绑定(call,apply,bind)
  3. ```javascript
  4. funciton fn(){
  5. console.log(this)
  6. }
  7. fn.bind(1).bind(2)
  8. //可以绑定基本类型?因为有装箱操作----Number(1)
  9. //多次绑定,只看第一次bind

实现bind
  1. function myBind(){
  2. }

//箭头函数没有this,所以没有比较优先级的意义

  1. function foo(){
  2. console.log(this.a)
  3. }
  4. var a = 2;
  5. (function (){
  6. "use strict"
  7. foo()
  8. })()
  9. //输出2
  10. var name = "the window"
  11. var object = {
  12. name:'my object',
  13. getName:function(){
  14. return this.name
  15. }
  16. }
  17. object.getName() //my object
  18. (object.getName)() //my object 前面这个()不会没有作用
  19. (object.getName = object.getName)() //发生了赋值,丢失了this指向,the window
  20. (object.getName,object.getName)() //the window
  21. 发生了赋值,运算操作等,都会丢失this指向
  22. //骚气的题,完全搞不懂
  23. function a(x){
  24. this.x = x;
  25. return this
  26. }
  27. var x = a(5)
  28. var y = a(6)
  29. console.log(x.x)
  30. console.log(y.x)

作用域和闭包

储存空间和上下文

了解一下 v8中 数组的实现
递归和尾调用优化(返回一个)

构造函数的scope只有全局

闭包:函数拥有对其词法作用域的访问,哪怕是在当前作用域之外执行

逃逸分析,gc

闭包的数据存在堆内存

node 中的gc ,浏览器中的gc ,新生代,老生代

1、什么是原型和原型链?
答:(js高程中的概念):每一个js对象(除null)在创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中继承属性
每个js对象(除了null),都具有一个属性,叫做proto,这个属性会指向该对象的原型;
原型链:一条由proto 和prototype组成的表示对象关系的链条
850375-20190708153139577-2105652554.png
为什么要设计原型?
节省内存;当我们使用构造函数实例化一个实例,里面公用的方法或者属性可以放在构造函数的prototype上(组合继承),这样多个实例的方法\属性指向的是同一个(共享一个内存地址)
2、谈谈js中的this?
答:指代函数当前的运行环境,由于函数可以在不同的运行环境中执行,所以需要有一种机制,能在函数内部获得当前的运行环境。this的设计目的,就是在函数体内部,指代函数当前的运行环境;
是js中的动态作用域机制;
js中 this的绑定分为4中,1是默认绑定,就是函数的直接调用。2是隐式绑定,比如对象属性访问调用。3是显示绑定,比如call,apply,bind 4是构造函数绑定,通过new 出来的实例,this始终指向这个实例
3、结合作用域谈一下闭包?扩展垃圾回收机制
变量环境(函数声明和var变量声明提升,在编译阶段就被存在的内存中),词法环境(let 和const声明的变量在词法环境中)
调用栈:管理函数调用关系的一种数据结构,来管理执行上下文
全局执行上下文
函数执行上下文
作用域就是变量与函数的可访问范围,