JS 面试 手写代码

instanceof

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上

js 复制代码
function myInstanceof(left, right) {
  // 基本类型直接返回 false
  if (
    left === null ||
    (typeof left !== "object" && typeof left !== "function")
  ) {
    return false;
  }

  // 获取对象的原型
  let proto = Object.getPrototypeOf(left);

  while (true) {
    // 到达原型链末端
    if (proto === null) return false;

    // 找到匹配的原型对象
    if (proto === right.prototype) return true;

    // 继续向上遍历原型链
    proto = Object.getPrototypeOf(proto);
  }
}

console.log(myInstanceof([], Array)); // true
console.log(myInstanceof({}, Object)); // true
console.log(myInstanceof(123, Number)); // false (基本类型不适用)

bind

  1. bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,
  2. 还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。
  3. 如果目标函数是可构造的,绑定函数也可以使用 new 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数,而提供的 this 值会被忽略(因为构造函数会准备自己的 this)
js 复制代码
Function.prototype.myBind = function (NThis) {
  // 要绑定的函数
  var FToBind = this;
  /**
   * arguments 是一个对应于传递给函数的参数的类数组对象,有 长度 属性 并且属性的索引是从零开始的,但是它没有 Array的 内置方法
   * arguments对象是所有(非箭头)函数中都可用的局部变量
   * Array.from(arguments) / [...arguments]
   */
  var args = Array.prototype.slice.call(arguments, 1);
  // new
  var FNop = function () {};

  /**
   * new 流程
   * 1. 创建一个新的对象
   * 2. 如果构造函数的 prototype 属性是一个对象,则将 新对象 的 [[Prototype]] 指向构造函数的 prototype 属性,否则 新对象 将保持为一个普通对象,其 [[Prototype]] 为 Object.prototype。
   * 3. this 绑定到 这对象执行函数
   * 4. 如果构造函数返回非原始值,则该返回值成为整个 new 表达式的结果。否则,如果构造函数未返回任何值或返回了一个原始值,则返回 新对象。(通常构造函数不返回值,但可以选择返回值,以覆盖正常的对象创建过程。)
   */

  // 已经绑定了的函数
  FBound = function bound() {
    FToBind.apply(
      this instanceof FNop ? this : NThis,
      args.concat(Array.prototype.slice.call(arguments))
    );
  };

  FNop.prototype = this.prototype;
  FBound.prototype = new FNop();

  return FBound;
};

Function.prototype.myBind2 = function (NThis, ...args) {
  // 要绑定的函数
  const FToBind = this;
  // new
  const FNop = function () {};
  // 已经绑定了的函数
  const FBound = function bound(...rest) {
    FToBind.apply(
      FBound.prototype.isPrototypeOf(this) ? this : NThis,
      args.concat(rest)
    );
  };

  FNop.prototype = this.prototype;
  FBound.prototype = new FNop();

  return FBound;
};

function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const person = { name: "Alice" };

// 基础绑定
const boundGreet = greet.myBind(person, "Hello");
boundGreet("!"); // 输出: "Hello, Alice!"

// new 调用测试
function Person(name) {
  this.name = name;
}
const BoundPerson = Person.myBind({});
const obj = new BoundPerson("Bob");
console.log(obj.name); // 输出: "Bob"(证明 new 生效)
console.log(obj instanceof Person); // 输出: true(原型链正确)

柯里化

js 复制代码
function curry(fn, ...args) {
  // 函数参数长度
  const pLength = fn.length;

  return function curryFn(...rest) {
    const params = [...args, ...rest];
    // 大于等于参数数量
    if (params.length >= pLength) {
      return fn.apply(this, params);
    } else {
      return curry.call(this, fn, ...params);
    }
  };
}

function curry2(fn) {
    const pLength = fn.length;

    return function curryFn(...rest) {
        if (rest.length >= pLength) {
            return fn.apply(this, rest);
        } else {
            return function subFn(...subRest) {
                return curryFn.call(this, ...rest, ...subRest)
            }
        }
    }
}

const add = (a, b, c) => a + b + c;
const curriedAdd = curry2(add);

// 测试不同调用方式
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6

array 扁平化

js 复制代码
/**
 * 数组扁平化
 */
const arr = [0, 1, [2, [3, [4, 5]]]];

console.log(arr.flat()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.flat(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.flat(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.flat(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.flat(4)); // [ 0, 1, 2, 3, 4, 5 ]

Array.prototype.myFlat2 = function myFlat2(depth = 1) {
  const res = [];

  (function flatten(source, depth) {
    for (const item of source) {
      if (Array.isArray(item) && depth > 0) {
        flatten(item, depth - 1);
      } else {
        res.push(item);
      }
    }
  })(this, depth);

  return res;
};

console.log(arr.myFlat2()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.myFlat2(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.myFlat2(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.myFlat2(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.myFlat2(4)); // [ 0, 1, 2, 3, 4, 5 ]

Array.prototype.myFlat3 = function myFlat3(depth = 1) {
  return depth > 0
    ? this.reduce((pre, cur) => {
        return Array.isArray(cur)
          ? pre.concat(cur.myFlat3(depth - 1))
          : pre.concat(cur);
      }, [])
    : this.slice();
};

console.log(arr.myFlat3()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.myFlat3(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.myFlat3(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.myFlat3(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.myFlat3(4)); // [ 0, 1, 2, 3, 4, 5 ]

Array.prototype.myFlat4 = function myFlat4(depth = 1) {
  let arr = this.slice();

  while (arr.some((item) => Array.isArray(item) && depth > 0)) {
    arr = [].concat(...arr);
    depth--;
  }

  return arr;
};

console.log(arr.myFlat4()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.myFlat4(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.myFlat4(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.myFlat4(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.myFlat4(4)); // [ 0, 1, 2, 3, 4, 5 ]

/**
 * 不考虑 depth
 */
/**
 * 数组的 toString 方法实际上在内部调用了 join() 方法来拼接数组并返回一个包含所有数组元素的字符串,元素之间用逗号分隔。
 */
console.log(arr.toString()); // 0,1,2,3,4,5
console.log(arr.toString().split(",").map(Number)); // [ 0, 1, 2, 3, 4, 5 ]

/**
 * join 为什么会展开数据
 * https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.join
 */
Array.prototype.myJoin = function myJoin(symbol = ",") {
  let result = "";
  let k = 0;
  const len = this.length;

  while (k < len) {
    if (k > 0) {
      result += symbol;
    }
    /**
     * 这里 数组的 toString 方法实际上在内部调用了 join()
     */
    result += this[k].toString();
    k++;
  }

  return result;
};

console.log(arr.myJoin()); // 0,1,2,3,4,5
console.log(arr.myJoin().split(",").map(Number)); // [ 0, 1, 2, 3, 4, 5 ]
相关推荐
小小小小宇3 分钟前
前端 Service Worker
前端
只喜欢赚钱的棉花没有糖39 分钟前
http的缓存问题
前端·javascript·http
小小小小宇1 小时前
请求竞态问题统一封装
前端
loriloy1 小时前
前端资源帖
前端
源码超级联盟1 小时前
display的block和inline-block有什么区别
前端
GISer_Jing1 小时前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js
让梦想疯狂1 小时前
开源、免费、美观的 Vue 后台管理系统模板
前端·javascript·vue.js
海云前端1 小时前
前端写简历有个很大的误区,就是夸张自己做过的东西。
前端
葡萄糖o_o2 小时前
ResizeObserver的错误
前端·javascript·html
AntBlack2 小时前
Python : AI 太牛了 ,撸了两个 Markdown 阅读器 ,谈谈使用感受
前端·人工智能·后端