数组扁平化

  1. //32手写
  2. //01.数组扁平化
  3. let arr = [1, [2, [3, [4, 5]]], 6];
  4. // => [1, 2, 3, 4, 5, 6]
  5. let res;
  6. res = arr.flat(Infinity);
  7. res = JSON.stringify(arr).replace(/\[|\]/g, "").split(",");
  8. res = JSON.parse("[" + JSON.stringify(arr).replace(/\[|\]/g, "") + "]");
  9. const flatten = arr => {
  10. return arr.reduce((pre, cur) => {
  11. return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  12. }, []);
  13. };
  14. res = flatten(res);
  15. const result = [];
  16. const f2 = arr => {
  17. for (let i = 0; i < arr.length; i++) {
  18. Array.isArray(arr[i]) ? f2(arr[i]) : result.push(arr[i]);
  19. }
  20. return result;
  21. };
  22. res = f2(arr);
  23. console.log(res);

数组去重

  1. // 02.数组去重
  2. arr = [1, 1, "1", 17, true, true, false, false, "true", "a", {}, {}];
  3. // => [1, '1', 17, true, false, 'true', 'a', {}, {}]
  4. res = [...new Set(arr)];
  5. const unique = arr => {
  6. return arr.filter((item, idx) => {
  7. return arr.indexOf(item) === idx;
  8. });
  9. };
  10. res = unique(arr);
  11. console.log(res);

类数组转化为数组

  1. // 03.类数组转化为数组
  2. // const div = document.querySelector('div')
  3. const div = [1, 1]; //一般是 arguments 或是 dom 操作方法返回的数据
  4. res = Array.from(div);
  5. res = Array.prototype.slice.call(div);
  6. res = [...div];
  7. res = Array.prototype.concat.call(div);
  8. console.log(res);

Array.prototype.filter()

  1. //04.Array.prototype.filter()
  2. Array.prototype.fakeFilter = function(callback,thisArg){
  3. if(this == undefined)throw new TypeError('this is null or undefined');
  4. if(typeof callback !== "function")throw new TypeError(callback+'is not a function');
  5. const res = [];
  6. const O = Object(this)
  7. const len = O.length >>> 0
  8. for(let i = 0;i<len;i++){
  9. if(i in O){
  10. if(callback.call(thisArg,O[i],i,O)){
  11. res.push(O[i])
  12. }
  13. }
  14. }
  15. return res
  16. }
  17. const arr =[-1,-1,11,1,1]
  18. console.log(arr.fakeFilter((item)=>{
  19. return item > 0
  20. }))

Array.prototype.map()

  1. //05.Array.prototype.map()
  2. Array.prototype.myMap = function(callback,thisArg){
  3. if(this == undefined)throw new TypeError('this is null or undefined');
  4. if(typeof callback !== "function")throw new TypeError(callback+'is not a function');
  5. const res = []
  6. const O = Object(this)
  7. const len = O.length>>>0
  8. for(let i =0;i<len;i++){
  9. if( i in O){
  10. res[i] = callback.call(thisArg,O[i],i,this)
  11. }
  12. }
  13. return res
  14. }
  15. function arrDouble(i){
  16. return i*2
  17. }
  18. res = arr.myMap(arrDouble)
  19. console.log(res);

Array.prototype.forEach()

  1. //06.Array.prototype.forEach()
  2. Array.prototype.myForEach = function(callback,thisArg){
  3. if(this == undefined)throw new TypeError('this is null or undefined');
  4. if(typeof callback !== "function")throw new TypeError(callback+'is not a function');
  5. const O = Object(this)
  6. const len = O.length>>>0
  7. let k =0
  8. while(k < len){
  9. if(k in O){
  10. callback.call(thisArg,O[k],k,this)
  11. }
  12. k++
  13. }
  14. }
  15. function clog(i){
  16. console.log(i)
  17. }
  18. res.myForEach(clog)
  19. // console.log(res)

关于其中的in

Array.prototype.reduce()

  1. //07.Array.prototype.reduce()
  2. Array.prototype.myReduce = function(callback,initValue){
  3. if (this == undefined) throw new TypeError("this is null or undefined");
  4. if (typeof callback !== "function")
  5. throw new TypeError(callback + "is not a function");
  6. const O = Object(this)
  7. const len = O.length >>> 0
  8. let accumulator = initValue
  9. let k = 0
  10. if(accumulator === undefined){
  11. while(k < len && !(k in O))k++//找到数组的第一个有效值
  12. if(k >= len)throw new TypeError('Reduce of empty array with no initial Value')
  13. accumulator = O[k++]
  14. }
  15. while(k<len){
  16. if(k in O)accumulator = callback.call(undefined,accumulator,O[k],k,O)
  17. k++
  18. }
  19. return accumulator
  20. }
  21. const arr = [1,1,1,,4,5]
  22. function add(a,b){
  23. return a+b
  24. }
  25. let res = arr.myReduce(add)
  26. // console.log(res)

Function.prototype.call

  1. //08.Function.prototype.call
  2. Function.prototype.myCall = function (context = window, ...args) {
  3. if (typeof this !== 'function') throw new TypeError('Type Error')
  4. const fn = Symbol('fn')
  5. context[fn] = this
  6. const res = context[fn](...args)
  7. delete context[fn]
  8. return res
  9. }
  10. const a = {
  11. name: 'aaa',
  12. sayName: function () {
  13. console.log(this.name)
  14. },
  15. }
  16. const b = {
  17. name: 'bbb',
  18. }
  19. // a.sayName.myCall(b) //bbb
  20. function Parent(name) {
  21. this.name = name
  22. this.showName = function () {
  23. console.log(this.name)
  24. }
  25. }
  26. function Child(name) {
  27. Parent.myCall(this, name)
  28. }
  29. const c = new Child('ccc')
  30. // c.showName() //ccc

Function.prototype.apply()

  1. //09.Function.prototype.apply()
  2. //与call不同的是 函数接收的参数是数组
  3. Function.prototype.myApply = function (context = window, args) {
  4. if (typeof this !== 'function') throw new TypeError('Type Error')
  5. const fn = Symbol('fn')
  6. context[fn] = this
  7. if (args) {
  8. const res = context[fn](...args)
  9. } else {
  10. const res = context[fn]()
  11. }
  12. delete context[fn]
  13. return res
  14. }
  15. // a.sayName.myApply(b) //bbb
  16. function C2(name) {
  17. Parent.myApply(this, [name])
  18. }
  19. const c2 = new C2('c222')
  20. // c2.showName() //c222

Function.prototype.bind()

  1. //10.Function.prototype.bind
  2. Function.prototype.myBind = function (context, ...args) {
  3. if (typeof this !== 'function') throw new TypeError('Type Error')
  4. const fnToBind = this
  5. const fuBound = function () {
  6. return fnToBind.apply(context, [...args, ...arguments])
  7. }
  8. return fuBound
  9. }
  10. const boy = {
  11. age: 18,
  12. getAge() {
  13. return this.age
  14. },
  15. }
  16. const getX = boy.getAge.myBind(boy)
  17. // console.log(getX())//18
  18. function add(a, b) {
  19. return a + b
  20. }
  21. // console.log(add.myBind(null, 10)(12, 1)) //22

深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。 假设B复制了A,修改A的时候,看B是否发生变化:

如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)

如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值) 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,

debounce

  1. //11 debounce
  2. const myDebounce = (fn, time) => {
  3. let timeout = null
  4. return function () {
  5. clearTimeout(timeout)
  6. timeout = setTimeout(() => {
  7. fn.apply(this, arguments)
  8. }, time)
  9. }
  10. }

throttle

  1. //12. throttle
  2. const myThrottle = (fn, time) => {
  3. let flag = true
  4. return function () {
  5. if (!flag) return
  6. flag = false
  7. setTimeout(() => {
  8. fn.apply(this, arguments)
  9. flag = true
  10. }, time)
  11. }
  12. }

函数柯里化

  1. //13.函数柯里化
  2. //实现add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;
  3. //直接简单add版本
  4. function add2() {
  5. const _args = [...arguments]
  6. function inner() {
  7. _args.push(...arguments)
  8. return inner
  9. }
  10. inner.toString = function () {
  11. return _args.reduce((sum, cur) => sum + cur)
  12. }
  13. return inner
  14. }
  15. const resAdd = add2(1)(2)(3)(4)
  16. // console.log(resAdd == 10) //true
  17. // console.log(resAdd === 10) //false
  18. // //实际上得到的 resAdd 是一个函数
  19. //参数定长柯里化
  20. function myCurry(fn) {
  21. //获取原函数的参数长度
  22. const argLen = fn.length
  23. //保留预置参数
  24. const presetArgs = [].slice.call(arguments, 1) //去掉传入的第一个参数(fn)
  25. // console.log(presetArgs)
  26. //返回新函数
  27. return function () {
  28. // const restArgs = [].slice.call(arguments) //和下面的写法一样效果
  29. const restArgs = [...arguments]
  30. // console.log(restArgs)
  31. const allArgs = [...presetArgs, ...restArgs]
  32. // console.log(allArgs)
  33. if (allArgs.length >= argLen) {
  34. //如果参数够了,就执行原函数
  35. // console.log(this)
  36. // console.log(allArgs)
  37. // console.log(fn.apply(this, [1, 2, 3]))
  38. return fn.apply(null, allArgs)
  39. } else {
  40. //否则继续柯里化
  41. return myCurry.call(null, fn, ...allArgs)
  42. }
  43. }
  44. }
  45. function add(a, b, c) {
  46. return a + b + c
  47. }
  48. // 把函数传进去就可以了
  49. const addCurry = myCurry(add)
  50. // console.log(addCurry)
  51. // console.log(addCurry(1)(2)(3))
  52. // console.log(addCurry(1)(2, 3))
  53. // console.log(addCurry(1, 2, 4))
  54. // console.log(addCurry(1, 2)(3))
  55. //参数不定长柯里化
  56. function myCurry2(fn) {
  57. const preset = [].slice.call(arguments, 1)
  58. function curried() {
  59. const rest = [].slice.call(arguments)
  60. const all = [...preset, rest]
  61. //新函数执行后仍然返回一个结果函数。
  62. return myCurry2.call(null, fn, ...all)
  63. }
  64. //结果函数可以被Javascript引擎解析 ==> 重写toString
  65. curried.toString = function () {
  66. return fn.apply(null, preset)
  67. }
  68. return curried
  69. }
  70. function dynamicAdd() {
  71. return [...arguments].reduce((prev, curr) => {
  72. return prev + curr
  73. }, 0)
  74. }
  75. const addPro = myCurry2(dynamicAdd) //经 curry处理,得到一个新函数,这一点不变。
  76. console.log(typeof addPro)
  77. // addPro(1)(2)(3)(4) // 10
  78. // addPro(1, 2)(3, 4)(5, 6) // 21

模拟new操作

  1. //模拟new操作
  2. function myNew(someConstructor, ...args) {
  3. if (typeof someConstructor !== 'function') throw new TypeError('Type Error')
  4. //以 someConstructor 为原型创建一个对象
  5. const obj = Object.create(someConstructor.prototype)
  6. //执行构造函数并将this绑定到新创建的对象上
  7. const res = someConstructor.apply(obj, args)
  8. //判断构造函数执行返回的结果是否是引用数据类型
  9. //若是则返回构造函数返回的结果
  10. //若不是则返回创建的对象
  11. const isObj = typeof res === 'object' && res !== null
  12. const isFuc = typeof res === 'function'
  13. return isObj || isFuc ? res : obj
  14. }
  15. function Otest() {
  16. this.a = 'a'
  17. }
  18. const o1 = myNew(Otest)
  19. // console.log(o1.a) //a

instanceof

  1. //15. instanceof
  2. //左侧是对象,右侧是函数
  3. const myInstanceof = (left, right) => {
  4. //基本数据类型 false
  5. if (typeof left !== 'object' || left === null) return false
  6. let proto = Object.getPrototypeOf(left)
  7. while (true) {
  8. if (proto === null) return false
  9. if (proto === right.prototype) return true
  10. proto = Object.getPrototypeOf(proto)
  11. }
  12. }
  13. function grandpa() {}
  14. function father() {}
  15. father.prototype = new grandpa()
  16. function son() {}
  17. son.prototype = new father()
  18. let xiaoming = new son()
  19. // console.log(myInstanceof(xiaoming, son)) //true
  20. // console.log(myInstanceof(xiaoming, father)) //true
  21. // console.log(myInstanceof(xiaoming, grandpa)) //true


  1. //16. 原型继承——寄生组合继承
  2. function Parent() {
  3. this.name = 'parent'
  4. }
  5. function Child() {
  6. Parent.call(this)
  7. this.type = 'children'
  8. }
  9. Child.prototype = Object.create(Parent.prototype)
  10. Child.prototype.constructor = Child
  11. const c = new Child()
  12. // console.log(c.name) //parent
  13. // console.log(c.type) //children
  14. // console.log(c instanceof Parent) //true
  15. //17. Object.is
  16. /* 主要解决:
  17. 1. +0 === -0 //true
  18. 2. NaN === NaN //false
  19. */
  20. const myIs = (x, y) => {
  21. if (x === y) {
  22. return x !== 0 || y !== 0 || 1 / x === 1 / y
  23. } else {
  24. return x !== x && y !== y
  25. }
  26. }
  27. // console.log(myIs(+0,-0))//false
  28. // console.log(myIs(NaN,NaN))//true

Object.assign

还没写呢

深拷贝

  1. //19. 深拷贝
  2. const myCloneDeep = (target, hash = new WeakMap()) => {
  3. //对于传入参数进行处理
  4. if (typeof target !== 'object' || target === null)
  5. return target
  6. //哈希表中存在则直接返回
  7. if (hash.has(target)) return hash.get(target)
  8. const cloneTarget = Array.isArray(target) ? [] : {}
  9. hash.set(target, cloneTarget)
  10. //针对Symbol属性
  11. const symKeys = Object.getOwnPropertySymbols(target)
  12. if (symKeys.length) {
  13. symKeys.forEach((symKey) => {
  14. if (
  15. typeof target[symKey] === 'object' &&
  16. target[symKey] !== null
  17. ) {
  18. cloneTarget[symKey] = myCloneDeep(target[symKey])
  19. } else {
  20. cloneTarget[symKey] = target[symKey]
  21. }
  22. })
  23. }
  24. for (const i in target) {
  25. if (Object.prototype.hasOwnProperty.call(target, i)) {
  26. cloneTarget[i] =
  27. typeof target[i] === 'object' && target[i] !== null
  28. ? myCloneDeep(target[i], hash)
  29. : target[i]
  30. }
  31. }
  32. return cloneTarget
  33. }
  34. const obj = {
  35. a: 1,
  36. }
  37. const obj2 = myCloneDeep(obj)
  38. // console.log(obj2) //{a:1}
  39. // obj2.a = 2
  40. // console.log(obj) //{a:1}
  41. // console.log(obj2) //{a:2,b:2}

promise

Promise.all()会等待所有Promise完成,Promise.race()只会执行一个Promise。

  1. //20.promise
  2. // 模拟实现Promise
  3. // Promise利用三大手段解决回调地狱:
  4. // 1. 回调函数延迟绑定
  5. // 2. 返回值穿透
  6. // 3. 错误冒泡
  7. const PENDING = 'PENDING'
  8. const FULFILLED = 'FULFILLED'
  9. const REJECTED = 'REJECTED'
  10. class myPromise {
  11. constructor(exector) {
  12. //初始化状态
  13. this.status = PENDING
  14. //将成功、失败结果放在 this 上,便于then、catch 访问
  15. this.value = undefined
  16. this.reason = undefined
  17. //成功态回调函数队列
  18. this.onFulfilledCallbacks = []
  19. //失败态回调函数队列
  20. this.onRejectedCallback = []
  21. const resolve = (value) => {
  22. //只有进行中状态才能更改状态
  23. if (this.status === PENDING) {
  24. this.status = FULFILLED
  25. this.value = value
  26. //成功态函数依次执行
  27. this.onFulfilledCallbacks.forEach((fn) =>
  28. fn(this.value),
  29. )
  30. }
  31. }
  32. const reject = (reason) => {
  33. if (this.status === PENDING) {
  34. this.status = REJECTED
  35. this.reason = reason
  36. //失败态的函数依次执行
  37. this.onRejectedCallback.forEach((fn) =>
  38. fn(this.reason),
  39. )
  40. }
  41. }
  42. try {
  43. //立即执行executor
  44. //把内部的resolve和reject 传入executor,用户可调用resolve和reject
  45. exector(resolve, reject)
  46. } catch (e) {
  47. //exrcutor 执行出错,将错误内容reject抛出去
  48. reject(e)
  49. }
  50. }
  51. then(onFulfilled, onRejected) {
  52. onFulfilled =
  53. typeof onFulfilled === 'function'
  54. ? onFulfilled
  55. : (value) => value
  56. onRejected =
  57. typeof onRejected === 'function'
  58. ? onRejected
  59. : (reason) => {
  60. throw new Error(
  61. reason instanceof Error
  62. ? reason.message
  63. : reason,
  64. )
  65. }
  66. //保存this
  67. const self = this
  68. return new myPromise((resolve, reject) => {
  69. if (self.status === PENDING) {
  70. self.onFulfilledCallbacks.push(() => {
  71. //try捕获错误
  72. try {
  73. //模拟微任务
  74. setTimeout(() => {
  75. const result = onFulfilled(self.value)
  76. //分两种情况:
  77. //1.回调函数返回值是 Promise 执行then操作
  78. //2.如果不是Promise,调用新Promise的resolve函数
  79. result instanceof myPromise
  80. ? result.then(resolve, reject)
  81. : resolve(result)
  82. })
  83. } catch (e) {
  84. reject(e)
  85. }
  86. })
  87. self.onRejectedCallback.push(() => {
  88. try {
  89. setTimeout(() => {
  90. const result = onRejected(self.reason)
  91. result instanceof myPromise
  92. ? result.then(resolve, reject)
  93. : resolve(result)
  94. })
  95. } catch (e) {
  96. reject(e)
  97. }
  98. })
  99. } else if (self.status === FULFILLED) {
  100. try {
  101. setTimeout(() => {
  102. const result = onFulfilled(self.value)
  103. result instanceof myPromise
  104. ? result.then(resolve, reject)
  105. : resolve(result)
  106. })
  107. } catch (e) {
  108. reject(e)
  109. }
  110. } else if (self.status === REJECTED) {
  111. try {
  112. setTimeout(() => {
  113. const result = onRejected(self.reason)
  114. result instanceof myPromise
  115. ? result.then(resolve, reject)
  116. : resolve(result)
  117. })
  118. } catch (e) {
  119. reject(e)
  120. }
  121. }
  122. })
  123. }
  124. catch(onRejected) {
  125. return this.then(null, onRejected)
  126. }
  127. static resolve(value) {
  128. if (value instanceof myPromise) {
  129. //如果是Promise实例,则直接返回
  130. return value
  131. } else {
  132. //如果不是,则返回一个新的 Promise对象,状态为FULFILLED
  133. return new myPromise((resolve, reject) =>
  134. resolve(value),
  135. )
  136. }
  137. }
  138. static reject(reason) {
  139. return new Promise((resolve, reject) => {
  140. reject(reason)
  141. })
  142. }
  143. //Promise.all是支持链式调用的,本质上就是返回了一个Promise实例,通过resolve和reject来改变实例状态。
  144. static all(promiseArr) {
  145. return new myPromise((resolve, reject) => {
  146. const len = promiseArr.length
  147. const values = new Array(len)
  148. //记录已经成功执行的promise个数
  149. let count = 0
  150. for (let i = 0; i < len; i++) {
  151. //Promise.resolve()处理,确保每一个都是promise实例
  152. myPromise.resolve(promiseArr[i]).then(
  153. (val) => {
  154. values[i] = val
  155. count++
  156. //如果全部执行完,返回promise状态就可以改变
  157. if (count === len) resolve(values)
  158. },
  159. (err) => reject(err),
  160. )
  161. }
  162. })
  163. }
  164. //Promise.race(iterable) 方法返回一个 promise
  165. //一旦迭代器中的某个promise解决或拒绝
  166. //返回的 promise就会解决或拒绝。
  167. static race(promiseArr) {
  168. return new Promise((resolve, reject) => {
  169. promiseArr.forEach((p) => {
  170. Promise.resolve(p).then(
  171. (val) => resolve(val),
  172. (err) => reject(err),
  173. )
  174. })
  175. })
  176. }
  177. }

Promise并行限制

  1. //23. Promise并行限制
  2. class Scheduler {
  3. constructor() {
  4. this.queue = []
  5. //最大并行个数
  6. this.maxCount = 2
  7. this.runCount = 0
  8. }
  9. //往队列中插入Promise Creator函数
  10. add(promiseCreator) {
  11. this.queue.push(promiseCreator)
  12. }
  13. //每次从队列中取出Promise Creator并执行
  14. //此Promise执行完成之后
  15. //应该递归调用request函数来执行下一个Promise
  16. request() {
  17. if (
  18. !this.queue ||
  19. !this.queue.length ||
  20. this.runCount >= this.maxCount
  21. ) {
  22. return
  23. }
  24. this.runCount++
  25. this.queue
  26. .shift()()
  27. .then(() => {
  28. this.runCount--
  29. this.request()
  30. })
  31. }
  32. //启动函数,将队列中的两个Promise启动起来
  33. taskStart() {
  34. for (let i = 0; i < this.maxCount; i++) {
  35. this.request()
  36. }
  37. }
  38. }
  39. //test
  40. const timeout = (time) =>
  41. new Promise((resolve) => {
  42. setTimeout(resolve, time)
  43. })
  44. const scheduler = new Scheduler()
  45. const addTask = (time, order) => {
  46. scheduler.add(() =>
  47. timeout(time).then(() => console.log(order)),
  48. )
  49. }
  50. addTask(800, '1')
  51. addTask(1100, '2')
  52. addTask(600, '3')
  53. addTask(300, '4')
  54. scheduler.taskStart()

image.png

JSONP(JSON with Padding)

  1. //24. JSONP(JSON with Padding)
  2. //jsonp的核心原理是利用script标签没有同源限制的方式,可以发送跨域的get请求(只能发送get请求)。
  3. const jsonp = ({ url, params, callbackName }) => {
  4. //script标签中的src属性将请求参数和当前请求的回调函数名拼接在链接上。最终由服务端接收到请求之后拼接成前端可执行的字符串的形式返回。这个结果字符串最终会在前端的script标签中解析并执行。
  5. const generateUrl = () => {
  6. let dataSrc = ''
  7. for (let key in params) {
  8. if (
  9. Object.prototype.hasOwnProperty.call(params, key)
  10. ) {
  11. dataSrc += `${key}=${params[key]}&`
  12. }
  13. }
  14. dataSrc += `callback=${callbackName}`
  15. return `${url}?${dataSrc}`
  16. }
  17. return new Promise((resolve, reject) => {
  18. const scriptEle = document.createElement('script')
  19. scriptEle.src = generateUrl()
  20. document.body.appendChild(scriptEle)
  21. window[callbackName] = (data) => {
  22. resolve(data)
  23. document.removeChild(scriptEle)
  24. }
  25. })
  26. }
  27. /* btn.addEventListener('click', () => {
  28. jsonp({
  29. url: 'http://localhost:3000/xxx',
  30. params: {
  31. name: 'xx',
  32. age: 18,
  33. },
  34. callback: 'show',
  35. }).then((data) => {
  36. console.log(data)
  37. })
  38. }) */

AJAX

  1. //25.AJAX
  2. // xhr 具有一个 open 方法,这个方法的作用类似于初始化,并不会发起真正的请求
  3. // open 方法具有 5 个参数,但是常用的是前 3 个
  4. // method: 请求方式 —— get / post
  5. // url:请求的地址
  6. // async:是否异步请求,默认为 true(异步)
  7. //xhr.open(method, url, async)
  8. // send 方法发送请求,并接受一个可选参数
  9. // 当请求方式为 post 时,可以将请求体的参数传入
  10. // 当请求方式为 get 时,可以不传或传入 null
  11. // 不管是 get 还是 post,参数都需要通过 encodeURIComponent 编码后拼接
  12. //xhr.send(data)
  13. //注意:XMLHttpRequest不是node内置的,需要安装
  14. //npm install xmlhttprequest
  15. // const XMLHttpRequest =
  16. // require('xmlhttprequest').XMLHttpRequest
  17. //或者安装quokka browser插件jsdom-quokka-plugin可以在vscode中调试
  18. const ajax = function (options) {
  19. const url = options.url
  20. const method = options.method.toLowerCase() || 'get'
  21. const async = options.async != false //默认为true
  22. const data = options.data
  23. //兼容IE
  24. const xhr = XMLHttpRequest
  25. ? new XMLHttpRequest()
  26. : new ActiveXObject('Mscrosoft.XMLHttp')
  27. if (options.timeout && options.timeout > 0) {
  28. xhr.timeout = options.timeout
  29. }
  30. return new Promise((resolve, reject) => {
  31. //处理超时
  32. xhr.onTimeout = () => reject && reject('请求超时')
  33. xhr.onreadystatechange = () => {
  34. if (xhr.readyState !== 4) return
  35. // HTTP 状态在 200-300 之间表示请求成功
  36. // HTTP 状态为 304 表示请求内容未发生改变,可直接从缓存中读取
  37. if (
  38. (xhr.status >= 200 && xhr.status < 300) ||
  39. xhr.status === 304
  40. ) {
  41. resolve && resolve(xhr.responseText)
  42. } else {
  43. reject && reject(new Error(xhr.responseText))
  44. }
  45. }
  46. xhr.onerror = (err) => reject && reject(err)
  47. let paramArr = []
  48. let encodeData
  49. if (data instanceof Object) {
  50. for (let key in data) {
  51. //通过encodeURIComponent编码后再拼接参数
  52. paramArr.push(
  53. `${encodeURIComponent(key)}=${encodeURIComponent(
  54. data[key],
  55. )}`,
  56. )
  57. }
  58. encodeData = paramArr.join('&')
  59. }
  60. //看要不要处理url
  61. if (method === 'get') {
  62. //检测url中是否存在?以及其下标
  63. const index = url.indexOf('?')
  64. if (index === -1) url += '?'
  65. else if (index !== url.length - 1) url += '&'
  66. //拼接
  67. url += encodeData
  68. }
  69. //初始化请求
  70. xhr.open(method, url, async)
  71. if (method === 'get') xhr.send(null)
  72. else {
  73. //post方法,设置请求头
  74. xhr.setRequestHeader(
  75. 'Content-Type',
  76. 'application/x-www-form-urlencoded;charset=UTF-8',
  77. )
  78. xhr.send(encodeData)
  79. }
  80. })
  81. }
  82. //test
  83. ajax({
  84. url: 'http://xxx',
  85. method: 'get',
  86. async: true,
  87. timeout: 1000,
  88. data: {
  89. k1: 111,
  90. k2: 211,
  91. },
  92. }).then(
  93. (res) => console.log('请求成功: ' + res),
  94. (err) => console.log('请求失败: ' + err),
  95. )

event

  • on(eventName, func): 监听 eventName事件, 事件触发的时候调用 func函数
  • emit(eventName, arg1, arg2, arg3,arg4…) : 触发eventName 事件, 并且把参数 arg1, arg2, arg3,arg4…传给事件处理函数
  • off(eventName, func) : 停止监听某个事件

addListener是emitter.on(eventName, listener) 的别名。

  1. //26.event模块
  2. //events只对外暴露一个对象,就是EventEmitter,EventEmitter作用只有2个,分别是:事件的发射和事件的监听。
  3. class Event {
  4. constructor() {
  5. //储存事件的数据结构——字典
  6. this.cache = new Map()
  7. }
  8. //绑定
  9. on(type, func) {
  10. //出于查找方便和节省空间将同一类型事件放到一个模拟队列的数组中
  11. let fns = (this.cache[type] = this.cache[type] || [])
  12. if (fns.indexOf(func) === -1) {
  13. fns.push(func)
  14. }
  15. return this
  16. }
  17. //触发
  18. emit(type, data) {
  19. let fns = this.cache[type]
  20. if (Array.isArray(fns)) {
  21. fns.forEach((fn) => {
  22. fn(data)
  23. })
  24. }
  25. return this
  26. }
  27. //解绑
  28. off(type, func) {
  29. let fns = this.cache[type]
  30. if (Array.isArray(fns)) {
  31. if (func) {
  32. let index = fns.indexOf(func)
  33. if (index !== -1) {
  34. fns.splice(index, 1)
  35. }
  36. } else {
  37. //没指定就清空
  38. fns.length = 0
  39. }
  40. }
  41. return this
  42. }
  43. }
  44. //test
  45. const events = new Event()
  46. events.on('test', (val) => {
  47. console.log(val)
  48. })
  49. events.on('test2', (val) => {
  50. console.log('@2' + val)
  51. })
  52. events.emit('test', 'runnnnn')
  53. events.emit('test2', 'runnnnn')
  54. events.off('test')
  55. events.emit('test', 'none')
  56. events.emit('test2', 'runnnnn')

image.png

图片懒加载

  1. //27. 图片懒加载
  2. //先设置一个临时的 data-src 属性进行src的占位
  3. const lazyLoad = () => {
  4. const imgs = document.querySelectorAll('img')
  5. const len = imgs.length
  6. //窗口高度
  7. const viewH = document.documentElement.clientHeight
  8. //滚动条高度
  9. const scrollH =
  10. //这两者有个特点:同时只有一个值生效,另一个始终为0
  11. document.documentElement.scrollTop +
  12. document.body.scrollTop
  13. for (let i = 0; i < len; i++) {
  14. const offsetH = imgs[i].offsetTop
  15. if (offsetH < viewH + scrollH) {
  16. const src = imgs[i].dataset.src
  17. imgs[i].src = src
  18. }
  19. }
  20. }
  21. //使用
  22. lazyLoad() //首屏加载
  23. window.addEventListener('scroll', lazyLoad)
  24. //还可以配合节流一起使用
  25. //28. 滚动加载
  26. window.addEventListener('scroll', () => {
  27. const { clientHeight, scrollTop, scrollHeight } =
  28. document.documentElement
  29. //检测到滚动页面接近页面底部
  30. if (clientHeight + scrollTop >= scrollHeight + 50) {
  31. //加载后面的部分窗口
  32. }
  33. })

渲染大数据

  1. //29.较大的数据量需要渲染时
  2. //合理使用createDocumentFragment和requestAnimationFrame,将操作切分为一小段一小段执行。
  3. setTimeout(() => {
  4. //插入十万条数据
  5. const total = 100000
  6. //一次插入的数据
  7. const once = 20
  8. //插入数据需要的次数
  9. const loopCount = Math.ceil(total / once)
  10. let countOfRender = 0
  11. const ul = document.querySelector('ul')
  12. //添加数据
  13. const add = () => {
  14. //在虚拟节点对象上操作
  15. const fragment = document.createDocumentFragment()
  16. for (let i = 0; i < once; i++) {
  17. const li = document.createElement('li')
  18. li.innerText = Math.floor(Math.random * total)
  19. fragment.appendChild(li)
  20. }
  21. //插入的实际操作
  22. ul.appendChild(fragment)
  23. countOfRender++
  24. loop()
  25. }
  26. //刷新
  27. const loop = () => {
  28. if (countOfRender < loopCount) {
  29. //window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
  30. window.requestAnimationFrame(add)
  31. }
  32. }
  33. loop()
  34. }, 0)

获取当前页面html元素种类

  1. //30 当前页面html元素种类
  2. const printSpeciesOfHtml = () => {
  3. return [
  4. ...new Set(),
  5. [...document.querySelectorAll('*')].map(
  6. (el) => el.tagName,
  7. ),
  8. ].length
  9. }

将VirtualDom转换为真实DOM

  1. //31.将VirtualDom 转化为真实DOM结构
  2. // vnode结构:
  3. // {
  4. // tag,
  5. // attrs,
  6. // children,
  7. // }
  8. //Virtual DOM => DOM
  9. const render = (vnode, container) => {
  10. container.appendChild(_render(vnode))
  11. }
  12. const _render = (vnode) => {
  13. //如果是数字类型就转化为字符串
  14. if (typeof vnode === 'number ') {
  15. vnode = String(vnode)
  16. }
  17. //字符串类型则直接作为文本节点
  18. if (typeof vnode == 'string') {
  19. return document.createTextNode(vnode)
  20. }
  21. //else就是
  22. //普通DOM
  23. const dom = document.createElement(vnode.tag)
  24. if (vnode.attrs) {
  25. //遍历属性
  26. Object.keys(vnode.attrs).forEach((key) => {
  27. const value = vnode.attrs[key]
  28. dom.setAttribute(key, value)
  29. })
  30. }
  31. //子数组进行递归操作
  32. vnode.children.forEach((child) => render(child, dom))
  33. return dom
  34. }

字符串解析

  1. //32.字符串解析问题
  2. const a = {
  3. z: 123,
  4. q: '123',
  5. }
  6. //实现函数使得将str字符串中的{}内的变量替换,如果属性不存在保持原样(比如{a.d})
  7. //类似于模版字符串,但有一点出入,实际上原理大差不差
  8. const parseStr = (str, obj) => {
  9. let res = ''
  10. //标志位,标志前面是否有{
  11. let flag = false
  12. let start
  13. for (let i = 0; i < str.length; i++) {
  14. if (str[i] === '{') {
  15. flag = true
  16. start = i + 1
  17. continue
  18. }
  19. if (!flag) res += str[i]
  20. else {
  21. if (str[i] === '}') {
  22. flag = false
  23. res += match(str.slice(start, i), obj)
  24. }
  25. }
  26. }
  27. return res
  28. }
  29. //对象匹配操作
  30. const match = (str, obj) => {
  31. const keys = str.split('.').slice(1)
  32. let index = 0
  33. let o = obj
  34. while (index < keys.length) {
  35. const key = keys[index]
  36. if (!o[jey]) return `{${str}}`
  37. else o = o[key]
  38. index++
  39. }
  40. return o
  41. }