interview shorthand

interview shorthand

十一月 12, 2021

记录下最近找工作的一些常见面试速记点

手写,实现

  • new

    new 的过程

    1. 开辟一块内存空间,创建一个新的空对象
    2. 执行构造函数,对这个空对象进行构造
    3. 给这个空对象添加proto属性(绑定原型)
    4. 返回操作后的对象 隐式的 return this
    1
    2
    3
    4
    5
    6
    7
    function customNew(tarConstructor, ...args) {
    const obj = Object.create({});
    const res = tarConstructor.apply(obj, args);
    obj.__proto__ = tarConstructor.prototype;
    // 注意执行构造函数的返回 是否 为对象类型
    return typeof res === "object" ? res : obj;
    }
  • call, apply

    参考

    f.call(o);
    f.apply(o);
    //以上代码 和 以下代码功能类似(假设对象 o 中预先不存在名为 m 的属性)
    o.m = f; //将 f 存储为 o 的临时方法
    o.m(); //调用它,不传入参数
    delete o.m; //将临时方法删除

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Function.prototype.myCall = function (context = globalThis) {
    // 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖
    const key = Symbol("key");
    context[key] = this;
    // es5 可通过 for 遍历 arguments 得到参数数组
    const args = [...arguments].slice(1);
    const res = context[key](...args);
    delete context[key];
    return res;
    };
  • bind

    fun.bind(thisArg[, arg1[, arg2[, ...]]])
    当绑定函数被调用时,参数thisArg会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Function.prototype.myBind = function (context = globalThis) {
    const fn = this;
    const args = [...arguments].slice(1);
    const newFunc = function (...args) {
    // 区分 是否 new
    return fn.apply(this instanceof newFunc ? this : context, args);
    };
    // 支持 new 调用方式
    newFunc.prototype = Object.create(fn.prototype);
    return newFunc;
    };

    进行多次 bind 其结果还是第一个 bind, 内部的fn.apply(context)上下文不变

  • promise

    参考

    • promise 三种状态 pending | fulfilled | rejected
    • 一旦状态从pending变为fulfilled或者rejected,那么此 Promise 实例的状态就不再改变
    • 定时器宏任务模拟 微任务队列
    • then方法链式调用返回promise实例, 但不能 resolve 自身
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    class MyPromise {
    constructor(fn) {
    // pending | fulfilled | rejected
    this.state = "pending";
    this.value = null;
    this.resolveCallbacks = [];
    this.rejectedCallbacks = [];
    try {
    fn(this.resolve, this.reject);
    } catch (error) {
    this.reject(error);
    }
    }
    resolve(value) {
    if (this.state !== "pending") return;
    if (value instanceof MyPromise) {
    return value.then(this.resolve, this.reject);
    }
    setTimeout(() => {
    this.state = "fulfilled";
    this.value = value;
    this.resolveCallbacks.forEach((per) => {
    per(this.value);
    });
    });
    }
    reject(value) {
    if (this.state !== "pending") return;
    setTimeout(() => {
    this.state = "rejected";
    this.value = value;
    this.rejectedCallbacks.forEach((per) => {
    per(value);
    });
    });
    }
    then(onFulfilled, onRejected) {
    onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
    typeof onRejected === "function"
    ? onRejected
    : (err) => {
    throw err;
    };

    const thenPromise = new MyPromise((resolve, reject) => {
    const onPromiseResolve = (func) => {
    setTimeout(() => {
    try {
    const x = func(this.value);
    if (x === thenPromise) {
    throw Error("must`not be self");
    }
    if (x instanceof MyPromise) {
    x.then(resolve, reject);
    }
    resolve(x);
    } catch (error) {
    reject(error);
    }
    });
    };

    if (this.state === "fulfilled") {
    onPromiseResolve(onFulfilled);
    }
    if (this.state === "rejected") {
    onPromiseResolve(onRejected);
    }
    if (this.state === "pending") {
    this.resolveCallbacks.push(onFulfilled);
    this.rejectedCallbacks.push(onRejected);
    }
    });
    return thenPromise;
    }
    }
  • deepClone

    简单深拷贝

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function deepClone(obj, map = new WeakMap()) {
    if (typeof obj !== "object") return obj;
    const res = Array.isArray(obj) ? [] : {};
    // 防止循环引用
    if (map.get(obj)) {
    return map.get(obj);
    }
    map.set(obj, res);
    for (const key in obj) {
    const val = obj[key];
    res[key] = deepClone(val, map);
    }
    return res;
    }
  • 节流与防抖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 节流
    function throttle(fun, delay) {
    var last = null;
    return function (...args) {
    var now = new Date();
    if (now - last > delay) {
    fun.apply(this, args);
    last = now;
    }
    };
    }
    // 防抖
    function debouce(fun, delay) {
    var timer = null;
    return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(function () {
    fun.apply(this, args);
    }, delay);
    };
    }
  • instanceof

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function isInstanceOf(instance, constructor) {
    let left = instance.__proto__
    let right = constructor.prototype
    while (left) {
    if (left === right) return true
    left = left.__proto__
    }
    return false
    }
  • 树形结构的展开与构建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    const tree = [
    {
    id: "1",
    pid: null,
    children: [
    {
    id: "1-1",
    pid: "1",
    children: [
    {
    id: "1-1-1",
    pid: "1-1",
    },
    ],
    },
    ],
    },
    ];

    function myFlat(tree, res = []) {
    for (let per of tree) {
    res.push({
    id: per.id,
    pid: per.pid,
    });
    if (per.children && per.children.length > 0) {
    myFlat(per.children, res);
    }
    }
    return res;
    }

    const arr = myFlat(tree).reverse();

    function myArrToTree(arr) {
    const res = [];
    const map = {};
    for (let per of arr) {
    if (map[per.id]) {
    map[per.id].pid = per.pid;
    } else {
    map[per.id] = per;
    }

    let cur = map[per.id];

    if (!cur.pid) {
    res.push(cur);
    } else {
    if (map[cur.pid]) {
    map[cur.pid].children
    ? map[cur.pid].children.push(cur)
    : (map[cur.pid].children = [cur]);
    } else {
    map[cur.pid] = {
    id: cur.pid,
    children: [cur],
    };
    }
    }
    }
    return res;
    }
    console.log(myArrToTree(arr));

概念

  • 闭包 (函数中套函数, 内部函数可访问外部作用域链, 甚至当外部函数销毁内部函数还会保留作用域链)

  • this (函数运行时的上下文)

  • 箭头函数

    1. 箭头函数没有自己的this对象, 会向上层作用域链寻找能访问到的this,即箭头函数运行时的this在声明时就已经确定
    2. 不可以当作构造函数,也就是说,不可以对箭头函数使用new命令,否则会抛出一个错误
    3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    4. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  • 原型链 prototype

    • __proto__ 实例属性 指向 构造函数的原型对象(即prototype)
    • instanceof
    • extends
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // Parent
    function Parent(name) {
    this.name = name
    }

    Parent.prototype.sayName = function () {
    console.log(this.name)
    };

    // Child
    function Child(age, name) {
    // 相当于class写法constructor中 super
    Parent.call(this, name)
    this.age = age
    }
    Child.prototype = Object.create(Parent.prototype)
    Child.prototype.constructor = Child

    Child.prototype.sayAge = function () {
    console.log(this.age)
    }

    // 测试
    const child = new Child(18, 'Jack')
    child.sayName()
    child.sayAge()

  • ESM, commonJs, Amd, Cmd, Umd

    • AMD/CMD/CommonJs 是 js 模块化开发的规范,对应的实现是 require.js/sea.js/Node.js
    • AMD 依赖前置, CMD 依赖就近
    • CommonJs导出的是变量的一份拷贝,ESM导出的是变量的引用
    • CommonJS 模块是运行时加载(可以引用动态路径),ESM 模块是编译时输出接口
    • ESM 静态编译的特性可以很容易实现 Tree Shaking, scope hosting

Vue 相关

  • 双向绑定原理
  • 组件相关
    • 组件通信
    • 公共组件封装: UI,app,第三方
  • 生命周期
  • router
    • hash, history
    • keep alive

web 安全

  • xss, csp 内容安全策略
  • csrf
  • 劫持 -> https
  • 隐藏 iframe 点击
  • 请求针对 ip 地理位置,访问量限制
  • 关键操作多次验证(发送验证码)

网络

  • 浏览器 url 输入到展示过程
  • tcp/ip 3 次握手 4 次挥手
  • https ssl 握手, 非对称加密/对称加密

参考

性能优化

参考

监控平台

  • 性能监控 performance Api (timing)
  • 错误监控 onError, unhandleRejection, Vue.config.errorHandler
  • 行为监控
  • 数据收集, 上传时机(requestIdleCallback)与方式(img, sendBeacon)
  • 数据展示,处理,分析

参考

部署, devops

参考

其他

  • 小程序相关
  • hybird Js bridge
  • webpack 相关
  • 微前端
  • 单元测试
  • react 与 vue
  • 跨域相关及处理

算法

项目相关

说一个项目/最有挑战/困难与解决


等等再补充