ajax

  1. function ajax(url) {
  2. const p = new Promise((resolve, reject) => {
  3. const xhr = new XMLHttpRequest()
  4. xhr.open('GET', url, true)
  5. xhr.onreadystatechange = function() {
  6. if (xhr.readyState === 4) {
  7. if (xhr.status === 200) {
  8. resolve(JSON.parse(xhr.responseText))
  9. } else if (xhr.status === 404) {
  10. reject(new Error('404 not found'))
  11. }
  12. }
  13. }
  14. xhr.send(null)
  15. })
  16. return p
  17. }
  18. const url = '/a.json'
  19. ajax(url)
  20. .then(res => console.log(res))
  21. .catch(err => console.log(err))

ajax 的串行、并行、并发限制

  • 串行:一个异步请求完了之后再进行下一个请求
  • 并行:多个异步请求同时进行
  • 并发限制:每个时刻并发执行的异步请求数量是固定的

    串行

    ```javascript const createPromise = (time, id) => () => new Promise(resolve => setTimeout(() => {
    1. console.log("promise", id);
    2. resolve();
    }, time) );

let promiseArr = [ createPromise(3000, 1), createPromise(2000, 2), createPromise(1000, 3) ]

// 方法一 function execute() { if (promiseArr.length === 0) return const fn = promiseArr.shift() fn().then(() => { execute() }) }

execute()

// 方法二 !(async function () { for (let fn of promiseArr) { await fn() } })()

// 方法三 const [p1, p2, p3] = promiseArr

p1().then(() => { return p2() }).then(() => { return p3() }).then(() => { console.log(‘end’) })

  1. <a name="3lmD6"></a>
  2. ### 并行
  3. ```javascript
  4. const createPromise = (time, id) => () =>
  5. new Promise(resolve =>
  6. setTimeout(() => {
  7. console.log("promise", id);
  8. resolve();
  9. }, time)
  10. );
  11. let promiseArr = [
  12. createPromise(3000, 1),
  13. createPromise(2000, 2),
  14. createPromise(1000, 3)
  15. ]
  16. Promise.all(promiseArr.map(fn => fn())).then(() => {
  17. console.log('end')
  18. })

并发限制

整体采用递归调用来实现:最初发送的请求数量上限为允许的最大值,并且这些请求中的每一个都应该在完成时继续递归发送,通过传入的索引来确定了urls里面具体是那个URL,保证最后输出的顺序不会乱,而是依次输出。

  1. function multiRequest(urls = [], maxNum) {
  2. // 请求总数量
  3. const len = urls.length;
  4. // 根据请求数量创建一个数组来保存请求的结果
  5. const result = new Array(len).fill(false);
  6. // 当前完成的数量
  7. let count = 0;
  8. return new Promise((resolve, reject) => {
  9. // 请求maxNum个
  10. while (count < maxNum) {
  11. next();
  12. }
  13. function next() {
  14. let current = count++;
  15. // 处理边界条件
  16. if (current >= len) {
  17. // 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回
  18. !result.includes(false) && resolve(result);
  19. return;
  20. }
  21. const url = urls[current];
  22. console.log(`开始 ${current}`, new Date().toLocaleString());
  23. fetch(url)
  24. .then((res) => {
  25. // 保存请求结果
  26. result[current] = res;
  27. console.log(`完成 ${current}`, new Date().toLocaleString());
  28. // 请求没有全部完成, 就递归
  29. if (current < len) {
  30. next();
  31. }
  32. })
  33. .catch((err) => {
  34. console.log(`结束 ${current}`, new Date().toLocaleString());
  35. result[current] = err;
  36. // 请求没有全部完成, 就递归
  37. if (current < len) {
  38. next();
  39. }
  40. });
  41. }
  42. });
  43. }

事件绑定

  1. function bindEvent(elem, type, selector, fn) {
  2. if (fn == null) {
  3. fn = selector
  4. selector = null
  5. }
  6. elem.addEventListener(type, event => {
  7. const target = event.target
  8. if (selector) {
  9. // 代理绑定
  10. if (target.matches(selector)) {
  11. fn.call(target, event)
  12. }
  13. } else {
  14. // 普通绑定
  15. fn.call(target, event)
  16. }
  17. })
  18. }
  19. // 普通绑定
  20. const btn1 = document.getElementById('btn1')
  21. bindEvent(btn1, 'click', event => {
  22. console.log(event.target)
  23. event.preventDefault()
  24. alert(this.innerComments)
  25. })
  26. // 代理绑定
  27. const div3 = document.getElementById('div3')
  28. bindEvent(div3, 'click', 'a', event => {
  29. event.preventDefault()
  30. alert(this.innerComments)
  31. })

Call、apply、bind

call

  1. Function.prototype.myCall = function (context = window, ...args) {
  2. context.fn = this
  3. const result = context.fn(...args)
  4. delete context.fn
  5. return result
  6. }

apply

  1. Function.prototype.myApply = function (context = window, args) {
  2. context.fn = this
  3. const result = !args ? context.fn() : context.fn(...args)
  4. delete context.fn
  5. return result
  6. }

bind

  1. Function.prototype.myBind = function (context, ...args) {
  2. const self = this
  3. return function () {
  4. return self.apply(context, args)
  5. }
  6. }
  7. function fn1(a, b, c) {
  8. console.log('this', this)
  9. console.log(a, b, c)
  10. return 'this is fn1'
  11. }
  12. const fn2 = fn1.myBind({x: 100}, 10, 20, 30)
  13. const res = fn2()
  14. console.log(res)

深拷贝

  1. /**
  2. * 深拷贝
  3. */
  4. const obj1 = {
  5. age: 20,
  6. name: 'xxx',
  7. address: {
  8. city: 'beijing'
  9. },
  10. arr: ['a', 'b', 'c']
  11. }
  12. const obj2 = deepClone(obj1)
  13. obj2.address.city = 'shanghai'
  14. obj2.arr[0] = 'a1'
  15. console.log(obj1.address.city)
  16. console.log(obj1.arr[0])
  17. /**
  18. * 深拷贝
  19. * @param {Object} obj 要拷贝的对象
  20. */
  21. function deepClone(obj = {}) {
  22. if (typeof obj !== 'object' || obj == null) {
  23. // obj 是 null ,或者不是对象和数组,直接返回
  24. return obj
  25. }
  26. // 初始化返回结果
  27. let result
  28. if (obj instanceof Array) {
  29. result = []
  30. } else {
  31. result = {}
  32. }
  33. for (let key in obj) {
  34. // 保证 key 不是原型的属性
  35. if (obj.hasOwnProperty(key)) {
  36. // 递归调用!!!
  37. result[key] = deepClone(obj[key])
  38. }
  39. }
  40. // 返回结果
  41. return result
  42. }

全相等 isEqual

  1. const obj1 = {
  2. a: 100,
  3. b: {
  4. x: 100,
  5. y: 200,
  6. }
  7. }
  8. const obj2 = {
  9. a: 100,
  10. b: {
  11. x: 100,
  12. y: 200
  13. },
  14. }
  15. // 判断是否是对象或数组
  16. function isObject(obj) {
  17. return typeof obj === 'object' && obj !== null
  18. }
  19. // 全相等
  20. function isEqual(obj1, obj2) {
  21. if (!isObject(obj1) || !isObject(obj2)) return obj1 === obj2
  22. if (obj1 === obj2) return true
  23. // 两个都是对象或数组,而且不相等
  24. // 1. 先取出 obj1 和 obj2 的 keys,比较个数
  25. const obj1Keys = Object.keys(obj1)
  26. const obj2Keys = Object.keys(obj2)
  27. if (obj1Keys.length !== obj2Keys.length) {
  28. return false
  29. }
  30. // 2. 以 obj1 为基准,和 obj2 依次递归比较
  31. return obj1Keys.every(key => isEqual(obj1[key], obj2[key]))
  32. }
  33. console.log(isEqual(obj1, obj2))

jquery

  1. class jQuery {
  2. constructor(selector) {
  3. const result = document.querySelectorAll(selector)
  4. const length = result.length
  5. for (let i = 0; i < length; i++) {
  6. this[i] = result[i]
  7. }
  8. this.length = length
  9. this.selector = selector
  10. }
  11. get(index) {
  12. return this[index]
  13. }
  14. each(fn) {
  15. for (let i = 0; i < this.length; i++) {
  16. const elem = this[i]
  17. fn(elem)
  18. }
  19. }
  20. on(type, fn) {
  21. return this.each(elem => {
  22. elem.addEventListener(type, fn, false)
  23. })
  24. }
  25. // 扩展很多 DOM API
  26. }
  27. // 插件
  28. jQuery.prototype.dialog = function (info) {
  29. alert(info)
  30. }
  31. // “造轮子”
  32. class myJQuery extends jQuery {
  33. constructor(selector) {
  34. super(selector)
  35. }
  36. // 扩展自己的方法
  37. addClass(className) {
  38. }
  39. style(data) {
  40. }
  41. }
  42. // const $p = new jQuery('p')
  43. // $p.get(1)
  44. // $p.each((elem) => console.log(elem.nodeName))
  45. // $p.on('click', () => alert('clicked'))

promise.all

  1. function promiseAll(promises) {
  2. return new Promise((resolve, reject) => {
  3. if (!Array.isArray(promises)) {
  4. throw new Error('promises must be array')
  5. }
  6. let result = []
  7. let count = 0
  8. promises.forEach((promise, index) => {
  9. promise.then((res) => {
  10. result[index] = res
  11. count++
  12. count === promises.length && resolve(result)
  13. }, (err) => {
  14. reject(err)
  15. })
  16. })
  17. })
  18. }
  19. // 测试用例
  20. let p1 = Promise.resolve(1),
  21. p2 = Promise.resolve(2),
  22. p3 = Promise.resolve(3);
  23. promiseAll([p1, p2, p3]).then(values => {
  24. console.log(values)
  25. }, (err)=>{
  26. console.log(err, 'err')
  27. })

promise

链式调用还需优化

  1. const STATUS_PENDING = 'pending'
  2. const STATUS_FULFILLED = 'fulfilled'
  3. const STATUS_REJECTED = 'rejected'
  4. class myPromise {
  5. constructor(executor) {
  6. this.status = STATUS_PENDING
  7. this.value = ''
  8. this.reason = ''
  9. // 成功存放的数组
  10. this.onResolvedCallbacks = [];
  11. // 失败存放法数组
  12. this.onRejectedCallbacks = [];
  13. let resolve = value => {
  14. if (this.status === STATUS_PENDING) {
  15. this.status = STATUS_FULFILLED
  16. this.value = value
  17. // pending->fulfilled 按照成功清单执行
  18. this.onResolvedCallbacks.forEach(fn => fn())
  19. }
  20. }
  21. let reject = reason => {
  22. if (this.status === STATUS_PENDING) {
  23. this.status = STATUS_REJECTED
  24. this.reason = reason
  25. // pending->rejected 按照异常清单执行
  26. this.onRejectedCallbacks.forEach(fn => fn());
  27. }
  28. }
  29. try {
  30. executor(resolve, reject);
  31. } catch (err) {
  32. reject(err);
  33. }
  34. }
  35. then(onFulfilled = () => {}, onRejected = () => {}) {
  36. if (this.status === STATUS_FULFILLED) {
  37. onFulfilled(this.value)
  38. }
  39. if (this.status === STATUS_REJECTED) {
  40. onRejected(this.reason)
  41. }
  42. // 忙碌状态,先记录老板吩咐的内容
  43. if (this.status === STATUS_PENDING) {
  44. // onFulfilled传入到成功数组
  45. this.onResolvedCallbacks.push(() => onFulfilled(this.value))
  46. // onRejected传入到失败数组
  47. this.onRejectedCallbacks.push(() => onRejected(this.reason))
  48. }
  49. }
  50. catch(fn){
  51. return this.then(null,fn)
  52. }
  53. }
  54. new myPromise((resolve, reject) => {
  55. console.log('老板曰: 一秒做完手上的事来一下我办公室,做不完滚蛋')
  56. setTimeout(() => {
  57. if (false) { // 臣妾做不到啊
  58. resolve('做完了手上的事,去老板办公室')
  59. } else {
  60. reject('做不完,滚蛋')
  61. }
  62. }, 1000)
  63. }).then(res => {
  64. console.log(`1s 后:${res}`)
  65. }).catch(error => {
  66. console.log(`1s 后:${error}`)
  67. })

防抖 debounce

  1. const input1 = document.getElementById('input1')
  2. // 防抖
  3. function debounce(fn, delay = 500) {
  4. // timer 是闭包中的
  5. let timer = null
  6. return function () {
  7. if (timer) {
  8. clearTimeout(timer)
  9. }
  10. timer = setTimeout(() => {
  11. fn.apply(this, arguments)
  12. timer = null
  13. }, delay)
  14. }
  15. }
  16. input1.addEventListener('keyup', debounce(function (e) {
  17. console.log(e.target)
  18. console.log(input1.value)
  19. }, 600))

节流 throttle

  1. const div1 = document.getElementById('div1')
  2. // 节流
  3. function throttle(fn, delay = 100) {
  4. let timer = null
  5. return function () {
  6. if (timer) {
  7. return
  8. }
  9. timer = setTimeout(() => {
  10. fn.apply(this, arguments)
  11. timer = null
  12. }, delay)
  13. }
  14. }
  15. div1.addEventListener('drag', throttle(function (e) {
  16. console.log(e.offsetX, e.offsetY)
  17. }))

数组 flat

  1. function flat(arr) {
  2. // 验证 arr 中,是否还有深层数组
  3. const isDeep = arr.some(v => v instanceof Array)
  4. if (!isDeep) return arr
  5. const res = Array.prototype.concat.apply([], arr)
  6. return flat(res)
  7. }
  8. const arr = [1, 2, [3, 4, [10, 20, [100, 200]]], 5]
  9. console.log(flat(arr))

数组去重

  1. // 传统方式
  2. function unique(arr) {
  3. const res = []
  4. arr.forEach(item => {
  5. if (res.indexOf(item) === -1) {
  6. res.push(item)
  7. }
  8. })
  9. return res
  10. }
  11. // 使用 Set
  12. function unique(arr) {
  13. return [...new Set(arr)]
  14. }
  15. console.log(unique([1, 2, 2, 3, 4, 4]))

柯里化实现 add(1)(2)(3)

需要实现一个工具专门生成柯里化函数。主要思路是要判断当前传入函数的参数个数 (args.length) 是否大于等于原函数所需参数个数 (fn.length) ,如果是,则执行当前函数;如果是小于,则返回一个函数

  1. // 简易版
  2. const add = x => y => z => x + y + z;
  3. console.log(add(1)(2)(3));
  4. // 加强版,需要实现
  5. // add(1, 2, 3);
  6. // add(1, 2)(3);
  7. // add(1)(2, 3);
  8. const curry = (fn, ...args) =>
  9. // 函数的参数个数可以直接通过函数数的.length属性来访问
  10. args.length >= fn.length // 这个判断很关键!!!
  11. // 传入的参数大于等于原始函数fn的参数个数,则直接执行该函数
  12. ? fn(...args)
  13. /**
  14. * 传入的参数小于原始函数fn的参数个数时
  15. * 则继续对当前函数进行柯里化,返回一个接受所有参数(当前参数和剩余参数) 的函数
  16. */
  17. : (..._args) => curry(fn, ...args, ..._args);
  18. function add1(x, y, z) {
  19. return x + y + z;
  20. }
  21. const add = curry(add1);
  22. console.log(add(1, 2, 3));
  23. console.log(add(1)(2)(3));
  24. console.log(add(1, 2)(3));
  25. console.log(add(1)(2, 3));

大数相加

  1. let a = "9007199254740991";
  2. let b = "1234567899999999999";
  3. function add(a ,b){
  4. //取两个数字的最大长度
  5. let maxLength = Math.max(a.length, b.length);
  6. //用0去补齐长度
  7. a = a.padStart(maxLength , 0);//"0009007199254740991"
  8. b = b.padStart(maxLength , 0);//"1234567899999999999"
  9. //定义加法过程中需要用到的变量
  10. let t = 0;
  11. let f = 0; //"进位"
  12. let sum = "";
  13. for(let i=maxLength-1 ; i>=0 ; i--){
  14. t = parseInt(a[i]) + parseInt(b[i]) + f;
  15. f = Math.floor(t/10);
  16. sum = t%10 + sum;
  17. }
  18. if(f == 1){
  19. sum = "1" + sum;
  20. }
  21. return sum;
  22. }
  23. add(a,b)

千位分隔符

方法一:

  • 将数字转换为字符数组(需考虑小数点)
  • 倒序字符数组,进行遍历,每三位加一个逗号
  • 遍历完成后,再将字符数组倒序回来,再合并成字符串即可 ```javascript function numFormat(num) { num = num.toString().split(‘.’) // 分隔小数点 const arr = num[0].split(‘’).reverse() // 转换为字符数组,并倒序 const res = []

    for (let i = 0; i < arr.length; i += 1) {

    1. if (i % 3 === 0 && i !== 0) {
    2. res.push(',')
    3. }
    4. res.push(arr[i])

    } res.reverse() // 再次倒序恢复正常顺序 if (num[1]) {

    1. return `${res.join('')}.${num[1]}`

    } else {

    1. return res.join('')

    } }

const a = 1234567894532; const b = 673439.4542; console.log(numFormat(a)); // “1,234,567,894,532” console.log(numFormat(b)); // “673,439.4542”

  1. 方法二:
  2. - 使用JS自带的函数 [toLocaleString](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString)
  3. ```javascript
  4. const a = 1234567894532;
  5. const b = 673439.4542;
  6. console.log(a.toLocaleString()); // "1,234,567,894,532"
  7. console.log(b.toLocaleString()); // "673,439.454" (小数部分四舍五入了)

方法三:

  • 使用 正则表达式replace 函数
  • replace 语法:str.replace(regexp|substr, newSubStr|function) ```javascript function numFormat(num){ const res = num.toString().replace(/\d+/, function(n) { // 先提取整数部分
    1. return n.replace(/(\d)(?=(\d{3})+$)/g, function($1) {
    2. return $1+",";
    3. });
    }) return res; }

const a = 1234567894532; const b = 673439.4542; console.log(numFormat(a)); // “1,234,567,894,532” console.log(numFormat(b)); // “673,439.4542” ```