实际常用

节流函数throttle

预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。

  1. function throttle(fn, delay = 500) {
  2. let isCall = true;
  3. return function (...args) {
  4. if (!isCall) return;
  5. isCall = false; // 不能进来了
  6. setTimeout(() => {
  7. fn.apply(this, args);
  8. isCall = true;
  9. }, delay); // 第一次不调用
  10. };
  11. }
  12. // 这种第一次会调用
  13. function throttle2(fn, delay = 500) {
  14. let oldTime = 0;
  15. return function (...args) {
  16. if (oldTime + delay <= Date.now()) {
  17. oldTime = Date.now();
  18. fn.apply(this, args);
  19. }
  20. };
  21. }

防抖函数debounce

在函数需要频繁触发时,只有当有足够空闲的时间时,才执行一次。就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。

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

深拷贝

  1. function deepClone(obj,type=1){
  2. // 如果不是Object类型或者没有值 直接原样返回
  3. if (typeof obj !== "object" || obj==null) {
  4. return obj
  5. }
  6. if(type==1){
  7. return JSON.parse(JSON.stringify(obj));
  8. }else if(type==2){
  9. // 初始化result
  10. let result=null;
  11. if(obj instanceof Array){
  12. // 如果是数组
  13. result=[];
  14. }else{
  15. // 不是数组那就是对象了
  16. result={};
  17. }
  18. for(let key in obj){
  19. // 判断key是否为obj自身属性值
  20. if(obj.hasOwnProperty(key)){
  21. result[key]=deepClone(obj[key],2);
  22. }
  23. }
  24. return result;
  25. }
  26. }
  27. let a = {
  28. name:'小明',
  29. age:15,
  30. address:"九龙广场",
  31. fun:["零食","羽毛球","篮球"],
  32. parent:{
  33. father:{
  34. name:"小明爸爸",
  35. age:32
  36. },
  37. mother:{
  38. name:"小明妈妈",
  39. age:34
  40. }
  41. }
  42. }
  43. let b = a;
  44. // 查看当前a和b的原始数据
  45. console.log('a',a);
  46. // 使用json方式克隆
  47. let c = deepClone(a,2);
  48. a.name="小红";
  49. console.log('a',a)
  50. console.log('c',c)
  51. console.log('修改a的父母信息');
  52. a.parent.father="小红爸爸";
  53. a.parent.mother="小红妈妈";
  54. console.log('查看b和c')
  55. console.log('b',b)
  56. console.log('c',c)

简单的jquery, 考虑插件和扩展性

  1. class Jquery{
  2. constructor(selector){
  3. let result=null;
  4. if(selector.startsWith('.')){
  5. result=document.getElementsByClassName(selector.slice(1))
  6. }else{
  7. result = document.querySelectorAll(selector);
  8. }
  9. let length = result.length;
  10. for(let i = 0;i<length;i++){
  11. this[i]=result[i];
  12. }
  13. this.length=length;
  14. this.selector=selector;
  15. }
  16. get(index){
  17. return this[index];
  18. }
  19. each(fun){
  20. for(let i=0;i<this.length;i++){
  21. fun(this[i]);
  22. }
  23. }
  24. on(type,fun){
  25. return this.each(elem=>{
  26. elem.addEventListener(type,fun,false);
  27. })
  28. }
  29. }

深拷贝( 破解递归爆栈 )

其实破解递归爆栈的方法有两条路,第一种是消除尾递归,但在这个例子中貌似行不通,第二种方法就是干脆不用递归,改用循环

用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点

首先我们往栈里放入种子数据,key用来存储放哪一个父元素的那一个子元素拷贝对象

然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝

  1. function cloneLoop(x) {
  2. const root = {};
  3. // 栈
  4. const loopList = [
  5. {
  6. parent: root,
  7. key: undefined,
  8. data: x,
  9. }
  10. ];
  11. while(loopList.length) {
  12. // 深度优先
  13. const node = loopList.pop();
  14. const parent = node.parent;
  15. const key = node.key;
  16. const data = node.data;
  17. // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
  18. let res = parent;
  19. if (typeof key !== 'undefined') {
  20. res = parent[key] = {};
  21. }
  22. for(let k in data) {
  23. if (data.hasOwnProperty(k)) {
  24. if (typeof data[k] === 'object') {
  25. // 下一次循环
  26. loopList.push({
  27. parent: res,
  28. key: k,
  29. data: data[k],
  30. });
  31. } else {
  32. res[k] = data[k];
  33. }
  34. }
  35. }
  36. }
  37. return root;
  38. }

深拷贝( 破解循环引用 )

引入一个数组uniqueList用来存储已经拷贝的数组,每次循环遍历时,先判断对象是否在uniqueList中了,如果在的话就不执行拷贝逻辑了

find是抽象的一个函数,其实就是遍历uniqueList

  1. // 保持引用关系
  2. function cloneForce(x) {
  3. // =============
  4. const uniqueList = []; // 用来去重
  5. // =============
  6. let root = {};
  7. // 循环数组
  8. const loopList = [
  9. {
  10. parent: root,
  11. key: undefined,
  12. data: x,
  13. }
  14. ];
  15. while(loopList.length) {
  16. // 深度优先
  17. const node = loopList.pop();
  18. const parent = node.parent;
  19. const key = node.key;
  20. const data = node.data;
  21. // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
  22. let res = parent;
  23. if (typeof key !== 'undefined') {
  24. res = parent[key] = {};
  25. }
  26. // =============
  27. // 数据已经存在
  28. let uniqueData = find(uniqueList, data);
  29. if (uniqueData) {
  30. parent[key] = uniqueData.target;
  31. continue; // 中断本次循环
  32. }
  33. // 数据不存在
  34. // 保存源数据,在拷贝数据中对应的引用
  35. uniqueList.push({
  36. source: data,
  37. target: res,
  38. });
  39. // =============
  40. for(let k in data) {
  41. if (data.hasOwnProperty(k)) {
  42. if (typeof data[k] === 'object') {
  43. // 下一次循环
  44. loopList.push({
  45. parent: res,
  46. key: k,
  47. data: data[k],
  48. });
  49. } else {
  50. res[k] = data[k];
  51. }
  52. }
  53. }
  54. }
  55. return root;
  56. }
  57. function find(arr, item) {
  58. for(let i = 0; i < arr.length; i++) {
  59. if (arr[i].source === item) {
  60. return arr[i];
  61. }
  62. }
  63. return null;
  64. }
  65. // 测试
  66. var b = {};
  67. var a = {a1: b, a2: b};
  68. a.a1 === a.a2 // true
  69. var c = cloneForce(a);
  70. c.a1 === c.a2 // true

使用setTimeout模拟setInterval

  1. function mySetInterval(cb,t){
  2. setTimeout (function () {
  3. cb()
  4. setTimeout (arguments.callee, t)
  5. }, t)
  6. }
  7. // return timer 使得定时器是可以取消的
  8. function mySetInterval(cb,t){
  9. return setTimeout (function () {
  10. cb()
  11. setTimeout (arguments.callee, t)
  12. }, t)
  13. }

js实现一个继承方法

  1. // 借用构造函数继承实例属性
  2. function Child () {
  3. Parent.call(this)
  4. }
  5. // 寄生继承原型属性
  6. (function () {
  7. let Super = function () {}
  8. Super.prototype = Parent.prototype
  9. Child.prototype = new Super()
  10. })()

手写ajax

  1. //1.简单流程
  2. //实例化
  3. let xhr = XMLHttpRequest()
  4. //初始化
  5. xhr.open(method,url,async)
  6. //发送请求
  7. xhr.send(data)
  8. //设置状态变化回调处理请求结果
  9. xhr.onreadystatechange = ()=>{
  10. if(xhr.readyState === 4 && xhr.state === 200){
  11. console.log(xhr.responseText)
  12. }
  13. }
  14. //2.基于promise实现
  15. function ajax(options) {
  16. //请求地址
  17. const url = options.url,
  18. //请求方法
  19. const method = options.method.toLocaleLowerCase() || 'get'
  20. //默认为异步
  21. const async = options.async
  22. //请求参数
  23. const data = options.data
  24. //实例化
  25. const xhr = new XMLHtmlRequset()
  26. //请求超时
  27. if(options.timeout && options.timeout > 0) {
  28. xhr.timeout = options.timeout
  29. }
  30. //返回一个promise实例
  31. xhr.ontimeout = ()=> reject && reject('请求超时')
  32. //监听状态变化回调
  33. xhr.onreadystatechange = ()=> {
  34. if(xhr.readyState == 4){
  35. //200--300 表示请求成功 304 资源未变 取缓存
  36. if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
  37. resolve && resolve(xhr.responseText)
  38. }else{
  39. reject && reject()
  40. }
  41. }
  42. }
  43. // 错误回调
  44. xhr.onerror = err => reject && reject(err)
  45. let paramArr = []
  46. let encodeData
  47. //处理请求参数 {a:1,b:'1212'} 处理成 a=1&b=1212
  48. if(data instanceof Object){
  49. for(let key in data){
  50. //参数拼接需要通过 encodeURIComponent 进行编码
  51. paramArr.push(encodeURIComponent(key)+'='+encodeURIComponent(data[key]))
  52. }
  53. encodeData = paramArr.join('&')
  54. }
  55. if(method === 'get'){
  56. //检测url中是否已存在 '?' 及其位置
  57. }
  58. //初始化
  59. xhr.open(method,url,async)
  60. //发送请求
  61. if(method === 'get') xhr.send(null)
  62. else{
  63. //post需要设置请求头
  64. xhr.setRequestHeader('Content-Type','appliction/x-www-form-urlencoded;charset=UTF-8')
  65. xhr.send(encodeData)
  66. }
  67. }

http请求封装

使用XMLHttpRequestfetch 等原生方法

Content-Type 类型 application/x-www-form-urlencoded 纯文本传输 multipart/form-data 可用于上传文件

  1. const baseUrl = 'http://localhost:3000'
  2. export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {
  3. type = type.toUpperCase();
  4. url = baseUrl + url;
  5. if (type == 'GET') {
  6. let dataStr = ''; //数据拼接字符串
  7. Object.keys(data).forEach(key => {
  8. dataStr += key + '=' + data[key] + '&';
  9. })
  10. if (dataStr !== '') {
  11. dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
  12. url = url + '?' + dataStr;
  13. }
  14. }
  15. if (window.fetch && method == 'fetch') {
  16. let requestConfig = {
  17. credentials: 'include',
  18. method: type,
  19. headers: {
  20. 'Accept': 'application/json',
  21. 'Content-Type': 'application/json'
  22. },
  23. mode: "cors",
  24. cache: "force-cache"
  25. }
  26. if (type == 'POST') {
  27. Object.defineProperty(requestConfig, 'body', {
  28. value: JSON.stringify(data)
  29. })
  30. }
  31. try {
  32. const response = await fetch(url, requestConfig);
  33. const responseJson = await response.json();
  34. return responseJson
  35. } catch (error) {
  36. throw new Error(error)
  37. }
  38. } else {
  39. return new Promise((resolve, reject) => {
  40. let requestObj;
  41. if (window.XMLHttpRequest) {
  42. requestObj = new XMLHttpRequest();
  43. } else {
  44. requestObj = new ActiveXObject;
  45. }
  46. let sendData = '';
  47. if (type == 'POST') {
  48. sendData = JSON.stringify(data);
  49. }
  50. requestObj.open(type, url, true);
  51. requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  52. requestObj.send(sendData);
  53. requestObj.onreadystatechange = () => {
  54. if (requestObj.readyState == 4) {
  55. if (requestObj.status == 200) {
  56. let obj = requestObj.response
  57. if (typeof obj !== 'object') {
  58. obj = JSON.parse(obj);
  59. }
  60. resolve(obj)
  61. } else {
  62. reject(requestObj)
  63. }
  64. }
  65. }
  66. })
  67. }
  68. }

注意一下 vue 中 mixins 的优先级,component > mixins > extends。

  1. export function mergeOptions(
  2. parent: Object,
  3. child: Object,
  4. vm?: Component
  5. ): Object {
  6. ...
  7. // 如果有 child.extends 递归调用 mergeOptions 实现属性拷贝
  8. const extendsFrom = child.extends
  9. if (extendsFrom) {
  10. parent = mergeOptions(parent, extendsFrom, vm)
  11. }
  12. // 如果有 child.mixins 递归调用 mergeOptions 实现属性拷贝
  13. if (child.mixins) {
  14. for (let i = 0, l = child.mixins.length; i < l; i++) {
  15. parent = mergeOptions(parent, child.mixins[i], vm)
  16. }
  17. }
  18. // 申明 options 空对象,用来保存属性拷贝结果
  19. const options = {}
  20. let key
  21. // 遍历 parent 对象,调用 mergeField 进行属性拷贝
  22. for (key in parent) {
  23. mergeField(key)
  24. }
  25. // 遍历 parent 对象,调用 mergeField 进行属性拷贝
  26. for (key in child) {
  27. if (!hasOwn(parent, key)) {
  28. mergeField(key)
  29. }
  30. }
  31. // 属性拷贝实现方法
  32. function mergeField(key) {
  33. // 穿透赋值,默认为 defaultStrat
  34. const strat = strats[key] || defaultStrat
  35. options[key] = strat(parent[key], child[key], vm, key)
  36. }
  37. return options
  38. }

介绍下深度优先遍历和广度优先遍历,如何实现?

  1. 深度优先遍历——是指从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个顶点进行访问。重复此步骤,直至所有结点都被访问完为止。
  2. 广度优先遍历——是从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点所有未被访问的邻结点,访问完后再访问这些结点中第一个邻结点的所有结点,重复此方法,直到所有结点都被访问完为止。
  3. ```js
  4. //1.深度优先遍历的递归写法
  5. function deepTraversal(node) {
  6. let nodes = []
  7. if (node != null) {
  8. nodes.push[node]
  9. let childrens = node.children
  10. for (let i = 0; i < childrens.length; i++)
  11. deepTraversal(childrens[i])
  12. }
  13. return nodes
  14. }
  15. //2.深度优先遍历的非递归写法
  16. function deepTraversal(node) {
  17. let nodes = []
  18. if (node != null) {
  19. let stack = [] //同来存放将来要访问的节点
  20. stack.push(node)
  21. while (stack.length != 0) {
  22. let item = stack.pop() //正在访问的节点
  23. nodes.push(item)
  24. let childrens = item.children
  25. for (
  26. let i = childrens.length - 1;
  27. i >= 0;
  28. i-- //将现在访问点的节点的子节点存入stack,供将来访问
  29. )
  30. stack.push(childrens[i])
  31. }
  32. }
  33. return nodes
  34. }
  35. //3.广度优先遍历的递归写法
  36. function wideTraversal(node) {
  37. let nodes = [],
  38. i = 0
  39. if (node != null) {
  40. nodes.push(node)
  41. wideTraversal(node.nextElementSibling)
  42. node = nodes[i++]
  43. wideTraversal(node.firstElementChild)
  44. }
  45. return nodes
  46. }
  47. //4.广度优先遍历的非递归写法
  48. function wideTraversal(node) {
  49. let nodes = [],
  50. i = 0
  51. while (node != null) {
  52. nodes.push(node)
  53. node = nodes[i++]
  54. let childrens = node.children
  55. for (let i = 0; i < childrens.length; i++) {
  56. nodes.push(childrens[i])
  57. }
  58. }
  59. return nodes
  60. }
  1. <a name="g74YS"></a>
  2. #### 使用Promise封装AJAX请求
  3. ```javascript
  4. // promise 封装实现:
  5. function getJSON(url) {
  6. // 创建一个 promise 对象
  7. let promise = new Promise(function(resolve, reject) {
  8. let xhr = new XMLHttpRequest();
  9. // 新建一个 http 请求
  10. xhr.open("GET", url, true);
  11. // 设置状态的监听函数
  12. xhr.onreadystatechange = function() {
  13. if (this.readyState !== 4) return;
  14. // 当请求成功或失败时,改变 promise 的状态
  15. if (this.status === 200) {
  16. resolve(this.response);
  17. } else {
  18. reject(new Error(this.statusText));
  19. }
  20. };
  21. // 设置错误监听函数
  22. xhr.onerror = function() {
  23. reject(new Error(this.statusText));
  24. };
  25. // 设置响应的数据类型
  26. xhr.responseType = "json";
  27. // 设置请求头信息
  28. xhr.setRequestHeader("Accept", "application/json");
  29. // 发送 http 请求
  30. xhr.send(null); // 参数data为携带的数据
  31. });
  32. return promise;
  33. }

实现sleep函数(使用Promise封装setTimeout)

  1. function timeout(delay) {
  2. return new Promise(resolve => {
  3. setTimeout(resolve, delay)
  4. })
  5. };

使用 setTimeout 实现 setInterval

  1. function mySetInterval(fn, timeout) {
  2. // 控制器,控制定时器是否继续执行
  3. var timer = {
  4. flag: true
  5. };
  6. // 设置递归函数,模拟定时器执行。
  7. function interval() {
  8. if (timer.flag) {
  9. fn();
  10. setTimeout(interval, timeout);
  11. }
  12. }
  13. // 启动定时器
  14. setTimeout(interval, timeout);
  15. // 返回控制器
  16. return timer;
  17. }

判断对象是否存在循环引用

循环引用对象本来没有什么问题,但是序列化的时候就会发生问题,比如调用JSON.stringify()对该类对象进行序列化,就会报错: Converting circular structure to JSON.

  1. const isCycleObject = (obj,parent) => {
  2. const parentArr = parent || [obj];
  3. for(let i in obj) {
  4. if(typeof obj[i] === 'object') {
  5. let flag = false;
  6. parentArr.forEach((pObj) => {
  7. if(pObj === obj[i]){
  8. flag = true;
  9. }
  10. })
  11. if(flag) return true;
  12. flag = isCycleObject(obj[i],[...parentArr,obj[i]]);
  13. if(flag) return true;
  14. }
  15. }
  16. return false;
  17. }
  18. const a = 1;
  19. const b = {a};
  20. const c = {b};
  21. const o = {d:{a:3},c}
  22. o.c.b.aa = a;
  23. console.log(isCycleObject(o))

手写一个观察者模式

  1. class Subject{ // 被观察者的类 被观察者 需要将观察者收集起来
  2. constructor(name){
  3. this.name = name;
  4. this.state = '非常开心'
  5. this.observers = [];
  6. }
  7. attach(o){ // 进行收集
  8. this.observers.push(o); // on
  9. }
  10. setState(newState){
  11. this.state = newState;
  12. this.observers.forEach(o=>o.update(this.name,newState)) // emit
  13. }
  14. }
  15. class Observer{ // 观察者
  16. constructor(name){
  17. this.name = name;
  18. }
  19. update(s,state){
  20. console.log(this.name+":" + s + '当前'+state);
  21. }
  22. }
  23. // vue 数据变了(状态) 视图要更新 (通知依赖的人)
  24. let s = new Subject('小宝宝');
  25. let o1 = new Observer('爸爸');
  26. let o2 = new Observer('妈妈');
  27. s.attach(o1)
  28. s.attach(o2)
  29. s.setState('不开心了')
  30. s.setState('开心了')

手写一个观察者模式

  1. class Subject{ // 被观察者的类 被观察者 需要将观察者收集起来
  2. constructor(name){
  3. this.name = name;
  4. this.state = '非常开心'
  5. this.observers = [];
  6. }
  7. attach(o){ // 进行收集
  8. this.observers.push(o); // on
  9. }
  10. setState(newState){
  11. this.state = newState;
  12. this.observers.forEach(o=>o.update(this.name,newState)) // emit
  13. }
  14. }
  15. class Observer{ // 观察者
  16. constructor(name){
  17. this.name = name;
  18. }
  19. update(s,state){
  20. console.log(this.name+":" + s + '当前'+state);
  21. }
  22. }
  23. // vue 数据变了(状态) 视图要更新 (通知依赖的人)
  24. let s = new Subject('小宝宝');
  25. let o1 = new Observer('爸爸');
  26. let o2 = new Observer('妈妈');
  27. s.attach(o1)
  28. s.attach(o2)
  29. s.setState('不开心了')
  30. s.setState('开心了')

手写去重

使用闭包实现每隔一秒打印 1,2,3,4

  1. // 使用闭包实现
  2. for (var i = 0; i < 5; i++) {
  3. (function(i) {
  4. setTimeout(function() {
  5. console.log(i);
  6. }, i * 1000);
  7. })(i);
  8. }
  9. // 使用 let 块级作用域
  10. for (let i = 0; i < 5; i++) {
  11. setTimeout(function() {
  12. console.log(i);
  13. }, i * 1000);
  14. }

手写一个 jsonp

  1. function jsonp(url, params, callback) {
  2. // 判断是否含有参数
  3. let queryString = url.indexOf("?") === -1 ? "?" : "&";
  4. // 添加参数
  5. for (var k in params) {
  6. if (params.hasOwnProperty(k)) {
  7. queryString += k + "=" + params[k] + "&";
  8. }
  9. }
  10. // 处理回调函数名
  11. let random = Math.random()
  12. .toString
  13. .replace(".", ""),
  14. callbackName = "myJsonp" + random;
  15. // 添加回调函数
  16. queryString += "callback=" + callbackName;
  17. // 构建请求
  18. let scriptNode = document.createElement("script");
  19. scriptNode.src = url + queryString;
  20. window[callbackName] = function() {
  21. // 调用回调函数
  22. callback(...arguments);
  23. // 删除这个引入的脚本
  24. document.getElementsByTagName("head")[0].removeChild(scriptNode);
  25. };
  26. // 发起请求
  27. document.getElementsByTagName("head")[0].appendChild(scriptNode);
  28. }

详细资料可以参考:《原生 jsonp 具体实现》《jsonp 的原理与实现》

实现 compose 函数

compose(组合)函数特点:

  • compose 的参数是函数,返回的也是一个函数
  • 因为除了第一个函数的接受参数,其他函数的接受参数都是上一个函数的返回值,所以初始函数的参数是多元的,而其他函数的接受值是一元的
  • compsoe 函数可以接受任意的参数,所有的参数都是函数,且执行方向是自右向左的,初始函数一定放到参数的最右面
  1. 实现
  2. ```js
  3. function compose(...fns) {
  4. let isFirst = true
  5. return (...args) => {
  6. return fns.reduceRight((result, fn) => {
  7. if (!isFirst) return fn(result)
  8. isFirst = false
  9. return fn(...result)
  10. }, args)
  11. }
  12. }

测试

  1. const greeting = (firstName, lastName) => 'hello, ' + firstName + ' ' + lastName
  2. const toUpper = str => str.toUpperCase()
  3. const fn = compose(toUpper, greeting)
  4. console.log(fn('jack', 'smith')) // HELLO, JACK SMITH
  1. <a name="FaW6h"></a>
  2. #### 格式化金钱,每千分位加逗号
  3. ```javascript
  4. // 1
  5. function format(str) {
  6. let s = ''
  7. let count = 0
  8. for (let i = str.length - 1; i >= 0; i--) {
  9. s = str[i] + s
  10. count++
  11. if (count % 3 == 0 && i != 0) {
  12. s = ',' + s
  13. }
  14. }
  15. return s
  16. }
  17. // 2
  18. function format(str) {
  19. return str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
  20. }

请用js去除字符串空格

  1. ## 去除所有空格
  2. str.replace(/\s/g, '')
  3. ## 去除两边空格
  4. str.replace(/^\s+|\s+$/g, '')
  5. // 原生方法
  6. str.trim()

反转数组

要求

input: I am a student output: student a am I 输入是数组 输出也是数组不允许用 split splice reverse

  1. // 1
  2. function reverseArry(arr) {
  3. const result = []
  4. while (arr.length) {
  5. result.push(arr.pop())
  6. }
  7. return result
  8. }
  9. console.log(reverseArry(['I', 'am', 'a', 'student']))
  10. // ["student", "a", "am", "I"]
  11. // 2
  12. function reverseArry(arry) {
  13. const result = []
  14. const distance = arry.length - 1
  15. for (let i = distance; i >= 0; i--) {
  16. result[distance - i] = arry[i]
  17. }
  18. return result
  19. }

将金额12345转成中文金额表示

要求

12345 => 一万两千三百四十五 10086 => 一万零八十六 100010001 => 一亿零一万零一 100000000 => 一亿 单位支持到亿

  1. function numToString(num) {
  2. if (num > 999999999) throw '超过金额上限,最大单位为亿'
  3. const unitMap = ['', '十', '百', '千', '万', '十', '百', '千', '亿']
  4. const stringMap = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
  5. const n = num + ''
  6. const len = n.length
  7. let lastIndex = len - 1
  8. let result = ''
  9. for (let i = 0; i < len; i++) {
  10. if (i > 0 && n[i] == '0') {
  11. if (n[i - 1] != '0') result += '零'
  12. } else {
  13. result += stringMap[n[i]] + unitMap[lastIndex]
  14. }
  15. lastIndex--
  16. }
  17. lastIndex = result.length - 1
  18. if (result[lastIndex] == '零') return result.slice(0, lastIndex)
  19. return result
  20. }
  21. console.log(numToString(12345)) // 一万二千三百四十五
  22. console.log(numToString(10086)) // 一万零八十六
  23. console.log(numToString(100010001)) // 一亿零一万零一
  24. console.log(numToString(100000000)) // 一亿

异步求和

要求

提供一个异步 add 方法如下,需要实现一个 await sum(…args) 函数: function asyncAdd(a, b, callback) { setTimeout(function() { callback(null, a + b) }, 1000) }

  1. function sum(...args) {
  2. let start = 0
  3. let result = 0
  4. let count = 0 // 用于计算开启了多少个 Promise
  5. function _sum(resolve) {
  6. count++
  7. new Promise((r, j) => {
  8. let a = args[start++]
  9. let b = args[start++]
  10. a = a !== undefined? a : 0
  11. b = b !== undefined? b : 0 // 如果访问的元素超出了数组范围,则转为 0
  12. asyncAdd(a, b, (context, sum) => {
  13. r(sum)
  14. })
  15. if (start < args.length) {
  16. _sum(resolve)
  17. }
  18. })
  19. .then(sum => {
  20. result += sum
  21. count--
  22. if (count == 0) resolve(result) // 所有的 Promise 执行完毕,返回结果
  23. })
  24. }
  25. return new Promise((resolve, reject) => {
  26. if (!args || !args.length) return resolve(0)
  27. if (args.length == 1) return resolve(args[0])
  28. _sum(resolve)
  29. })
  30. }
  31. // 测试
  32. sum(1,2,3,4,5,6,7,8,9,10,11).then(sum => console.log(sum)) // 66
  33. // or
  34. async function test() {
  35. console.log(await sum(1,2,3,4,5,6,7,8,9,10,11))
  36. }
  37. test() // 66

数字集转换成字母集

要求

a~z 有 26个字母,按照 1~26 编码,现在给定一个数字字符串,输出所有可能的解码结果,如:输入 1234,输出 [‘awd’, ‘abcd’, ‘lcd’]

  1. const map = [0,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
  2. function getDecodes(num) {
  3. if (!num) return []
  4. num += ''
  5. const result = []
  6. _getDecodes(num, 0, [], result)
  7. return result
  8. }
  9. function _getDecodes(num, start, path, result) {
  10. if (start == num.length) return result.push([...path])
  11. let c = num[start++]
  12. path.push(map[c])
  13. _getDecodes(num, start, path, result)
  14. path.pop()
  15. if (start == num.length) return
  16. c += num[start]
  17. if (c > 26) return
  18. path.push(map[c])
  19. _getDecodes(num, start + 1, path, result)
  20. path.pop()
  21. }