前端常见手写 JS 题(巩固你的前端基础)

深碍√TFBOYSˉ_ 2022-10-08 02:26 235阅读 0赞

在这里插入图片描述

数组扁平化

  1. const arr = [1, [2, [3, [4, 5]]], 6]; // => [1, 2, 3, 4, 5, 6]
  2. function flatten(arr) {
  3. let result = [];
  4. for (let i = 0; i < arr.length; i++) {
  5. if (Array.isArray(arr[i])) {
  6. result = result.concat(flatten(arr[i]));
  7. } else {
  8. result = result.concat(arr[i]);
  9. }
  10. }
  11. return result;
  12. }

flat()

  1. const res1 = arr.flat(Infinity)

正则

  1. const res2 = JSON.stringify(arr).replace(/[\[|\]]/g, '').split(',').map(e => parseInt(e))

正则改良版本

  1. const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/[\[|\]]/g, '') + ']')

reduce

  1. const flatten = arr => {
  2. return arr.reduce((pre, cur) => {
  3. return pre.concat(Array.isArray(cur) ? flatten(cur) : cur)
  4. }, [])
  5. }
  6. const res4 = flatten(arr)

函数递归

  1. const res5 = []
  2. const fn = arr => {
  3. for (let i = 0; i < arr.length; i++) {
  4. if (Array.isArray(arr[i])) {
  5. fn(arr[i])
  6. } else {
  7. res5.push(arr[i])
  8. }
  9. }
  10. }
  11. fn(arr)

数组去重

  1. const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', { }, { }];

Set

  1. const res1 = Array.from(new Set(arr));

两层for循环+splice

  1. const unique1 = arr => {
  2. let len = arr.length;
  3. for (let i = 0; i < len; i++) {
  4. for (let j = i + 1; j < len; j++) {
  5. if (arr[i] === arr[j]) {
  6. arr.splice(j, 1);
  7. // 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能
  8. len--;
  9. j--;
  10. }
  11. }
  12. }
  13. return arr;
  14. }

indexOf

  1. const unique2 = arr => {
  2. const res = [];
  3. for (let i = 0; i < arr.length; i++) {
  4. if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  5. }
  6. return res;
  7. }

include

  1. const unique3 = arr => {
  2. const res = [];
  3. for (let i = 0; i < arr.length; i++) {
  4. if (!res.includes(arr[i])) res.push(arr[i]);
  5. }
  6. return res;
  7. }

filter

  1. const unique4 = arr => {
  2. return arr.filter((item, index) => {
  3. return arr.indexOf(item) === index;
  4. });
  5. }

Map

  1. const unique5 = arr => {
  2. const map = new Map();
  3. const res = [];
  4. for (let i = 0; i < arr.length; i++) {
  5. if (!map.has(arr[i])) {
  6. map.set(arr[i], true)
  7. res.push(arr[i]);
  8. }
  9. }
  10. return res;
  11. }

初始化二维数组

  1. function initializeArr(row, column) {
  2. let arr = [];
  3. for (let i = 0; i < row; i++) {
  4. arr[i] = [];
  5. for (let j = 0; j < column; j++) {
  6. arr[i][j] = 0;
  7. }
  8. }
  9. return arr
  10. }
  11. const arr = new Array(row).fill(0).map(v => new Array(column).fill(0));

类数组转化为数组

Array.from

  1. Array.from(document.querySelectorAll('div'))

Array.prototype.slice.call()

  1. Array.prototype.slice.call(document.querySelectorAll('div'))

扩展运算符

  1. [...document.querySelectorAll('div')]

concat

  1. Array.prototype.concat.apply([], document.querySelectorAll('div'));

求数组最大值

  1. Math.max(...arr)
  2. Math.max.apply(null, arr)
  3. Math.max.call(null, ...arr)
  4. let arr1 = arr.reduce(function (prev, next) {
  5. return Math.max(prev, next)
  6. })

String.prototype.indexOf()

  1. String.prototype.myIndexOf = function (str) {
  2. let sourceArr = this.split('');
  3. let num = -1;
  4. for (let i in sourceArr) {
  5. if (sourceArr[i] === str.slice(0, 1)) {
  6. if (str === this.slice(i, Number(i) + str.length)) {
  7. num = i
  8. }
  9. }
  10. }
  11. return num
  12. }

Array.prototype.filter()

  1. Array.prototype.filter = function(callbackFn, thisArg) {
  2. console.log('this is my filter function')
  3. // 处理数组类型异常
  4. if (this === null || this === undefined) {
  5. throw new TypeError("Cannot read property 'filter' of null or undefined")
  6. }
  7. // 处理回调类型异常
  8. if (Object.prototype.toString.call(callbackFn) !== '[object Function]') {
  9. throw new TypeError(callbackFn + 'is not a function')
  10. }
  11. let O = Object(this)
  12. let len = O.length >>> 0
  13. let resLen = 0
  14. let res = []
  15. for (let i = 0; i < len; i++) {
  16. if (i in O) {
  17. let element = O[i]
  18. // 是否满足回调函数,如果满足,则放入数组中
  19. if (callbackFn.call(thisArg, O[i], i, O)) {
  20. res[resLen++] = element
  21. }
  22. }
  23. }
  24. return res
  25. }
  26. const tempArr = [10, 23, 443, 23, 8, 6, 34, 67]
  27. let res = tempArr.filter((e) => {
  28. if (e > 100) {
  29. return e
  30. }
  31. })
  32. console.log(res)

Array.prototype.map()

  1. Array.prototype.map = function(callbackFn, thisArg) {
  2. // 处理数组类型异常
  3. if (this === null || this === undefined) {
  4. throw new TypeError("Cannot read property 'map' of null or undefined");
  5. }
  6. // 处理回调类型异常
  7. if (Object.prototype.toString.call(callbackFn) != "[object Function]") {
  8. throw new TypeError(callbackFn + ' is not a function')
  9. }
  10. let O = Object(this);
  11. let T = thisArg;
  12. let len = O.length >>> 0;
  13. let A = new Array(len);
  14. for(let k = 0; k < len; k++) {
  15. // 如果用 hasOwnProperty 是有问题的,它只能找私有属性
  16. if (k in O) {
  17. let kValue = O[k];
  18. // 依次传入this, 当前项,当前索引,整个数组
  19. let mappedValue = callbackFn.call(T, kValue, k, O);
  20. A[k] = mappedValue;
  21. }
  22. }
  23. return A;
  24. }
  25. const tempArr = [10, 23, 443, 23, 8, 6, 34, 67]
  26. let res = tempArr.map((e) => e * 2)
  27. console.log(res)

处理数组类型异常,先转换为对象,获取参数的长度,声明数组。对生成的 O 对象进行遍历,获取每一个 key 对应的值 O[key],传入到当前的回调函数中,进行计算,将值重新放入对应 key 的位置。

Array.prototype.forEach()

  1. Array.prototype.forEach = function(callback, thisArg) {
  2. console.log('this is my filter')
  3. if (this == undefined) {
  4. throw new TypeError('this is null or not undefined')
  5. }
  6. if (typeof callback !== 'function') {
  7. throw new TypeError(callback + 'is not a function')
  8. }
  9. const o = Object(this)
  10. const len = o.length >>> 0
  11. for (let i = 0; i < len; i++) {
  12. if (i in o) {
  13. callback.call(thisArg, o[i], i)
  14. }
  15. }
  16. }

Array.prototype.reduce()

  1. Array.prototype.reduce = function(callbackfn, initialValue) {
  2. // 处理数组类型异常
  3. if (this === null || this === undefined) {
  4. throw new TypeError("Cannot read property 'reduce' of null or undefined");
  5. }
  6. // 处理回调类型异常
  7. if (Object.prototype.toString.call(callbackfn) != "[object Function]") {
  8. throw new TypeError(callbackfn + ' is not a function')
  9. }
  10. let O = Object(this);
  11. // console.log(this)
  12. // O = (4) [1, 2, 3, 4]
  13. let len = O.length >>> 0;
  14. let k = 0;
  15. let accumulator = initialValue;
  16. if (accumulator === undefined) {
  17. for(; k < len ; k++) {
  18. // 查找原型链
  19. if (k in O) {
  20. accumulator = O[k];
  21. k++;
  22. break;
  23. }
  24. }
  25. }
  26. // 表示数组全为空
  27. if(k === len && accumulator === undefined)
  28. throw new Error('Each element of the array is empty');
  29. for(;k < len; k++) {
  30. if (k in O) {
  31. // 注意,核心!
  32. accumulator = callbackfn.call(undefined, accumulator, O[k], k, O);
  33. }
  34. }
  35. return accumulator;
  36. }
  37. let tempArr = [1, 2, 3, 4]
  38. let res = tempArr.reduce((a, b) => a - b)
  39. console.log(res)

处理数组类型异常,处理回调类型异常,获取传入参数列表的长度,如果初始值为 undefined,则取参数列表的第一个数做为初始值,将前一个数字和后一个数字传入回调函数中,进行计算。把两个计算出来的值,重新赋值给前一个。

Array.prototype.push()

  1. Array.prototype.push = function(...items) {
  2. let O = Object(this);
  3. let len = this.length >>> 0;
  4. let argCount = items.length >>> 0;
  5. // 2 ** 53 - 1 为JS能表示的最大正整数
  6. if (len + argCount > 2 ** 53 - 1) {
  7. throw new TypeError("The number of array is over the max value restricted!")
  8. }
  9. for(let i = 0; i < argCount; i++) {
  10. O[len + i] = items[i];
  11. }
  12. let newLength = len + argCount;
  13. O.length = newLength;
  14. return newLength;
  15. }

Array.prototype.pop()

  1. Array.prototype.pop = function () {
  2. console.log('this is my pop function')
  3. let O = Object(this)
  4. let len = this.length >>> 0
  5. if (len === 0) {
  6. O.length = 0
  7. return undefined
  8. }
  9. len--
  10. let value = O[len]
  11. delete O[len]
  12. O.length = len
  13. return value
  14. }

Function.prototype.call()

  1. Function.prototype.call = function(context) {
  2. console.log('this is my call function')
  3. if (typeof this !== 'function') {
  4. throw new Error('Error')
  5. }
  6. context = context || window
  7. context.fn = this
  8. const args = Array.from(arguments).slice(1)
  9. const result = context.fn(...args)
  10. delete context.fn
  11. return result
  12. }
  13. function print(age) {
  14. console.log(this.name + ' ' +age)
  15. }
  16. let obj = {
  17. name: '奶油桃子'
  18. }
  19. print.call(obj, 1, 2, 3)

Function.prototype.apply()

  1. Function.prototype.myApply = function(context) {
  2. console.log('this is my apply')
  3. if (typeof this !== 'function') {
  4. throw new TypeError('Error')
  5. }
  6. context = context || window
  7. context.fn = this
  8. let result;
  9. if (arguments[1]) {
  10. result = context.fn(...arguments[1])
  11. } else {
  12. result = context.fn()
  13. }
  14. delete context.fn
  15. return result
  16. }
  17. function print(age, age2, age3) {
  18. console.log(`${ this.name} ${ age} ${ age2} ${ age3}`)
  19. }
  20. let obj = {
  21. name: '奶油桃子'
  22. }
  23. print.myApply(obj, [1, 2, 3])

Function.prototype.bind()

  1. Function.prototype.myBind = function(context) {
  2. console.log('this is my bind')
  3. if (typeof this !== 'function') {
  4. throw new Error('Error')
  5. }
  6. let self = this
  7. let args = [].slice.call(arguments, 1)
  8. let bound = function() {
  9. let boundArgs = [].slice.call(arguments)
  10. return self.apply(context, args.concat(boundArgs))
  11. }
  12. return bound
  13. }
  14. function print(age) {
  15. console.log(`${ this.name} ${ age}`)
  16. }
  17. let obj = {
  18. name: '奶油桃子'
  19. }
  20. let bound = print.myBind(obj, 1)
  21. bound()

debounce(防抖)

  1. function debounce(fn, delay) {
  2. let timer = null
  3. return function (...args) {
  4. let context = this
  5. if (timer) clearTimeout(timer)
  6. timer = setTimeout(function () {
  7. fn.apply(context, args)
  8. }, delay)
  9. }
  10. }

throttle(节流)

  1. function throttle(fn, delay) {
  2. let flag = true,
  3. timer = null
  4. return function (...args) {
  5. let context = this
  6. if (!flag) return
  7. flag = false
  8. clearTimeout(timer)
  9. timer = setTimeout(function () {
  10. fn.apply(context, args)
  11. flag = true
  12. }, delay)
  13. }
  14. }

函数柯里化

  1. function add() {
  2. // 第一次执行时,定义一个数组专门用来存储所有的参数
  3. let _args = Array.prototype.slice.call(arguments)
  4. // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
  5. let _adder = function () {
  6. _args.push(...arguments)
  7. return _adder
  8. };
  9. // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
  10. _adder.toString = function () {
  11. return _args.reduce(function (a, b) {
  12. return a + b
  13. });
  14. }
  15. return _adder
  16. }
  17. alert(add(1)(2)(3)) // 6
  18. add(1, 2, 3)(4) // 10
  19. add(1)(2)(3)(4)(5) // 15
  20. add(2, 6)(1) // 9

compose

  1. function compose() {
  2. let fns = [].slice.call(arguments)
  3. return function(initalArg) {
  4. let res = initalArg
  5. for (let i = fns.length - 1; i > -1; i--) {
  6. res = fns[i](res)
  7. }
  8. return res
  9. }
  10. }
  11. let greet = function (name) { return 'hi:' + name }
  12. let exclaim = function (statement) { return statement.toUpperCase() + '!' }
  13. let transform = function (str) { return str.replace(/[dD]/, 'DDDDD') }
  14. let result = compose(greet, exclaim, transform)
  15. console.log(result('dot'))

实现 new

  1. function create(Con, ...args) {
  2. let obj = { }
  3. obj.__proto__ = Con.prototype
  4. let result = Con.apply(obj, args)
  5. return result instanceof Object ? result : obj
  6. }

instanceof

  1. function Foo() { }
  2. var f1 = new Foo()
  3. // 根据 instanceof 原理
  4. function myInstanceof(left, right) {
  5. let proto = Object.getPrototypeOf(left)
  6. while(true) {
  7. if (proto === null) return false
  8. if (proto === right.prototype) return true
  9. proto = Object.getPrototypeOf(proto)
  10. }
  11. }
  12. console.log(myInstanceof(f1, Foo))

ES5 继承

  1. function Father() { }
  2. function Son() {
  3. Father.call(this)
  4. }
  5. Son.prototype = Object.create(Father.prototype)
  6. Son.prototype.constructor = Son

Object.is

bject.is解决的主要是这两个问题:

  1. +0 === -0 // true
  2. NaN === NaN // false
  3. const is = (x, y) => {
  4. if (x === y) {
  5. return x !== 0 || y !== 0 || 1/x === 1/y
  6. } else {
  7. return x !== x && y !== y
  8. }
  9. }

模拟Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

  1. function create(proto) {
  2. // 新声明一个函数
  3. function F() { }
  4. // 将函数的原型指向obj
  5. F.prototype = proto
  6. // 返回这个函数的实例化对象
  7. return new F()
  8. }

Object.assign

Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象(请注意这个操作是浅拷贝)

  1. Object.assign2 = function (target, ...source) {
  2. if (target == null) {
  3. throw new TypeError('Cannot convert undefined or null to object')
  4. }
  5. let ret = Object(target)
  6. source.forEach(function (obj) {
  7. if (obj != null) {
  8. for (let key in obj) {
  9. if (obj.hasOwnProperty(key)) {
  10. ret[key] = obj[key]
  11. }
  12. }
  13. }
  14. })
  15. return ret
  16. }
  17. let a = {
  18. b11: 1
  19. }
  20. let b = Object.assign2({ }, a)
  21. a.b11 = 111111
  22. console.log(a, b)

深拷贝

  1. function isObject(x) {
  2. return Object.prototype.toString.call(x) === '[object Object]';
  3. }
  4. function cloneDeep(obj, hash = new Map()) {
  5. // 非对象返回自身
  6. if (!isObject(obj)) return obj;
  7. // 循环检测 —— 如果已存在,直接返回该值
  8. if (hash.has(obj)) return hash.get(obj);
  9. // 判断数组和是对象
  10. var target = Array.isArray(obj) ? [] : { };
  11. // 每次都添加未有的对象
  12. hash.set(obj, target);
  13. // 开始循环遍历拷贝
  14. for (let key in obj) {
  15. if (obj.hasOwnProperty(key)) {
  16. if (isObject(obj[key])) {
  17. target[key] = cloneDeep(obj[key], hash); // 新增代码,传入哈希表
  18. } else {
  19. target[key] = obj[key];
  20. }
  21. }
  22. }
  23. return target;
  24. }

JSON.stringify()

  1. function stringfy(value) {
  2. // 获取数据类型
  3. let type = typeof value
  4. function getValues(value) {
  5. if (type === 'undefined' || type === 'symbol' || type === 'function') {
  6. return undefined
  7. }
  8. if (type === 'number' || type === 'boolean') {
  9. return `${ value}`
  10. }
  11. if (type === 'string') {
  12. return `"${ value}"`
  13. }
  14. }
  15. if (type === 'object') {
  16. if (!value) {
  17. return `${ value}`
  18. }
  19. if (value instanceof Date) {
  20. return `"${ new Date(value).toISOString()}"`
  21. }
  22. // 数据类型
  23. if (value instanceof Array) {
  24. return `[${ value.map(stringfy)}]`
  25. } else {
  26. // 对象类型
  27. return (
  28. '{' + Object.keys(value).map(key => {
  29. let result = stringfy(value[key])
  30. if (result === undefined) {
  31. return undefined
  32. }
  33. return `"${ key}":${ result}`
  34. }).filter(item => item !== undefined) + '}'
  35. )
  36. }
  37. }
  38. return getValues(value)
  39. }

JSON.parse()

  1. function parse(value) {
  2. return eval('(' + value + ')')
  3. }
  4. function parse(value) {
  5. return new Function('return' + value)()
  6. }

JSONP

  1. const jsonp = function (url, data) {
  2. return new Promise((resolve, reject) => {
  3. // 初始化url
  4. let dataString = url.indexOf('?') === -1 ? '?' : ''
  5. let callbackName = `jsonpCB_${ Date.now()}`
  6. url += `${ dataString}callback=${ callbackName}`
  7. if (data) {
  8. // 有请求参数,依次添加到url
  9. for (let k in data) {
  10. url += `${ k}=${ data[k]}`
  11. }
  12. }
  13. let jsNode = document.createElement('script')
  14. jsNode.src = url
  15. // 触发callback,触发后删除js标签和绑定在window上的callback
  16. window[callbackName] = result => {
  17. delete window[callbackName]
  18. document.body.removeChild(jsNode)
  19. if (result) {
  20. resolve(result)
  21. } else {
  22. reject('没有返回数据')
  23. }
  24. }
  25. // js加载异常的情况
  26. jsNode.addEventListener('error', () => {
  27. delete window[callbackName]
  28. document.body.removeChild(jsNode)
  29. reject('JavaScript资源加载失败')
  30. }, false)
  31. // 添加js节点到document上时,开始请求
  32. document.body.appendChild(jsNode)
  33. })
  34. }
  35. jsonp('http://139.224.66.67:3000/comment/music?id=186016&limit=1').then(result => {
  36. console.log(result)
  37. }).catch(err => {
  38. console.error(err)
  39. })

AJAX

  1. function ajax(method, url) {
  2. return new Promise((resolve, reject) => {
  3. const request = new XMLHttpRequest();
  4. request.open(method, url);
  5. request.onreadystatechange = () => {
  6. if (request.readyState === 4) {
  7. if (request.status === 200 || request.status === 304) {
  8. resolve(request.response);
  9. } else {
  10. reject(request);
  11. }
  12. }
  13. };
  14. request.send();
  15. });
  16. }
  17. ajax("get", "http://139.224.66.67:3000/comment/music?id=186016&limit=1").then(res => {
  18. console.log(res);
  19. });

字符串模板

  1. function render(template, data) {
  2. const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  3. if (reg.test(template)) { // 判断模板里是否有模板字符串
  4. const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
  5. template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
  6. return render(template, data); // 递归的渲染并返回渲染后的结构
  7. }
  8. return template; // 如果模板没有模板字符串直接返回
  9. }
  10. let template = '我是{ {name}},年龄{ {age}},性别{ {sex}}';
  11. let person = {
  12. name: '淘淘',
  13. age: 12
  14. }
  15. console.log(render(template, person))

解析 URL 参数为对象

  1. function parseParam(url) {
  2. const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
  3. const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
  4. let paramsObj = { };
  5. // 将 params 存到对象中
  6. paramsArr.forEach(param => {
  7. if (/=/.test(param)) { // 处理有 value 的参数
  8. let [key, val] = param.split('='); // 分割 key 和 value
  9. val = decodeURIComponent(val); // 解码
  10. val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
  11. if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
  12. paramsObj[key] = [].concat(paramsObj[key], val);
  13. } else { // 如果对象没有这个 key,创建 key 并设置值
  14. paramsObj[key] = val;
  15. }
  16. } else { // 处理没有 value 的参数
  17. paramsObj[param] = true;
  18. }
  19. })
  20. return paramsObj;
  21. }

用正则实现 trim()

  1. String.prototype.trim = function() {
  2. return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  3. }
  4. String.prototype.trim = function(){
  5. return this.replace(/^\s+|\s+$/g, '')
  6. }
  7. function trim(string){
  8. return string.replace(/^\s+|\s+$/g, '')
  9. }

手写观察者模式

  1. class Subject {
  2. constructor(name) {
  3. this.name = name
  4. this.observers = []
  5. this.state = ''
  6. }
  7. // 被观察者要提供一个接受观察者的方式
  8. attach(observer) {
  9. this.observers.push(observer)
  10. }
  11. // 改变被观察者的状态
  12. setState(newState) {
  13. this.state = newState
  14. this.observers.forEach(o => {
  15. o.update(newState)
  16. })
  17. }
  18. }
  19. class Observer {
  20. constructor(name) {
  21. this.name = name
  22. }
  23. update(newState) {
  24. console.log(`${ this.name} say: ${ newState}`)
  25. }
  26. }
  27. // 被观察者 灯
  28. let sub = new Subject('灯')
  29. let mm = new Observer('小明')
  30. let jj = new Observer('小健')
  31. // 订阅 观察者
  32. sub.attach(mm)
  33. sub.attach(jj)
  34. sub.setState('灯亮了来电了')

转化为驼峰命名

  1. let f = function(s) {
  2. return s.replace(/-\w/g, function(x) {
  3. return x.slice(1).toUpperCase()
  4. })
  5. }

实现千位分隔符

  1. function parseToMoney(num) {
  2. num = parseFloat(num.toFixed(3))
  3. let [integer, decimal] = String.prototype.split.call(num, '.')
  4. integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,')
  5. return integer + '.' + (decimal ? decimal : '')
  6. }

repeat

  1. function repeat(func, times, wait) {
  2. return function() {
  3. let args = arguments
  4. let handle = function(i) {
  5. setTimeout(() => {
  6. func.apply(null, args)
  7. }, wait * i)
  8. }
  9. for (let i = 0; i < times; i++) {
  10. handle(i)
  11. }
  12. }
  13. }
  14. const repeatFunc = repeat(console.log, 4, 1000)
  15. repeatFunc('hello world')

sleep

  1. function sleep(fn, time) {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve(fn)
  5. }, time)
  6. })
  7. }
  8. let saySomrthing = (name) => console.log(`hello, ${ name}`)
  9. async function autoPlay() {
  10. let demo = await sleep(saySomrthing('lily'), 1000)
  11. let demo1 = await sleep(saySomrthing('mike'), 2000)
  12. }
  13. autoPlay()

将VirtualDom转化为真实DOM结构

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

图片懒加载

可以给img标签统一自定义属性data-src='default.png',当检测到图片出现在窗口之后再补充src属性,此时才会进行图片资源加载。

  1. function lazyload() {
  2. const imgs = document.getElementsByTagName('img');
  3. const len = imgs.length;
  4. // 视口的高度
  5. const viewHeight = document.documentElement.clientHeight;
  6. // 滚动条高度
  7. const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
  8. for (let i = 0; i < len; i++) {
  9. const offsetHeight = imgs[i].offsetTop;
  10. if (offsetHeight < viewHeight + scrollHeight) {
  11. const src = imgs[i].dataset.src;
  12. imgs[i].src = src;
  13. }
  14. }
  15. }
  16. // 可以使用节流优化一下
  17. window.addEventListener('scroll', lazyload);

渲染几万条数据不卡住页面

渲染大数据时,合理使用createDocumentFragmentrequestAnimationFrame,将操作切分为一小段一小段执行。

  1. setTimeout(() => {
  2. // 插入十万条数据
  3. const total = 100000;
  4. // 一次插入的数据
  5. const once = 20;
  6. // 插入数据需要的次数
  7. const loopCount = Math.ceil(total / once);
  8. let countOfRender = 0;
  9. const ul = document.querySelector('ul');
  10. // 添加数据的方法
  11. function add() {
  12. const fragment = document.createDocumentFragment();
  13. for(let i = 0; i < once; i++) {
  14. const li = document.createElement('li');
  15. li.innerText = Math.floor(Math.random() * total);
  16. fragment.appendChild(li);
  17. }
  18. ul.appendChild(fragment);
  19. countOfRender += 1;
  20. loop();
  21. }
  22. function loop() {
  23. if(countOfRender < loopCount) {
  24. window.requestAnimationFrame(add);
  25. }
  26. }
  27. loop();
  28. }, 0)

EventEmitter 实现

  1. // 手写发布订阅模式 EventEmitter
  2. class EventEmitter {
  3. constructor() {
  4. this.events = { };
  5. }
  6. // 实现订阅
  7. on(type, callBack) {
  8. if (!this.events) this.events = Object.create(null);
  9. if (!this.events[type]) {
  10. this.events[type] = [callBack];
  11. } else {
  12. this.events[type].push(callBack);
  13. }
  14. }
  15. // 删除订阅
  16. off(type, callBack) {
  17. if (!this.events[type]) return;
  18. this.events[type] = this.events[type].filter(item => {
  19. return item !== callBack;
  20. });
  21. }
  22. // 只执行一次订阅事件
  23. once(type, callBack) {
  24. function fn() {
  25. callBack();
  26. this.off(type, fn);
  27. }
  28. this.on(type, fn);
  29. }
  30. // 触发事件
  31. emit(type, ...rest) {
  32. this.events[type] &&
  33. this.events[type].forEach(fn => fn.apply(this, rest));
  34. }
  35. }
  36. // 使用如下
  37. const event = new EventEmitter();
  38. const handle = (...rest) => {
  39. console.log(rest);
  40. };
  41. event.on("click", handle);
  42. event.emit("click", 1, 2, 3, 4);
  43. event.off("click", handle);
  44. event.emit("click", 1, 2);
  45. event.once("dbClick", () => {
  46. console.log(123456);
  47. });
  48. event.emit("dbClick");
  49. event.emit("dbClick");

事件委托

  1. ul.addEventListener('click', function (e) {
  2. if (e.target.tagName.toLowerCase() === 'span') {
  3. }
  4. })

格式化时间

  1. let format = (date) => {
  2. let fmt = 'yyyy-MM-dd hh:mm:ss'
  3. const o = {
  4. 'M+': date.getMonth() + 1, // 月份
  5. 'd+': date.getDate(), // 日
  6. 'h+': date.getHours(), // 小时
  7. 'm+': date.getMinutes(), // 分钟
  8. 's+': date.getSeconds(), // 秒
  9. }
  10. if (/(y+)/.test(fmt)) {
  11. fmt = fmt.replace(RegExp.$1, date.getFullYear())
  12. }
  13. for (let k in o) {
  14. if (new RegExp('(' + k + ')').test(fmt)) {
  15. fmt = fmt.replace(RegExp.$1, o[k].toString().length == 1 ? '0' + o[k] : o[k])
  16. }
  17. }
  18. return fmt
  19. }

实现一个同时允许任务数量最大为 n 的函数

  1. function limitRunTask(tasks, n) {
  2. return new Promise((resolve, reject) => {
  3. let index = 0, finish = 0, start = 0, res = []
  4. function run() {
  5. if (finish === tasks.length) {
  6. resolve(res)
  7. return;
  8. }
  9. while(start < n && index < tasks.length) {
  10. start++
  11. let cur = index
  12. tasks[index++]().then(v => {
  13. start--
  14. finish++
  15. res[cur] = v
  16. run()
  17. })
  18. }
  19. }
  20. run()
  21. })
  22. }

10进制转换

  1. function Conver(number, base = 2) {
  2. let rem, res = '', digits = '0123456789ABCDEF', stack = []
  3. while(number) {
  4. rem = number % base
  5. stack.push(rem)
  6. number = Math.floor(number / base)
  7. }
  8. while(stack.length) {
  9. res += digits[stack.pop()].toString()
  10. }
  11. return res
  12. }

是否为素数

  1. function isPrime(num) {
  2. let temp = Math.sqrt(num)
  3. for (let i = 2; i <= temp; i++) {
  4. if (num % i === 0) {
  5. return false
  6. }
  7. }
  8. return true
  9. }

大数运算

  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. }

生成随机数

  1. function getRandom(min, max) {
  2. return Math.floor(Math.random() * (max - min)) + min
  3. }

缓存函数memozition

  1. function memoize(func, hashFunc) {
  2. let memoize = function(key) {
  3. let cache = memoize.cache
  4. let address = '' + (hashFunc ? hashFunc.apply(this, arguments) : key)
  5. if (Object.getOwnPropertyNames(cache).indexOf(address) === -1) {
  6. cache[address] = func.apply(this, arguments)
  7. }
  8. return cache[address]
  9. }
  10. memoize.cache = { }
  11. return memoize
  12. }

数组随机排序

  1. let arr = [2, 3, 454, 34, 324, 32]
  2. arr.sort(function() {
  3. return Math.random() > 0.5 ? -1 : 1
  4. })

合并有序数组

  1. function mergeArray(arr1, arr2) {
  2. let ind1 = 0;
  3. let ind2 = 0;
  4. let arr = [];
  5. while (ind1 < arr1.length && ind2 < arr2.length) {
  6. if (arr1[ind1] <= arr2[ind2]) {
  7. arr.push(arr1.slice(ind1, ind1 + 1)[0]);
  8. ind1++;
  9. } else {
  10. arr.push(arr2.slice(ind2, ind2 + 1)[0]);
  11. ind2++;
  12. }
  13. }
  14. while (ind1 < arr1.length) {
  15. arr.push(arr1.slice(ind1, ind1 + 1)[0]);
  16. ind1++;
  17. }
  18. while (ind2 < arr2.length) {
  19. arr.push(arr2.slice(ind2, ind2 + 1)[0]);
  20. ind2++;
  21. }
  22. return arr;
  23. }

二分查找

二分查找的前提为:数组、有序。

  1. function binarySearch(target, arr, start, end) {
  2. if (start > end) { return -1 }
  3. let start = start || 0;
  4. let end = end || arr.length - 1;
  5. let mid = parseInt(start + (end - start) / 2);
  6. if (target == arr[mid]) {
  7. return mid;
  8. } else if (target > arr[mid]) {
  9. return binarySearch(target, arr, mid + 1, end);
  10. } else {
  11. return binarySearch(target, arr, start, mid - 1);
  12. }
  13. return -1;
  14. }
  15. function binarySearch(target, arr) {
  16. let start = 0;
  17. let end = arr.length - 1;
  18. while (start <= end) {
  19. let mid = parseInt(start + (end - start) / 2);
  20. if (target == arr[mid]) {
  21. return mid;
  22. } else if (target > arr[mid]) {
  23. start = mid + 1;
  24. } else {
  25. end = mid - 1;
  26. }
  27. }
  28. return -1;
  29. }

无序,使用快排分组,分好组再二分

  1. function binarySearch(target, arr) {
  2. while (arr.length > 0) {
  3. var left = [];
  4. var right = [];
  5. var pivot = arr[0];
  6. for (var i = 1; i < arr.length; i++) {
  7. var item = arr[i];
  8. item > pivot ? right.push(item) : left.push(item);
  9. }
  10. if (target == pivot) {
  11. return true;
  12. } else if (target > pivot) {
  13. arr = right;
  14. } else {
  15. arr = left;
  16. }
  17. }
  18. return false;
  19. }

已知excel表格的列命名方式(A.B.C…Z.AA.AB…AZ.AAA…AAZ)输出1-1000列的列名

  1. function execlNumChange(num) {
  2. if (num <= 0) {
  3. alert("excel表格貌似没有负数吧");
  4. return 0;
  5. }
  6. if (num > 26) {
  7. let newnum1 = parseInt(num / 26);
  8. let newnum2 = num % 26;
  9. let newnum1str, newnum2str = null;
  10. if (!newnum2) {
  11. newnum1str = execlNumChange(newnum1 - 1);
  12. newnum2str = "Z";
  13. } else {
  14. newnum1str = execlNumChange(newnum1);
  15. newnum2str = execlNumChange(newnum2);
  16. }
  17. return newnum1str + newnum2str;
  18. }
  19. let az = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  20. let arr = az.split("");
  21. for (let i = 0; i <= arr.length; i++) {
  22. if (num === i) {
  23. return arr[i - 1];
  24. }
  25. }
  26. }

版本号排序

  1. const versionSort = (arr) => {
  2. const p = 100000
  3. const maxLen = Math.max(
  4. ...arr.map((item) => item.split('.').length)
  5. );
  6. const reducer = (acc, value, index) => {
  7. return acc + (+value) * Math.pow(p, maxLen - index - 1);
  8. };
  9. const gen = (arr) => {
  10. return arr.split('.').reduce(reducer, 0)
  11. };
  12. return arr.sort((a, b) => gen(a) > gen(b) ? -1 : 1)
  13. };

获取 localStorage 的大小

  1. function getLocalStoreSize() {
  2. let sizeStore = 0;
  3. if (window.localStorage) {
  4. for (item in window.localStorage) {
  5. if (window.localStorage.hasOwnProperty(item)) {
  6. sizeStore += window.localStorage.getItem(item).length
  7. }
  8. }
  9. }
  10. return (sizeStore / 1024 / 1024).toFixed(2) + 'M'
  11. }

可拖拽的 DIV

  1. // 设置是否可拖拽
  2. let draggle = false;
  3. let position = null;
  4. let smallBox = document.getElementById('small');
  5. smallBox.addEventListener('mousedown', function(e) {
  6. draggle = true
  7. position = [e.clientX, e.clientY]
  8. });
  9. document.addEventListener('mousemove', function(e) {
  10. if (draggle === false) return null
  11. const x = e.clientX
  12. const y = e.clientY
  13. const deltaX = x - position[0]
  14. const deltaY = y - position[1]
  15. const left = parseInt(smallBox.style.left || e.clientX)
  16. const top = parseInt(smallBox.style.top || e.clientY)
  17. smallBox.style.left = left + deltaX + 'px'
  18. smallBox.style.top = top + deltaY + 'px'
  19. position = [x, y]
  20. });
  21. document.addEventListener('mouseup', function(e) {
  22. draggle = false
  23. });

简易版 Promise

  1. // 未添加异步处理等其他边界情况
  2. // ①自动执行函数,②三个状态,③then
  3. class Promise {
  4. constructor(fn) {
  5. // 三个状态
  6. this.state = 'pending'
  7. this.value = undefined
  8. this.reason = undefined
  9. let resolve = value => {
  10. if (this.state === 'pending') {
  11. this.state = 'fulfilled'
  12. this.value = value
  13. }
  14. }
  15. let reject = value => {
  16. if (this.state === 'pending') {
  17. this.state = 'rejected'
  18. this.reason = value
  19. }
  20. }
  21. // 自动执行函数
  22. try {
  23. fn(resolve, reject)
  24. } catch (e) {
  25. reject(e)
  26. }
  27. }
  28. // then
  29. then(onFulfilled, onRejected) {
  30. switch (this.state) {
  31. case 'fulfilled':
  32. onFulfilled()
  33. break
  34. case 'rejected':
  35. onRejected()
  36. break
  37. default:
  38. }
  39. }
  40. }

实现Promise.all和race

  1. Promise.myall = function (arr) {
  2. return new Promise((resolve, reject) => {
  3. if (arr.length === 0) {
  4. return resolve([])
  5. } else {
  6. let res = [],
  7. count = 0
  8. for (let i = 0; i < arr.length; i++) {
  9. // 同时也能处理arr数组中非Promise对象
  10. if (!(arr[i] instanceof Promise)) {
  11. res[i] = arr[i]
  12. if (++count === arr.length)
  13. resolve(res)
  14. } else {
  15. arr[i].then(data => {
  16. res[i] = data
  17. if (++count === arr.length)
  18. resolve(res)
  19. }, err => {
  20. reject(err)
  21. })
  22. }
  23. }
  24. }
  25. })
  26. }
  27. Promise.myrace = function (arr) {
  28. return new Promise((resolve, reject) => {
  29. for (let i = 0; i < arr.length; i++) {
  30. // 同时也能处理arr数组中非Promise对象
  31. if (!(arr[i] instanceof Promise)) {
  32. Promise.resolve(arr[i]).then(resolve, reject)
  33. } else {
  34. arr[i].then(resolve, reject)
  35. }
  36. }
  37. })
  38. }

Promise

  1. // 判断变量否为function
  2. const isFunction = variable => typeof variable === 'function'
  3. // 定义Promise的三种状态常量
  4. const PENDING = 'PENDING'
  5. const FULFILLED = 'FULFILLED'
  6. const REJECTED = 'REJECTED'
  7. class MyPromise {
  8. constructor(handle) {
  9. if (!isFunction(handle)) {
  10. throw new Error('MyPromise must accept a function as a parameter')
  11. }
  12. // 添加状态
  13. this._status = PENDING
  14. // 添加状态
  15. this._value = undefined
  16. // 添加成功回调函数队列
  17. this._fulfilledQueues = []
  18. // 添加失败回调函数队列
  19. this._rejectedQueues = []
  20. // 执行handle
  21. try {
  22. handle(this._resolve.bind(this), this._reject.bind(this))
  23. } catch (err) {
  24. this._reject(err)
  25. }
  26. }
  27. // 添加resovle时执行的函数
  28. _resolve(val) {
  29. const run = () => {
  30. if (this._status !== PENDING) return
  31. // 依次执行成功队列中的函数,并清空队列
  32. const runFulfilled = (value) => {
  33. let cb;
  34. while (cb = this._fulfilledQueues.shift()) {
  35. cb(value)
  36. }
  37. }
  38. // 依次执行失败队列中的函数,并清空队列
  39. const runRejected = (error) => {
  40. let cb;
  41. while (cb = this._rejectedQueues.shift()) {
  42. cb(error)
  43. }
  44. }
  45. /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后, 当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态 */
  46. if (val instanceof MyPromise) {
  47. val.then(value => {
  48. this._value = value
  49. this._status = FULFILLED
  50. runFulfilled(value)
  51. }, err => {
  52. this._value = err
  53. this._status = REJECTED
  54. runRejected(err)
  55. })
  56. } else {
  57. this._value = val
  58. this._status = FULFILLED
  59. runFulfilled(val)
  60. }
  61. }
  62. // 为了支持同步的Promise,这里采用异步调用
  63. setTimeout(run, 0)
  64. }
  65. // 添加reject时执行的函数
  66. _reject(err) {
  67. if (this._status !== PENDING) return
  68. // 依次执行失败队列中的函数,并清空队列
  69. const run = () => {
  70. this._status = REJECTED
  71. this._value = err
  72. let cb;
  73. while (cb = this._rejectedQueues.shift()) {
  74. cb(err)
  75. }
  76. }
  77. // 为了支持同步的Promise,这里采用异步调用
  78. setTimeout(run, 0)
  79. }
  80. // 添加then方法
  81. then(onFulfilled, onRejected) {
  82. const { _value, _status } = this
  83. // 返回一个新的Promise对象
  84. return new MyPromise((onFulfilledNext, onRejectedNext) => {
  85. // 封装一个成功时执行的函数
  86. let fulfilled = value => {
  87. try {
  88. if (!isFunction(onFulfilled)) {
  89. onFulfilledNext(value)
  90. } else {
  91. let res = onFulfilled(value);
  92. if (res instanceof MyPromise) {
  93. // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
  94. res.then(onFulfilledNext, onRejectedNext)
  95. } else {
  96. //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
  97. onFulfilledNext(res)
  98. }
  99. }
  100. } catch (err) {
  101. // 如果函数执行出错,新的Promise对象的状态为失败
  102. onRejectedNext(err)
  103. }
  104. }
  105. // 封装一个失败时执行的函数
  106. let rejected = error => {
  107. try {
  108. if (!isFunction(onRejected)) {
  109. onRejectedNext(error)
  110. } else {
  111. let res = onRejected(error);
  112. if (res instanceof MyPromise) {
  113. // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
  114. res.then(onFulfilledNext, onRejectedNext)
  115. } else {
  116. //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
  117. onFulfilledNext(res)
  118. }
  119. }
  120. } catch (err) {
  121. // 如果函数执行出错,新的Promise对象的状态为失败
  122. onRejectedNext(err)
  123. }
  124. }
  125. switch (_status) {
  126. // 当状态为pending时,将then方法回调函数加入执行队列等待执行
  127. case PENDING:
  128. this._fulfilledQueues.push(fulfilled)
  129. this._rejectedQueues.push(rejected)
  130. break
  131. // 当状态已经改变时,立即执行对应的回调函数
  132. case FULFILLED:
  133. fulfilled(_value)
  134. break
  135. case REJECTED:
  136. rejected(_value)
  137. break
  138. }
  139. })
  140. }
  141. // 添加catch方法
  142. catch(onRejected) {
  143. return this.then(undefined, onRejected)
  144. }
  145. // 添加静态resolve方法
  146. static resolve(value) {
  147. // 如果参数是MyPromise实例,直接返回这个实例
  148. if (value instanceof MyPromise) return value
  149. return new MyPromise(resolve => resolve(value))
  150. }
  151. // 添加静态reject方法
  152. static reject(value) {
  153. return new MyPromise((resolve, reject) => reject(value))
  154. }
  155. // 添加静态all方法
  156. static all(list) {
  157. return new MyPromise((resolve, reject) => {
  158. /** * 返回值的集合 */
  159. let values = []
  160. let count = 0
  161. for (let [i, p] of list.entries()) {
  162. // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
  163. this.resolve(p).then(res => {
  164. values[i] = res
  165. count++
  166. // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
  167. if (count === list.length) resolve(values)
  168. }, err => {
  169. // 有一个被rejected时返回的MyPromise状态就变成rejected
  170. reject(err)
  171. })
  172. }
  173. })
  174. }
  175. // 添加静态race方法
  176. static race(list) {
  177. return new MyPromise((resolve, reject) => {
  178. for (let p of list) {
  179. // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
  180. this.resolve(p).then(res => {
  181. resolve(res)
  182. }, err => {
  183. reject(err)
  184. })
  185. }
  186. })
  187. }
  188. finally(cb) {
  189. return this.then(
  190. value => MyPromise.resolve(cb()).then(() => value),
  191. reason => MyPromise.resolve(cb()).then(() => { throw reason })
  192. );
  193. }
  194. }

发表评论

表情:
评论列表 (有 0 条评论,235人围观)

还没有评论,来说两句吧...

相关阅读