# 手写常见的JS面试题 31-36

# 31、将虚拟DOM转为真实DOM;

{
  tag: 'DIV',
  attrs:{
  id:'app'
  },
  children: [
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] }
      ]
    },
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] },
        { tag: 'A', children: [] }
      ]
    }
  ]
}
把上诉虚拟Dom转化成下方真实Dom
<div id="app">
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>
</div>


function _vnode(vnode) {
    if (typeof vnode === "number") {
        vnode = String(vnode);
    }
    if (typeof vnode === 'string') {
        return document.createTextNode(vnode);
    }
    // 普通DOM
    const dom = document.createElement(vnode.tag);
    if (vnode.attrs) {
        // 遍历属性
        Object.keys(vnode.attrs).forEach((key) => {
            const value = vnode.attrs[key];
            dom.setAttribute(key, value);
        });
    }
    vnode.children.map(child => dom.appendChild(_vnode(child)));
    return dom;
}

# 32、实现摸板字符串解析功能

let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
    name: '姓名',
    age: 18
}
render(template, data); // 我是姓名,年龄18,性别undefined
function render(template, data) {
    const computed = template.replace(/\{\{(\w+)\}\}/g, function (match, key) {
        return data[key];
    })
    return computed;
}

# 33、实现一个对象的flatten方法

const obj = {
 a: {
        b: 1,
        c: 2,
        d: {e: 5}
    },
 b: [1, 3, {a: 2, b: 3}],
 c: 3
}

flatten(obj) 结果返回如下
// {
//  'a.b': 1,
//  'a.c': 2,
//  'a.d.e': 5,
//  'b[0]': 1,
//  'b[1]': 3,
//  'b[2].a': 2,
//  'b[2].b': 3
//   c: 3
// }

function isObject(val) {
    return typeof val === 'object' && val !== null;
}

function objFlatten(obj) {
    if (!isObject(obj)) return;
    const res = {};

    function dfs(cur, prefix) {
        if (isObject(cur)) {
            if (Array.isArray(cur)) {
                cur.forEach((item, index) => {
                    dfs(item, `${prefix}[${index}]`)
                })
            }
            Object.keys(cur).forEach(key => {
                dfs(cur[key], `${prefix}${prefix ? '.' : ''}${key}`)
            })
        } else {
            res[prefix] = cur;
        }
    }

    dfs(obj, "");
    return res;
}
function objectFlat(obj = {}) {
  const res = {}
  function flat(item, preKey = '') {
    Object.entries(item).forEach(([key, val]) => {
      const newKey = preKey ? `${preKey}.${key}` : key
      if (val && typeof val === 'object') {
        flat(val, newKey)
      } else {
        res[newKey] = val
      }
    })
  }
  flat(obj)
  return res
}

# 34、列表转成树结构

[
    {
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
    ...
]

转成
[
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
]

function convert(list) {
    const map = new Map(); // 借用对象内存指针指向堆
    return list.reduce((pre, cur) => {
        const {id, parentId} = cur;
        if (parentId === 0) {
            pre.push(cur);
        } else {
            const itemCache = map.get(parentId);
            if (itemCache) {
                itemCache.children = itemCache.children ? [...itemCache.children, cur] : [cur];
            }
        }
        if (!map.has(id)) {
            map.set(id, cur);
        }
        return pre;
    }, []);
}

# 35、树结构转换成列表

[
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
]
转成
[
    {
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
    ...
]

function cover(arr) {
    const result = [];

    function dfs(cur) {
        for (let i = 0; i < cur.length; i++) {
            if (cur[i].child) {
                dfs(cur[i].child);
                delete cur[i].child;
            }
            result.push(cur[i]);
        }
    }

    dfs(arr);
    return res;
}

# 36、大数相加

题目描述:实现一个add方法完成两个大数相加

let a = "9007199254740991";
let b = "1234567899999999999";

function add(a, b) {
    //...
}

function add(a, b) {
    const maxLength = Math.max(a.length, b.length);
    a.padStart(maxLength, '0');
    b.padStart(maxLength, '0');
    //定义加法过程中需要用到的变量
    let f = 0;   // "进位"
    let sum = "";
    for (let i = maxLength - 1; i >= 0; i--) {
        const t = parseInt(a[i]) + parseInt(b[i]) + f;
        f = Math.floor(t / 10);
        sum = t % 10 + sum;
    }
    if (!f !== 0) {
        sum = '' + f + sum;
    }
    return sum;
}

# 37、模拟Object.create

function create(proto) {
    function Fn() {
    }

    Fn.prototype = proto;
    return new Fn();
}

# 38、实现一个JSON.stringify

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

# 38、实现一个JSON.parse

function jsonParse(opt) {
    return eval('(' + opt + ')')
}

# 39、JS解析URL Params对象

    let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';

function parseParam(url) {
    let paramsArr = url.slice(url.indexOf('?') + 1).split('&');
    let paramsObj = {};
    paramsArr.forEach(item => {
        if (item.indexOf('=') !== -1) {
            let [key, val] = item.split('=');
            val = decodeURIComponent(val);
            val = /^d+$/.test(val) ? parseFloat(val) : val;
            if (paramsObj.hasOwnProperty(key)) {
                paramsObj[key] = [].concat(paramsObj[key], val)
            } else {
                paramsObj[key] = val;
            }
        } else {
            paramsObj[item] = true
        }
    })
    return paramsObj;
}

# 40、转化驼峰命名

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

// 转化为 getElementById

var f = function(s) {
    return s.replace(/-\w/g, function(x) {
        return x.slice(1).toUpperCase();
    })
}
Last Updated: 6/22/2022, 9:31:27 AM