手写算法: 丢失的数字

268. 丢失的数字open in new window

sort 排序

时间复杂度 O(nlogn), 空间复杂度:O(logn)

/**
 * @param {number[]} nums
 * @return {number}
 */
var missingNumber = function (nums) {
  const len = nums.length;
  nums.sort((a, b) => a - b);
  for (let i = 0; i < len; i++) {
    if (i !== nums[i]) {
      return i;
    }
  }
  return len;
};

哈希表

时间复杂度 O(n), 空间复杂度:O(n)

/**
 * @param {number[]} nums
 * @return {number}
 */
var missingNumber = function (nums) {
  const set = new Set();
  const len = nums.length;
  for (let i = 0; i < len; i++) {
    set.add(nums[i]);
  }
  let miss = -1;
  for (let i = 0; i <= len; i++) {
    if (!set.has(i)) {
      miss = i;
      break;
    }
  }
  return miss;
};

手写题: 实现一个自己的 Promise

67. 实现一个自己的 Promiseopen in new window

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.handlers = [];
    try {
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (err) {
      this._reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this.handlers.push({
        fulfilled: value => {
          if (typeof onFulfilled !== 'function') {
            resolve(value);
            return;
          }
          try {
            resolve(onFulfilled(value));
          } catch (err) {
            reject(err);
          }
        },
        rejected: error => {
          if (typeof onRejected !== 'function') {
            reject(error);
            return;
          }

          try {
            resolve(onRejected(error));
          } catch (err) {
            reject(err);
          }
        },
      });

      this._executeHandlers();
    });
  }

  _executeHandlers() {
    if (this.state === 'pending') return;
    for (const handler of this.handlers) {
      queueMicrotask(() => {
        handler[this.state](this.result);
      });
    }

    this.handlers = [];
  }

  _resolve(value) {
    if (this.state !== 'pending') return;
    if (value instanceof MyPromise) {
      value.then(this._resolve.bind(this), this._reject.bind(this));
      return;
    }

    this.state = 'fulfilled';
    this.result = value;
    this._executeHandlers();
  }

  _reject(error) {
    if (this.state !== 'pending') return;
    this.state = 'rejected';
    this.result = error;
    this._executeHandlers();
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  static resolve(value) {
    return new MyPromise(resolve => {
      resolve(value);
    });
  }

  static reject(value) {
    return new MyPromise((resolve, reject) => {
      reject(value);
    });
  }
}

JS 简答题

Array I

21. Array Iopen in new window

const a = [0];
console.log(a.length);
a[3] = 3;
console.log(a.length);
for (let item of a) {
  console.log(item);
}
a.map(item => {
  console.log(item);
});
a.forEach(item => {
  console.log(item);
});
console.log(Object.keys(a));
delete a[3];
console.log(a.length);
a[2] = 2;
a.length = 1;
console.log(a[0], a[1], a[2]);

解析

  1. 普通 for 循环,对稀疏数组没有特殊化,仍会执行,返回 undefined

  2. Array.prototype.map()、Array.prototype.forEach() 回调仅针对于具有值的数组索引调用,缺失元素不调用

  3. Object.keys()同样也是,只返回有值的索引

  4. delete 删除元素只是删除值,并不缩短长度

稀疏数组

const arr = [1, , , 2];

// forEach
arr.forEach(i => console.log(i));

// map
console.log(arr.map(i => i * 2));

// for ... of
for (const i of arr) {
  console.log(i);
}

// spread
console.log([...arr]);

解析

Array.prototype.map() 返回一个新数组,这个新数组由原数组中的每个元素执行完回调后的返回值组成。如果被 map 调用的数组是离散的,新数组也是保持离散状态。即之前是稀疏数组后面也还是稀疏数组。

数组的元素如果不是连续有值,我们就称作为稀疏数组。

reduce

17. reduceopen in new window

[1, 2, 3].reduce((a, b) => {
  console.log(a, b);
});

[1, 2, 3].reduce((a, b) => {
  console.log(a, b);
}, 0);

解析

✅ 注意!!它没有 return prev,拿不到之前的值,所以是 undefined

Array.prototype.sort()

84. Array.prototype.sort()open in new window

const a = [999, 1111, 111, 2, 0];
const b = a.sort();

console.log(a);
console.log(b);

解析

  • Array.prototype.sort(),用原地算法对数组的元素进行排序,并返回数组。

  • 不传回调的函数的话,默认排序顺序是将元素转换成字符串,比较它们的 unicode 代码单元值。

  • 在字符串比较时,'111' < '2'、 '1111' < '999'

// 这两种方式都可以求对应unicode码的值
'A'.codePointAt(); // 65
'A'.charCodeAt(0); // 65

'111'.codePointAt(); // 49,不传取的第一个

'111' < '1111'; // true, 49 49 49 < 49 49 49 49

// 同理,

'111' < '2'; // 49 49 49 < 50, 所以'2' 比 '111'大

this III

41. this IIIopen in new window

const obj = {
  a: 1,
  b: this.a + 1,
  c: () => this.a + 1,
  d() {
    return this.a + 1;
  },
  e() {
    return (() => this.a + 1)();
  },
};
console.log(obj.b);
console.log(obj.c());
console.log(obj.d());
console.log(obj.e());

解析

  1. obj.b 定义函数的时候,已经确定好作用域,此时作用域为 window

  2. obj.c() 是一个箭头函数,同样定义函数是已经确定好作用域,此时的作用域指向 window

// 箭头函数可以看作这样
var _this = this;
var obj = {
  c: function () {
    return _this.a + 1;
  },
};
  1. obj.d() 是个普通函数,谁调用它,它的作用域指向谁,所以此时作用域指向 obj

  2. obj.e() 是个普通函数,在定义函数时,同样已经确定好作用域,此时执行函数返回个箭头函数,this 的指向为:谁调用 e,this 就指向谁,所以此时 this 为 obj


warning 🤔 如果是这样呢

const obj = {
  e: () => {
    return (() => this.a + 1)();
  },
};

此时obj.e()是个箭头函数,在定义的时候就确定好了作用域,此时指向window
Last Updated:
Contributors: kk