面试中常考的源码实现

面试中常被考到的手撕代码:

  1. call/apply/bind
  2. instanceof
  3. 深拷贝
  4. 基于 ES5/ES6 实现“双向绑定”
  5. promise相关

1. call/apply/bind的代码实现

call

Function.prototype.call2 = function(context) {
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }

  // 默认上下文是window
  context = context || window;
  // 保存默认的fn
  const { fn } = context;

  // 将函数本身作为对象context的属性调用,自动绑定this
  context.fn = this;
  const args = [...arguments].slice(1);
  const result = context.fn(...args);

  // 恢复默认的fn
  context.fn = fn;
  return result;
};

apply

Function.prototype.apply2 = function(context) {
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }

  context = context || window;
  const { fn } = context;

  context.fn = this;
  let result;
  if (Array.isArray(arguments[1])) {
    // 通过...运算符将数组转换为用逗号分隔的参数序列
    result = context.fn(...arguments[1]);
  } else {
    result = context.fn();
  }

  context.fn = fn;
  return result;
};

bind

Function.prototype.bind2 = function(context) {
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }

  const that = this;
  // 保留之前的参数,为了下面的参数拼接
  const args = [...arguments].slice(1);

  return function F() {
    // 如果被new创建实例,不会被改变上下文!
    if (this instanceof F) {
      return new that(...args, ...arguments);
    }

    // args.concat(...arguments): 拼接之前和现在的参数
    // 注意:arguments是个类Array的Object, 用解构运算符..., 直接拿值拼接
    return that.apply(context, args.concat(...arguments));
  };
};

2.instanceof代码实现

function instanceof2(left, right) {
  let prototype = right.prototype;

  // 沿着left的原型链, 看看是否有何prototype相等的节点
  left = left.__proto__;
  while (1) {
    if (left === null || left === undefined) {
      return false;
    }
    if (left === prototype) {
      return true;
    }
    left = left.__proto__;
  }
}

3.深拷贝

function cloneArr(src, target) {
  for (let item of src) {
    if (Array.isArray(item)) {
      target.push(cloneArr(item, []));
    } else if (typeof item === "object") {
      target.push(deepClone(item, {}));
    } else {
      target.push(item);
    }
  }
  return target;
}
function deepClone(src, target) {
  const keys = Reflect.ownKeys(src);
  let value = null;

  for (let key of keys) {
    value = src[key];

    if (Array.isArray(value)) {
      target[key] = cloneArr(value, []);
    } else if (typeof value === "object") {
      // 如果是对象而且不是数组, 那么递归调用深拷贝
      target[key] = deepClone(value, {});
    } else {
      target[key] = value;
    }
  }

  return target;
}

4.基于 ES5/ES6 实现“双向绑定”

ES5的Object.defineProperty()


<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Documenttitle>
    <script>
      const obj = {
        value: ""
      };

      function onKeyUp(event) {
        obj.value = event.target.value;
      }

      // 对 obj.value 进行拦截
      Object.defineProperty(obj, "value", {
        get: function() {
          return value;
        },
        set: function(newValue) {
          value = newValue;
          document.querySelector("#value").innerHTML = newValue; // 更新视图层
          document.querySelector("input").value = newValue; // 数据模型改变
        }
      });
    script>
  head>
  <body>
    <p>值是:<span id="value">span>p>
    <input type="text" onkeyup="onKeyUp(event)" />
  body>
html>

ES6的proxy


<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Documenttitle>
  <script>
    const obj = {}

    const newObj = new Proxy(obj, {
      get: function(target, key, receiver) {
        return Reflect.get(target, key, receiver)
      },
      set: function(target, key, value, receiver) {
        if(key === 'value') {
          document.querySelector('#value').innerHTML = value
          document.querySelector('input').value = value
        }
        return Reflect.set(target, key, value, receiver)
      }
    })

    function onKeyUp(event) {
      newObj.value = event.target.value
    }

  script>
head>
<body>
  <p>
    值是:<span id="value">span>
  p>
  <input type="text" onkeyup="onKeyUp(event)">
body>
html>

你可能感兴趣的:(面试)