一月19个人页面-阿里云开发者社区

个人头像照片
1
47

个人介绍

暂无个人介绍

擅长的技术

  • JavaScript
  • PHP
  • 前端开发
  • Java
  • 关系型数据库
  • NoSQL
获得更多能力
通用技术能力:
  • Java
    中级

    能力说明:

    掌握封装、继承和多态设计Java类的方法,能够设计较复杂的Java类结构;能够使用泛型与集合的概念与方法,创建泛型类,使用ArrayList,TreeSet,TreeMap等对象掌握Java I/O原理从控制台读取和写入数据,能够使用BufferedReader,BufferedWriter文件创建输出、输入对象。

    获取记录:

    • 2020-03-17大学考试 大学/社区-用户参加考试
    • 2020-03-17大学考试 Java开发中级 大学/社区用户通过技能测试
    • 2020-03-17大学考试 大学/社区-用户参加考试
    • 2020-03-17大学考试 Java开发初级 大学/社区用户通过技能测试
    • 2019-10-16大学考试 大学/社区-用户参加考试
  • 前端开发
    初级

    能力说明:

    基本的计算机知识与操作能力,具备Web基础知识,掌握Web的常见标准、常用浏览器的不同特性,掌握HTML与CSS的入门知识,可进行静态网页的制作与发布。

    获取记录:

云产品技术能力:

暂时未有相关云产品技术能力~

阿里云技能认证

详细说明
  • 高分内容
  • 最新动态
  • 文章
  • 问答
正在加载, 请稍后...
暂无更多信息

2020年05月

  • 05.23 14:34:00
    回答了问题 2020-05-23 14:34:00

    如何模拟实现 Array.prototype.splice#前端面试

    Array.prototype._splice = function(start, deleteCount, ...items) {
      if (start > 0) {
        if (start > this.length - 1) {
          start = this.length - 1;
        }
      } else  {
        if (Math.abs(start) > this.length - 1) {
          start = 0;
        } else {
          start = this.length - 1 + start;
        }
      }
    
      const newArr = [];
      const removed = [];
    
      deleteCount = isNaN(Number(deleteCount)) ? 0 : Number(deleteCount);
    
      for (let i = 0; i < this.length; i++) {
        if (i < start || i > start + deleteCount - 1) {
          newArr.push(this[i]);
        } else {
          removed.push(this[i])
        }
        if (i === start + deleteCount - 1) {
          newArr.push(...items)
        }
      }
    
      for (let i = 0; i < newArr.length; i++) {
        this[i] = newArr[i];
      }
    
      this.length = newArr.length;
    
      return removed;
    }
    
    踩0 评论0
  • 05.23 14:33:22
    回答了问题 2020-05-23 14:33:22

    实现 Promise.retry#前端面试

    image.png

    踩0 评论0
  • 05.23 14:32:46
    回答了问题 2020-05-23 14:32:46

    浏览器缓存 ETag 里的值是怎么生成的#前端面试

    Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)。 语法:

    ETag: W/"<etag_value>"
    ETag: "<etag_value>"
    

    'W/'(大小写敏感) 表示使用弱验证器,可选。 弱验证器很容易生成,但不利于比较。 强验证器是比较的理想选择,但很难有效地生成。 相同资源的两个弱Etag值可能语义等同,但不是每个字节都相同。 "<etag_value>" 实体标签唯一地表示所请求的资源。 它们是位于双引号之间的ASCII字符串(如“675af34563dc-tr34”)。 没有明确指定生成ETag值的方法。 通常,使用内容的散列,最后修改时间戳的哈希值,或简单地使用版本号。 例如,MDN使用wiki内容的十六进制数字的哈希值。

    踩0 评论0
  • 05.23 14:31:39
    回答了问题 2020-05-23 14:31:39

    求最终 left、right 的宽度#前端面试

    剩余的空间:600 - (300 + 200) = 100。 子元素的 flex-grow 的值分别为 1,2, 剩余空间用3等分来分 100 / 3 = 33.3333333 所以 left = 300 + 1 * 33.33 = 333.33 right = 200 + 2 * 33.33 = 266.67

    踩0 评论0
  • 05.23 14:31:15
    回答了问题 2020-05-23 14:31:15

    求最终 left、right 的宽度#前端面试题

    子元素的 flex-shrink 的值分别为 2,1 溢出:500+400 - 600 = 300。 总权重为 2 * 500+ 1 * 400 = 1400 两个元素分别收缩: 300 * 2(flex-shrink) * 500(width) / 1400= 214.28 300 * 1(flex-shrink) * 400(width) / 1400= 85.72 三个元素的最终宽度分别为: 500 - 214.28 = 285.72 400 - 85.72 = 314.28

    踩0 评论0
  • 05.23 14:30:23
    回答了问题 2020-05-23 14:30:23

    弹性盒子中 flex: 0 1 auto 表示什么意思#前端面试

    flex :flex-grow flex-shrink flex-basis
    
    ①.flex-grow 剩余空间索取
    
    默认值为0,不索取
    
    eg:父元素400,子元素A为100px,B为200px.则剩余空间为100
    
    此时A的flex-grow 为1,B为2,
    
    则A=100px+1001/3; B=200px+1002/3
    
    ②.flex-shrink 子元素总宽度大于复制元素如何缩小
    
    父400px,A 200px B 300px
    
    AB总宽度超出父元素100px;
    
    如果A不减少,则flex-shrink :0,B减少;
    
    ②,flex-basis
    
    该属性用来设置元素的宽度,当然width也可以用来设置元素的宽度,如果设置了width和flex-basis,那么flex-basis会覆盖width值。
    
    踩0 评论0
  • 05.23 14:28:46
    回答了问题 2020-05-23 14:28:46

    实现一个批量请求函数 multiRequest(urls, maxNum)#前端面试

    借鉴了一些题解的实现,用例跑通了,有问题或者可优化的话请各位大佬指正。 解题的关键是:队列和递归

    代码如下

    function handleFetchQueue(urls, max, callback) {
      const urlCount = urls.length;
      const requestsQueue = [];
      const results = [];
      let i = 0;
      const handleRequest = url => {
        const req = fetchFunc(url)
          .then(res => {
            results.push(res);
          })
          .catch(e => {
            results.push(e);
          })
          .finally(() => {
            const len = results.length;
            if (len < urlCount) {
              // 完成请求就出队
              requestsQueue.shift();
              handleRequest(urls[++i]);
            } else if (len === urlCount) {
              "function" === typeof callback && callback(results);
            }
          });
        requestsQueue.push(req);
        // 只要满足就继续请求
        if (requestsQueue.length <= max) {
          handleRequest(urls[++i]);
        }
      };
      handleRequest(urls[i]);
    }
    
    踩0 评论0
  • 05.23 14:23:25
    回答了问题 2020-05-23 14:23:25

    实现一个 normalize 函数,能将输入的特定的字符串转化为特定的结构化数据#前端面试

    let normalize = str => {
      let result = {}
      let c
      
      // 字符串转化为数组
      let arr = str.split(/[\[\]]/g).filter(Boolean)
      
      // 生成结构化数据
      arr.forEach((item, index) => {
        if(index != 0) {
          c.children = {}
          c.children.value = item
          c= c.children
        } else {
          result.value = item
          c= result
        }
      })
      
      return result
    }
    let str = '[abc[bcd[def]]]'
    normalize(str)
    // {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}
    
    踩0 评论0
  • 05.23 14:21:47
    回答了问题 2020-05-23 14:21:47

    用最简洁代码实现 indexOf 方法#前端面试

    function indexOf(str1,str2){
    	let len1 = str1.length;
    	let len2 = str2.length;
    	for (var i = 0; i < len1; i++) {
    		//从当前i开始截取一直到str2的长度+1,返回的字符串若是和str2相等就说明i就是这个str2在str1开始的位置
    		if(str1.slice(i,len2+i) == str2){
    			return i;
    		}
    	}
    	return -1;
    	
    }
    console.log(indexOf("abcdef","cde"));
    

    image.png

    踩0 评论0
  • 05.23 14:02:42
    回答了问题 2020-05-23 14:02:42

    二分查找如何定位左边界和右边界#前端面试

    //递归查找
    function erfen_digui(arr, val, left = 0, right = arr.length - 1) {
            if (left > right) {
              return -1;
            }
            let cent = Math.floor((right + left) / 2);
            if (arr[cent] === val) {
              return `最终查找结果下标为${cent}`;
            } else if (arr[cent] > val) {
              right = cent - 1;
            } else {
              left = cent + 1;
            }
            return erfen_digui(arr, val, left, right);
          }
    //非递归查找
          function erfen_feidigui(arr, val) {
            let left = 0,
              right = arr.length - 1;
            while (left <= right) {
              let cent = left + Math.floor((right - left) / 2);
              if (arr[cent] === val) {
                return `最终查找结果下标为${cent}`;
              } else if (arr[cent] > val) {
                right = cent - 1;
              } else {
                left = cent + 1;
              }
            }
            return -1;
          }
    
    //左边界查找(查找第一个元素)
    function erfen_digui(arr, val, left = 0, right = arr.length - 1) {
            if (left > right) {
              return -1;
            }
            let cent = Math.floor((right + left) / 2);
            if (arr[cent] === val) {
              /****************改动点********************/
              if (arr[cent - 1] === val) {
                right = cent - 1;
              } else {
                return `最终查找结果下标为${cent}`;
              }
              /*****************************************/
            } else if (arr[cent] > val) {
              right = cent - 1;
            } else {
              left = cent + 1;
            }
            return erfen_digui(arr, val, left, right);
          }
    
    // 二分查找右边界(查找最后一个元素)
    function erfen_digui(arr, val, left = 0, right = arr.length - 1) {
            if (left > right) {
              return -1;
            }
            let cent = Math.floor((right + left) / 2);
            if (arr[cent] === val) {
              /****************改动点********************/
              if (arr[cent + 1] === val) {
                left = cent + 1;
              } else {
                return `最终查找结果下标为${cent}`;
              }
              /*****************************************/
            } else if (arr[cent] > val) {
              right = cent - 1;
            } else {
              left = cent + 1;
            }
            return erfen_digui(arr, val, left, right);
          }
    
    踩0 评论0
  • 05.23 14:01:54
    回答了问题 2020-05-23 14:01:54

    babel 怎么把字符串解析成 AST,是怎么进行词法/语法分析的?#前端面试

    Babel

    定义 - Babel 是我们知道的将 ES6、ES7等代码转译为 ES5 代码且能安全稳定运行最好的工具 - 同时它允许开发者开发插件,能够在编译时期转换 JavaScript 的结构。

    Babel概述 我们需要知道 3 个 Babel 处理流程中的重要工具; 1. 解析

    • Babylon是一个解析器,它可以将javascript字符串,转化为更加友好的表现形式,称之为抽象语法树; 在解析过程中有两个阶段:词法分析和语法分析,
    • 词法分析阶段:字符串形式的代码转换为令牌(tokens)流,令牌类似于AST中的节点;
    • 语法分析阶段:把一个令牌流转化为AST的形式,同时这个阶段会把令牌中的信息转化为AST的表述结构
    • 转换

    • babel-traverse 模块允许你浏览、分析和修改抽象语法树(AST Abstract Syntax Tree)

    • Babel接收解析得到的AST并通过babel-traverse对其进行深度优先遍历,在此过程中对节点进行添加、更新及移除操作。
    • 生成
    • babel-generator 模块用来将转换后的抽象语法树(AST Abstract Syntax Tree)转化为Javascript 字符串
    • 将经过转换的AST通过babel-generator再转换为js代码,过程及时深度遍历整个AST,然后构建转换后的代码字符串。

    ** Babel 中重要的对象Vistor** babel 在处理一个节点时,是以访问者的形式获取节点的信息,并进行相关的操作,这种操作是通过visitor对象实现的。 在 visitor 中定义了处理不同节点的函数。

    visitor: {
      Program: {
        enter(path, state) {
          console.log('start processing this module...');
        },
          exit(path, state) {
            console.log('end processing this module!');
          }
      },
        ImportDeclaration:{
          enter(path, state) {
            console.log('start processing ImportDeclaration...');
            // do something
          },
            exit(path, state) {
              console.log('end processing ImportDeclaration!');
              // do something
            }
        }
    }
    

    AST#

    定义 - AST (Abstract Syntax Tree)是抽象语法树英文的缩写,AST语法树每一层都拥有相同的结构,这样的每一层结构也被叫做节点(Node)。 - AST 是源代码的抽象语法结构树状表现形式,Webpack、ESLint、JSX、TypeScript 的编译和模块化规则之间的转化都是通过 AST 来实现对代码的检查、分析以及编译等操作。 - 一个 AST 可以由单一的节点或是成百上千个节点构成。 它们组合在一起可以描述用于静态分析的程序语法。 Javascript 语法的AST(抽象语法树) javascript 语句要想知道抽象语法树之后的代码是什么,里面的字段都代表什么含义以及遍历的规则,可以通过javascript语法转换AST工具来实现javascript语法的在线转换; 例如: image.png - esprima、estraverse 和 escodegen 模块是操作 AST 的三个重要模块,也是实现 babel 的核心依赖。

    例如:语法转换插件需要借助 babel-core 和 babel-types 两个模块,就是依赖 esprima、estraverse 和 escodegen 每一个含有type属性的对象,我们称之为节点,修改是指获取对应的类型并修改改节点的属性即可;

    {
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "answer"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "*",
                        "left": {
                            "type": "Literal",
                            "value": 6,
                            "raw": "6"
                        },
                        "right": {
                            "type": "Literal",
                            "value": 7,
                            "raw": "7"
                        }
                    }
                }
            ],
            "kind": "var"
        }
      ],
      "sourceType": "script" 
    }```  
    
    **estraverse 遍历和修改AST**
    查看遍历过程:
    
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/f22149f4ce544f33a8c707b8b3e2443c.png)
    

    const esprima = require("esprima"); const estraverse = require("estraverse");

    let code = "var answer=6 * 7";

    // 遍历语法树 estraverse.traverse(esprima.parseScript(code), { enter(node) { console.log("enter", node.type); }, leave(node) { console.log("leave", node.type); } });

     - 打印结果如下:
    
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/ee3b38ff5e7f4679b14b7d29f9aef9b5.png)
    
    - 以上代码通过estraverse模块的traverse方法,将esprima模块装换的AST进行了遍历。
    - 通过打印type属性,可以知道深度遍历AST就是遍历每一层的type属性,所以遍历会分为两个阶段,进入阶段和离开阶段,在traverse方法中分别用参数enter和leave两个函数监听;
    **escodegen 将 AST 转换成 JS**
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/e303480d43374734aa893c135cfb27cc.png)
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/20ddfc433d134d20843b54bbec0b5258.png)
    
    踩0 评论0
  • 05.23 13:50:52
    回答了问题 2020-05-23 13:50:52

    webpack 中 loader 和 plugin 的区别是什么(平安)#前端面试

    loader:webpack自身只支持js和json这两种格式的文件,对于其他文件需要通过loader将其转换为commonJS规范的文件后,webpack才能解析到 plugin:是用于在webpack打包编译过程里,在对应的事件节点里执行自定义操作,比如资源管理、bundle文件优化等操作

    踩0 评论0
  • 05.23 13:49:32
    回答了问题 2020-05-23 13:49:32

    v-if、v-show、v-html 的原理是什么,它是如何封装的?#前端面试

    v-if会调用addIfCondition方法,生成vnode的时候会忽略对应节点,render的时候就不会渲染; v-show会生成vnode,render的时候也会渲染成真实节点,只是在render过程中会在节点的属性中修改属性display值; v-html会先移除节点下的所有节点,调用html方法,通过addProp添加innerHTML属性,归根结底还是设置innerHTML为v-html的值

    踩0 评论0
  • 05.23 13:47:22
    回答了问题 2020-05-23 13:47:22

    Vue 中的 computed 和 watch 的区别在哪里(虾皮)#前端面试

    computed:计算属性

    计算属性是由data中的已知值,得到的一个新值。 这个新值只会根据已知值的变化而变化,其他不相关的数据的变化不会影响该新值。 计算属性不在data中,计算属性新值的相关已知值在data中。 别人变化影响我自己。 watch:监听数据的变化

    监听data中数据的变化 监听的数据就是data中的已知值 我的变化影响别人

    1.watch擅长处理的场景:一个数据影响多个数据

    2.computed擅长处理的场景:一个数据受多个数据影响

    踩0 评论0
  • 05.23 13:46:13
    回答了问题 2020-05-23 13:46:13

    前端项目如何找出性能瓶颈(阿里)#前端面试

    我觉得应该首先理一理会造成性能损耗的一些场景:

    比如大列表的渲染,大量dom的渲染 2.大量图片的加载,过多资源的请求. 3.代码中有没有耗时的计算操作,或则大量循环.递归 编写的组件过于庞大 层级过深,依赖模块过多等. 我觉得首先就是查看请求的资源体积是否过大,如果过大考虑压缩,减少不必要的资源的请求,不必要的js代码的代码加载,用字体图标代替图片,异步加载等等. 但是我觉得基本的优化策略(减少请求数,压缩请求资源的体积)都已经做过了,感觉性能还是没有提升,可能应该关注与代码层面的优化吧,比如过大的第三方库能不能换成轻量级的,代码中有没有很耗时的操作循环和递归,过多的分支条件语句,能不能改写以提高执行效率,简化复杂的组件逻辑,减少不必要的依赖,是否有杀鸡用了牛刀的操作等.暂时想到的就这些.

    踩0 评论0
  • 05.23 13:43:59
    回答了问题 2020-05-23 13:43:59

    手写二进制转 Base64(阿里)#前端面试

    方法一:

    let encodedData = window.btoa("this is a example");
    console.log(encodedData); // dGhpcyBpcyBhIGV4YW1wbGU=
    
    let decodeData = window.atob(encodedData);
    console.log(decodeData); // this is a example
    

    方法二:

    j// 将二进制数据每 6bit 位替换成一个 base64 字符
    function binaryTobase64(code) {
      let base64Code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      let res = '';
      // 1 bytes = 8bit,6bit 位替换成一个 base64 字符
      // 所以每 3 bytes 的数据,能成功替换成 4 个 base64 字符
        
      // 对不足 24 bit (也就是 3 bytes) 的情况进行特殊处理
      if (code.length % 24 === 8) {
        code += '0000';
        res += '=='
      }
      if (code.length % 24 === 16) {
        code += '00';
        res += '='
      }
    
      let encode = '';
      // code 按 6bit 一组,转换为
      for (let i = 0; i < code.length; i += 6) {
        let item = code.slice(i, i + 6);
        encode += base64Code[parseInt(item, 2)];
      }
      return encode + res;
    }```  
    
    
    踩0 评论0
  • 05.23 13:41:04
    回答了问题 2020-05-23 13:41:04

    将 '10000000000' 形式的字符串,以每 3 位进行分隔展示 '10.000.000.00

    方法一:

    // 德国以 . 分割金钱, 转到德国当地格式化方案即可
    10000000000..toLocaleString('de-DE') 
    
    // 寻找字符空隙加 .
    '10000000000'.replace(/\B(?=(\d{3})+(?!\d))/g, '.')
    
    // 寻找数字并在其后面加 . 
    '10000000000'.replace(/(\d)(?=(\d{3})+\b)/g, '$1.')
    

    方法二:

    var str = '10000000000';
    var arr = [];
    for(var i=0; i<Math.round(a.length/3); i++){
    arr.push(str.substring(str.length-3*(i+1), str.length-i*3));
    }
    arr.reverse();
    arr.join('.')
    
    踩0 评论0
  • 05.23 13:34:36
    回答了问题 2020-05-23 13:34:36

    算法题)求多个数组之间的交集(阿里)#前端面试

    方法一:

    let a = new Set([1, 2, 3]);
    let b = new Set([4, 3, 2]);
    # 把aSet结构成数组,然后根据filter去过滤掉只保留存在b数组中的元素
    let intersect = new Set([...a].filter(x => b.has(x)));
    // set {2, 3}
    

    方法二:

    function intersect(...args) {
      if (args.length === 0) {
        return []
      }
    
      if (args.length === 1) {
        return args[0]
      }
    
      return args.reduce((result, arg) => {
        return result.filter(item => arg.includes(item))
      })
    }
    
    踩0 评论0
  • 05.23 13:29:52
    回答了问题 2020-05-23 13:29:52

    Vue 中的 computed 是如何实现的(腾讯、平安)#前端面试

    computed本身是通过代理的方式代理到组件实例上的,所以读取计算属性的时候,执行的是一个内部的getter,而不是用户定义的方法。

    computed内部实现了一个惰性的watcher,在实例化的时候不会去求值,其内部通过dirty属性标记计算属性是否需要重新求值。当computed依赖的任一状态(不一定是return中的)发生变化,都会通知这个惰性watcher,让它把dirty属性设置为true。所以,当再次读取这个计算属性的时候,就会重新去求值。

    踩0 评论0
  • 05.23 13:27:38
    回答了问题 2020-05-23 13:27:38

    为什么 HTTP1.1 不能实现多路复用#前端面试

    HTTP1.x是序列和阻塞机制

    HTTP 2.0 是多工复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。

    举例来说,在一个TCP连接里面,服务器同时收到了A请求和B请求,于是先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。

    旧的http1.1是会等 A请求完全处理完后在 处理B请求,会阻塞

    另:http1.1已经实现了管道机制:即 在同一个TCP连接里面,客户端可以同时发送多个请求。http 1.0并做不到,所以效率很低

    踩0 评论0
正在加载, 请稍后...
滑动查看更多
  • 发表了文章 2020-05-22

    如何在 H5 和小程序项目中计算白屏时间和首屏时间#前端面试

正在加载, 请稍后...
滑动查看更多
  • 回答了问题 2020-05-23

    如何模拟实现 Array.prototype.splice#前端面试

    Array.prototype._splice = function(start, deleteCount, ...items) {
      if (start > 0) {
        if (start > this.length - 1) {
          start = this.length - 1;
        }
      } else  {
        if (Math.abs(start) > this.length - 1) {
          start = 0;
        } else {
          start = this.length - 1 + start;
        }
      }
    
      const newArr = [];
      const removed = [];
    
      deleteCount = isNaN(Number(deleteCount)) ? 0 : Number(deleteCount);
    
      for (let i = 0; i < this.length; i++) {
        if (i < start || i > start + deleteCount - 1) {
          newArr.push(this[i]);
        } else {
          removed.push(this[i])
        }
        if (i === start + deleteCount - 1) {
          newArr.push(...items)
        }
      }
    
      for (let i = 0; i < newArr.length; i++) {
        this[i] = newArr[i];
      }
    
      this.length = newArr.length;
    
      return removed;
    }
    
    踩0 评论0
  • 回答了问题 2020-05-23

    实现 Promise.retry#前端面试

    image.png

    踩0 评论0
  • 回答了问题 2020-05-23

    浏览器缓存 ETag 里的值是怎么生成的#前端面试

    Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)。 语法:

    ETag: W/"<etag_value>"
    ETag: "<etag_value>"
    

    'W/'(大小写敏感) 表示使用弱验证器,可选。 弱验证器很容易生成,但不利于比较。 强验证器是比较的理想选择,但很难有效地生成。 相同资源的两个弱Etag值可能语义等同,但不是每个字节都相同。 "<etag_value>" 实体标签唯一地表示所请求的资源。 它们是位于双引号之间的ASCII字符串(如“675af34563dc-tr34”)。 没有明确指定生成ETag值的方法。 通常,使用内容的散列,最后修改时间戳的哈希值,或简单地使用版本号。 例如,MDN使用wiki内容的十六进制数字的哈希值。

    踩0 评论0
  • 回答了问题 2020-05-23

    求最终 left、right 的宽度#前端面试

    剩余的空间:600 - (300 + 200) = 100。 子元素的 flex-grow 的值分别为 1,2, 剩余空间用3等分来分 100 / 3 = 33.3333333 所以 left = 300 + 1 * 33.33 = 333.33 right = 200 + 2 * 33.33 = 266.67

    踩0 评论0
  • 回答了问题 2020-05-23

    求最终 left、right 的宽度#前端面试题

    子元素的 flex-shrink 的值分别为 2,1 溢出:500+400 - 600 = 300。 总权重为 2 * 500+ 1 * 400 = 1400 两个元素分别收缩: 300 * 2(flex-shrink) * 500(width) / 1400= 214.28 300 * 1(flex-shrink) * 400(width) / 1400= 85.72 三个元素的最终宽度分别为: 500 - 214.28 = 285.72 400 - 85.72 = 314.28

    踩0 评论0
  • 回答了问题 2020-05-23

    弹性盒子中 flex: 0 1 auto 表示什么意思#前端面试

    flex :flex-grow flex-shrink flex-basis
    
    ①.flex-grow 剩余空间索取
    
    默认值为0,不索取
    
    eg:父元素400,子元素A为100px,B为200px.则剩余空间为100
    
    此时A的flex-grow 为1,B为2,
    
    则A=100px+1001/3; B=200px+1002/3
    
    ②.flex-shrink 子元素总宽度大于复制元素如何缩小
    
    父400px,A 200px B 300px
    
    AB总宽度超出父元素100px;
    
    如果A不减少,则flex-shrink :0,B减少;
    
    ②,flex-basis
    
    该属性用来设置元素的宽度,当然width也可以用来设置元素的宽度,如果设置了width和flex-basis,那么flex-basis会覆盖width值。
    
    踩0 评论0
  • 回答了问题 2020-05-23

    实现一个批量请求函数 multiRequest(urls, maxNum)#前端面试

    借鉴了一些题解的实现,用例跑通了,有问题或者可优化的话请各位大佬指正。 解题的关键是:队列和递归

    代码如下

    function handleFetchQueue(urls, max, callback) {
      const urlCount = urls.length;
      const requestsQueue = [];
      const results = [];
      let i = 0;
      const handleRequest = url => {
        const req = fetchFunc(url)
          .then(res => {
            results.push(res);
          })
          .catch(e => {
            results.push(e);
          })
          .finally(() => {
            const len = results.length;
            if (len < urlCount) {
              // 完成请求就出队
              requestsQueue.shift();
              handleRequest(urls[++i]);
            } else if (len === urlCount) {
              "function" === typeof callback && callback(results);
            }
          });
        requestsQueue.push(req);
        // 只要满足就继续请求
        if (requestsQueue.length <= max) {
          handleRequest(urls[++i]);
        }
      };
      handleRequest(urls[i]);
    }
    
    踩0 评论0
  • 回答了问题 2020-05-23

    实现一个 normalize 函数,能将输入的特定的字符串转化为特定的结构化数据#前端面试

    let normalize = str => {
      let result = {}
      let c
      
      // 字符串转化为数组
      let arr = str.split(/[\[\]]/g).filter(Boolean)
      
      // 生成结构化数据
      arr.forEach((item, index) => {
        if(index != 0) {
          c.children = {}
          c.children.value = item
          c= c.children
        } else {
          result.value = item
          c= result
        }
      })
      
      return result
    }
    let str = '[abc[bcd[def]]]'
    normalize(str)
    // {value: 'abc', children: {value: 'bcd', children: {value: 'def'}}}
    
    踩0 评论0
  • 回答了问题 2020-05-23

    用最简洁代码实现 indexOf 方法#前端面试

    function indexOf(str1,str2){
    	let len1 = str1.length;
    	let len2 = str2.length;
    	for (var i = 0; i < len1; i++) {
    		//从当前i开始截取一直到str2的长度+1,返回的字符串若是和str2相等就说明i就是这个str2在str1开始的位置
    		if(str1.slice(i,len2+i) == str2){
    			return i;
    		}
    	}
    	return -1;
    	
    }
    console.log(indexOf("abcdef","cde"));
    

    image.png

    踩0 评论0
  • 回答了问题 2020-05-23

    二分查找如何定位左边界和右边界#前端面试

    //递归查找
    function erfen_digui(arr, val, left = 0, right = arr.length - 1) {
            if (left > right) {
              return -1;
            }
            let cent = Math.floor((right + left) / 2);
            if (arr[cent] === val) {
              return `最终查找结果下标为${cent}`;
            } else if (arr[cent] > val) {
              right = cent - 1;
            } else {
              left = cent + 1;
            }
            return erfen_digui(arr, val, left, right);
          }
    //非递归查找
          function erfen_feidigui(arr, val) {
            let left = 0,
              right = arr.length - 1;
            while (left <= right) {
              let cent = left + Math.floor((right - left) / 2);
              if (arr[cent] === val) {
                return `最终查找结果下标为${cent}`;
              } else if (arr[cent] > val) {
                right = cent - 1;
              } else {
                left = cent + 1;
              }
            }
            return -1;
          }
    
    //左边界查找(查找第一个元素)
    function erfen_digui(arr, val, left = 0, right = arr.length - 1) {
            if (left > right) {
              return -1;
            }
            let cent = Math.floor((right + left) / 2);
            if (arr[cent] === val) {
              /****************改动点********************/
              if (arr[cent - 1] === val) {
                right = cent - 1;
              } else {
                return `最终查找结果下标为${cent}`;
              }
              /*****************************************/
            } else if (arr[cent] > val) {
              right = cent - 1;
            } else {
              left = cent + 1;
            }
            return erfen_digui(arr, val, left, right);
          }
    
    // 二分查找右边界(查找最后一个元素)
    function erfen_digui(arr, val, left = 0, right = arr.length - 1) {
            if (left > right) {
              return -1;
            }
            let cent = Math.floor((right + left) / 2);
            if (arr[cent] === val) {
              /****************改动点********************/
              if (arr[cent + 1] === val) {
                left = cent + 1;
              } else {
                return `最终查找结果下标为${cent}`;
              }
              /*****************************************/
            } else if (arr[cent] > val) {
              right = cent - 1;
            } else {
              left = cent + 1;
            }
            return erfen_digui(arr, val, left, right);
          }
    
    踩0 评论0
  • 回答了问题 2020-05-23

    babel 怎么把字符串解析成 AST,是怎么进行词法/语法分析的?#前端面试

    Babel

    定义 - Babel 是我们知道的将 ES6、ES7等代码转译为 ES5 代码且能安全稳定运行最好的工具 - 同时它允许开发者开发插件,能够在编译时期转换 JavaScript 的结构。

    Babel概述 我们需要知道 3 个 Babel 处理流程中的重要工具; 1. 解析

    • Babylon是一个解析器,它可以将javascript字符串,转化为更加友好的表现形式,称之为抽象语法树; 在解析过程中有两个阶段:词法分析和语法分析,
    • 词法分析阶段:字符串形式的代码转换为令牌(tokens)流,令牌类似于AST中的节点;
    • 语法分析阶段:把一个令牌流转化为AST的形式,同时这个阶段会把令牌中的信息转化为AST的表述结构
    • 转换

    • babel-traverse 模块允许你浏览、分析和修改抽象语法树(AST Abstract Syntax Tree)

    • Babel接收解析得到的AST并通过babel-traverse对其进行深度优先遍历,在此过程中对节点进行添加、更新及移除操作。
    • 生成
    • babel-generator 模块用来将转换后的抽象语法树(AST Abstract Syntax Tree)转化为Javascript 字符串
    • 将经过转换的AST通过babel-generator再转换为js代码,过程及时深度遍历整个AST,然后构建转换后的代码字符串。

    ** Babel 中重要的对象Vistor** babel 在处理一个节点时,是以访问者的形式获取节点的信息,并进行相关的操作,这种操作是通过visitor对象实现的。 在 visitor 中定义了处理不同节点的函数。

    visitor: {
      Program: {
        enter(path, state) {
          console.log('start processing this module...');
        },
          exit(path, state) {
            console.log('end processing this module!');
          }
      },
        ImportDeclaration:{
          enter(path, state) {
            console.log('start processing ImportDeclaration...');
            // do something
          },
            exit(path, state) {
              console.log('end processing ImportDeclaration!');
              // do something
            }
        }
    }
    

    AST#

    定义 - AST (Abstract Syntax Tree)是抽象语法树英文的缩写,AST语法树每一层都拥有相同的结构,这样的每一层结构也被叫做节点(Node)。 - AST 是源代码的抽象语法结构树状表现形式,Webpack、ESLint、JSX、TypeScript 的编译和模块化规则之间的转化都是通过 AST 来实现对代码的检查、分析以及编译等操作。 - 一个 AST 可以由单一的节点或是成百上千个节点构成。 它们组合在一起可以描述用于静态分析的程序语法。 Javascript 语法的AST(抽象语法树) javascript 语句要想知道抽象语法树之后的代码是什么,里面的字段都代表什么含义以及遍历的规则,可以通过javascript语法转换AST工具来实现javascript语法的在线转换; 例如: image.png - esprima、estraverse 和 escodegen 模块是操作 AST 的三个重要模块,也是实现 babel 的核心依赖。

    例如:语法转换插件需要借助 babel-core 和 babel-types 两个模块,就是依赖 esprima、estraverse 和 escodegen 每一个含有type属性的对象,我们称之为节点,修改是指获取对应的类型并修改改节点的属性即可;

    {
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "answer"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "*",
                        "left": {
                            "type": "Literal",
                            "value": 6,
                            "raw": "6"
                        },
                        "right": {
                            "type": "Literal",
                            "value": 7,
                            "raw": "7"
                        }
                    }
                }
            ],
            "kind": "var"
        }
      ],
      "sourceType": "script" 
    }```  
    
    **estraverse 遍历和修改AST**
    查看遍历过程:
    
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/f22149f4ce544f33a8c707b8b3e2443c.png)
    

    const esprima = require("esprima"); const estraverse = require("estraverse");

    let code = "var answer=6 * 7";

    // 遍历语法树 estraverse.traverse(esprima.parseScript(code), { enter(node) { console.log("enter", node.type); }, leave(node) { console.log("leave", node.type); } });

     - 打印结果如下:
    
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/ee3b38ff5e7f4679b14b7d29f9aef9b5.png)
    
    - 以上代码通过estraverse模块的traverse方法,将esprima模块装换的AST进行了遍历。
    - 通过打印type属性,可以知道深度遍历AST就是遍历每一层的type属性,所以遍历会分为两个阶段,进入阶段和离开阶段,在traverse方法中分别用参数enter和leave两个函数监听;
    **escodegen 将 AST 转换成 JS**
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/e303480d43374734aa893c135cfb27cc.png)
    ![image.png](https://ucc.alicdn.com/pic/developer-ecology/20ddfc433d134d20843b54bbec0b5258.png)
    
    踩0 评论0
  • 回答了问题 2020-05-23

    webpack 中 loader 和 plugin 的区别是什么(平安)#前端面试

    loader:webpack自身只支持js和json这两种格式的文件,对于其他文件需要通过loader将其转换为commonJS规范的文件后,webpack才能解析到 plugin:是用于在webpack打包编译过程里,在对应的事件节点里执行自定义操作,比如资源管理、bundle文件优化等操作

    踩0 评论0
  • 回答了问题 2020-05-23

    v-if、v-show、v-html 的原理是什么,它是如何封装的?#前端面试

    v-if会调用addIfCondition方法,生成vnode的时候会忽略对应节点,render的时候就不会渲染; v-show会生成vnode,render的时候也会渲染成真实节点,只是在render过程中会在节点的属性中修改属性display值; v-html会先移除节点下的所有节点,调用html方法,通过addProp添加innerHTML属性,归根结底还是设置innerHTML为v-html的值

    踩0 评论0
  • 回答了问题 2020-05-23

    Vue 中的 computed 和 watch 的区别在哪里(虾皮)#前端面试

    computed:计算属性

    计算属性是由data中的已知值,得到的一个新值。 这个新值只会根据已知值的变化而变化,其他不相关的数据的变化不会影响该新值。 计算属性不在data中,计算属性新值的相关已知值在data中。 别人变化影响我自己。 watch:监听数据的变化

    监听data中数据的变化 监听的数据就是data中的已知值 我的变化影响别人

    1.watch擅长处理的场景:一个数据影响多个数据

    2.computed擅长处理的场景:一个数据受多个数据影响

    踩0 评论0
  • 回答了问题 2020-05-23

    前端项目如何找出性能瓶颈(阿里)#前端面试

    我觉得应该首先理一理会造成性能损耗的一些场景:

    比如大列表的渲染,大量dom的渲染 2.大量图片的加载,过多资源的请求. 3.代码中有没有耗时的计算操作,或则大量循环.递归 编写的组件过于庞大 层级过深,依赖模块过多等. 我觉得首先就是查看请求的资源体积是否过大,如果过大考虑压缩,减少不必要的资源的请求,不必要的js代码的代码加载,用字体图标代替图片,异步加载等等. 但是我觉得基本的优化策略(减少请求数,压缩请求资源的体积)都已经做过了,感觉性能还是没有提升,可能应该关注与代码层面的优化吧,比如过大的第三方库能不能换成轻量级的,代码中有没有很耗时的操作循环和递归,过多的分支条件语句,能不能改写以提高执行效率,简化复杂的组件逻辑,减少不必要的依赖,是否有杀鸡用了牛刀的操作等.暂时想到的就这些.

    踩0 评论0
  • 回答了问题 2020-05-23

    手写二进制转 Base64(阿里)#前端面试

    方法一:

    let encodedData = window.btoa("this is a example");
    console.log(encodedData); // dGhpcyBpcyBhIGV4YW1wbGU=
    
    let decodeData = window.atob(encodedData);
    console.log(decodeData); // this is a example
    

    方法二:

    j// 将二进制数据每 6bit 位替换成一个 base64 字符
    function binaryTobase64(code) {
      let base64Code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      let res = '';
      // 1 bytes = 8bit,6bit 位替换成一个 base64 字符
      // 所以每 3 bytes 的数据,能成功替换成 4 个 base64 字符
        
      // 对不足 24 bit (也就是 3 bytes) 的情况进行特殊处理
      if (code.length % 24 === 8) {
        code += '0000';
        res += '=='
      }
      if (code.length % 24 === 16) {
        code += '00';
        res += '='
      }
    
      let encode = '';
      // code 按 6bit 一组,转换为
      for (let i = 0; i < code.length; i += 6) {
        let item = code.slice(i, i + 6);
        encode += base64Code[parseInt(item, 2)];
      }
      return encode + res;
    }```  
    
    
    踩0 评论0
  • 回答了问题 2020-05-23

    将 '10000000000' 形式的字符串,以每 3 位进行分隔展示 '10.000.000.00

    方法一:

    // 德国以 . 分割金钱, 转到德国当地格式化方案即可
    10000000000..toLocaleString('de-DE') 
    
    // 寻找字符空隙加 .
    '10000000000'.replace(/\B(?=(\d{3})+(?!\d))/g, '.')
    
    // 寻找数字并在其后面加 . 
    '10000000000'.replace(/(\d)(?=(\d{3})+\b)/g, '$1.')
    

    方法二:

    var str = '10000000000';
    var arr = [];
    for(var i=0; i<Math.round(a.length/3); i++){
    arr.push(str.substring(str.length-3*(i+1), str.length-i*3));
    }
    arr.reverse();
    arr.join('.')
    
    踩0 评论0
  • 回答了问题 2020-05-23

    算法题)求多个数组之间的交集(阿里)#前端面试

    方法一:

    let a = new Set([1, 2, 3]);
    let b = new Set([4, 3, 2]);
    # 把aSet结构成数组,然后根据filter去过滤掉只保留存在b数组中的元素
    let intersect = new Set([...a].filter(x => b.has(x)));
    // set {2, 3}
    

    方法二:

    function intersect(...args) {
      if (args.length === 0) {
        return []
      }
    
      if (args.length === 1) {
        return args[0]
      }
    
      return args.reduce((result, arg) => {
        return result.filter(item => arg.includes(item))
      })
    }
    
    踩0 评论0
  • 回答了问题 2020-05-23

    Vue 中的 computed 是如何实现的(腾讯、平安)#前端面试

    computed本身是通过代理的方式代理到组件实例上的,所以读取计算属性的时候,执行的是一个内部的getter,而不是用户定义的方法。

    computed内部实现了一个惰性的watcher,在实例化的时候不会去求值,其内部通过dirty属性标记计算属性是否需要重新求值。当computed依赖的任一状态(不一定是return中的)发生变化,都会通知这个惰性watcher,让它把dirty属性设置为true。所以,当再次读取这个计算属性的时候,就会重新去求值。

    踩0 评论0
  • 回答了问题 2020-05-23

    为什么 HTTP1.1 不能实现多路复用#前端面试

    HTTP1.x是序列和阻塞机制

    HTTP 2.0 是多工复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。

    举例来说,在一个TCP连接里面,服务器同时收到了A请求和B请求,于是先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。

    旧的http1.1是会等 A请求完全处理完后在 处理B请求,会阻塞

    另:http1.1已经实现了管道机制:即 在同一个TCP连接里面,客户端可以同时发送多个请求。http 1.0并做不到,所以效率很低

    踩0 评论0
正在加载, 请稍后...
滑动查看更多