js面试题(一) JavaScript 手写代码无敌秘籍

10/27/2019 javaScript面试

# 手写路径导航

  • 实现一个new操作符
  • 实现一个JSON.stringify
  • 实现一个JSON.parse
  • 实现一个call或 apply
  • 实现一个Function.bind
  • 实现一个继承
  • 实现一个JS函数柯里化
  • 手写一个Promise(中高级必考)
  • 手写防抖(Debouncing)和节流(Throttling)
  • 手写一个JS深拷贝
  • 实现一个instanceOf

# 1. 实现一个 new操作符

new操作符做了这些事:

  • 它创建了一个全新的对象。

  • 它会被执行 [[Prototype]](也就是 __proto__)链接。

  • 它使 this指向新创建的对象。

  • 通过 new创建的每个对象将最终被 [[Prototype]]链接到这个函数的 prototype对象上。

  • 如果函数没有返回对象类型 Object(包含 Functoin,Array,Date,RegExg,Error),那么 new表达式中的函数调用将返回该对象引用。

function New(func) {
  var res = {};
  if (func.prototype !== null) {
    res.__proto__ = func.prototype;
  }
  var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
  if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
    return ret;
  }
  return res;
}

var obj = New(A, 1, 2)
// equals to
var obj = new A(1, 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2. 实现一个 JSON.stringify

JSON.stringify(value[,replacer[,space]])

  • Boolean|Number|String 类型会自动转换成对应的原始值。

  • undefined、任意函数以及symbol,会被忽略(出现在非数组对象的属性值中时),或者被转换成 null(出现在数组中时)。

  • 不可枚举的属性会被忽略

  • 如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略。

function jsonStringify(obj) {
  let type = typeof obj;
  if (type !== 'object' || type ===null) {
    if (/string|undefined|function/.test(type)) {
      obj = '"' + obj + '"'
    }
    return String(obj)
  } else {
    let json = []
    arr = (obj && obj.constructor === Array);
    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"}"
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

# 3. 实现一个 JSON.parse

JSON.parse(text[,reviver])
1

用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的reviver函数用以在返回之前对所得到的对象执行变换(操作)。

# 4.实现一个call或 apply

# call

  • call语法:
// 调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)
fun.call(thisArg,arg1,arg2,...)
1
2
  • 分析:
    • 确定上下文为
    • 将函数设为对象的属性
    • 执行&删除这个函数
    • 指定this到函数并传入给定参数执行函数
    • 如果不传入参数,默认指向window
Function.prototype.myCall = 
  function(context = window, ...rest) {
  // 上下文
  context.fn = this
  let ret ;
  if(rest) {
    ret = context.fn(...rest)
  } else {
    ret = context.fn()
  }
  delete context.fn;
  return ret
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# apply

  • apply语法:
// 调用一个函数,以及作为一个数组(或类似数组对象)提供的参数
func.apply(thisArg,[argsArray])
1
2
  • 分析:
    • 类似call,只是参数类型不同
Function.prototype.myApply = 
  function (context = window, ...rest) {
  // 上下文context
  // 扩展对象属性
  context.fn = this
  let ret;
  // 判断是否有第二个参数
  if (rest) {
    ret = context.fn(...rest[0])
  } else {
    ret = context.fn()
  }
  delete context.fn
  return ret
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5.实现一个Function.bind

  • bind语法:
// 会创建一个新函数。
// 当这个新函数被调用时,
// bind() 的第一个参数将作为它运行时的 this,
// 之后的一序列参数将会在传递的实参前传入作为它的参数
func.bind(args)
1
2
3
4
5
  • 分析:
    • 类似call,只是返回的是一个函数
Function.prototype.myBind = function(context, ...rest) {
  return (...args) => {
    // arguments此处不能使用
    this.apply(context, [...rest, ...args])
  }
}
1
2
3
4
5
6