关于前端:JavaScript常见手写题熬夜整理

1次阅读

共计 19126 个字符,预计需要花费 48 分钟才能阅读完成。

实现 new 的过程

new 操作符做了这些事:

  • 创立一个全新的对象
  • 这个对象的 __proto__ 要指向构造函数的原型 prototype
  • 执行构造函数,应用 call/apply 扭转 this 的指向
  • 返回值为 object 类型则作为 new 办法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) {
  // 基于原型链 创立一个新对象
  let newObj = Object.create(fn.prototype);
  // 增加属性到新对象上 并获取 obj 函数的后果
  let res = fn.apply(newObj, args); // 扭转 this 指向

  // 如果执行后果有返回值并且是一个对象, 返回执行的后果, 否则, 返回新创建的对象
  return typeof res === 'object' ? res: newObj;
}
// 用法
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.say = function() {console.log(this.age);
};
let p1 = myNew(Person, "poety", 18);
console.log(p1.name);
console.log(p1);
p1.say();

实现 getValue/setValue 函数来获取 path 对应的值

// 示例
var object = {a: [{ b: { c: 3} }] }; // path: 'a[0].b.c'
var array = [{a: { b: [1] } }]; // path: '[0].a.b[0]'

function getValue(target, valuePath, defaultValue) {}

console.log(getValue(object, "a[0].b.c", 0)); // 输入 3
console.log(getValue(array, "[0].a.b[0]", 12)); // 输入 1
console.log(getValue(array, "[0].a.b[0].c", 12)); // 输入 12

实现

/**
 * 测试属性是否匹配
 */
export function testPropTypes(value, type, dev) {const sEnums = ['number', 'string', 'boolean', 'undefined', 'function']; // NaN
  const oEnums = ['Null', 'Object', 'Array', 'Date', 'RegExp', 'Error'];
  const nEnums = ['[object Number]',
    '[object String]',
    '[object Boolean]',
    '[object Undefined]',
    '[object Function]',
    '[object Null]',
    '[object Object]',
    '[object Array]',
    '[object Date]',
    '[object RegExp]',
    '[object Error]',
  ];
  const reg = new RegExp('\\[object (.*?)\\]');

  // 齐全匹配模式,type 应该传递相似格局[object Window] [object HTMLDocument] ...
  if (reg.test(type)) {
    // 排除 nEnums 的 12 种
    if (~nEnums.indexOf(type)) {if (dev === true) {console.warn(value, 'The parameter type belongs to one of 12 types:number string boolean undefined Null Object Array Date RegExp function Error NaN');
      }
    }

    if (Object.prototype.toString.call(value) === type) {return true;}

    return false;
  }
}
const syncVarIterator = {getter: function (obj, key, defaultValue) {
    // 后果变量
    const defaultResult = defaultValue === undefined ? undefined : defaultValue;

    if (testPropTypes(obj, 'Object') === false && testPropTypes(obj, 'Array') === false) {return defaultResult;}

    // 后果变量,临时指向 obj 持有的援用,后续将可能被一直的批改
    let result = obj;

    // 失去晓得值
    try {
      // 解析属性档次序列
      const keyArr = key.split('.');

      // 迭代 obj 对象属性
      for (let i = 0; i < keyArr.length; i++) {
        // 如果第 i 层属性存在对应的值则迭代该属性值
        if (result[keyArr[i]] !== undefined) {result = result[keyArr[i]];

          // 如果不存在则返回未定义
        } else {return defaultResult;}
      }
    } catch (e) {return defaultResult;}

    // 返回获取的后果
    return result;
  },
  setter: function (obj, key, val) {
    // 如果不存在 obj 则返回未定义
    if (testPropTypes(obj, 'Object') === false) {return false;}

    // 后果变量,临时指向 obj 持有的援用,后续将可能被一直的批改
    let result = obj;

    try {
      // 解析属性档次序列
      const keyArr = key.split('.');

      let i = 0;

      // 迭代 obj 对象属性
      for (; i < keyArr.length - 1; i++) {
        // 如果第 i 层属性对应的值不存在,则定义为对象
        if (result[keyArr[i]] === undefined) {result[keyArr[i]] = {};}

        // 如果第 i 层属性对应的值不是对象(Object)的一个实例,则抛出谬误
        if (!(result[keyArr[i]] instanceof Object)) {throw new Error('obj.' + keyArr.splice(0, i + 1).join('.') + 'is not Object');
        }

        // 迭代该层属性值
        result = result[keyArr[i]];
      }

      // 设置属性值
      result[keyArr[i]] = val;

      return true;
    } catch (e) {return false;}
  },
};

应用 promise 来实现

创立 enhancedObject 函数

const enhancedObject = (target) =>
  new Proxy(target, {get(target, property) {if (property in target) {return target[property];
      } else {return searchFor(property, target); // 理论应用时要对 value 值进行复位
      }
    },
  });

let value = null;
function searchFor(property, target) {for (const key of Object.keys(target)) {if (typeof target[key] === "object") {searchFor(property, target[key]);
    } else if (typeof target[property] !== "undefined") {value = target[property];
      break;
    }
  }
  return value;
}

应用 enhancedObject 函数

const data = enhancedObject({
  user: {
    name: "test",
    settings: {theme: "dark",},
  },
});

console.log(data.user.settings.theme); // dark
console.log(data.theme); // dark

以上代码运行后,控制台会输入以下代码:

dark
dark

通过观察以上的输入后果可知,应用 enhancedObject 函数解决过的对象,咱们就能够不便地拜访一般对象外部的深层属性。

前端手写面试题具体解答

用正则写一个依据 name 获取 cookie 中的值的办法

function getCookie(name) {var match = document.cookie.match(new RegExp('(^|)' + name + '=([^;]*)'));
  if (match) return unescape(match[2]);
}
  1. 获取页面上的 cookie 能够应用 document.cookie

这里获取到的是相似于这样的字符串:

'username=poetry; user-id=12345; user-roles=home, me, setting'

能够看到这么几个信息:

  • 每一个 cookie 都是由 name=value 这样的模式存储的
  • 每一项的结尾可能是一个空串 ''(比方username 的结尾其实就是), 也可能是一个空字符串 ' '(比方user-id 的结尾就是)
  • 每一项用 ";" 来辨别
  • 如果某项中有多个值的时候,是用 "," 来连贯的 (比方user-roles 的值)
  • 每一项的结尾可能是有 ";" 的(比方 username 的结尾),也可能是没有的 (比方user-roles 的结尾)
  • 所以咱们将这里的正则拆分一下:
  • '(^|)'示意的就是获取每一项的结尾,因为咱们晓得如果 ^ 不是放在 [] 里的话就是示意结尾匹配。所以这里 (^|) 的意思其实就被拆分为 (^) 示意的匹配 username 这种状况,它后面什么都没有是一个空串 (你能够把(^) 了解为 ^ 它前面还有一个暗藏的 '');而| 示意的就是或者是一个 " "(为了匹配user-id 结尾的这种状况)
  • +name+这没什么好说的
  • =([^;]*)这里匹配的就是 = 前面的值了,比方 poetry;刚刚说了^ 要是放在 [] 里的话就示意 "除了 ^ 前面的内容都能匹配",也就是非的意思。所以这里([^;]*) 示意的是除了 ";" 这个字符串别的都匹配 (* 应该都晓得什么意思吧,匹配 0 次或屡次)
  • 有的大佬等号前面是这样写的 '=([^;]*)(;|$)',而最初为什么能够把'(;|$)' 给省略呢?因为其实最初一个 cookie 项是没有 ';' 的,所以它能够合并到 =([^;]*) 这一步。
  • 最初获取到的 match 其实是一个长度为 4 的数组。比方:
[
  "username=poetry;",
  "","poetry",";"
]
  • 第 0 项:全量
  • 第 1 项:结尾
  • 第 2 项:两头的值
  • 第 3 项:结尾

所以咱们是要拿第 2 项 match[2] 的值。

  1. 为了避免获取到的值是 %xxx 这样的字符序列,须要用 unescape() 办法解码。

解析 URL Params 为对象

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 后果
{ user: 'anonymous',
  id: [123, 456], // 反复呈现的 key 要组装成数组,能被转成数字的就转成数字类型
  city: '北京', // 中文需解码
  enabled: true, // 未指定值得 key 约定为 true
}
*/
function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 前面的字符串取出来
  const paramsArr = paramsStr.split('&'); // 将字符串以 & 宰割后存到数组中
  let paramsObj = {};
  // 将 params 存到对象中
  paramsArr.forEach(param => {if (/=/.test(param)) { // 解决有 value 的参数
      let [key, val] = param.split('='); // 宰割 key 和 value
      val = decodeURIComponent(val); // 解码
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

      if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则增加一个值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // 如果对象没有这个 key,创立 key 并设置值
        paramsObj[key] = val;
      }
    } else { // 解决没有 value 的参数
      paramsObj[param] = true;
    }
  })

  return paramsObj;
}

字符串最长的不反复子串

题目形容

给定一个字符串 s,请你找出其中不含有反复字符的 最长子串 的长度。示例 1:

输出: s = "abcabcbb"
输入: 3
解释: 因为无反复字符的最长子串是 "abc",所以其长度为 3。示例 2:

输出: s = "bbbbb"
输入: 1
解释: 因为无反复字符的最长子串是 "b",所以其长度为 1。示例 3:

输出: s = "pwwkew"
输入: 3
解释: 因为无反复字符的最长子串是 "wke",所以其长度为 3。请留神,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。示例 4:

输出: s = ""
输入: 0

答案

const lengthOfLongestSubstring = function (s) {if (s.length === 0) {return 0;}

  let left = 0;
  let right = 1;
  let max = 0;
  while (right <= s.length) {let lr = s.slice(left, right);
    const index = lr.indexOf(s[right]);

    if (index > -1) {left = index + left + 1;} else {lr = s.slice(left, right + 1);
      max = Math.max(max, lr.length);
    }
    right++;
  }
  return max;
};

验证是否是身份证

function isCardNo(number) {var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
    return regx.test(number);
}

字符串查找

请应用最根本的遍从来实现判断字符串 a 是否被蕴含在字符串 b 中,并返回第一次呈现的地位(找不到返回 -1)。

a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isContain(a,b);
function isContain(a, b) {for (let i in b) {if (a[0] === b[i]) {
      let tmp = true;
      for (let j in a) {if (a[j] !== b[~~i + ~~j]) {tmp = false;}
      }
      if (tmp) {return i;}
    }
  }
  return -1;
}

实现 redux 中间件

简略实现

function createStore(reducer) {
  let currentState
  let listeners = []

  function getState() {return currentState}

  function dispatch(action) {currentState = reducer(currentState, action)
    listeners.map(listener => {listener()
    })
    return action
  }

  function subscribe(cb) {listeners.push(cb)
    return () => {}
  }

  dispatch({type: 'ZZZZZZZZZZ'})

  return {
    getState,
    dispatch,
    subscribe
  }
}

// 利用实例如下:function reducer(state = 0, action) {switch (action.type) {
    case 'ADD':
      return state + 1
    case 'MINUS':
      return state - 1
    default:
      return state
  }
}

const store = createStore(reducer)

console.log(store);
store.subscribe(() => {console.log('change');
})
console.log(store.getState());
console.log(store.dispatch({type: 'ADD'}));
console.log(store.getState());

2. 迷你版

export const createStore = (reducer,enhancer)=>{if(enhancer) {return enhancer(createStore)(reducer)
    }
    let currentState = {}
    let currentListeners = []

    const getState = ()=>currentState
    const subscribe = (listener)=>{currentListeners.push(listener)
    }
    const dispatch = action=>{currentState = reducer(currentState, action)
        currentListeners.forEach(v=>v())
        return action
    }
    dispatch({type:'@@INIT'})
    return {getState,subscribe,dispatch}
}

// 中间件实现
export applyMiddleWare(...middlewares){
    return createStore=>...args=>{const store = createStore(...args)
        let dispatch = store.dispatch

        const midApi = {
            getState:store.getState,
            dispatch:...args=>dispatch(...args)
        }
        const middlewaresChain = middlewares.map(middleware=>middleware(midApi))
        dispatch = compose(...middlewaresChain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }

// fn1(fn2(fn3())) 把函数嵌套顺次调用
export function compose(...funcs){if(funcs.length===0){return arg=>arg}
    if(funs.length===1){return funs[0]
    }
    return funcs.reduce((ret,item)=>(...args)=>ret(item(...args)))
}


//bindActionCreator 实现

function bindActionCreator(creator,dispatch){return ...args=>dispatch(creator(...args))
}
function bindActionCreators(creators,didpatch){//let bound = {}
    //Object.keys(creators).forEach(v=>{//     let creator = creator[v]
     //   bound[v] = bindActionCreator(creator,dispatch)
    //})
    //return bound

    return Object.keys(creators).reduce((ret,item)=>{ret[item] = bindActionCreator(creators[item],dispatch)
        return ret
    },{})
}

基于 Promise.all 实现 Ajax 的串行和并行

基于 Promise.all 实现 Ajax 的串行和并行

  • 串行:申请是异步的,须要期待上一个申请胜利,能力执行下一个申请
  • 并行:同时发送多个申请「HTTP申请能够同时进行,然而 JS 的操作都是一步步的来的,因为 JS 是单线程」, 期待所有申请都胜利,咱们再去做什么事件?
Promise.all([axios.get('/user/list'),
    axios.get('/user/list'),
    axios.get('/user/list')
]).then(results => {console.log(results);
}).catch(reason => {});

Promise.all 并发限度及 async-pool 的利用

并发限度指的是,每个时刻并发执行的 promise 数量是固定的,最终的执行后果还是放弃与原来的

const delay = function delay(interval) {return new Promise((resolve, reject) => {setTimeout(() => {// if (interval === 1003) reject('xxx');
            resolve(interval);
        }, interval);
    });
};
let tasks = [() => {return delay(1000);
}, () => {return delay(1003);
}, () => {return delay(1005);
}, () => {return delay(1002);
}, () => {return delay(1004);
}, () => {return delay(1006);
}];

/* Promise.all(tasks.map(task => task())).then(results => {console.log(results);
}); */

let results = [];
asyncPool(2, tasks, (task, next) => {task().then(result => {results.push(result);
        next();});
}, () => {console.log(results);
});
const delay = function delay(interval) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(interval);
        }, interval);
    });
};
let tasks = [() => {return delay(1000);
}, () => {return delay(1003);
}, () => {return delay(1005);
}, () => {return delay(1002);
}, () => {return delay(1004);
}, () => {return delay(1006);
}];

JS 实现 Ajax 并发申请管制的两大解决方案

tasks:数组,数组蕴含很多办法,每一个办法执行就是发送一个申请「基于 Promise 治理」

function createRequest(tasks, pool) {
    pool = pool || 5;
    let results = [],
        together = new Array(pool).fill(null),
        index = 0;
    together = together.map(() => {return new Promise((resolve, reject) => {const run = function run() {if (index >= tasks.length) {resolve();
                    return;
                };
                let old_index = index,
                    task = tasks[index++];
                task().then(result => {results[old_index] = result;
                    run();}).catch(reason => {reject(reason);
                });
            };
            run();});
    });
    return Promise.all(together).then(() => results);
} 

/* createRequest(tasks, 2).then(results => {
    // 都胜利,整体才是胜利,按顺序存储后果
    console.log('胜利 -->', results);
}).catch(reason => {
    // 只有有也给失败,整体就是失败
    console.log('失败 -->', reason);
}); */
function createRequest(tasks, pool, callback) {if (typeof pool === "function") {
        callback = pool;
        pool = 5;
    }
    if (typeof pool !== "number") pool = 5;
    if (typeof callback !== "function") callback = function () {};
    //------
    class TaskQueue {
        running = 0;
        queue = [];
        results = [];
        pushTask(task) {
            let self = this;
            self.queue.push(task);
            self.next();}
        next() {
            let self = this;
            while (self.running < pool && self.queue.length) {
                self.running++;
                let task = self.queue.shift();
                task().then(result => {self.results.push(result);
                }).finally(() => {
                    self.running--;
                    self.next();});
            }
            if (self.running === 0) callback(self.results);
        }
    }
    let TQ = new TaskQueue;
    tasks.forEach(task => TQ.pushTask(task));
}
createRequest(tasks, 2, results => {console.log(results);
});

实现 bind 办法

bind 的实现比照其余两个函数稍微地简单了一点,波及到参数合并(相似函数柯里化),因为 bind 须要返回一个函数,须要判断一些边界问题,以下是 bind 的实现

  • bind 返回了一个函数,对于函数来说有两种形式调用,一种是间接调用,一种是通过 new 的形式,咱们先来说间接调用的形式
  • 对于间接调用来说,这里抉择了 apply 的形式实现,然而对于参数须要留神以下状况:因为 bind 能够实现相似这样的代码 f.bind(obj, 1)(2),所以咱们须要将两边的参数拼接起来
  • 最初来说通过 new 的形式,对于 new 的状况来说,不会被任何形式扭转 this,所以对于这种状况咱们须要疏忽传入的 this

简洁版本

  • 对于一般函数,绑定 this 指向
  • 对于构造函数,要保障原函数的原型对象上的属性不能失落
Function.prototype.myBind = function(context = window, ...args) {
  // this 示意调用 bind 的函数
  let self = this;

  // 返回了一个函数,...innerArgs 为理论调用时传入的参数
  let fBound = function(...innerArgs) {//this instanceof fBound 为 true 示意构造函数的状况。如 new func.bind(obj)
      // 当作为构造函数时,this 指向实例,此时 this instanceof fBound 后果为 true,能够让实例取得来自绑定函数的值
      // 当作为一般函数时,this 指向 window,此时后果为 false,将绑定函数的 this 指向 context
      return self.apply(
        this instanceof fBound ? this : context, 
        args.concat(innerArgs)
      );
  }

  // 如果绑定的是构造函数,那么须要继承构造函数原型属性和办法:保障原函数的原型对象上的属性不失落
  // 实现继承的形式: 应用 Object.create
  fBound.prototype = Object.create(this.prototype);
  return fBound;
}
// 测试用例

function Person(name, age) {console.log('Person name:', name);
  console.log('Person age:', age);
  console.log('Person this:', this); // 构造函数 this 指向实例对象
}

// 构造函数原型的办法
Person.prototype.say = function() {console.log('person say');
}

// 一般函数
function normalFun(name, age) {console.log('一般函数 name:', name); 
  console.log('一般函数 age:', age); 
  console.log('一般函数 this:', this);  // 一般函数 this 指向绑定 bind 的第一个参数 也就是例子中的 obj
}


var obj = {
  name: 'poetries',
  age: 18
}

// 先测试作为结构函数调用
var bindFun = Person.myBind(obj, 'poetry1') // undefined
var a = new bindFun(10) // Person name: poetry1、Person age: 10、Person this: fBound {}
a.say() // person say

// 再测试作为一般函数调用
var bindNormalFun = normalFun.myBind(obj, 'poetry2') // undefined
bindNormalFun(12) // 一般函数 name: poetry2 一般函数 age: 12 一般函数 this: {name: 'poetries', age: 18}

留神:bind之后不能再次批改 this 的指向,bind屡次后执行,函数 this 还是指向第一次 bind 的对象

实现模板字符串解析性能

let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄 18,性别 undefined
function render(template, data) {const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找以后模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的构造
  }
  return template; // 如果模板没有模板字符串间接返回
}

版本号排序的办法

题目形容: 有一组版本号如下 ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。当初须要对其进行排序,排序的后果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']

arr.sort((a, b) => {
  let i = 0;
  const arr1 = a.split(".");
  const arr2 = b.split(".");

  while (true) {const s1 = arr1[i];
    const s2 = arr2[i];
    i++;
    if (s1 === undefined || s2 === undefined) {return arr2.length - arr1.length;}

    if (s1 === s2) continue;

    return s2 - s1;
  }
});
console.log(arr);

实现一个治理本地缓存过期的函数

封装一个能够设置过期工夫的 localStorage 存储函数

class Storage{constructor(name){this.name = 'storage';}
  // 设置缓存
  setItem(params){
      let obj = {
          name:'', // 存入数据  属性
          value:'',// 属性值
          expires:"", // 过期工夫
          startTime:new Date().getTime()// 记录何时将值存入缓存,毫秒级
      }
      let options = {};
      // 将 obj 和传进来的 params 合并
      Object.assign(options,obj,params);
      if(options.expires){
      // 如果 options.expires 设置了的话
      // 以 options.name 为 key,options 为值放进去
          localStorage.setItem(options.name,JSON.stringify(options));
      }else{
      // 如果 options.expires 没有设置,就判断一下 value 的类型
          let type = Object.prototype.toString.call(options.value);
          // 如果 value 是对象或者数组对象的类型,就先用 JSON.stringify 转一下,再存进去
          if(Object.prototype.toString.call(options.value) == '[object Object]'){options.value = JSON.stringify(options.value);
          }
          if(Object.prototype.toString.call(options.value) == '[object Array]'){options.value = JSON.stringify(options.value);
          }
          localStorage.setItem(options.name,options.value);
      }
  }
  // 拿到缓存
  getItem(name){let item = localStorage.getItem(name);
      // 先将拿到的试着进行 json 转为对象的模式
      try{item = JSON.parse(item);
      }catch(error){
      // 如果不行就不是 json 的字符串,就间接返回
          item = item;
      }
      // 如果有 startTime 的值,阐明设置了生效工夫
      if(item.startTime){let date = new Date().getTime();
          // 何时将值取出减去刚存入的工夫,与 item.expires 比拟,如果大于就是过期了,如果小于或等于就还没过期
          if(date - item.startTime > item.expires){
          // 缓存过期,革除缓存,返回 false
              localStorage.removeItem(name);
              return false;
          }else{
          // 缓存未过期,返回值
              return item.value;
          }
      }else{
      // 如果没有设置生效工夫,间接返回值
          return item;
      }
  }
  // 移出缓存
  removeItem(name){localStorage.removeItem(name);
  }
  // 移出全副缓存
  clear(){localStorage.clear();
  }
}

用法

let storage = new Storage();
storage.setItem({
  name:"name",
  value:"ppp"
})

上面我把值取出来

let value = storage.getItem('name');
console.log('我是 value',value);

设置 5 秒过期

let storage = new Storage();
storage.setItem({
  name:"name",
  value:"ppp",
  expires: 5000
})
// 过期后再取出来会变为 false
let value = storage.getItem('name');
console.log('我是 value',value);

验证是否是邮箱

function isEmail(email) {var regx = /^([a-zA-Z0-9_\-])[email protected]([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/;
    return regx.test(email);
}

实现一个 JSON.stringify

JSON.stringify(value[, replacer [, space]]):
  • Boolean | Number| String类型会主动转换成对应的原始值。
  • undefined、任意函数以及symbol,会被疏忽(呈现在非数组对象的属性值中时),或者被转换成 null(呈现在数组中时)。
  • 不可枚举的属性会被疏忽如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
  • 如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
function jsonStringify(obj) {
    let type = typeof obj;
    if (type !== "object") {if (/string|undefined|function/.test(type)) {obj = '"'+ obj +'"';}
        return String(obj);
    } else {let json = []
        let arr = Array.isArray(obj)
        for (let k in obj) {let v = obj[k];
            let type = typeof v;
            if (/string|undefined|function/.test(type)) {v = '"'+ v +'"';} else if (type === "object") {v = jsonStringify(v);
            }
            json.push((arr ? "":'"' + k + '":') + String(v));
        }
        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
    }
}
jsonStringify({x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"

异步串行 | 异步并行

// 字节面试题,实现一个异步加法
function asyncAdd(a, b, callback) {setTimeout(function () {callback(null, a + b);
  }, 500);
}

// 解决方案
// 1. promisify
const promiseAdd = (a, b) => new Promise((resolve, reject) => {asyncAdd(a, b, (err, res) => {if (err) {reject(err)
    } else {resolve(res)
    }
  })
})

// 2. 串行解决
async function serialSum(...args) {return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))
}

// 3. 并行处理
async function parallelSum(...args) {if (args.length === 1) return args[0]
  const tasks = []
  for (let i = 0; i < args.length; i += 2) {tasks.push(promiseAdd(args[i], args[i + 1] || 0))
  }
  const results = await Promise.all(tasks)
  return parallelSum(...results)
}

// 测试
(async () => {console.log('Running...');
  const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)
  console.log(res1)
  const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)
  console.log(res2)
  console.log('Done');
})()

二叉树深度遍历

// 二叉树深度遍历

class Node {constructor(element, parent) {
    this.parent = parent // 父节点 
    this.element = element // 以后存储内容
    this.left = null // 左子树
    this.right = null // 右子树
  }
}

class BST {constructor(compare) {
    this.root = null // 树根
    this.size = 0 // 树中的节点个数

    this.compare = compare || this.compare
  }
  compare(a,b) {return a - b}
  add(element) {if(this.root === null) {this.root = new Node(element, null)
      this.size++
      return
    }
    // 获取根节点 用以后增加的进行判断 放右边还是放左边
    let currentNode = this.root 
    let compare
    let parent = null 
    while (currentNode) {compare = this.compare(element, currentNode.element)
      parent = currentNode // 先将父亲保存起来
      // currentNode 要不停的变动
      if(compare > 0) {currentNode = currentNode.right} else if(compare < 0) {currentNode = currentNode.left} else {currentNode.element = element // 相等时 先笼罩后续解决}
    }

    let newNode = new Node(element, parent)
    if(compare > 0) {parent.right = newNode} else if(compare < 0) {parent.left = newNode}

    this.size++
  }
  // 前序遍历
  preorderTraversal(visitor) {
    const traversal = node=>{if(node === null) return 
      visitor.visit(node.element)
      traversal(node.left)
      traversal(node.right)
    }
    traversal(this.root)
  }
  // 中序遍历
  inorderTraversal(visitor) {
    const traversal = node=>{if(node === null) return 
      traversal(node.left)
      visitor.visit(node.element)
      traversal(node.right)
    }
    traversal(this.root)
  }
  // 后序遍历
  posterorderTraversal(visitor) {
    const traversal = node=>{if(node === null) return 
      traversal(node.left)
      traversal(node.right)
      visitor.visit(node.element)
    }
    traversal(this.root)
  }
  // 反转二叉树:无论先序、中序、后序、层级都能够反转
  invertTree() {
    const traversal = node=>{if(node === null) return 
      let temp = node.left 
      node.left = node.right 
      node.right = temp
      traversal(node.left)
      traversal(node.right)
    }
    traversal(this.root)
    return this.root
  }
}

先序遍历

二叉树的遍历形式

// 测试
var bst = new BST((a,b)=>a.age-b.age) // 模仿 sort 办法

bst.add({age: 10})
bst.add({age: 8})
bst.add({age:19})
bst.add({age:6})
bst.add({age: 15})
bst.add({age: 22})
bst.add({age: 20})

// 先序遍历
// console.log(bst.preorderTraversal(),'先序遍历')
// console.log(bst.inorderTraversal(),'中序遍历')
// ![](http://img-repo.poetries.top/images/20210522214837.png)
// console.log(bst.posterorderTraversal(),'后序遍历')


// 深度遍历:先序遍历、中序遍历、后续遍历
// 广度遍历:档次遍历(同层级遍历)// 都可拿到树中的节点

// 应用访问者模式
class Visitor {constructor() {this.visit = function (elem) {elem.age = elem.age*2}
  }
}

// bst.posterorderTraversal({//   visit(elem) {
//     elem.age = elem.age*10
//   }
// })

// 不能通过索引操作 拿到节点去操作
// bst.posterorderTraversal(new Visitor())

console.log(bst.invertTree(),'反转二叉树')

转化为驼峰命名

var s1 = "get-element-by-id"

// 转化为 getElementById

var f = function(s) {return s.replace(/-\w/g, function(x) {return x.slice(1).toUpperCase();})
}

实现 Array.isArray 办法

Array.myIsArray = function(o) {return Object.prototype.toString.call(Object(o)) === '[object Array]';
};

console.log(Array.myIsArray([])); // true

转化为驼峰命名

var s1 = "get-element-by-id"

// 转化为 getElementById
var f = function(s) {return s.replace(/-\w/g, function(x) {return x.slice(1).toUpperCase();})
}

正文完
 0