# 刷题记录
# 1. 能过的
日期 | 题目 | 难度 | 备注 |
---|---|---|---|
07.02 | 正则表达式匹配 | 困难 | 还算熟练 |
07.03 | 有序矩阵第k小的元素 | 中等 | 能过,但不是最优解 |
07.03 | 股票系列 | 简单/中等/困难 | 比较熟练 |
07.04 | 打家劫舍系列 | 简单/中等 | 比较熟练 |
07.05 | 通配符匹配 | 困难 | 有思路,小修补 |
07.05 | 课程表I | 中等 | 有思路,小修补 |
07.06 | 不同路径 II | 中等 | 最开始思路超时,换解法通过 |
07.06 | 括号生成 | 中等 | 能写DFS,但不能DP |
07.06 | 下一个排列 | 中等 | 没太大问题 |
07.07 | 在排序数组中查找元素的第一个和最后一个位置 | 中等 | 能过,但冗余,还有点卡手 |
07.07 | 二叉树的先序遍历 | 中等 | 应该掌握了,还要练习 |
07.07 | 二叉树的中序遍历 | 中等 | 能独立思考并写出来,还要练习 |
07.07 | 二叉树的后序遍历 | 困难 | 勉强掌握,还要练习 |
07.07 | 组合总和 | 中等 | 没太大问题 |
07.10 | 找到所有数组中消失的数字 | 简单 | 不使用额外空间和O(n)的限制,可以更熟练一点 |
07.15 | 股票价格跨度 | 中等 | 没问题 |
07.17 | 三数之和 | 中等 | oklll |
07.17 | 环形链表 II | 中等 | 没问题,注意Floyd算法 |
07.31 | 接雨水 | 困难 | 只写对了一种解法,其他解法也很妙。 |
08.05 | 474. 一和零 | 中等 | 还好吧 |
# 2. 没掌握的
日期 | 题目 | 难度 | 备注 |
---|---|---|---|
07.03 | 寻找正序数组的中位数 | 困难 | 知道解法,不会写 |
07.04 | 最长有效括号 | 困难 | 两解法,不会写,也都想不到 |
07.05 | 鸡蛋掉落 | 困难 | 勉强有思路,不会写 |
07.05 | 盛最多水的容器 | 中等 | 好写,但第一次做没思路 |
07.06 | 课程表 III | 困难 | 鲨了我吧!!! |
07.06 | 搜索旋转排序数组 | 中等 | 我知道是二分法的变种,但是,操你妈的,具体怎么变种我想不到 |
07.07 | 不同的二叉搜索树 II | 中等 | 有思路,但,这么巧妙的递归,啊这... |
07.08 | 寻找峰值 | 中等 | 这种题别再栽跟头 |
07.08 | 寻找旋转排序数组中的最小值 | 中等 | 别每次都靠试,要自己去debug,检查 |
07.08 | 在排序数组中查找元素的第一个和最后一个位置 | 中等 | 这个题我是真的不熟练 |
07.09 | 恢复空格 | 中等 | 能过,但卡手的一匹 |
07.09 | 颜色分类 | 中等 | 一般吧,知道思路但细枝末节很差。 |
07.10 | 多数元素 | 简单 | 注意掌握摩尔投票算法 |
07.10 | 把二叉搜索树转换为累加树 | 简单 | 回溯还没掌握 |
07.11 | 乘积最大子数组 | 中等 | 题还行,但是思路很差,思路的实现也不好 |
07.11 | 最大正方形 | 中等 | 题还好,思路不行,只能想到暴力 |
07.12 | 地下城游戏 | 困难 | 思路不大好,很勉强 |
07.12 | 搜索二维矩阵 II | 中等 | 勉强能想到,但还需要强化思路 |
07.18 | 完全平方数 | 中等 | 有很大进步,再多练习 |
07.18 | 和为K的子数组 | 中等 | 有进步,还要再尝试 |
07.19 | 戳气球 | 困难 | 其实也没有那么变态难 |
07.19 | 四数之和 | 中等 | 一点也没思路啊,白做三数之和了 |
07.21 | 不同的二叉搜索树 II | 中等 | 未通过 |
07.25 | 410. 分割数组的最大值 | 困难 | 完全没思路 |
07.28 | 面试题 02.06. 回文链表 | 简单 | 卡手 |
07.28 | 146. LRU缓存机制 | 中等 | 一般吧 |
07.30 | 160. 相交链表 | 简单 | 忘记了 |
07.30 | 面试题 08.05. 递归乘法 | 中等 | 不会 |
08.01 | 76. 最小覆盖子串 | 困难 | 看了题解能写 |
08.01 | 632. 最小区间 | 困难 | 看了题解能写 |
08.02 | 消失的两个数字 | 困难 | 丢了丢了 |
08.02 | 114. 二叉树展开为链表 | 中等 | 真难 |
08.03 | 322. 零钱兑换 | 中等 | 卡手,卡手,卡手 |
08.03 | 518. 零钱兑换 II | 中等 | 好一点,好不到哪去 |
08.04 | 207. 课程表 | 中等 | 一般般,不算快 |
08.05 | 25. K 个一组翻转链表 | 困难 | 菜是原罪 |
08.05 | 152. 乘积最大子数组 | 中等 | 很差劲 |
08.09 | 93. 复原IP地址 | 中等 | 能做但卡手 |
08.21 | 1262. 可被三整除的最大和 | 中等 | 理解的不太好 |
# 3. 再刷剑指offer
日期 | 题目 |
---|---|
07.21 | 剑指 Offer 11. 旋转数组的最小数字 |
07.21 | 剑指 Offer 13. 机器人的运动范围 |
07.23 | 剑指 Offer 15. 二进制中1的个数 |
07.23 | 剑指 Offer 19. 正则表达式匹配 |
07.23 | 剑指 Offer 20. 表示数值的字符串 |
07.23 | 剑指 Offer 26. 树的子结构 |
07.24 | 剑指 Offer 33. 二叉搜索树的后序遍历序列 |
07.27 | 剑指 Offer 41. 数据流中的中位数 |
07.27 | 剑指 Offer 42. 连续子数组的最大和 |
07.27 | 剑指 Offer 43. 1~n整数中1出现的次数 |
07.29 | 剑指 Offer 45. 把数组排成最小的数 |
07.29 | 剑指 Offer 46. 把数字翻译成字符串 |
07.29 | 剑指 Offer 48. 最长不含重复字符的子字符串 |
07.29 | 剑指 Offer 49. 丑数 |
07.30 | 剑指 Offer 51. 数组中的逆序对 |
07.30 | 剑指 Offer 65. 不用加减乘除做加法 |
08.07 | 剑指 Offer 56 - I. 数组中数字出现的次数 |
08.07 | 剑指 Offer 56 - II. 数组中数字出现的次数 II |
08.07 | 剑指 Offer 57 - II. 和为s的连续正数序列 |
08.08 | 剑指 Offer 60. n个骰子的点数 |
08.08 | 剑指 Offer 62. 圆圈中最后剩下的数字 |
08.08 | 剑指 Offer 64. 求1+2+…+n |
08.08 | 剑指 Offer 65. 不用加减乘除做加法 |
08.08 | 剑指 Offer 66. 构建乘积数组 |
08.09 | 剑指 Offer 68 - II. 二叉树的最近公共祖先 |
# 4. 手写代码系列
# 4.1 new
/*
1.创建一个全新的对象。
2.被执行`[[Prototype]]`链接,也就是`__proto__`链接。
3.使`this`指向新创建的对象。
4.通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`上。
5.如果函数没有返回对象类型Object(包含Function,Array,Date,RegExg,Error),那么`new`表达式中的函数调用将返回该对象引用。
*/
function _new(fn) {
let res = {};
if (fn.prototype !== null) {
res.__proto__ = fn.prototype;
}
let args = [...arguments].slice(1);
let ret = fn.apply(res, args);
if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
return ret;
} else {
return res;
}
}
var obj = _new(A, 1, 2);
// <=>
var obj = new A(1, 2);
/*
我自己的理解,为了清楚,说的啰嗦一点。
首先,创建一个空的对象。
然后要对这个对象进行链接,链接有两部分:
1.该对象,作为child,要指向他的parent,也就是它的构造函数(即new函数传入的第一个参数)的prototype。
这一步的作用是如果以后要访问该对象中的某个属性,属性不存在的时候就可以通过原型链向上查找,而这一步就是建议原型链的链接。
2.在构造函数里面,可能有一些赋值(比如 this.name = "range"),我们需要对这个新的对象进行一个类似初始化的操作
这一步的作用是,将构造函数的初始化的一些值,对当前的新对象进行作用。
最后,需要分情况:
1.如果这个函数有返回值,如果不是 object/function 类型,就使用原构造函数的返回值,而不是我们根绝构造函数创建的新对象。
这里要注意的是,有的函数会返回一些基本的数据类型 比如数字,就像我们平时使用 new 的时候一样,如果函数的返回值是 数字,那么忽略它,然后返回我们创建的新对象。
2.如果这个函数没有设置返回值,那么就直接返回我们创建的新对象。
*/
# 4.2 call
/*
1.将函数设为对象的属性
2.执行并删除这个函数
3.指定`this`到函数并传入给定参数执行函数
4.如果不传入参数,默认指向为window
*/
Function.prototype._call = function(thisArg = window) {
thisArg.fn = this;
let args = [...arguments].slice(1);
let ret = thisArg.fn(...args);
delete thisArg.fn;
return ret;
}
/*
首先,函数需要有一个执行者,这个执行者可以是用户创建的某一个对象实例`obj`,也可以是浏览器的`window`,不管是谁,必须要有。
所以这里`thisArg`的默认值为`window`,形如`func._call()`这样的调用其实就是`window`在调用。
而形如`obj.func()`的调用,`obj`就是上面函数中的`thisArg`,这是默认传入的第一个参数。
`this`则指的是调用`_call`所调用的函数,用完了要删除,不然这个函数就绑定给调用它的对象了。
比如:以`func._call()`的形式调用函数,也就是`window`调用,如果不删除,则调用完之后,`window.func()`还可以继续执行,这样不好,因为我们就好像只是路过,借用了一下`window`,但是却给把我们自己的东西忘记拿走了。
接着通过`slice`选取第一个以外的参数,这些是传给函数的参数,然后把参数传给这个函数并得到执行的结果 ret,然后返回
*/
# 4.3 apply
// 与`call`不同的地方在于传入的参数(也就是`arguments[1]`)是`Array`
Function.prototype._apply = function (thisArg = window) {
thisArg.fn = this;
let res;
if (arguments[1]) {
res = thisArg.fn(...arguments[1]);
} else {
res = thisArg.fn();
}
delete thisArg.fn;
return res;
}
# 4.4 bind
Function.prototype._bind = function (thisArg) {
// 存储函数本身
let self = this;
let args = [...arguments].slice(1)
let bound = function() {
// args是之前使用·bind·绑定的参数
// (当前花括号的代码块内的)·arguments·是在·bind·操作之后,其他的对象(比如·window·)调用·bound·函数的时候传入的参数
// 所以在调用·bound·的时候,需要将·bind·操作绑定的函数,和后面自主传入的参数拼接起来,得到·finalArgs·
let boundArgs = [...arguments]
let finalArgs = args.concat(boundArgs);
if (this instanceof bound) {
// 因为·bind·是返回一个函数,所以这个函数可以作为构造函数加·new·创建一个实例对象
// 而·new·出来的这个实例对象的优先级高于·bind·,所以这里需要判断·this·的指向
// 这里是判断是否是通过·new·来创建对象的,如果是·new·上面的语句就是·true·
// 为什么使用`this instanceof bound`来判断是不是使用了·new·?
// 因为·new·的操作中第二步为,obj.__proto__ = constructor.prototype
// 这里的`this`就是指 new 的过程中创建的新的对象 obj
// constructor 就是这个 bound
function F () {}
F.prototype = this.prototype;
bound.prototype = new F();
return this;
} else {
// 修改·this·的指向,将合并的参数传递给·self·函数,并执行·self·,最后返回执行结果。
return self.apply(thisArg, finalArgs);
}
}
return bound;
}
// `bind`的实现需要考虑实例化后对原型链的影响。
// 注意这里的`this`是指调用`bind`的对象(也就是一个`function`),不是第一个传入的对象,第一个传入的对象是`thisArg`。
// `bind`返回的是一个新的函数,这个函数永久性(不可再更改)绑定了传入给`bind`的第一个参数,后面的参数作为该函数的调用参数。
// 但其实上面的·instanceof·并不准确,可以使用·ES6·的·new.target·解决
// 举例说明
function Person(name) {
if (this instanceof Person){
// 这里的·this·我不太确定是什么,打印输出来看,应该是正在·new·的一个对象,只不过这个对象目前还是空的,还没有被赋值
this.name = name;
console.log('name', name);
} else {
throw new Error("必须通过new关键字来调用Person");
}
}
let person1 = new Person("range"); // 正常创建了一个对象
let person2 = Person.call(person1, "sirius"); // 这里创建的对象是一个 undefined,但是创建的这句并未抛出错误
// 我这里还有很大的疑惑
// Person.call(person, "sirius") 这句话本来就没有返回值,person2 的值为·undefined·应该没什么毛病啊
// 而且这句话其实是生效了,·person1·的name变成了·sirius·
// 解决方法
function Person(name) {
if (typeof new.target !== 'undefined') {
// 这里的判读就更加直观可读
this.name = name;
console.log('name', name);
} else {
throw new Error("必须通过new关键字来调用Person");
}
}
let person1 = new Person("range"); // name: range
let person2 = Person.call(person1, "sirius"); // 必须通过new关键字来调用Person
# 4.5 debounce
<!-- 无防抖 -->
<div id="content"
style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
let num = 1;
const content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
// 非立即执行
// 触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
function debounce(func, delay) {
let timeout;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, arguments)
}, delay);
}
}
// html中修改
content.onmousemove = debounce(count, 100);
// 立即执行
// 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。
function debounce(func, delay) {
let timeout;
return function () {
const callNow = !timeout;
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, delay);
if (callNow) {
func.apply(this, arguments);
}
}
}
content.onmousemove = debounce(count, 100);
# 4.6 throttle
// 时间戳 实现
function throttle(func, delay) {
let pre = 0;
return function() {
let now = Date.now();
if (now - pre > delay) {
func.apply(this, arguments);
pre = now;
}
}
}
// 定时器 实现
function throttle(func, delay) {
let timeout;
return function () {
if (!timeout) {
func.apply(this, arguments);
timeout = setTimeout(() => {
timeout = null;
}, delay)
}
};
}
# 4.7 deepClone
// 深拷贝
function deepClone(obj) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== "object") return obj;
let cloneObj = new obj.constructor();
for (let key in obj) {
if(obj.hasOwnProperty(key)){
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
# 4.8 shallowClone
// 浅拷贝
function shallowClone(obj) {
let cloneObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = obj[key];
}
}
return cloneObj
}
# 4.9 generator
function* genDemo() {
console.log(" 开始执行第一段 ")
yield 'generator 2'
console.log(" 开始执行第二段 ")
yield 'generator 2'
console.log(" 开始执行第三段 ")
yield 'generator 2'
console.log(" 执行结束 ")
return 'generator 2'
}
console.log('main 0')
let gen = genDemo()
console.log(gen.next().value)
console.log('main 1')
console.log(gen.next().value)
console.log('main 2')
console.log(gen.next().value)
console.log('main 3')
console.log(gen.next().value)
console.log('main 4')
# 4.10 inherit
function Parent() {
this.name = "parent";
}
Parent.prototype.getParentName = function() {
console.log(this.name);
}
function Child() {
this.name = "child";
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
/*
思考:为什么不写成 Child.prototype = Parent.prototype ?
先思考,prototype什么要叫原型对象,而不是直接叫做原型?
我猜测:说白了,原型对象其实也是一个存在的实例化的对象
只不过这个东西一般是隐藏起来的,
*/
# 4.11 AJAX
function request(method, url, data) {
return new Promise((resolve, reject) => {
let handler = function () {
if (this.readyState !== 4) return;
if (this.status === 200) {
return resolve(this.response);
} else {
return reject(this.statusText);
}
}
let xhr = XMLHttpRequest();
xhr.onreadystatechange = handler;
if (method.toLowerCase() === 'get') {
url += '?';
for (let key in data) {
url += `${key}=${data[key]}&`;
}
url = url.slice(0, url.length - 1);
xhr.open(method, url);
xhr.send();
} else {
xhr.open(method, url);
xhr.send(data);
}
})
}
# 4.12 Promise
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function MyPromise(executor) {
const self = this;
self.value = null;
self.error = null;
self.status = PENDING;
self.onFulfilledCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
if (self.status === PENDING) {
setTimeout(() => {
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach((callback) => callback(self.value));
}, 0);
}
}
function reject(error) {
if (self.status === PENDING) {
setTimeout(function () {
self.status = REJECTED;
self.error = error;
self.onRejectedCallbacks.forEach((callback) => callback(self.error));
}, 0);
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
} else if (this.status === FULFILLED) {
onFulfilled(this.value);
} else {
onRejected(this.error);
}
return this;
};
MyPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
// 但这个 promise 还有几个缺陷:
// 1.then 方法返回的是 this,本应该返回一个新的 promise 实例。因此,不支持串行异步任务
// 诸如:串行异步任务:p.then(f1).then(f2).then(f3).catch(errorLog),分别读取文件 f1、f2、f3 并且在读取完之后打印
// 但这个会一直输出 f1、f1、f1,因为这几个任务都放到了 p1 的回调队列 onFulfilledCallbacks 里面
// 2.一个状态为 resolve / reject 的 promise,在调用 then 的时候,也不应该被立即执行
// 3.在代码执行了函数 resolve() / reject() 之后,手写代码并没有马上将状态改为 resolved / rejected,而是等待 resolve() / reject() 的回调函数执行了之后,才更改状态,而在真正的 promise 中,是立即改变的。
# 4.13 heap
function heapify(heap, n) {
let minN = n, size = heap.length;
let left = 2 * n + 1, right = 2 * n + 2;
if (left < heap.length && heap[left] < heap[minN]) minN = left;
if (right < heap.length && heap[right] < heap[minN]) minN = right;
if (minN !== n) {
[heap[minN], heap[n]] = [heap[n], heap[minN]];
heapify(heap, minN);
}
}
function heapBuild(heap) {
let lastParent = Math.floor(heap.length / 2) - 1;
for (let i = lastParent; i >= 0; i--) {
heapify(heap, i);
}
}
function heapPush(heap, n) {
heap.push(n);
let node = heap.length - 1;
while (node > 0) {
node = Math.floor((node - 1) / 2);
heapify(heap, node);
}
}
function heapPop(heap) {
if (!heap.length) return null;
[heap[0], heap[heap.length - 1]] = [heap[heap.length - 1], heap[0]];
let ans = heap.pop();
heapify(heap, 0);
return ans;
}
let arr = [1, 13, 21, 16, 18, 29, 3, 7, 2];
heapBuild(arr);
console.log(arr); // [1, 2, 3, 7, 18, 29, 21, 13, 16]
heapPush(arr, 20);
console.log(arr); // [1, 2, 3, 7, 18, 29, 21, 13, 16, 20]
let ans = heapPop(arr);
console.log(ans); // 1
console.log(arr); // [2, 7, 3, 13, 18, 29, 21, 20, 16]
# 4.14 Jsonp
// 原生
function jsonp(url, data, callback) {
let dataStr = '?';
for (let key in data) {
dataStr += `${key}=${data[key]}&`
}
dataStr += 'callback=' + callback.name;
let script = document.createElement('script');
script.src = url + dataStr;
document.head.appendChild(script);
}
# 4.15 fetch
fetch('https://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
})
# 4.16 axios
// GET
axios({
method:'get',
url:'http://image.png',
})
.then(function(response) {
console.log(response);
});
// POST
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
# 4.17 数组去重
// 1.Set
function unique (arr) {
return Array.from(new Set(arr))
}
// 或者
[...new Set(arr)]
// 2.splice O(N^2)
function unique(arr){
for(var i = 0; i < arr.length; i++){
for(var j = arr.length - 1; j > i; j--) {
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
}
}
}
return arr;
}
// 3.indexOf
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
// 4.sort
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return;
}
arr = arr.sort()
var arrry= [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
// 5.filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
# 4.18 快排时间复杂度
- 最差情况:
- 分为了「n - 1 个元素」和「0 个元素」两部分。
- 𝑇(𝑛) = 𝑇(𝑛−1) + 𝑇(0) + Θ(𝑛) = 𝑇(𝑛−1) + Θ(𝑛),
- 其中 𝑇(0) = Θ(1),空数组不用排
- 解得:𝑇(𝑛) = Θ(𝑛^2)
- 最优情况:
- 均分为两个「n / 2 个元素」,或者是 「n / 2」+「n / 2 - 1」
- 𝑇(𝑛) = 2𝑇(𝑛/2) + Θ(𝑛)
- 可以递推,或者根据主定理,可以得到:𝑇(𝑛) = Θ(𝑛lg𝑛)
# 4.19 柯里化
概念:将函数与传递给函数的参数结合在一起,产生出一个新的函数(有点工厂函数的意思?)
sum(2, 2, 3)(4, 5)(5).toValue() === 21 function sum() { let args = [...arguments]; let fn = function () { args.push(...arguments); return fn; }; fn.toValue = function () { return args.reduce((x, y) => x + y); }; return fn; } let ans = sum(2, 2, 3)(4, 5)(5); console.log(ans.toValue()); // 什么是柯里化?将函数与传递给函数的参数结合在一起,产生出一个新的函数(有点工厂函数的意思?) Function.method('curry', function() { let args = arguments; let that = this; return function() { return that.apply(null, args.concat(arguments)); }; });
// 柯里化:只传递给函数一部分参数来调用它,让它返回一个新函数去处理剩下的参数。 const add = (x, y, z) => x + y + z; const curry = function (fn) { return function () { // 判断传入的参数是否满足fn所需要的参数数量 if (arguments.length >= fn.length) { // 是,则返回函数执行的结果。 return fn.apply(this, arguments); } else { // 否,则继续收集参数,bind给函数,并且返回这个bind // 注意这里,因为可能会多次收集参数,所以要递归curry // 而不是直接返回bind后的新函数。 return curry(fn.bind(this, ...arguments)); } }; }; const addCurry = curry(add); const addOne = addCurry(1); const addTwo = addOne(1); let ans = addOne(10, 10); console.log(ans); // 21 let res = addTwo(10); console.log(res); // 12
# 4.20 promise.all()
Promise.prototype._all = function (iterable) {
return new Promise((resolve, reject) => {
let index = 0;
for (const promise of iterable) {
const currentIndex = index;
promise.then(
(value) => {
if (anErrorOccurred) return;
result[currentIndex] = value;
elementCount++;
if (elementCount === result.length) {
resolve(result);
}
},
(err) => {
if (anErrorOccurred) return;
anErrorOccurred = true;
reject(err);
}
);
index++;
}
if (index === 0) {
resolve([]);
return;
}
let elementCount = 0;
let anErrorOccurred = false;
const result = new Array(index);
});
};
# 4.21 打印red,停1s,打印yellow,停2s,打印green,停4s。循环5次。
const red = () => console.log('red');
const green = () => console.log('green');
const yellow = () => console.log('yellow');
const light = function (delay, callback) {
return new Promise(resolve => {
callback();
setTimeout(() => {
resolve();
}, delay);
});
};
const step = function (times) {
Promise.resolve()
.then(() => light(1000, red))
.then(() => light(2000, yellow))
.then(() => light(4000, green))
.then(() => {
if (times > 1) return step(times - 1);
});
};
step(5);
# 4.22 日期格式化函数
// 乞丐版
function formatDate(date) {
let d = new Date(date);
let year = '' + d.getFullYear();
let month = '' + (d.getMonth() + 1);
let day = '' + d.getDate();
let hour = '' + d.getHours();
let minute = '' + d.getMinutes();
let second = '' + d.getSeconds();
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
if (hour.length < 2) hour = '0' + hour;
if (minute.length < 2) minute = '0' + minute;
if (second.length < 2) second = '0' + second;
return [year, month, day].join('-') + ' ' + [hour, minute, second].join(':');
}
// 完全版
function formatDate(date, fmt) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
}
function padLeftZero (str) {
return ('00' + str).substr(str.length);
}
# 4.23 Promise异步编程题 - eat
// ES6 class
class Person {
constructor() {
this.tasks = [];
setTimeout(()=> this.run(), 0);
}
eat(food) {
this.tasks.push(() => {
return new Promise((resolve) => {
console.log(food);
resolve();
})
});
return this;
}
sleep(time) {
this.tasks.push(() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, time);
})
});
return this;
}
run() {
this.tasks.reduce((pre, next) => {
return pre.then(() => next());
}, Promise.resolve());
}
}
let person = new Person();
person.sleep(2000).eat('包子');
person.sleep(1000).sleep(2000).eat('饺子').sleep(3000).eat('吃了睡睡了吃');
// ES5 朴素版
function Person() {
this.tasks = [];
setTimeout(() => this.run(), 0);
this.eat = function (food) {
this.tasks.push(() => {
return new Promise(resolve => {
console.log(food);
resolve();
});
});
return this;
};
this.sleep = function (delay) {
this.tasks.push(() => {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, delay);
});
});
return this;
};
this.run = function () {
this.tasks.reduce((pre, next) => {
return pre.then(() => next());
}, Promise.resolve());
};
}
let person = new Person();
person.sleep(2000).eat('包子');
person.sleep(1000).sleep(2000).eat('饺子').sleep(3000).eat('吃了睡睡了吃');
# 5.promise习题
# 5.1 习题一
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一个then");
return new Promise((resolve, reject) => {
console.log("内部promise");
resolve();
})
.then(() => {
console.log("内部第一个then");
})
.then(() => {
console.log("内部第二个then");
});
})
.then(() => {
console.log("外部第二个then");
});
# 5.2 习题二
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一个then");
new Promise((resolve, reject) => {
console.log("内部promise");
resolve();
})
.then(() => {
console.log("内部第一个then");
})
.then(() => {
console.log("内部第二个then");
});
})
.then(() => {
console.log("外部第二个then");
});
# 5.3 习题三
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一个then");
let p = new Promise((resolve, reject) => {
console.log("内部promise");
resolve();
});
p.then(() => {
console.log("内部第一个then");
});
p.then(() => {
console.log("内部第二个then");
});
})
.then(() => {
console.log("外部第二个then");
});
# 5.4 习题四
let p = new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
});
p.then(() => {
console.log("外部第一个then");
new Promise((resolve, reject) => {
console.log("内部promise");
resolve();
})
.then(() => {
console.log("内部第一个then");
})
.then(() => {
console.log("内部第二个then");
});
});
p.then(() => {
console.log("外部第二个then");
});
# 5.5 习题五
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一个then");
new Promise((resolve, reject) => {
console.log("内部promise");
resolve();
})
.then(() => {
console.log("内部第一个then");
})
.then(() => {
console.log("内部第二个then");
});
return new Promise((resolve, reject) => {
console.log("内部promise2");
resolve();
})
.then(() => {
console.log("内部第一个then2");
})
.then(() => {
console.log("内部第二个then2");
});
})
.then(() => {
console.log("外部第二个then");
});
# 5.6 习题六
new Promise((resolve, reject) => {
console.log("外部promise");
resolve();
})
.then(() => {
console.log("外部第一个then");
new Promise((resolve, reject) => {
console.log("内部promise");
resolve();
})
.then(() => {
console.log("内部第一个then");
})
.then(() => {
console.log("内部第二个then");
});
return new Promise((resolve, reject) => {
console.log("内部promise2");
resolve();
})
.then(() => {
console.log("内部第一个then2");
})
.then(() => {
console.log("内部第二个then2");
});
})
.then(() => {
console.log("外部第二个then");
});
# 5.7 习题七
var p1 = new Promise(function(resolve, reject){
resolve("success1");
resolve("success2");
});
var p2 = new Promise(function(resolve, reject){
resolve("success");
reject("reject");
});
p1.then(function(value){
console.log(value);
});
p2.then(function(value){
console.log(value);
});
# 5.8 习题八
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一个then
console.log(value);
return value*2;
}).then(function(value){ //第二个then
console.log(value);
}).then(function(value){ //第三个then
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ //第四个then
console.log(value);
return Promise.reject('reject');
}).then(function(value){ //第五个then
console.log('resolve: '+ value);
}, function(err){
console.log('reject: ' + err);
})
# 5.9 习题九
var p1 = new Promise( function(resolve,reject){
foo.bar();
resolve( 1 );
});
p1.then(
function(value){
console.log('p1 then value: ' + value);
},
function(err){
console.log('p1 then err: ' + err);
}
).then(
function(value){
console.log('p1 then then value: '+value);
},
function(err){
console.log('p1 then then err: ' + err);
}
);
var p2 = new Promise(function(resolve,reject){
resolve( 2 );
});
p2.then(
function(value){
console.log('p2 then value: ' + value);
foo.bar();
},
function(err){
console.log('p2 then err: ' + err);
}
).then(
function(value){
console.log('p2 then then value: ' + value);
},
function(err){
console.log('p2 then then err: ' + err);
return 1;
}
).then(
function(value){
console.log('p2 then then then value: ' + value);
},
function(err){
console.log('p2 then then then err: ' + err);
}
);
# 5.10 习题十
var p1 = Promise.resolve( 1 );
var p2 = Promise.resolve( p1 );
var p3 = new Promise(function(resolve, reject){
resolve(1);
});
var p4 = new Promise(function(resolve, reject){
resolve(p1);
});
console.log(p1 === p2);
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);
p4.then(function(value){
console.log('p4=' + value);
});
p2.then(function(value){
console.log('p2=' + value);
})
p1.then(function(value){
console.log('p1=' + value);
})
# 5.11 习题十一
var p1 = new Promise(function(resolve, reject){
resolve(Promise.resolve('resolve'));
});
var p2 = new Promise(function(resolve, reject){
resolve(Promise.reject('reject'));
});
var p3 = new Promise(function(resolve, reject){
reject(Promise.resolve('resolve'));
});
var p4 = new Promise(function(resolve, reject){
reject(Promise.reject('reject'));
});
p1.then(
function fulfilled(value){
console.log('fulfilled1: ' + value);
},
function rejected(err){
console.log('rejected1: ' + err);
}
);
p2.then(
function fulfilled(value){
console.log('fulfilled2: ' + value);
},
function rejected(err){
console.log('rejected2: ' + err);
}
);
p3.then(
function fulfilled(value){
console.log('fulfilled3: ' + value);
},
function rejected(err){
console.log('rejected3: ' + err);
}
);
p4.then(
function fulfilled(value){
console.log('fulfilled4: ' + value);
},
function rejected(err){
console.log('rejected4: ' + err);
}
);
# 5.12 习题十二
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => resolve("success"), 3000);
});
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000);
});
p2
.then(result => console.log("then1", result))
.then(result => console.log("then2", result));
// 输出
// then1 success
// then2 undefined
// 正如上面的 promise 解决过程中所提到的
// 上面代码中,p1是一个 Promise,3 秒之后变为 fulfilled。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为 fulfilled,导致触发 then 方法指定的回调函数。
// 因此,p2 接受了 p1 的状态,导致自己的状态无效了。
# 5.13 习题十三
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
const promise2 = promise1.then(() => {
throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {
console.log('promise1', promise1)
console.log('promise2', promise2)
}, 2000)
# 5.14 习题14
Promise.resolve(1)
.then((res) => {
console.log(res)
return 2
})
.catch((err) => {
return 3
})
.then((res) => {
console.log(res)
})
# 5.15 习题十五
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
# 5.16 习题十六
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
# 5.17 习题十七
Promise.resolve()
.then(function success (res) {
throw new Error('error')
}, function fail1 (e) {
console.error('fail1: ', e)
})
.catch(function fail2 (e) {
console.error('fail2: ', e)
})
# 5.18 习题十八
setTimeout(() => {
console.log('log-timeout');
}, 0);
process.nextTick(() => {
console.log('tick');
});
const promise = new Promise(resolve => {
console.log('log-promise');
resolve('promise resolve');
});
(async () => {
console.log('async start');
const str = await promise;
console.log(str);
})()
promise.then(() => {
console.log('log-promise1-then');
})
console.log('log-end');
# 6. this习题
# 6.1 普通例子
function func(){
console.log(this)
}
func();
<=>
func.call(undefined); // 可以简写为 func.call()
但这里打印出来的this并不是undefined,因为浏览器的规则:
“如果你传入的context是null或者undefined,那么window对象就是默认的context(严格模式下默认context是undefined)”
let obj = {
foo: function() {
console.log(this)
}
}
obj.foo();
<=>
obj.foo.call(obj)
// 所以这里的this就是obj
let obj = {
foo: function() {
console.log(this)
}
}
let bar = obj.foo;
// 考虑两者的输出
obj.foo()
bar()
// 答案
obj.foo() // <=> obj.foo.call(obj), 输出obj
bar() // <=> bar.call() 没有传入context,this为undefined,浏览器返回一个默认的this,即window对象,所以打印的就是window
# 6.2 全局环境下的this
function f1 () {
console.log(this);
}
function f2 () {
'use strict'
conso.log(this)
}
f1()
f2()
// 答案
f1(); // window
f2(); // undefined
const foo = {
bar: 10,
fn: function() {
console.log(this);
console.log(this.bar);
}
}
let fn1 = foo.fn;
fn1();
// 答案
fn1(); // window, undefined
const foo = {
bar: 10,
fn: function() {
console.log(this);
console.log(this.bar);
}
}
foo.fn();
// 答案
foo.fn(); // {bar: 10, fn: f} (也就是foo这个对象), 10
在执行函数时,如果函数中的this
是被上一级的对象所调用,那么this
指向的就是上一级的对象;否则指向全局环境。
# 6.3 上下文对象调用中的this
const o1 = {
text: 'o1',
fn: function() {
return this.text
}
}
const o2 = {
text: 'o2',
fn: function() {
return o1.fn()
}
}
const o3 = {
text: 'o3',
fn: function() {
var fn = o1.fn
return fn()
}
}
console.log(o1.fn())
console.log(o2.fn())
console.log(o3.fn())
// 答案
o1, o1, undefined
第一个 console 最简单,o1 没有问题。难点在第二个和第三个上面,关键还是看调用 this 的那个函数。
第二个 console 的 o2.fn(),最终还是调用 o1.fn(),因此答案仍然是 o1。
最后一个,在进行 var fn = o1.fn 赋值之后,是“裸奔”调用,因此这里的 this 指向 window,答案当然是 undefined。
# 6.4 bind
&call
&apply
改变this
指向
const foo = {
name: 'range',
logName: function() {
console.log(this.name);
}
}
const bar = {
name: 'mike'
}
console.log(foo.logName.call(bar))
// 答案
console.log(foo.logName.call(bar)) // mike
# 6.5 构造函数和this
function Foo() {
this.bar = "range";
}
const instance = new Foo();
console.log(instance.bar);
// 答案
console.log(instance.bar); // range
function Foo() {
this.user = "range";
const obj = {}
return obj
}
const instance = new Foo();
console.log(instance.user);
// 答案
console.log(instance.user); // undefined
// 此时instance返回的是空对象 obj
function Foo() {
this.user = "range";
return 1
}
const instance = new Foo();
console.log(instance.user);
// 答案
console.log(instance.user); // range
如果构造函数中显式返回一个值,且返回的是一个对象,那么 this
就指向这个返回的对象;如果返回的不是一个对象,那么 this
仍然指向实例。
# 6.6 箭头函数中的this
箭头函数内部的this
是词法作用域,由父上下文确定。
const foo = {
fn: function() {
setTimeout(function() {
console.log(this);
})
}
}
console.log(foo.fn());
// 答案
console.log(foo.fn()); // window
// 因为this出现在setTimeout()的匿名函数里,因此this指向window
const foo = {
fn: function() {
setTimeout(() => {
console.log(this);
})
}
}
console.log(foo.fn());
// 答案
console.log(foo.fn()); // {fn: f}
var x = 11;
var obj = {
x: 22,
say: () => {
console.log(this.x);
}
}
obj.say()
// 答案
obj.say() // 11
// 箭头函数中的 this
var a = 11;
function foo() {
this.a = 22;
let b = function() {
console.log(this.a);
};
b();
}
var x = new foo();
// 答案
var x = new foo(); // 11
/*
在执行这行代码时,先 new,进入到 foo函数中
执行函数 b(),此时 this 指向全局对象 window
所以输出 11.
但是如果打印 x
则得到对象 { a: 22}
var a = 11;
function foo() {
this.a = 22;
let b = () => {
console.log(this.a);
}
b();
}
var x = new foo();
// 答案
var x = new foo(); // 22
// 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
// this 继承父执行上下文中的this
# 6.7 this
优先级相关
function foo(a) {
console.log(this.a)
}
const obj1 = {
a: 1,
foo: foo
}
const obj2 = {
a: 2,
foo: foo
}
obj1.foo.call(obj2);
obj2.foo.call(obj1);
// 答案
obj1.foo.call(obj2); // 2
obj2.foo.call(obj1); // 1
// call、apply的显示绑定一般来说优先级比隐式绑定更高
function foo(a) {
this.a = a;
}
const obj1 = {};
var bar = foo.bind(obj1);
var baz = new bar(3);
bar(2);
console.log(obj1.a);
console.log(baz.a);
// 答案
console.log(obj1.a); // 2
// 通过 bind 将 bar 函数中的 this 绑定为 obj1 对象。
// 执行 bar(2) 后,obj1.a 的值为 2。
// 也就是说
// 执行 bar(2) 后,obj1 为 {a: 2}。
console.log(baz.a) // 3
// 尽管 bar 函数本身是通过 bind 构造的新函数
// 其内部已经将 this 绑定为 obj1
// 但是再次作为构造函数 构造 baz 的时候,返回的实例与 obj1 解绑
// 即,new 绑定修改了 bind 绑定的 this
// 所以, new 的优先级 高于 bind
function foo() {
return a => {
console.log(this.a);
};
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
const bar = foo.call(obj1);
console.log(bar.call(obj2));
// 答案
console.log(bar.call(obj2)); // 2
// 由于 foo() 的this 绑定到了 obj1
// bar(引用箭头函数) 的 this 也会绑定到 obj1
// 箭头函数的绑定无法被修改
var a = 123;
const foo = () => a => {
console.log(this.a);
}
// 上面的相当于
/*
var foo = function foo() {
return function (a) {
console.log(_this.a);
};
};
*/
const obj1 = {
a: 2;
}
const obj2 = {
a: 3;
}
var bar = foo.call(obj1);
console.log(bar.call(obj2));
// 答案
console.log(bar.call(obj2)); // 123
const a = 123;
const foo = () => a => {
console.log(this.a);
}
const obj1 = {
a: 2;
}
const obj2 = {
a: 3;
}
var bar = foo.call(obj1);
console.log(bar.call(obj2));
// 答案
console.log(bar.call(obj2)); // undefined
// let 和 const 声明的变量都不会挂载到 window 全局对象中。
# 6.8 闭包/嵌套中的this
嵌套函数(闭包)不会从调用它的函数中继承this。若想访问外部函数的this值,需要将它的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。
// 普通的嵌套
var name = "sirius";
let foo = {
name: "range",
foo1: function() {
console.log("foo1: ", this.name);
function foo2(){
console.log("foo2: ", this.name);
function foo3() {
console.log("foo3: ", this.name);
}
foo3()
}
foo2();
}
}
foo.foo1();
// 答案
foo.foo1();
// foo1: range
// foo2: sirius
// foo3: sirius
/*
这里,foo 对象调用了它内部的函数 foo1,foo1是被对象 foo 调用,所以才会打印出 foo1:range。
嵌套函数 foo2 不是被对象 foo 调用,而是被函数 foo1 调用。
但是嵌套函数 foo2 不会从调用它的函数 foo1 中继承 this。
因此 foo2 的 this 指向的是 undefined。
所以 foo2 的 this 指向全局对象 window(严格模式下为 undefined)
*/
但是,即使是在闭包中,箭头函数的this
也是声明时绑定的。
// 多层嵌套箭头函数
var name = "sirius";
let foo = {
name: "range",
// 第一层箭头函数
foo0: () => {
console.log("arrow1: ", this.name);
// 第二层箭头函数
(() => {
console.log("arrow2: ", this.name);
// 第三层箭头函数
(() => {
console.log("arrow3: ", this.name);
})()
})()
}
};
foo.foo0();
// 答案
foo.foo0();
// arrow1: sirius
// arrow2: sirius
// arrow2: sirius
// 因为对象不构成单独的作用域
// 所以 foo0 箭头函数被调用时的作用域,就是全局作用域
// 多层嵌套箭头函数
var name = "sirius";
let foo = {
name: "range",
// 第一层普通函数
foo0: function() {
console.log("foo0: ", this.name);
// 第二层箭头函数
(() => {
console.log("arrow1: ", this.name);
// 第三层箭头函数
(() => {
console.log("arrow2: ", this.name);
})()
})()
}
};
foo.foo0();
// 答案
foo.foo0();
// arrow1: range
// arrow2: range
// arrow2: range
// 虽然对象不构成单独的作用域,但是函数有作用域
// 所以后面所有的箭头函数的 this,都被阻挡在了函数作用域内
// 因此,箭头函数的 this 都是第一层函数的this,
// 多层嵌套箭头函数
var name = "sirius";
let foo = {
name: "range",
// 第一层普通函数
foo0: function () {
console.log("foo0: ", this.name);
// 第二层普通函数
function foo1() {
console.log("foo1: ", this.name);
// 第三层箭头函数
(() => {
console.log("arrow1: ", this.name);
// 第四层箭头函数
(() => {
console.log("arrow2: ", this.name);
})()
})()
}
foo1();
}
};
foo.foo0();
// 答案
foo.foo0();
// foo0: range
// foo1: sirius
// arrow1: sirius
// arrow2: sirius
// 还是因为函数有单独的作用域
// foo0 的作用域内,又形成了一个小的 foo1 的作用域
// foo1 的作用域 阻挡了内部的箭头函数访问 foo0 的作用域
// 所以箭头函数都指向 foo1 的 this
// foo1 的 this 就是 undefined
// 下面的代码就是多余的了,当练习吧
// 代码是键盘的体操
// 不同的嵌套方式
var name = "sirius";
let foo = {
name: "range",
// 1.箭头函数 嵌套 普通函数
foo0: () => {
console.log("arrow1: ", this.name);
function foo1() {
console.log("foo1: ", this.name);
}
foo1();
},
// 2.1 普通函数 嵌套 箭头函数
foo2: function() {
console.log("foo2: ", this.name);
(() => {
console.log("arrow2: ", this.name);
})()
},
// 2.2 箭头函数 嵌套 箭头函数
foo3: () => {
console.log("arrow3: ", this.name);
(() => {
console.log("arrow4: ", this.name);
})()
}
}
foo.foo0()
foo.foo2()
foo.foo3()
// 答案
foo.foo0()
// arrow1: sirius,因为对象不构成单独的作用域
// foo1: sirius,因为普通嵌套函数的 this 指向全局对象
foo.foo2()
// foo2: range,因为是 foo 对象调用该函数
// arrow2: range
// 因为函数 foo2 构成了单独的作用域,而箭头函数没有作用域
// 所以箭头函数内外的 this 都是一样的
// 即 箭头函数的 this 就是包含这个箭头函数的函数 foo2 的 this
foo.foo3()
// arrow3: sirius
// arrow4: sirius 即使多层嵌套,箭头函数都是指向外面的(缺少屏障隔开)