手写算法: 丢失的数字
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
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
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]);
解析
普通 for 循环,对稀疏数组没有特殊化,仍会执行,返回 undefined
Array.prototype.map()、Array.prototype.forEach() 回调仅针对于具有值的数组索引调用,缺失元素不调用
Object.keys()同样也是,只返回有值的索引
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
[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()
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
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());
解析
obj.b 定义函数的时候,已经确定好作用域,此时作用域为 window
obj.c() 是一个箭头函数,同样定义函数是已经确定好作用域,此时的作用域指向 window
// 箭头函数可以看作这样
var _this = this;
var obj = {
c: function () {
return _this.a + 1;
},
};
obj.d() 是个普通函数,谁调用它,它的作用域指向谁,所以此时作用域指向 obj
obj.e() 是个普通函数,在定义函数时,同样已经确定好作用域,此时执行函数返回个箭头函数,this 的指向为:谁调用 e,this 就指向谁,所以此时 this 为 obj
warning 🤔 如果是这样呢
const obj = {
e: () => {
return (() => this.a + 1)();
},
};
此时obj.e()是个箭头函数,在定义的时候就确定好了作用域,此时指向window