deepClone 手写递归,具体实现方式,特别数据的边界处理(时间,function,正则);如何解决循环引用的问题,处理方式,判断条件终止,deepClone 如何终止的?
- export function deepClone(obj, hash = new WeakMap()) {
- // 处理null或者undefined
- if (obj === null) return obj;
- // 处理日期类型
- if (obj instanceof Date) return new Date(obj);
- // 处理正则类型
- if (obj instanceof RegExp) return new RegExp(obj);
- // 普通值或函数不需要深拷贝
- if (typeof obj !== "object") return obj;
- // 对象进行深拷贝
- if (hash.get(obj)) return hash.get(obj);
- let cloneObj = new obj.constructor();
- // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
- hash.set(obj, cloneObj);
- for (let key in obj) {
- if (obj.hasOwnProperty(key)) {
- // 实现一个递归拷贝
- cloneObj[key] = deepClone(obj[key], hash);
- }
- }
- return cloneObj;
- }
- 复制代码
- // 求平均时间
- var arr = ["8:01", "9:30", "11:50"];
-
- const average = (arr) => {
- let minutes = 0;
- arr.forEach((o) => {
- let a = o.split(":");
- minutes = minutes + a[0] * 60 + parseInt(a[1]);
- });
- minutes = minutes / arr.length;
-
- let hour = Math.floor(minutes / 60);
- let minute = minutes - 60 * hour;
- console.log(hour, "...", minute);
-
- return `${hour}:${minute}`;
- };
-
- console.log(average(["8:01", "9:30", "11:50"])); // 9:47
- 复制代码
感谢评论区[MoTou]提供的如下方法,更加简洁:
- var arr = ['8:01', '9:30', '11:50']
- let res = 0
- arr.forEach(el => {
- res += new Date('2000/01/01 ' + el).getTime()
- })
- const a = new Date(res / arr.length)
- console.log(`${a.getHours()}:${a.getMinutes()}`)
- 复制代码
data-v-[hash]
,即 CSS 带属性选择器cache
属性,对性能优化作用最大,改善构建速度- // 构造函数的原型
- // function的原型
- // object的原型
- function Foo() {}
-
- const foo = new Foo()
- Foo.__proto__ = ?
- foo.__proto__ = ?
- Foo.prototype.__proto__ = ?
- 复制代码
- class EventEmitter {
- constructor() {
- this.arr = {}
- }
-
- on(name, fn) {
- this.arr[name] ? this.arr[name].push(fn) : this.arr[name] = [fn]
- }
-
- off(name, fn) {
- if(this.arr[name)) {
- if(fn) {
- this.arr[name] = this.arr[name].filter(o => o!==fn);
- } else {
- delete this.arr[name]
- }
- }
- }
-
- once(name, fn) {
- this.arr[name] = fn
- }
-
- emit(name, params) {
- // this.$emit('change', params)
- if(Array.isArray(this.arr[name]))
- {
- this.arr[name].forEach(fn => {
- fn(params)
- })
- } else{
- this.arr[name](params)
- off(name)
- }
- }
- }
- 复制代码
- // accumulator-累加器;currentValue-当前值;currentIndex-当前值索引;initialValue-初始值;
- array.reduce(function(accumulator, currentValue, currentIndex, array), initialValue)
-
- // 1. 数组求和
- let total = [1, 2, 3, 4].reduce((acc, cur) => (acc += cur), 90);
-
- // 2. 数组去重 [1,2,4,null]
- // 2.1 filter
- let arr = [1, 2, 2, 4, null, null].filter((item, index, arr) => arr.indexOf(item) === index);
- // 2.2 new Set
- let arr = [...new Set([1, 2, 2, 4, null, null])];
- // 2.3 reduce
- let arr = [1, 2, 2, 4, null, null].reduce((acc, cur) => {
- return acc.includes(cur) ? acc : acc.concat(cur);
- }, []);
-
- // 3. 扁平化多维数组 [0, 1, 2, 3, 4, 5, 6, 7]
- let arr = [0,[1],[2, 3],[4, [5, 6, 7]]]
- // 3.1 flat
- arr.flat(Infinity);
- // 3.2 reduce
- let dimensionReduction = function (arr) {
- return arr.reduce((acc, cur) => {
- return acc.concat(
- Array.isArray(cur) ?
- dimensionReduction(cur) :
- cur
- );
- }, []);
- }
- dimensionReduction(arr);
-
- // 4. 求字符串中字母出现的次数
- const str = 'sfhjasfjgfasjuwqrqadqeiqsajsdaiwqdaklldflas-cmxzmnha';
- const res = str.split('').reduce((acc, cur) => {acc[cur] ? acc[cur]++ : acc[cur] = 1; return acc;}, {});
-
- // 5. 数组转对象 按照id 取出stream
- var streams = [{name: '技术', id: 1}, {name: '设计', id: 2}];
- var obj = streams.reduce((acc, cur) => {acc[cur.id] = cur; return acc;}, {});
- 复制代码
- Array.prototype.myReduce = function (cb, initValue) {
- const array = this; // 获取数组
- let pre = initValue || array[0];
- const startIndex = initValue ? 1 : 0;
- for (let i = startIndex; i < array.length; i++) {
- const cur = array[i];
- pre = cb(pre, cur, i, array);
- }
- return pre;
- };
- 复制代码
给定一个二叉树和一个数字 n,判断二叉树中是否有一个路径上的数字之和等于给定的数字 n
- function hasPathSum(root, sum) {
- if (!root) {
- return false;
- }
- if (!root.left && !root.right) {
- return sum === root.val;
- }
- return (
- hasPathSum(root.left, sum - root.val) ||
- hasPathSum(root.right, sum - root.val)
- );
- }
- 复制代码
数据劫持
结合发布者-订阅者
模式实现两个字符串型数字的加法运算,返回字符串型的数字的和
注意:无法直接对传入参数进行转换成数字
思路:一位数一位数相加,采用隐式转化的方式相加
- sum("1233", "233"); // '1466'
-
- function sum(num_str1, num_str2) {
- // 字符串转字符数组
- const num_arr1 = num_str1.split("");
- // 字符串转字符数组
- const num_arr2 = num_str2.split("");
- // 竖式加法计算次数
- let sum_length = Math.max(num_arr1.length, num_arr2.length);
- // 求和结果
- const sum = [];
- // 进位标志位
- let CF = 0;
- // 循环语句为什么加CF进位存在判断呢,因为当最高位产生进位,(如 999 + 9999)
- // 计算到千分位9的时候,并且没有更多数要参与运算时,仍然需要进位参与一次运算,否则丢失进位。
- while (sum_length-- > 0 || CF) {
- // 求和,注意数组长度不够,按0算
- let temp =
- Number.parseInt(num_arr1.pop() || 0) +
- Number.parseInt(num_arr2.pop() || 0) +
- CF;
- if (temp >= 10) {
- temp %= 10; // 取个位数
- CF = 1; // 产生进位(对于加法,最多产生1个进位)
- } else {
- CF = 0; // 未产生进位
- }
- sum.unshift(temp); // 记录求和数据(从头插入)
- }
- return sum.join("");
- }
- 复制代码
如何实现一个拖拽指令?
vue.js 自定义指令(拖拽、拖动、移动) 指令 v-drag
挑战性项目;项目难点
虚拟 dom 作用:多次数据变化,一次更新;怎么收集数据变化;
对象的深拷贝实现,递归调用死循环问题如何解决
浏览器缓存对比:cookie;localStorage;sessionStorage
权限管理实现:咋实现的?具体怎么处理,代码实现
单点登录 SSO 实现
vue2.0 和 vue3.0 区别
v-model 在 2.0 和 3.0 里面的区别,实现原理
flex 弹性布局
z-index 无效问题:生效范围;什么情况下会失效,边界条件
移动端 1px 边框
协商缓存和强缓存使用场景
强缓存:访问服务器获取到数据后缓存下来,过期时间之内不会再重复请求,而是直接从本地缓存数据库读取
http1.0:Expire 相应头。expires 表示未来资源会过期的时间
http1.1:Cache-Control。max-age=xxx(缓存的资源将在 xxx 秒后过期);no-cache(需要使用协商缓存来验证是否过期);no-store(不可缓存)
协商缓存:每次读取数据都要和服务端通信,增加缓存标志。如果不匹配,表示资源更新,返回新数据;匹配则表示没有更新,返回 304
http1.0:Last-Modified;If-Modified-Since;弊端是时间的精确度只能在秒,资源没变但时间变了,也会更新
http1.1:Etag; If-None-Match
常见情况
vue-router 实现原理
快排实现:核心递归[算法]
- //快排实现
- var a = [3, 2, 8, 4, 5, 6, 7, 1];
- //递归调用
- QuickSort(a, 0, a.length - 1);
- console.log(a.toString());
-
- function QuickSort(a, l, r) {
- if (l >= r) {
- return;
- }
- var target = a[l];
- var left = l,
- right = r;
-
- while (left < right) {
- while (left < right && target <= a[right]) {
- //B往左走寻找比挖出来的第一个萝卜轻,且位置在A右边的萝卜
- right--;
- }
- a[left] = a[right];
- while (left < right && a[left] <= target) {
- //A要往右寻找比一个萝卜重,且位置在B左边的萝卜
- left++;
- }
- a[right] = a[left];
- }
- a[left] = target;
- QuickSort(a, l, left);
- QuickSort(a, left + 1, r);
- }
- 复制代码
通过 props 传值不随着变化而变化,原因及解决方法(实际问题)
- // Promise.all
- export const all = (promiseArr) => {
- let result = [];
- let count = 0;
- return new myPromise((resolve, reject) => {
- for (let i = 0; i < promiseArr.length; i++) {
- Promise.resolve(promiseArr[i]).then(
- (res) => {
- result[i] = res;
- count++;
- // 全部promise执行成功之后才resolve
- if (count === promiseArr.length) {
- resolve(result);
- }
- },
- (err) => {
- reject(err);
- }
- );
- }
- });
- };
-
- // Promise.race
- export const race = (promiseArr) => {
- return new myPromise((resolve, reject) => {
- for (let i = 0; i < promiseArr.length; i++) {
- Promise.resolve(promiseArr[i]).then(
- (res) => {
- // 只要有任何一个promise状态变更就resolve
- resolve(res);
- },
- (err) => {
- reject(err);
- }
- );
- }
- });
- };
- 复制代码
- // 1 1
- // 2 2
- // 3 4 111 12 21 3
- // 4 1111 121 112 211 13 31 4 22
- // 四级台阶有多少种方法?
- const sum = (n) => {
- if (n == 1 || n == 2) {
- return n;
- }
- return sum(n - 1) + sum(n - 2);
- };
- 复制代码
CSS 高度塌陷的原因;如何解决(BFC)
敏捷开发的理解
遇到的问题,怎么解决的
自己估计的工作量和实际不符,完不成的情况?
频繁变更了需求,会咋办,改咋处理?
webGL 三维场景渲染优化
改变 this 指向的方式:apply,call,bind()
有一个请求,没有收到数据,如何排查:postman,联调方式,日志工具
如何节省沟通成本
数据埋点如何实现
模块化方式,import 和 require 区别
运行时
确定这些东西编译时
就能确定模块的依赖关系,以及输入和输出的变量前端性能优化:评价指标;具体优化实现;
webpack 配置,优化实现;核心概念:plugin,loader;loader 实现原理,如对 css 的处理解析