├── .gitignore ├── .prettierrc.js ├── README.md ├── interview ├── html │ ├── checkbox.html │ ├── flex.html │ ├── index.html │ ├── rocket.html │ ├── shrink.html │ ├── style.html │ ├── 事件委托.html │ └── 靠右.html ├── js │ ├── LRU.JS │ ├── add&delete.js │ ├── async输出顺序.js │ ├── getcookie.js │ ├── loadImage.js │ ├── promise输出题.js │ ├── setTimeout.js │ ├── this.js │ ├── this指向.js │ ├── 三色球.js │ ├── 三行输出.js │ ├── 乱七八糟.js │ ├── 分红包.js │ ├── 前序遍历.js │ ├── 加法运算(进制).js │ ├── 包含宏任务、微任务的异步代码分析.js │ ├── 去除0.js │ ├── 只执行一次-闭包.js │ ├── 同类项统计.js │ ├── 多属性排序.js │ ├── 字节-Task类.js │ ├── 字节-异步.js │ ├── 宏任务微任务.js │ ├── 对象键名数组排序.js │ ├── 小红书.js │ ├── 手机生产.js │ ├── 打印输出.js │ ├── 最大出现次数.js │ ├── 求数组最大值和最小值之差.js │ ├── 百度笔试.js │ ├── 百词斩一面.js │ ├── 百词斩笔试.js │ ├── 第k大的值.js │ ├── 翻转不包含字母的字符串.js │ └── 访问所有子元素.js └── 手写代码 │ ├── Object.assign.js │ ├── Object.create.js │ ├── add.js │ ├── ajax.js │ ├── apply.js │ ├── bind.js │ ├── call.js │ ├── debounce.js │ ├── deepClone.js │ ├── flat.js │ ├── flat2.js │ ├── getType.js │ ├── instanceof.js │ ├── jsonp │ ├── index.html │ ├── package.json │ └── server.js │ ├── new.js │ ├── promise.all.js │ ├── promise.js │ ├── promise.race.js │ ├── proxy.js │ ├── reduce.js │ ├── reduce实现map.js │ ├── repeat.js │ ├── reverse.js │ ├── setInterval.js │ ├── shuffle洗牌算法.js │ ├── sleep.js │ ├── throttle.js │ ├── trim.js │ ├── 中序遍历.js │ ├── 二分查找.js │ ├── 二叉树.js │ ├── 冒泡排序.js │ ├── 函数柯里化.js │ ├── 千分位.js │ ├── 发布-订阅.js │ ├── 后序遍历.js │ ├── 寄生组合式继承.js │ ├── 希尔排序.js │ ├── 广度优先遍历.js │ ├── 快速排序.js │ ├── 插入排序.js │ ├── 深度优先遍历.js │ ├── 红绿灯.js │ ├── 继承.js │ ├── 选择排序.js │ └── 闭包.js ├── leetcode ├── 1013将数组分成和相等的三个部分.js ├── 101对称二叉树.js ├── 104二叉树的最大深度.js ├── 110平衡二叉树.js ├── 112路径总和.js ├── 121买卖股票的最佳时机.js ├── 129求根节点到叶节点数字之和.js ├── 136只出现一次的数字.js ├── 141环形链表.js ├── 144二叉树的前序遍历.js ├── 14最长公共前缀.js ├── 155最小栈.js ├── 160相交链表.js ├── 165比较版本号.js ├── 168.Excel表列名称.js ├── 169多数元素.js ├── 191位1的个数.js ├── 1两数之和.js ├── 200岛屿数量.js ├── 204计算质数.js ├── 206反转链表.js ├── 20有效的括号.js ├── 215数组中的第K个最大元素.js ├── 217存在重复元素.js ├── 21合并两个有序链表.js ├── 226翻转二叉树.js ├── 231二的幂.js ├── 237删除链表中的节点.js ├── 242有效的字母异位词.js ├── 257二叉树的所有路径.js ├── 26删除有序数组中的重复项.js ├── 283移动零.js ├── 2两数相加.js ├── 300最长递增子序列.js ├── 30在排序数组中查找元素的第一个和最后一个位置.js ├── 319灯泡开关.js ├── 322零钱兑换.js ├── 326三的幂.js ├── 349两个数组的交集.js ├── 384打乱数组.js ├── 387字符串中的第一个唯一字符.js ├── 3无重复字符的最长子串.js ├── 415字符串相加.js ├── 46全排列.js ├── 48旋转图像.js ├── 494目标和.js ├── 509斐波那契数.js ├── 53最大子序和.js ├── 54螺旋矩阵.js ├── 5最长回文子串.js ├── 64最小路径和.js ├── 67二进制求和.js ├── 704二分查找.js ├── 70爬楼梯.js ├── 7整数反转.js ├── 83删除排序链表中的重复元素.js ├── 88合并两个有序数组.js ├── 94二叉树的中序遍历.js ├── 剑指10-I.斐波那契数列.js ├── 剑指10-II.青蛙跳台阶问题.js ├── 剑指18删除链表的节点.js ├── 剑指21调整数组顺序使奇数位于偶数前面.js ├── 剑指22链表中倒数第k个节点.js ├── 剑指28对称的二叉树.js ├── 剑指62圆圈中最后剩下的数字.js ├── 剑指9用两个栈实现队列.js └── 面试题 08 06汉诺塔问题.js ├── markdown ├── Javascript相关.md ├── React │ ├── React学习笔记(一).md │ ├── React学习笔记(三).md │ └── React学习笔记(二).md ├── Vue相关.md ├── 字节跳动凉经.md ├── 快手实习.md ├── 排序算法.md ├── 百度面经.md ├── 网络相关.md └── 美团点评凉经.md └── test ├── index.html └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | yarn-error.log 4 | node_modules/ 5 | package-lock.json 6 | yarn.lock 7 | coverage/ 8 | .idea/ 9 | run/ 10 | .DS_Store 11 | .vscode 12 | *.sw* 13 | *.un~ 14 | typings/ 15 | .nyc_output/ 16 | workspace.code-workspace -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 80, // 每行代码长度 3 | tabWidth: 2, 4 | semi: true, 5 | singleQuote: true, 6 | jsxBracketSameLine: false, // 多行JSX中的>另起一行(默认false) 7 | jsxSingleQuote: true, 8 | bracketSpacing: true, 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前端面试 2 | 3 | #### 使用说明 4 | 5 | 1. interview: 是一些前端面试常见代码题 6 | 2. leetcode: 算法题的 JavaScript 实现 7 | 3. test: 测试运行 index.js 代码,成功后移动到其他目录 8 | 4. markdown: 常见八股文、面经总结 9 | 10 | #### 参与贡献 11 | 12 | 1. Fork 本仓库 13 | 2. 新建 Feat_xxx 分支 14 | 3. 提交代码 15 | 4. 新建 Pull Request 16 | -------------------------------------------------------------------------------- /interview/html/checkbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 漂洋过海来看你
12 | 后来
13 | 一眼万年
14 | 没那么简单
15 | 如果你要离去
16 | 恋恋风尘
17 | 南山南
18 | 涛声依旧
19 |
20 |
21 |
22 | 23 | 24 | 25 |
26 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /interview/html/flex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 40 | 41 | -------------------------------------------------------------------------------- /interview/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 18 | 19 | 20 |
21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /interview/html/rocket.html: -------------------------------------------------------------------------------- 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 | 201 | -------------------------------------------------------------------------------- /interview/html/shrink.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 30 | 31 | 32 |
33 |
1
34 |
2
35 |
3
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /interview/html/style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 16 | 17 | 18 |

111111

19 | 20 | 21 | -------------------------------------------------------------------------------- /interview/html/事件委托.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 事件委托 8 | 9 | 10 | 16 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /interview/html/靠右.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 33 | 34 | 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /interview/js/LRU.JS: -------------------------------------------------------------------------------- 1 | function LRU(operators, k) { 2 | // write code here 3 | let map = new Map(); 4 | let list = []; 5 | for (let operator of operators) { 6 | let key = operator[1]; 7 | switch (operator[0]) { 8 | case 1: 9 | if (map.has(key)) { 10 | map.delete(key); 11 | } 12 | map.set(key, operator[2]); 13 | if (map.size > k) { 14 | map.delete(map.entries().next().value[0]); 15 | } 16 | break; 17 | case 2: 18 | if (map.has(key)) { 19 | let value = map.get(key); 20 | list.push(value); 21 | map.delete(key); 22 | map.set(key, value); 23 | } else { 24 | list.push(-1); 25 | } 26 | break; 27 | default: 28 | } 29 | } 30 | return list; 31 | } 32 | 33 | let res = LRU( 34 | [ 35 | [1, 1, 1], 36 | [1, 2, 2], 37 | [1, 3, 2], 38 | [2, 1], 39 | [1, 4, 4], 40 | [2, 2], 41 | ], 42 | 3 43 | ); 44 | console.log(res); 45 | -------------------------------------------------------------------------------- /interview/js/add&delete.js: -------------------------------------------------------------------------------- 1 | function func() { 2 | this.list = []; 3 | } 4 | func.prototype.add = function (val, index) { 5 | this.list.splice(index, 0, val); 6 | }; 7 | 8 | func.prototype.delete = function (val) { 9 | this.list = this.list.filter((x) => x !== val); 10 | }; 11 | 12 | let test = new func(); 13 | test.add(10, 0); 14 | test.add(11, 1); 15 | test.add(13, 2); 16 | test.add(14, 1); 17 | console.log(test.list); 18 | test.delete(14); 19 | console.log(test.list); 20 | -------------------------------------------------------------------------------- /interview/js/async输出顺序.js: -------------------------------------------------------------------------------- 1 | async function async1() { 2 | console.log('async1 start'); // 2 3 | await async2(); 4 | console.log('async1 end'); // 9 5 | } 6 | 7 | async function async2() { 8 | console.log('async2 start'); // 3 9 | return new Promise((resolve, reject) => { 10 | resolve(); 11 | console.log('async2 promise'); // 4 12 | }); 13 | } 14 | 15 | console.log('script start'); // 1 16 | 17 | setTimeout(function () { 18 | console.log('setTimeout'); // 10 19 | }, 0); 20 | 21 | async1(); 22 | 23 | new Promise(function (resolve) { 24 | console.log('promise1'); // 5 25 | resolve(); 26 | }) 27 | .then(function () { 28 | console.log('promise2'); // 7 29 | }) 30 | .then(function () { 31 | console.log('promise3'); // 8 32 | }); 33 | 34 | console.log('script end'); // 6 35 | -------------------------------------------------------------------------------- /interview/js/getcookie.js: -------------------------------------------------------------------------------- 1 | function getCookie(name) { 2 | let data = ''; 3 | if (document.cookie) { 4 | let arr = document.cookie.split(';'); 5 | for (let str of arr) { 6 | let temp = str.split('='); 7 | if (temp[0].replace(/(^\s*)/g, '') === name) { 8 | data = unescape(temp[1]); 9 | break; 10 | } 11 | } 12 | } else { 13 | console.log('cookie不存在'); 14 | } 15 | return data; 16 | } 17 | -------------------------------------------------------------------------------- /interview/js/loadImage.js: -------------------------------------------------------------------------------- 1 | function loadImage(url) { 2 | return new Promise((resolve, reject) => { 3 | let image = new Image(); 4 | image.src = url; 5 | image.onload = () => { 6 | resolve(image); 7 | }; 8 | image.onerror = () => { 9 | reject(new Error('Cound not load image at ' + url)); 10 | }; 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /interview/js/promise输出题.js: -------------------------------------------------------------------------------- 1 | var p1 = new Promise(function (resolve, reject) { 2 | setTimeout(function () { 3 | console.log('1'); 4 | resolve(); 5 | }, 3000); 6 | }); 7 | 8 | function p2() { 9 | return new Promise(function (resolve, reject) { 10 | setTimeout(function () { 11 | console.log('2'); 12 | resolve(); 13 | }, 2000); 14 | }); 15 | } 16 | 17 | function p3() { 18 | return new Promise(function (resolve, reject) { 19 | setTimeout(function () { 20 | console.log('3'); 21 | resolve(); 22 | console.log('5'); 23 | }, 1000); 24 | }); 25 | } 26 | 27 | function p4() { 28 | return new Promise(function (resolve, reject) { 29 | setTimeout(function () { 30 | console.log('4'); 31 | resolve(); 32 | }, 500); 33 | }); 34 | } 35 | 36 | p1.then(function () { 37 | return p2(); 38 | }) 39 | .then(function () { 40 | return p3(); 41 | }) 42 | .then(function () { 43 | return p4(); 44 | }); 45 | -------------------------------------------------------------------------------- /interview/js/setTimeout.js: -------------------------------------------------------------------------------- 1 | // for (var i = 0; i < 5; i++) { 2 | // (function (i) { 3 | // setTimeout(function () { 4 | // console.log(i); 5 | // }, 1000 * i); 6 | // })(i); 7 | // } 8 | 9 | // for (let i = 0; i < 5; i++) { 10 | // setTimeout(function () { 11 | // console.log(i); 12 | // }, 1000 * i); 13 | // } 14 | 15 | for (var i = 0; i < 5; i++) { 16 | setTimeout( 17 | function (i) { 18 | console.log(i); 19 | }, 20 | 1000 * i, 21 | i 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /interview/js/this.js: -------------------------------------------------------------------------------- 1 | function fn() { 2 | return this.length + 1; 3 | } 4 | var length = 10; 5 | var obj = { 6 | length: 5, 7 | test1: function () { 8 | return fn(); 9 | }, 10 | }; 11 | obj.test2 = fn; 12 | //字节经典题,下面代码输出是什么 13 | console.log(obj.test1()); 14 | console.log(fn() === obj.test2()); 15 | -------------------------------------------------------------------------------- /interview/js/this指向.js: -------------------------------------------------------------------------------- 1 | /* 2 | let a = 1; 3 | 4 | const function1 = function () { 5 | console.log(a); 6 | a = 2; 7 | }; 8 | 9 | a = 3; 10 | const function2 = function () { 11 | console.log(a); 12 | }; 13 | 14 | function1(); 15 | function2(); 16 | */ 17 | 18 | var Person = { 19 | name: 'zhangsan', 20 | age: 19, 21 | }; 22 | 23 | function aa(x, y) { 24 | console.log(x + ',' + y); 25 | console.log(this); 26 | console.log(this.name); 27 | } 28 | 29 | aa(4, 5); //this指向window--4,5 window 空 30 | aa.call(Person, 4, 5); //this指向Person--4,5 Person{}对象 zhangsan 31 | 32 | aa.apply(Person, [4, 5]); //this指向Person--4,5 Person{}对象 zhangsan 33 | 34 | aa.bind(Person, 4, 5); //只是更改了this指向,没有输出 35 | aa.bind(Person, 4, 5)(); //this指向Person--4,5 Person{}对象 zhangsan 36 | 37 | /* 38 | var a = { 39 | b: function () { 40 | console.log(this); 41 | }, 42 | }; 43 | var c = a.b; 44 | c(); 45 | */ 46 | //这个this指向谁? 47 | //Window 48 | -------------------------------------------------------------------------------- /interview/js/三色球.js: -------------------------------------------------------------------------------- 1 | function Tricolore(arr) { 2 | let begin = 0, 3 | current = 0; 4 | let end = arr.length - 1; 5 | 6 | while (current <= end) { 7 | if (arr[current] === 0) { 8 | [arr[begin], arr[current]] = [arr[current], arr[begin]]; 9 | begin++; 10 | current++; 11 | } else if (arr[current] === 1) { 12 | current++; 13 | } else { 14 | [arr[end], arr[current]] = [arr[current], arr[end]]; 15 | end--; 16 | } 17 | } 18 | return arr; 19 | } 20 | 21 | let input = [2, 0, 1, 0, 0, 2, 1, 1, 2]; 22 | console.log(Tricolore(input)); 23 | -------------------------------------------------------------------------------- /interview/js/三行输出.js: -------------------------------------------------------------------------------- 1 | function fun(n, o) { 2 | console.log(o); 3 | return { 4 | fun: function (m) { 5 | return fun(m, n); 6 | }, 7 | }; 8 | } 9 | 10 | var a = fun(0); 11 | a.fun(1); 12 | a.fun(2); 13 | a.fun(3); 14 | 15 | var b = fun(0).fun(1).fun(2).fun(3); 16 | var c = fun(0).fun(1); 17 | c.fun(2); 18 | c.fun(3); 19 | 20 | // undefined 0 0 0 21 | // undefined 0 1 2 22 | // undefined 0 1 1 23 | -------------------------------------------------------------------------------- /interview/js/乱七八糟.js: -------------------------------------------------------------------------------- 1 | function even(arr) { 2 | let even = arr.filter((item) => item % 2 === 1); 3 | return even.reduce((sum, item) => (sum += item)) - Math.max(...even); 4 | } 5 | 6 | const arr = [1, 2, 3, 5, 5, 6, 9, 10, 10, 17]; 7 | let m = 2, 8 | n = 5; 9 | function digui(m, n) { 10 | if (m === n) { 11 | return m; 12 | } 13 | return n + digui(m, n - 1); 14 | } 15 | 16 | // 洗牌算法 17 | function shuffle(arr) { 18 | let i = arr.length; 19 | while (i) { 20 | let j = Math.floor(Math.random() * i--); 21 | [arr[i], arr[j]] = [arr[j], arr[i]]; 22 | } 23 | return arr; 24 | } 25 | 26 | // 元素为1,2,...,100的数组(非有序)中,有一个缺失的数字,如何找到缺失的数字 27 | function arrayList() { 28 | // 数组和,公式和 29 | let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 30 | arr = shuffle(arr); 31 | arr.splice(9, 1); 32 | console.log(arr); 33 | let target = (10 * (10 + 1)) / 2; 34 | let sum = arr.reduce((sum, val) => (sum += val)); 35 | console.log(target - sum); 36 | } 37 | arrayList(); 38 | -------------------------------------------------------------------------------- /interview/js/分红包.js: -------------------------------------------------------------------------------- 1 | function shuffle(arr) { 2 | let i = arr.length; 3 | let res = [...arr]; 4 | while (i) { 5 | let j = Math.floor(Math.random() * i--); 6 | [res[i], res[j]] = [res[j], res[i]]; 7 | } 8 | return res; 9 | } 10 | 11 | /** 12 | * 1. 获取红包的份额不能超过一个阈值 13 | * 2. 每个人至少抢到1分钱 14 | * 3. 所有人抢到的金额之和要等于红包金额,不能多也不能少 15 | * @param {Number} money 总额 16 | * @param {Number} count 人数 17 | * @param {Number} min 最小值 18 | * @param {Number} limit 阈值 19 | * @returns 20 | */ 21 | function randomRedPackage(money, count, min = 0.01, limit = 0.618) { 22 | if (limit < 0 || limit > 1) { 23 | throw new TypeError( 24 | "range of limit's value is wrong, it's value expect in range of [0, 1]" 25 | ); 26 | } 27 | 28 | let result = []; 29 | for (let i = 0; i < count - 1; i++) { 30 | const value = parseFloat( 31 | (Math.random() * (money * limit - min) + min).toFixed(2) 32 | ); 33 | result.push(value); 34 | money -= value; 35 | } 36 | result.push(parseFloat(money.toFixed(2))); 37 | return shuffle(result); 38 | } 39 | 40 | const arr = randomRedPackage(10, 10); 41 | const sum = arr.reduce((pre, cur) => pre + cur, 0); 42 | console.log(arr); 43 | console.log(sum); 44 | -------------------------------------------------------------------------------- /interview/js/前序遍历.js: -------------------------------------------------------------------------------- 1 | const bt = require("./二叉树"); 2 | 3 | function preorder1(root) { 4 | if (!root) { 5 | return; 6 | } 7 | console.log(root.val); 8 | preorder1(root.left); 9 | preorder1(root.right); 10 | } 11 | 12 | function preorder2(root) { 13 | if (!root) { 14 | return; 15 | } 16 | const stack = [root]; 17 | while (stack.length) { 18 | const n = stack.pop(); 19 | console.log(n.val); 20 | if (n.right) { 21 | stack.push(n.right); 22 | } 23 | if (n.left) { 24 | stack.push(n.left); 25 | } 26 | } 27 | } 28 | 29 | preorder1(bt); 30 | -------------------------------------------------------------------------------- /interview/js/加法运算(进制).js: -------------------------------------------------------------------------------- 1 | const add = function (a, b) { 2 | if (a == 0) return b; 3 | if (b == 0) return a; 4 | return add(a ^ b, (a & b) << 1); 5 | }; 6 | 7 | console.log(add(5, 4)); 8 | -------------------------------------------------------------------------------- /interview/js/包含宏任务、微任务的异步代码分析.js: -------------------------------------------------------------------------------- 1 | console.log('1'); // 1 2 | 3 | setTimeout(function () { 4 | console.log('2'); // 5 5 | process.nextTick(function () { 6 | console.log('3'); // 7 7 | }); 8 | new Promise(function (resolve) { 9 | console.log('4'); // 6 10 | resolve(); 11 | }).then(function () { 12 | console.log('5'); // 8 13 | }); 14 | }); 15 | 16 | process.nextTick(function () { 17 | console.log('6'); // 3 18 | }); 19 | 20 | new Promise(function (resolve) { 21 | console.log('7'); // 2 22 | resolve(); 23 | }).then(function () { 24 | console.log('8'); // 4 25 | }); 26 | 27 | setTimeout(function () { 28 | console.log('9'); // 9 29 | process.nextTick(function () { 30 | console.log('10'); // 11 31 | }); 32 | new Promise(function (resolve) { 33 | console.log('11'); // 10 34 | resolve(); 35 | }).then(function () { 36 | console.log('12'); // 12 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /interview/js/去除0.js: -------------------------------------------------------------------------------- 1 | //去除三个及以上相邻的0 2 | const data = [1, 2, 0, 0, 3, 0, 0, 0, 0, 5, 2, 0, 1, 0, 0, 0]; 3 | 4 | let start = 0, 5 | count = 0; // start记录0开始的位置 6 | let res = []; 7 | for (let i = 0; i < data.length; i++) { 8 | if (data[i] == 0) { 9 | count++; // 1.遇到0 10 | } else { 11 | // 2. 遇到非0,退出本次循环 清除计数 12 | start = i + 1; 13 | count = 0; 14 | continue; 15 | } 16 | if (count < 3) { 17 | // 3.遇到0的个数小于3,退出本次循环 18 | continue; 19 | } 20 | // 4. 0的个数>=3, 继续查找后面是否有0 21 | while (i + 1 < data.length && data[i + 1] == 0) { 22 | i++; 23 | count++; 24 | } 25 | //5. 删除0 26 | data.splice(start, count); 27 | //6. 数组长度变化,将i回退。 28 | i = i - count + 1; 29 | //7. 重置start和count 30 | start = i; 31 | count = 0; 32 | } 33 | console.log(data); 34 | -------------------------------------------------------------------------------- /interview/js/只执行一次-闭包.js: -------------------------------------------------------------------------------- 1 | function wrapper(func) { 2 | let judge = true; 3 | return function (arg) { 4 | if (judge) { 5 | judge = false; 6 | func(arg); 7 | } 8 | }; 9 | } 10 | 11 | let getResult = wrapper(console.log); 12 | getResult(1); 13 | getResult(1); 14 | getResult(1); 15 | -------------------------------------------------------------------------------- /interview/js/同类项统计.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 给定数组['1a','2b','13c','5a'] 3 | * 数组元素的格式是一个数字(可能多位)前缀与一个字母的组合 4 | * 输出出现次数最多的字母对应的前缀数字之和 5 | */ 6 | // 测试用例 7 | const test = ["1a", "2b", "13c", "5a"]; 8 | 9 | function func(arr) { 10 | const map = new Map(); 11 | for (let i = 0; i < arr.length; i++) { 12 | let letter = arr[i].substring(arr[i].length - 1); 13 | let num = parseInt(arr[i].substring(0, arr[i].length - 1)); 14 | if (!map.has(letter)) { 15 | // 如果这个字符串中的字母还没作为键存储在哈希表中,就它加进去 16 | map.set(letter, [1, num]); 17 | } else { 18 | // 否则出现次数+1,前缀数字之和也要改变 19 | const arr = map.get(letter); 20 | arr[0]++; 21 | arr[1] += num; 22 | map.set(letter, arr); 23 | } 24 | } 25 | let maxCount = 0, 26 | res; 27 | for (let [key, value] of map) { 28 | if (value[0] > maxCount) { 29 | maxCount = value[0]; 30 | res = [key, value[1]]; 31 | } 32 | } 33 | return res; 34 | } 35 | 36 | console.log(func(test)); 37 | -------------------------------------------------------------------------------- /interview/js/多属性排序.js: -------------------------------------------------------------------------------- 1 | const json = { 2 | data: [ 3 | { date: '2018-08-01', DIU: 1209, country: 'US' }, 4 | { date: '2018-08-01', DIU: 2311, country: 'CN' }, 5 | { date: '2018-08-02', DIU: 879, country: 'US' }, 6 | { date: '2018-08-02', DIU: 680, country: 'GB' }, 7 | { date: '2018-08-02', DIU: 1525, country: 'CN' }, 8 | ], 9 | }; 10 | 11 | function sortWrite(data) { 12 | let res = data.sort((a, b) => { 13 | if (a.date !== b.date) { 14 | return Date.parse(b.date) - Date.parse(a.date); 15 | } 16 | if (a.DIU !== b.DIU) { 17 | return b.DIU - a.DIU; 18 | } 19 | }); 20 | 21 | return res; 22 | } 23 | 24 | console.log(sortWrite(json.data)); 25 | -------------------------------------------------------------------------------- /interview/js/字节-Task类.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 写一个类,利用异步这些, 来实现add,sleep这些方法链式调用 3 | * 这里求优化,目前sleep在最后执行bug 4 | */ 5 | 6 | class Task { 7 | constructor() { 8 | this.queue = Promise.resolve(); 9 | } 10 | 11 | add(func) { 12 | this.queue = this.queue.then(() => { 13 | func; 14 | }); 15 | return this; 16 | } 17 | 18 | sleep(ms) { 19 | this.queue = this.queue.then(() => { 20 | return new Promise((resolve) => { 21 | setTimeout(() => { 22 | resolve(); 23 | }, ms); 24 | }); 25 | }); 26 | return this; 27 | } 28 | } 29 | 30 | let task = new Task(); 31 | task.add(console.log(1)).add(console.log(2)).sleep(2000).add(console.log(3)); 32 | -------------------------------------------------------------------------------- /interview/js/字节-异步.js: -------------------------------------------------------------------------------- 1 | // eventloop 2 | async function async1() { 3 | console.log('async1 start'); // 2 4 | await async2(); 5 | console.log('async1 end'); // 6 6 | } 7 | 8 | async function async2() { 9 | console.log('async2'); // 3 10 | } 11 | 12 | console.log('script start'); // 1 13 | 14 | setTimeout(function () { 15 | console.log('setTimeOut'); // 8 16 | }, 0); 17 | 18 | async1(); 19 | 20 | new Promise(function (resolve) { 21 | console.log('promise1'); // 4 22 | resolve(); 23 | }).then(function () { 24 | console.log('promise2'); // 7 25 | }); 26 | 27 | console.log('script end'); // 5 28 | -------------------------------------------------------------------------------- /interview/js/宏任务微任务.js: -------------------------------------------------------------------------------- 1 | function app() { 2 | setTimeout(() => { 3 | console.log("1-1"); 4 | Promise.resolve().then(() => { 5 | console.log("2-1"); 6 | }); 7 | }); 8 | console.log("1-2"); 9 | Promise.resolve().then(() => { 10 | console.log("1-3"); 11 | setTimeout(() => { 12 | console.log("3-1"); 13 | }); 14 | }); 15 | } 16 | app(); 17 | /* 18 | 1-2 19 | 1-3 20 | 1-1 21 | 2-1 22 | 3-1 23 | */ 24 | 25 | 26 | Promise.resolve() 27 | .then((res) => { 28 | setTimeout(() => { 29 | return 3 30 | }, 0); 31 | return 4; 32 | }) 33 | .then((data) => { 34 | console.log(data); 35 | console.log(222); 36 | }); 37 | -------------------------------------------------------------------------------- /interview/js/对象键名数组排序.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 给定一个对象,其中存储了世界富豪的名字(key)和资产(value) 3 | * 求排行榜(输出一个数组,其中是富豪的名字,按他们的资产从大到小排序) 4 | * 例如输入{bill:500,sam:480,roark:501},返回['roark','bill','sam'] 5 | */ 6 | 7 | //测试用例 8 | const test = { bill: 500, sam: 480, roark: 501, tom: 999 }; 9 | 10 | function func(obj) { 11 | // Object.keys()方***返回一个由一个给定对象的自身可枚举属性组成的数组 12 | // 数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 13 | const arr = Object.keys(obj); 14 | return arr.sort((a, b) => obj[b] - obj[a]); 15 | /* 16 | for (let i = 1; i < arr.length; i++) { 17 | for (let j = 0; j < arr.length - i; j++) { 18 | if (obj[arr[j]] < obj[arr[j + 1]]) { 19 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 20 | } 21 | } 22 | } 23 | return arr; 24 | */ 25 | } 26 | 27 | console.log(func(test)); 28 | -------------------------------------------------------------------------------- /interview/js/小红书.js: -------------------------------------------------------------------------------- 1 | // 给一个 id,需要找到所有父亲节点的 name 值 2 | const cityData = [ 3 | { 4 | id: "1", 5 | name: "广东省", 6 | children: [ 7 | { 8 | id: "11", 9 | name: "深圳市", 10 | children: [ 11 | { 12 | id: "111", 13 | name: "南山区", 14 | }, 15 | { 16 | id: "112", 17 | name: "福田区", 18 | children: [ 19 | { 20 | id: "1121", 21 | name: "A街道", 22 | }, 23 | ], 24 | }, 25 | ], 26 | }, 27 | { 28 | id: "12", 29 | name: "东莞市", 30 | children: [ 31 | { 32 | id: "121", 33 | name: "A区", 34 | }, 35 | { 36 | id: "122", 37 | name: "B区", 38 | }, 39 | ], 40 | }, 41 | ], 42 | }, 43 | ]; 44 | -------------------------------------------------------------------------------- /interview/js/手机生产.js: -------------------------------------------------------------------------------- 1 | function test(n) { 2 | let sum = 0; 3 | let day = 1; 4 | let cur = 1; 5 | 6 | while (day <= n) { 7 | for (let j = 1; j <= cur && day <= n; j++) { 8 | sum += cur; 9 | day++; 10 | } 11 | cur++; 12 | } 13 | return sum; 14 | } 15 | 16 | console.log(test(4)); 17 | -------------------------------------------------------------------------------- /interview/js/打印输出.js: -------------------------------------------------------------------------------- 1 | let inner = 'Window'; 2 | 3 | function say() { 4 | console.log(inner); 5 | console.log(this.inner); 6 | } 7 | 8 | var obj1 = (function () { 9 | var inner = '1-1'; 10 | return { 11 | inner: '1-2', 12 | say: function () { 13 | console.log(inner); 14 | console.log(this.inner); 15 | }, 16 | }; 17 | })(); 18 | 19 | var obj2 = (function () { 20 | var inner = '2-1'; 21 | return { 22 | inner: '2-2', 23 | say: function () { 24 | console.log(inner); 25 | console.log(this.inner); 26 | }, 27 | }; 28 | })(); 29 | 30 | say(); // Window undefined 31 | obj1.say(); // 1-1 1-2 32 | obj2.say(); // 2-1 2-2 33 | obj1.say = say; 34 | obj1.say(); // Window 1-2 35 | obj1.say = obj2.say; 36 | obj1.say(); // 2-1 1-2 37 | -------------------------------------------------------------------------------- /interview/js/最大出现次数.js: -------------------------------------------------------------------------------- 1 | let str = 'xyzzyxyx'; 2 | function computed(str) { 3 | let map = new Map(); 4 | let max = 0; 5 | let obj = {}; 6 | let value; 7 | 8 | str.split('').forEach((el) => { 9 | if (map.has(el)) { 10 | map.set(el, map.get(el) + 1); 11 | } else { 12 | map.set(el, 1); 13 | } 14 | value = map.get(el); 15 | if (value > max) { 16 | obj = {}; 17 | max = value; 18 | obj[el] = max; 19 | } 20 | if (value === max) { 21 | obj[el] = value; 22 | } 23 | }); 24 | return obj; 25 | } 26 | 27 | console.log(computed(str)); 28 | -------------------------------------------------------------------------------- /interview/js/求数组最大值和最小值之差.js: -------------------------------------------------------------------------------- 1 | function getMaxSubMin(arr) { 2 | return Math.max(...arr) - Math.min(...arr); 3 | } 4 | 5 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 6 | console.log(getMaxSubMin(arr)); 7 | -------------------------------------------------------------------------------- /interview/js/百度笔试.js: -------------------------------------------------------------------------------- 1 | // 1. 好像是叫像素比的题 2 | function func1(arr, n, k) { 3 | let res = []; 4 | for (let i = 0; i < n; i++) { 5 | let test = []; 6 | let arr = arr1[i]; 7 | for (let j = 0; j < n; j++) { 8 | let temp = Array(k).fill(arr[j]); 9 | test.push(...temp); 10 | } 11 | for (let m = 0; m < k; m++) { 12 | res.push(test); 13 | } 14 | } 15 | return res; 16 | } 17 | // 测试数据 18 | let n = 2, 19 | k = 2; 20 | let arr1 = [ 21 | [0, 1], 22 | [1, 0], 23 | ]; 24 | console.log(func1(arr1, n, k)); 25 | 26 | // 3. 找比数组中每个数小的只含123的数? 这个超时了 27 | function func2(arr, len) { 28 | let res = []; 29 | for (let i = 0; i < len; i++) { 30 | let line = arr[i]; 31 | while (line) { 32 | let test = line.toString().replace(/[1-3]/g, ""); 33 | if (test == "") { 34 | res.push(line); 35 | break; 36 | } 37 | line--; 38 | } 39 | } 40 | return res; 41 | } 42 | 43 | let arr2 = [213, 3204, 22, 100]; 44 | let len = 4; 45 | console.log(func2(arr2, len)); 46 | -------------------------------------------------------------------------------- /interview/js/百词斩一面.js: -------------------------------------------------------------------------------- 1 | const log = function (msg) { 2 | // TODO 3 | 4 | console.log(msg); 5 | }; 6 | log.before = function (func) {}; 7 | log('test'); 8 | -------------------------------------------------------------------------------- /interview/js/百词斩笔试.js: -------------------------------------------------------------------------------- 1 | function BaiCiZhan(key, index) { 2 | let arr = Array(key).fill(1); 3 | let sum = Array(key).fill(1); 4 | for (let i = key; i <= index; i++) { 5 | let num = sum.reduce((n, val) => n + val); 6 | arr[i] = num; 7 | sum.shift(); 8 | sum.push(num); 9 | } 10 | return arr[index]; 11 | } 12 | 13 | console.log(BaiCiZhan(3, 5)); 14 | -------------------------------------------------------------------------------- /interview/js/第k大的值.js: -------------------------------------------------------------------------------- 1 | function getKMax(arr, k) { 2 | let len = arr.length; 3 | for (let i = 1; i < len; i++) { 4 | for (let j = 0; j < len - i; j++) { 5 | if (arr[j] > arr[j + 1]) { 6 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 7 | } 8 | } 9 | } 10 | // arr.sort((a, b) => a - b); 11 | return arr[len - k]; 12 | } 13 | 14 | let arr = [2, 10, 9, 13, 8, 7, 20, 1]; 15 | console.log(getKMax(arr, 3)); 16 | -------------------------------------------------------------------------------- /interview/js/翻转不包含字母的字符串.js: -------------------------------------------------------------------------------- 1 | function reverseStr(str) { 2 | let res = ''; 3 | let needReverse = ''; 4 | 5 | function ReverseNotAlp(needReverse) { 6 | for (let i = needReverse.length - 1; i >= 0; i--) { 7 | res += needReverse[i]; 8 | } 9 | } 10 | 11 | for (let i = 0; i < str.length; i++) { 12 | if ((str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z')) { 13 | if (needReverse.length > 0) { 14 | ReverseNotAlp(needReverse); 15 | } 16 | res += str[i]; 17 | needReverse = ''; 18 | } else { 19 | needReverse += str[i]; // 非字母的时候一直添加到needReverse,直到循环遇上字母,进行转置 20 | } 21 | } 22 | return res; 23 | } 24 | 25 | const str = '123abd3-adfz-34-akjkfaf'; 26 | console.log(reverseStr(str)); 27 | -------------------------------------------------------------------------------- /interview/js/访问所有子元素.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 1. 创建一个函数, 给定页面上的DOM元素, 将访问元素本身及其所有后代(而不仅仅是它的直接子代) 3 | * 2. 对于访问的每个元素, 函数应将该元素传递给提供的回调函数 4 | * 3. 函数的参数应该是: 一个DOM元素, 一个回调函数(以DOM元素为参数) 5 | */ 6 | 7 | function Traverse(p_element, p_callback) { 8 | p_callback(p_element); 9 | var list = p_element.children; 10 | for (var i = 0; i < list.length; i++) { 11 | Traverse(list[i], p_callback); // recursive call 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /interview/手写代码/Object.assign.js: -------------------------------------------------------------------------------- 1 | Object.assign2 = function (target, ...source) { 2 | if (target == null) { 3 | throw new TypeError('Cannot convert undefined or null to object'); 4 | } 5 | let res = Object(target); 6 | source.forEach((obj) => { 7 | if (obj != null) { 8 | for (let key in obj) { 9 | if (obj.hasOwnProperty(key)) { 10 | res[key] = obj[key]; 11 | } 12 | } 13 | } 14 | }); 15 | return res; 16 | }; 17 | 18 | let a = { age: 18, arr: [10, 20] }; 19 | let b = Object.assign2({}, a); //浅拷贝 20 | console.log(b); 21 | -------------------------------------------------------------------------------- /interview/手写代码/Object.create.js: -------------------------------------------------------------------------------- 1 | // 思路: 将传入的对象作为原型 2 | function create(obj) { 3 | function F() {} 4 | F.prototype = obj; 5 | return new F(); 6 | } 7 | 8 | // 测试 9 | const person = { 10 | isHuman: false, 11 | printIntroduction: function () { 12 | console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); 13 | }, 14 | }; 15 | 16 | const me = create(person); 17 | me.name = 'Matthew'; 18 | me.isHuman = true; 19 | me.printIntroduction(); 20 | -------------------------------------------------------------------------------- /interview/手写代码/add.js: -------------------------------------------------------------------------------- 1 | function add(num) { 2 | let sum = num; 3 | return function tempFun(numB) { 4 | if (arguments.length === 0) { 5 | return sum; 6 | } else { 7 | sum += numB; 8 | return tempFun; 9 | } 10 | }; 11 | } 12 | 13 | console.log(add(1)(2)(7)(4)()); 14 | -------------------------------------------------------------------------------- /interview/手写代码/ajax.js: -------------------------------------------------------------------------------- 1 | function ajax(url, method, body, headers) { 2 | return new Promise((resolve, reject) => { 3 | let req = new XMLHttpRequest(); 4 | req.open(methods, url); 5 | for (let key in headers) { 6 | req.setRequestHeader(key, headers[key]); 7 | } 8 | 9 | req.onreadystatechange(() => { 10 | if (req.readystate == 4) { 11 | if (req.status >= '200' && req.status <= 300) { 12 | resolve(req.responeText); 13 | } else { 14 | reject(req); 15 | } 16 | } 17 | }); 18 | req.send(body); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /interview/手写代码/apply.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myApply = function (context, args) { 2 | if (typeof this !== 'function') { 3 | return; 4 | } 5 | let that = context || window; // 保存传入的this指向 6 | that.fn = this; // 保存当前调用的函数 7 | let result; 8 | 9 | if (args) { 10 | result = that.fn(...args); 11 | } else { 12 | result = that.fn(); 13 | } 14 | return result; 15 | }; 16 | 17 | var person = { 18 | fullName: function (city, country) { 19 | console.log( 20 | this.firstName + ' ' + this.lastName + ',' + city + ',' + country 21 | ); 22 | }, 23 | }; 24 | var person1 = { 25 | firstName: 'John', 26 | lastName: 'Doe', 27 | }; 28 | person.fullName.myApply(person1, ['Oslo', 'Norway']); 29 | -------------------------------------------------------------------------------- /interview/手写代码/bind.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myBind = function (context, ...args) { 2 | // 保存原函数 3 | let self = this; 4 | // 判断有没有传参进来,若为空则赋值[] 5 | args = args ? args : []; 6 | // 创建一个新函数 7 | return function (...newArgs) { 8 | return self.apply(context, [...args, ...newArgs]); 9 | }; 10 | }; 11 | 12 | let name = '小王', 13 | age = 17; 14 | let obj = { 15 | name: '小张', 16 | age: this.age, 17 | myFun: function (from, to) { 18 | console.log( 19 | this.name + ' 年龄 ' + this.age + '来自 ' + from + '; 去往' + to 20 | ); 21 | }, 22 | }; 23 | let db = { 24 | name: '德玛', 25 | age: 99, 26 | }; 27 | 28 | //结果 29 | obj.myFun.myBind(db, '成都', '上海')(); // 德玛 年龄 99 来自 成都去往上海 30 | obj.myFun.myBind(db, ['成都', '上海'])(); // 德玛 年龄 99 来自 成都, 上海去往 undefined 31 | -------------------------------------------------------------------------------- /interview/手写代码/call.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myCall = function (context, ...args) { 2 | if (typeof this !== 'function') { 3 | return; 4 | } 5 | let that = context || window; // 保存传入的this指向, 未传入则设置为window 6 | that.fn = this; // 保存当前调用的函数 7 | that.fn = this; // 保存当前调用的函数 8 | let result; 9 | 10 | if (args) { 11 | result = that.fn(...args); 12 | } else { 13 | result = that.fn(); 14 | } 15 | return result; 16 | }; 17 | 18 | var person = { 19 | fullName: function (city, country) { 20 | console.log( 21 | this.firstName + ' ' + this.lastName + ',' + city + ',' + country 22 | ); 23 | }, 24 | }; 25 | var person1 = { 26 | firstName: 'John', 27 | lastName: 'Doe', 28 | }; 29 | person.fullName.myCall(person1, 'Oslo', 'Norway'); 30 | -------------------------------------------------------------------------------- /interview/手写代码/debounce.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 防抖函数 3 | * @param {function} func 函数 4 | * @param {number} wait 延迟执行毫秒数 5 | * @param {boolean} immediate 是否立即执行 6 | * @returns {function} 7 | */ 8 | function debounce(func, wait, immediate = false) { 9 | let timeout; 10 | 11 | return function () { 12 | let context = this; 13 | let args = arguments; 14 | 15 | if (timeout) clearTimeout(timeout); 16 | 17 | if (immediate) { 18 | let callNow = !timeout; 19 | timeout = setTimeout(() => { 20 | timeout = null; 21 | }, wait); 22 | if (callNow) func.apply(context, args); 23 | } else { 24 | timeout = setTimeout(() => { 25 | func.apply(context, args); 26 | }, wait); 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /interview/手写代码/deepClone.js: -------------------------------------------------------------------------------- 1 | // 深拷贝 2 | function deepClone(obj) { 3 | if (typeof obj !== 'object') { 4 | return obj; 5 | } 6 | let newObj = obj instanceof Array ? [] : {}; 7 | for (let i in obj) { 8 | let template = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i]; 9 | newObj[i] = template; 10 | } 11 | return newObj; 12 | } 13 | 14 | let a = { age: 18, arr: [10, 20] }; 15 | let b = deepClone(a); //深拷贝 16 | let c = Object.assign({}, a); //浅拷贝 17 | let d = JSON.parse(JSON.stringify(a)); //深拷贝 18 | a.arr[0] = 99; 19 | console.log(a, a.arr[0]); 20 | console.log(b, b.arr[0]); 21 | console.log(c, c.arr[0]); 22 | console.log(d, d.arr[0]); 23 | 24 | let test1 = 1; 25 | let test2 = deepClone(test1); 26 | test1 = 3; 27 | console.log(test1); 28 | console.log(test2); 29 | -------------------------------------------------------------------------------- /interview/手写代码/flat.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myFlat = function (num = 1) { 2 | if (!Number(num) || Number(num) <= 0) { 3 | return this; 4 | } 5 | let arr = this; // 获得调用myFlat函数的数组 6 | while (num > 0) { 7 | if (arr.some((x) => Array.isArray(x))) { 8 | arr = [].concat(...arr); // 数组中还有数组元素的话并且 num > 0,继续展开一层数组 9 | } else { 10 | break; // 数组中没有数组元素并且不管 num 是否依旧大于 0,停止循环。 11 | } 12 | num--; 13 | } 14 | return arr; 15 | }; 16 | 17 | // flat方法 18 | const arr = [ 19 | 1, 20 | 2, 21 | 3, 22 | 4, 23 | [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 24 | 5, 25 | 'string', 26 | { name: '前端收割机' }, 27 | ]; 28 | console.log(arr.myFlat(1)); 29 | -------------------------------------------------------------------------------- /interview/手写代码/flat2.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myFlat = function () { 2 | let stack = [...this]; // 获得调用myFlat函数的数组 3 | let result = []; 4 | while (stack.length) { 5 | const first = stack.shift(); 6 | if (Array.isArray(first)) { 7 | stack.unshift(...first); 8 | } else { 9 | result.push(first); 10 | } 11 | } 12 | return result; 13 | }; 14 | 15 | // flat方法 16 | const arr = [ 17 | 1, 18 | 2, 19 | 3, 20 | 4, 21 | [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 22 | 5, 23 | 'string', 24 | { name: '前端收割机' }, 25 | ]; 26 | console.log(arr.myFlat()); 27 | -------------------------------------------------------------------------------- /interview/手写代码/getType.js: -------------------------------------------------------------------------------- 1 | function getType(value) { 2 | // 判断数据是 null 的情况 3 | if (value === null) { 4 | return value + ''; 5 | } 6 | // 判断数据是引用类型的情况 7 | if (typeof value === 'object') { 8 | let valueClass = Object.prototype.toString.call(value), 9 | type = valueClass.split(' ')[1].split(''); 10 | type.pop(); 11 | return type.join('').toLowerCase(); 12 | } else { 13 | // 判断数据是基本数据类型的情况和函数的情况 14 | return typeof value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /interview/手写代码/instanceof.js: -------------------------------------------------------------------------------- 1 | function instance_of(L, R) { 2 | const baseType = [ 3 | 'string', 4 | 'null', 5 | 'number', 6 | 'boolean', 7 | 'sybmol', 8 | 'undefined', 9 | 'bigint', 10 | ]; 11 | if (baseType.includes(typeof L)) { 12 | return false; 13 | } 14 | 15 | let RP = R.prototype; 16 | L = L.__proto__; 17 | while (true) { 18 | if (L === null) { 19 | return false; 20 | } 21 | if (L === RP) { 22 | return true; 23 | } 24 | L = L.__proto__; 25 | } 26 | } 27 | 28 | console.log(instance_of([1, 2, 3], Function)); 29 | -------------------------------------------------------------------------------- /interview/手写代码/jsonp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 13 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /interview/手写代码/jsonp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "koa": "^2.13.4" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /interview/手写代码/jsonp/server.js: -------------------------------------------------------------------------------- 1 | const Koa = require("koa"); 2 | const app = new Koa(); 3 | const items = [ 4 | { id: 1, title: "title1" }, 5 | { id: 2, title: "title2" }, 6 | ]; 7 | 8 | app.use(async (ctx, next) => { 9 | if (ctx.path === "/api/jsonp") { 10 | const { cb, id } = ctx.query; 11 | const title = items.find((item) => item.id == id)["title"]; 12 | ctx.body = `${cb}(${JSON.stringify({ title })})`; 13 | console.log(ctx.body); 14 | return; 15 | } 16 | }); 17 | console.log("listen 8080..."); 18 | app.listen(8080); 19 | -------------------------------------------------------------------------------- /interview/手写代码/new.js: -------------------------------------------------------------------------------- 1 | function New(ctor, ...args) { 2 | if (typeof ctor !== 'function') { 3 | throw 'myNew function the first param must be a function'; 4 | } 5 | let newObj = {}; 6 | newObj.__proto__ = ctor.prototype; 7 | let res = ctor.apply(newObj, args); // 将构造函数ctor的this绑定到newObj中 8 | 9 | if (res !== null && (typeof res === 'object' || typeof res === 'function')) { 10 | return res; 11 | } 12 | return newObj; 13 | } 14 | 15 | function father(name) { 16 | this.name = name; 17 | this.sayname = function () { 18 | console.log(this.name); 19 | }; 20 | } 21 | 22 | var son = New(father, 'dike'); 23 | son.sayname(); 24 | 25 | function A() { 26 | this.a = 1; 27 | } 28 | A.prototype.b = 2; 29 | var a1 = New(A); 30 | // var a2 = new A(); 31 | 32 | console.log(a1.a); // 1 33 | console.log(a1.b); // 2 34 | -------------------------------------------------------------------------------- /interview/手写代码/promise.all.js: -------------------------------------------------------------------------------- 1 | Promise.all = (promises) => { 2 | let count = 0; 3 | let arr = []; 4 | let len = promises.length; 5 | return new Promise((resolve, reject) => { 6 | promises.forEach((p, i) => { 7 | p.then( 8 | (res) => { 9 | count++; 10 | arr[i] = res; // 注意此处用下标 11 | if (count === len) { 12 | resolve(arr); 13 | } 14 | }, 15 | (err) => { 16 | reject(err); 17 | } 18 | ); 19 | }); 20 | }); 21 | }; 22 | 23 | let p1 = new Promise((resolve, reject) => { 24 | setTimeout(() => { 25 | resolve('p1'); 26 | }, 100); 27 | }); 28 | let p2 = Promise.resolve('p2'); 29 | let p3 = Promise.resolve('p3'); 30 | 31 | Promise.all([p1, p2, p3]).then( 32 | (res) => { 33 | console.log(res); 34 | }, 35 | (err) => { 36 | console.log(err); 37 | } 38 | ); 39 | -------------------------------------------------------------------------------- /interview/手写代码/promise.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建三变量记录表示状态 3 | * 用that保存this,避免后期闭包导致this的指向不对 4 | * value 变量用于保存 resolve 或者 reject 中传入的值 5 | * resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调, 6 | * 因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用 7 | */ 8 | 9 | const PENDING = 'pending'; 10 | const FULFILLED = 'fulfilled'; 11 | const REJECTED = 'rejected'; 12 | 13 | function myPromise(fn) { 14 | const that = this; 15 | that.value = null; 16 | that.status = PENDING; //默认状态 17 | that.fulfilledCallbacks = []; 18 | that.rejectedCallbacks = []; 19 | function resolve(value) { 20 | if (that.status === PENDING) { 21 | that.status = FULFILLED; 22 | that.value = value; 23 | //执行回调方法 24 | that.fulfilledCallbacks.forEach((myFn) => myFn(that.value)); 25 | } 26 | } 27 | function reject(value) { 28 | if (that.status === PENDING) { 29 | that.status = REJECTED; 30 | that.value = value; 31 | //执行回调方法 32 | that.rejectedCallbacks.forEach((myFn) => myFn(that.value)); 33 | } 34 | } 35 | // 执行回调函数 36 | try { 37 | fn(resolve, reject); 38 | } catch (e) { 39 | reject(e); 40 | } 41 | } 42 | myPromise.prototype.then = function (onFulfilled, onRejected) { 43 | let self = this; 44 | //等待状态,则添加回调函数到栈中 45 | if (self.status === PENDING) { 46 | self.fulfilledCallbacks.push(() => { 47 | onFulfilled(self.value); 48 | }); 49 | self.rejectedCallbacks.push(() => { 50 | onRejected(self.value); 51 | }); 52 | } 53 | if (self.status === FULFILLED) { 54 | onFulfilled(self.value); 55 | } 56 | 57 | if (self.status === REJECTED) { 58 | onRejected(self.value); 59 | } 60 | }; 61 | 62 | let p = new myPromise((resolve, reject) => { 63 | console.log('hello'); 64 | resolve(5); 65 | }); 66 | p.then((res) => { 67 | console.log(res); 68 | }); 69 | p.then(() => { 70 | console.log('jj'); 71 | }); 72 | -------------------------------------------------------------------------------- /interview/手写代码/promise.race.js: -------------------------------------------------------------------------------- 1 | Promise.race = function (args) { 2 | return new Promise((resolve, reject) => { 3 | for (let i = 0, len = args.length; i < len; i++) { 4 | args[i].then(resolve, reject); 5 | } 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /interview/手写代码/proxy.js: -------------------------------------------------------------------------------- 1 | let obj = { 2 | a: 1, 3 | b: 2, 4 | }; 5 | 6 | let proxy = new Proxy(obj, { 7 | get(target, prop) { 8 | return Reflect.get(...arguments); 9 | }, 10 | set(target, prop, value) { 11 | target[prop] = value; 12 | return Reflect.set(...arguments); 13 | }, 14 | }); 15 | 16 | proxy.a = 4; 17 | console.log(proxy, obj); 18 | console.log(proxy.a, obj.a); 19 | -------------------------------------------------------------------------------- /interview/手写代码/reduce.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myReduce = function (fn, initVal) { 2 | if (typeof fn !== 'function') { 3 | throw Error('fn should be a function'); 4 | } 5 | let pre = initVal ? initVal : 0; 6 | let arr = [...this]; 7 | for (let i = 0; i < arr.length; i++) { 8 | pre = fn(pre, arr[i], i, arr); 9 | } 10 | return pre; 11 | }; 12 | 13 | let arr = [0, 1, 2, 3]; 14 | let sum = arr.myReduce((accumulator, currentValue) => { 15 | return accumulator + currentValue; 16 | }, 0); 17 | console.log(arr, sum); 18 | 19 | var arrData = [ 20 | { id: 0, name: '小明' }, 21 | { id: 1, name: '小张' }, 22 | { id: 2, name: '小李' }, 23 | { id: 3, name: '小孙' }, 24 | { id: 1, name: '小周' }, 25 | { id: 2, name: '小陈' }, 26 | ]; 27 | var obj = {}; 28 | arrData = arrData.reduce((cur, next) => { 29 | if (!obj[next.id]) { 30 | obj[next.id] = true; 31 | cur.push(next); 32 | } 33 | return cur; 34 | }, []); //设置cur默认类型为数组,并且初始值为空的数组 35 | console.log(arrData); //打印出数组去重后的结果 36 | -------------------------------------------------------------------------------- /interview/手写代码/reduce实现map.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myMap = function (fn, thisValue) { 2 | let res = []; 3 | thisValue = thisValue || []; 4 | this.reduce((pre, cur, index, arr) => { 5 | return res.push(fn.call(thisValue, cur, index, arr)); 6 | }, []); 7 | return res; 8 | }; 9 | 10 | var arr = [2, 3, 1, 5]; 11 | arr.myMap(function (item, index, arr) { 12 | console.log(item, index, arr); 13 | }); 14 | -------------------------------------------------------------------------------- /interview/手写代码/repeat.js: -------------------------------------------------------------------------------- 1 | function repeat(func, times, wait) { 2 | return function (context) { 3 | for (let i = 0; i < times; i++) { 4 | setTimeout(() => { 5 | func(context, i); 6 | }, i * wait); 7 | } 8 | }; 9 | } 10 | 11 | // 使下面调用代码能正常工作 12 | const repeatFunc = repeat(console.log, 4, 3000); 13 | repeatFunc('hello world'); //会输出4次 hello world, 每次间隔3秒 14 | -------------------------------------------------------------------------------- /interview/手写代码/reverse.js: -------------------------------------------------------------------------------- 1 | function reverse(str) { 2 | str = str.split(''); 3 | for (let i = 0, j = str.length - 1; i <= j; i++, j--) { 4 | [str[i], str[j]] = [str[j], str[i]]; 5 | } 6 | return str.join(''); 7 | } 8 | 9 | console.log(reverse('hello')); 10 | -------------------------------------------------------------------------------- /interview/手写代码/setInterval.js: -------------------------------------------------------------------------------- 1 | function mySetInterval(func, ms, count) { 2 | function interval() { 3 | if (typeof count === 'undefined' || count-- > 0) { 4 | setTimeout(interval, ms); 5 | try { 6 | func(); 7 | } catch (e) { 8 | count = 0; 9 | throw e.toString(); 10 | } 11 | } 12 | } 13 | setTimeout(interval, ms); 14 | } 15 | 16 | mySetInterval( 17 | () => { 18 | console.log(222); 19 | }, 20 | 2000, 21 | 3 22 | ); 23 | -------------------------------------------------------------------------------- /interview/手写代码/shuffle洗牌算法.js: -------------------------------------------------------------------------------- 1 | // 洗牌算法 2 | function shuffle(arr) { 3 | let i = arr.length; 4 | while (i) { 5 | let j = Math.floor(Math.random() * i--); 6 | [arr[i], arr[j]] = [arr[j], arr[i]]; 7 | } 8 | return arr; 9 | } 10 | 11 | let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8]; 12 | console.log(shuffle(arr)); 13 | -------------------------------------------------------------------------------- /interview/手写代码/sleep.js: -------------------------------------------------------------------------------- 1 | function sleep(ms) { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, ms); 4 | }); 5 | } 6 | 7 | // sleep(5000).then(() => { 8 | // console.log(5); 9 | // }); 10 | 11 | function sleep2(ms) { 12 | let start = new Date().getTime(); 13 | while (new Date().getTime(- start < ms) {} 14 | } 15 | 16 | sleep2(2000); 17 | console.log(2); 18 | -------------------------------------------------------------------------------- /interview/手写代码/throttle.js: -------------------------------------------------------------------------------- 1 | function throttle(func, wait) { 2 | let previous = 0; 3 | return function () { 4 | let args = arguments; 5 | let context = this; 6 | let now = new Date(); 7 | if (now - previous > wait) { 8 | func.apply(context, args); 9 | previous = now; 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /interview/手写代码/trim.js: -------------------------------------------------------------------------------- 1 | function trim(str) { 2 | return str.replace(/(^\s*)|(\s*$)/g, ''); 3 | } 4 | 5 | console.log(trim(' fff dd ')); 6 | -------------------------------------------------------------------------------- /interview/手写代码/中序遍历.js: -------------------------------------------------------------------------------- 1 | const bt = require("./二叉树"); 2 | 3 | function inorder(root) { 4 | if (!root) { 5 | return; 6 | } 7 | inorder(root.left); 8 | console.log(root.val); 9 | inorder(root.right); 10 | } 11 | 12 | inorder(bt); 13 | -------------------------------------------------------------------------------- /interview/手写代码/二分查找.js: -------------------------------------------------------------------------------- 1 | function search(nums, target) { 2 | let left = 0; 3 | let right = nums.length - 1; 4 | let flag = false; 5 | let mid; 6 | while (left <= right) { 7 | mid = Math.floor(left + (right - left) / 2); 8 | if (nums[mid] === target) { 9 | flag = true; 10 | break; 11 | } else if (nums[mid] < target) { 12 | left = mid + 1; 13 | } else { 14 | right = mid - 1; 15 | } 16 | } 17 | while (nums[mid - 1] === target) { 18 | mid--; 19 | } 20 | return flag ? mid : -1; 21 | } 22 | 23 | let nums = [-1, 0, 3, 4, 5, 5, 9, 12]; 24 | let target = 5; 25 | console.log(search(nums, target)); 26 | -------------------------------------------------------------------------------- /interview/手写代码/二叉树.js: -------------------------------------------------------------------------------- 1 | const tree = { 2 | val: 1, 3 | left: { 4 | val: 2, 5 | left: { 6 | val: 4, 7 | left: null, 8 | right: null, 9 | }, 10 | right: { 11 | val: 5, 12 | left: null, 13 | right: null, 14 | }, 15 | }, 16 | right: { 17 | val: 3, 18 | left: { 19 | val: 6, 20 | left: null, 21 | right: null, 22 | }, 23 | right: { 24 | val: 7, 25 | left: null, 26 | right: null, 27 | }, 28 | }, 29 | }; 30 | 31 | module.exports = tree; 32 | -------------------------------------------------------------------------------- /interview/手写代码/冒泡排序.js: -------------------------------------------------------------------------------- 1 | function bubbleSort(arr) { 2 | for (let i = 1; i < arr.length; i++) { 3 | for (let j = 0; j < arr.length - i; j++) { 4 | if (arr[j] > arr[j + 1]) { 5 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 6 | } 7 | } 8 | } 9 | return arr; 10 | } 11 | 12 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 13 | console.log(bubbleSort(arr)); 14 | -------------------------------------------------------------------------------- /interview/手写代码/函数柯里化.js: -------------------------------------------------------------------------------- 1 | function curry(fn, ...args) { 2 | let fnLen = fn.length, 3 | argsLen = args.length; 4 | //对比函数的参数和当前传入参数 5 | //若参数不够就继续递归返回curry 6 | //若参数够就调用函数返回相应的值 7 | if (fnLen > argsLen) { 8 | return function (...args2) { 9 | return curry(fn, ...args, ...args2); 10 | }; 11 | } else { 12 | return fn(...args); 13 | } 14 | } 15 | 16 | let sum = curry((a, b, c, d) => { 17 | return a + b + c + d; 18 | }); 19 | 20 | console.log(sum(2)(3)(5)(6)); //16 21 | console.log(sum(2, 3)(5, 7)); //17 22 | -------------------------------------------------------------------------------- /interview/手写代码/千分位.js: -------------------------------------------------------------------------------- 1 | // The TestCase is shown below 2 | function parseToMoney(num) { 3 | let num1 = Math.floor(num); 4 | let num2 = num - num1; 5 | 6 | let res = []; 7 | while (num1 > 0) { 8 | res.unshift(num1.toString().slice(-3)); 9 | num1 = Math.floor(num1 / 1000); 10 | } 11 | return res.join(','); 12 | } 13 | 14 | function parseToMoney2(num) { 15 | return num.toLocaleString(); 16 | } 17 | 18 | // 考验正则,不会 19 | function parseToMoney3(num) { 20 | return num.replace; 21 | } 22 | 23 | let num = 01087654.321; 24 | console.log(parseToMoney(num)); 25 | -------------------------------------------------------------------------------- /interview/手写代码/发布-订阅.js: -------------------------------------------------------------------------------- 1 | // 对象 2 | let eventEmitter = { 3 | list: {}, // 缓存列表,存放event和fn 4 | 5 | on(event, fn) { 6 | let _this = this; 7 | // 如果对象中没有对应的event值,也就是说明没有订阅过,就给event创建个缓存列表 8 | // 如果对象中有相应的event值,把fn添加到对应event的缓存列表里 9 | (_this.list[event] || (_this.list[event] = [])).push(fn); 10 | return _this; 11 | }, 12 | 13 | emit() { 14 | let _this = this; 15 | // 第一个参数是对应的event值,直接用数组的shift方法取出 16 | // let event = [].shift.call(arguments), 17 | let event = [...arguments].shift(), 18 | fns = [..._this.list[event]]; 19 | // 如果缓存列表里没有fn就返回false 20 | if (!fns || fns.length === 0) { 21 | return false; 22 | } 23 | // 遍历event值对应的缓存列表,依次执行fn 24 | fns.forEach((fn) => { 25 | fn.apply(_this, arguments); 26 | }); 27 | return _this; 28 | }, 29 | 30 | // 监听一次 31 | once(event, fn) { 32 | // 先绑定,调用后删除 33 | let _this = this; 34 | function on() { 35 | _this.off(event, on); 36 | fn.apply(_this, arguments); 37 | } 38 | on.fn = fn; 39 | _this.on(event, on); 40 | return _this; 41 | }, 42 | 43 | // 取消订阅 44 | off(event, fn) { 45 | let _this = this; 46 | let fns = _this.list[event]; 47 | // 如果缓存列表中没有相应的 fn,返回false 48 | if (!fns) return false; 49 | if (!fn) { 50 | // 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fn 都清空 51 | fns && (fns.length = 0); 52 | } else { 53 | // 若有 fn,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可 54 | let cb; 55 | for (let i = 0, cbLen = fns.length; i < cbLen; i++) { 56 | cb = fns[i]; 57 | if (cb === fn || cb.fn === fn) { 58 | fns.splice(i, 1); 59 | break; 60 | } 61 | } 62 | } 63 | return _this; 64 | }, 65 | }; 66 | 67 | function user1(content) { 68 | console.log("用户1订阅了:", content); 69 | } 70 | function user2(content) { 71 | console.log("用户2订阅了:", content); 72 | } 73 | function user3(content) { 74 | console.log("用户3订阅了:", content); 75 | } 76 | 77 | function user4(content) { 78 | console.log("用户4订阅了:", content); 79 | } 80 | 81 | // 订阅 82 | eventEmitter.on("article1", user1); 83 | eventEmitter.on("article1", user2); 84 | eventEmitter.on("article1", user3); 85 | 86 | // 取消user2方法的订阅 87 | eventEmitter.off("article1", user2); 88 | 89 | eventEmitter.once("article2", user4); 90 | 91 | // 发布 92 | eventEmitter.emit("article1", "Javascript 发布-订阅模式"); 93 | eventEmitter.emit("article1", "Javascript 发布-订阅模式"); 94 | eventEmitter.emit("article2", "Javascript 观察者模式"); 95 | eventEmitter.emit("article2", "Javascript 观察者模式"); 96 | -------------------------------------------------------------------------------- /interview/手写代码/后序遍历.js: -------------------------------------------------------------------------------- 1 | const bt = require("./二叉树"); 2 | 3 | function postorder(root) { 4 | if (!root) { 5 | return; 6 | } 7 | postorder(root.left); 8 | postorder(root.right); 9 | console.log(root.val); 10 | } 11 | 12 | postorder(bt); 13 | -------------------------------------------------------------------------------- /interview/手写代码/寄生组合式继承.js: -------------------------------------------------------------------------------- 1 | function Person(name, age) { 2 | this.name = name; 3 | this.age = age; 4 | this.action = ['speak', 'run', 'eat']; 5 | console.log('寄生组合式继承: 我被调用了'); 6 | } 7 | Person.prototype.say = function () { 8 | console.log(`My name is ${this.name}, and I am ${this.age} years old!`); 9 | }; 10 | 11 | function Student(name, age, score) { 12 | Person.call(this, name, age); 13 | this.score = score; 14 | } 15 | Student.prototype = Object.create(Person.prototype); 16 | Student.prototype.constructor = Student; 17 | Student.prototype.showScore = function () { 18 | console.log(`${this.name}'s score is ${this.score}`); 19 | }; 20 | 21 | const xiaoming = new Student('xiaoming', 23, '78'); 22 | xiaoming.action.push('panio'); 23 | console.log(xiaoming.action); 24 | xiaoming.say(); 25 | xiaoming.showScore(); 26 | console.log(Student instanceof Person); // false 因未没有从原型上继承 27 | -------------------------------------------------------------------------------- /interview/手写代码/希尔排序.js: -------------------------------------------------------------------------------- 1 | function shellSort(arr) { 2 | var len = arr.length, 3 | temp, 4 | gap = 1; 5 | while (gap < len / 3) { 6 | //动态定义间隔序列 7 | gap = gap * 3 + 1; 8 | } 9 | for (gap; gap > 0; gap = Math.floor(gap / 3)) { 10 | for (var i = gap; i < len; i++) { 11 | temp = arr[i]; 12 | for (var j = i - gap; j >= 0 && arr[j] > temp; j -= gap) { 13 | arr[j + gap] = arr[j]; 14 | } 15 | arr[j + gap] = temp; 16 | } 17 | } 18 | return arr; 19 | } 20 | 21 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 22 | console.log(shellSort(arr)); 23 | -------------------------------------------------------------------------------- /interview/手写代码/广度优先遍历.js: -------------------------------------------------------------------------------- 1 | const tree = { 2 | val: "a", 3 | children: [ 4 | { 5 | val: "b", 6 | children: [ 7 | { 8 | val: "d", 9 | children: [], 10 | }, 11 | { 12 | val: "e", 13 | children: [], 14 | }, 15 | ], 16 | }, 17 | { 18 | val: "c", 19 | children: [ 20 | { 21 | val: "f", 22 | children: [], 23 | }, 24 | { 25 | val: "g", 26 | children: [], 27 | }, 28 | ], 29 | }, 30 | ], 31 | }; 32 | 33 | function bfs(root) { 34 | let ans = []; 35 | const q = [root]; 36 | while (q.length > 0) { 37 | const n = q.shift(); 38 | ans.push(n.val); 39 | n.children.forEach((child) => { 40 | q.push(child); 41 | }); 42 | } 43 | return ans; 44 | } 45 | 46 | console.log(bfs(tree)); 47 | -------------------------------------------------------------------------------- /interview/手写代码/快速排序.js: -------------------------------------------------------------------------------- 1 | function quickSort(arr) { 2 | // 考虑边界,终止条件 3 | if (arr.length <= 1) { 4 | return arr; 5 | } 6 | let newArr = [...arr]; // 腾讯面试,要求不影响原数组 7 | let basic = newArr.splice(Math.floor(newArr.length / 2), 1)[0]; 8 | let left = newArr.filter((num) => num < basic); 9 | let right = newArr.filter((num) => num >= basic); 10 | 11 | return quickSort(left).concat([basic], quickSort(right)); 12 | } 13 | 14 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 15 | console.log(quickSort(arr)); 16 | -------------------------------------------------------------------------------- /interview/手写代码/插入排序.js: -------------------------------------------------------------------------------- 1 | function insertionSort(arr) { 2 | let len = arr.length; 3 | let preIndex, current; 4 | for (let i = 1; i < len; i++) { 5 | preIndex = i - 1; 6 | current = arr[i]; 7 | while (preIndex >= 0 && arr[preIndex] > current) { 8 | arr[preIndex + 1] = arr[preIndex]; 9 | preIndex--; 10 | } 11 | arr[preIndex + 1] = current; 12 | } 13 | return arr; 14 | } 15 | 16 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 17 | console.log(insertionSort(arr)); 18 | -------------------------------------------------------------------------------- /interview/手写代码/深度优先遍历.js: -------------------------------------------------------------------------------- 1 | const tree = { 2 | val: "a", 3 | children: [ 4 | { 5 | val: "b", 6 | children: [ 7 | { 8 | val: "d", 9 | children: [], 10 | }, 11 | { 12 | val: "e", 13 | children: [], 14 | }, 15 | ], 16 | }, 17 | { 18 | val: "c", 19 | children: [ 20 | { 21 | val: "f", 22 | children: [], 23 | }, 24 | { 25 | val: "g", 26 | children: [], 27 | }, 28 | ], 29 | }, 30 | ], 31 | }; 32 | 33 | // 深度优先遍历 34 | function dfs(root, ans) { 35 | if (!ans) { 36 | ans = new Array(); 37 | } 38 | if (!root) { 39 | return ans; 40 | } 41 | ans.push(root.val); 42 | root.children.forEach((child) => { 43 | dfs(child, ans); 44 | }); 45 | return ans; 46 | } 47 | 48 | console.log(dfs(tree)); 49 | -------------------------------------------------------------------------------- /interview/手写代码/红绿灯.js: -------------------------------------------------------------------------------- 1 | function changeColor(color, ms) { 2 | return new Promise((resolve) => { 3 | console.log(`${color}, 等待${ms}`); 4 | setTimeout(resolve, ms); 5 | }); 6 | } 7 | 8 | function main() { 9 | changeColor('red', 2000).then(() => { 10 | changeColor('yello', 1000).then(() => { 11 | changeColor('red', 3000).then(() => { 12 | main(); 13 | }); 14 | }); 15 | }); 16 | } 17 | 18 | main(); 19 | -------------------------------------------------------------------------------- /interview/手写代码/继承.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 原型链继承 子类的原型指向父类的实例 3 | * 由于原型链继承共享属性实例属性的缺点,属于引用类型传值,引用副本实例属性的修改必然会引起其他副本实例属性的修改,所以不常使用 4 | * 构造函数随时传递参数,很不灵活 5 | */ 6 | 7 | function SuperType() { 8 | this.colors = ['red', 'yellow', 'blue']; 9 | } 10 | 11 | function SubType() {} 12 | SubType.prototype = new SuperType(); 13 | 14 | /* 15 | console.log("原型链继承"); 16 | let instance1 = new SubType(); 17 | instance1.colors.push("black"); 18 | console.log(instance1.colors); 19 | let instance2 = new SubType(); 20 | console.log(instance2.colors); 21 | */ 22 | 23 | /** 24 | * 借用构造函数继承,再执行Child构造函数的时候,子类的实例各自得到一份构造函数的副本,属于值传递,所以子类之间的属性修改是互不相关的 25 | * 缺点:单独使用无法达到函数复用,因为每一个函数和属性都需要在构造函数中定义,没法复用,即没有父类prototype上的函数,只有不能共用的实例属性 26 | * 而且instanceof操作无法确定子类实例和父类之间的关系,因为子类的prototype和父类无关 27 | */ 28 | function Parent() { 29 | this.colors = ['red', 'blue', 'green']; 30 | } 31 | function Child() { 32 | Parent.call(this); 33 | } 34 | /* 35 | console.log("构造函数继承"); 36 | let instance3 = new Child(); 37 | instance3.colors.push("white"); 38 | console.log(instance3.colors); 39 | let instance4 = new Child(); 40 | console.log(instance4.colors); 41 | */ 42 | 43 | /** 44 | * 组合式继承 常用原型链继承+构造函数继承 45 | * 原型链继承共享属性(属性方法和属性),构造函数继承父类构造函数的实例属性 46 | * 缺点:调用了两次父类构造函数,生成了两份实例,一个子类实例,一个父类实例,父类实例作为prototype使用 47 | */ 48 | function Person(name, age) { 49 | this.name = name; 50 | this.age = age; 51 | this.action = ['speak', 'run', 'eat']; 52 | console.log('组合式继承:我被调用了'); 53 | } 54 | Person.prototype.say = function () { 55 | console.log(`My name is ${this.name}, and I am ${this.age} years old!`); 56 | }; 57 | function Student(name, age, score) { 58 | Person.call(this, name, age); // 借用构造函数, 第一次调用父类构造函数 59 | this.score = score; 60 | } 61 | Student.prototype = new Person(); // 原型链继承, 第二次调用父类构造函数 62 | Student.prototype.constructor = Student; // 将实例的原型上的构造函数指定为当前子类的构造函数 63 | Student.prototype.showScore = function () { 64 | console.log(`My score is ${this.score}`); 65 | }; 66 | 67 | /* 68 | let xiaoming = new Student("xiaoming", 18, "88"); 69 | xiaoming.action.push("pinio"); 70 | console.log(xiaoming.action); 71 | xiaoming.say(); 72 | xiaoming.showScore(); 73 | 74 | let xiaohua = new Student("xiaohua", 24, "99"); 75 | console.log(xiaohua.action); 76 | xiaohua.say(); 77 | xiaohua.showScore(); 78 | */ 79 | 80 | /** 81 | * 寄生组合式继承 最好的方法,最理想的方法 82 | * 解决了两次调用父类组合函数问题 83 | */ 84 | function Person_1(name, age) { 85 | this.name = name; 86 | this.age = age; 87 | this.action = ['speak', 'run', 'eat']; 88 | console.log('寄生组合式继承:我被调用了'); 89 | } 90 | Person_1.prototype.say = function () { 91 | console.log(`My name is ${this.name}, and I am ${this.age} years old!`); 92 | }; 93 | function Student_1(name, age, score) { 94 | Person_1.call(this, name, age); // 借用构造函数, 第一次调用父类构造函数 95 | this.score = score; 96 | } 97 | Student_1.prototype = Object.create(Person_1.prototype); 98 | Student_1.prototype.constructor = Student_1; // 借用构造函数, 第一次调用父类构造函数 99 | Student_1.prototype.showScore = function () { 100 | console.log(`My score is ${this.score}`); 101 | }; 102 | 103 | const xiaoming_1 = new Student_1('xiaoming_1', 23, '78'); 104 | xiaoming_1.action.push('panio'); 105 | console.log(xiaoming_1.action); 106 | xiaoming_1.say(); 107 | xiaoming_1.showScore(); 108 | console.log(Student_1 instanceof Person_1); 109 | -------------------------------------------------------------------------------- /interview/手写代码/选择排序.js: -------------------------------------------------------------------------------- 1 | function selectSort(arr) { 2 | let len = arr.length; 3 | let minIndex; 4 | for (let i = 0; i < len; i++) { 5 | minIndex = i; 6 | for (let j = i; j < len; j++) { 7 | if (arr[minIndex] > arr[j]) { 8 | minIndex = j; 9 | } 10 | } 11 | if (i !== minIndex) { 12 | [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; 13 | } 14 | } 15 | return arr; 16 | } 17 | 18 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 19 | console.log(selectSort(arr)); 20 | -------------------------------------------------------------------------------- /interview/手写代码/闭包.js: -------------------------------------------------------------------------------- 1 | function f1() { 2 | let n = 0; 3 | return function () { 4 | n++; 5 | console.log(n); 6 | }; 7 | } 8 | 9 | let num = new Array(); 10 | for (var i = 0; i < 4; i++) { 11 | num[i] = f1(); 12 | } 13 | num[1](); 14 | num[2](); 15 | num[2](); 16 | 17 | function a() { 18 | let i = 10; 19 | return () => { 20 | console.log(i++); 21 | }; 22 | } 23 | var c = a(); 24 | c(); 25 | c(); 26 | c(); 27 | c(); 28 | 29 | function init() { 30 | var name = "Mozilla"; 31 | return function () { 32 | console.log(name); 33 | }; 34 | } 35 | init()(); 36 | -------------------------------------------------------------------------------- /leetcode/1013将数组分成和相等的三个部分.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} arr 3 | * @return {boolean} 4 | */ 5 | var canThreePartsEqualSum = function (arr) { 6 | let count = arr.reduce((prev, next) => { 7 | return prev + next; 8 | }); 9 | if (count % 3 != 0) { 10 | return false; 11 | } 12 | count = count / 3; 13 | let num = 0, 14 | time = 0; 15 | for (let i = 0; i < arr.length; i++) { 16 | num += arr[i]; 17 | if (num === count) { 18 | num = 0; 19 | time++; 20 | } 21 | if (time === 3) { 22 | return true; 23 | } 24 | } 25 | return false; 26 | }; 27 | 28 | let arr = [0, 2, 1, -6, 6, -7, 9, 1, 2, 0, 1]; 29 | let arr2 = [0, 2, 1, -6, 6, 7, 9, -1, 2, 0, 1]; 30 | console.log(canThreePartsEqualSum(arr2)); 31 | -------------------------------------------------------------------------------- /leetcode/101对称二叉树.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {boolean} 12 | */ 13 | var isSymmetric = function (root) { 14 | if (!root) { 15 | return true; 16 | } 17 | const dfs = (left, right) => { 18 | if (left === null && right === null) { 19 | return true; 20 | } 21 | if (left === null || right === null) { 22 | return false; 23 | } 24 | if (left.val !== right.val) { 25 | return false; 26 | } 27 | return dfs(left.left, right.right) && dfs(left.right, right.left); 28 | }; 29 | return dfs(root.left, root.right); 30 | }; 31 | -------------------------------------------------------------------------------- /leetcode/104二叉树的最大深度.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number} 12 | */ 13 | var maxDepth = function (root) { 14 | if (!root) { 15 | return 0; 16 | } 17 | return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; 18 | }; 19 | -------------------------------------------------------------------------------- /leetcode/110平衡二叉树.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {boolean} 12 | */ 13 | var isBalanced = function (root) { 14 | // 声明一个变量保存平衡状态 15 | let isBalanced = true; 16 | // 从根结点开始递归遍历 17 | function checkBanlance(node, level) { 18 | if (node === null) return level; // 如果节点为空直接返回level 19 | // 递归遍历各节点,获取其level值,检查是否平衡 如果平衡状态为false直接返回 20 | let left = checkBanlance(node.left, level + 1); 21 | if (!isBalanced) { 22 | return left; 23 | } 24 | let right = checkBanlance(node.right, level + 1); 25 | if (!isBalanced) { 26 | return right; 27 | } 28 | // 检测左右level平衡是否超过1,超过1更改平衡状态 29 | if (Math.abs(left - right) > 1) isBalanced = false; 30 | // 返回最大的level值 31 | return Math.max(left, right); 32 | } 33 | checkBanlance(root, 0); 34 | return isBalanced; 35 | }; 36 | -------------------------------------------------------------------------------- /leetcode/112路径总和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @param {number} targetSum 12 | * @return {boolean} 13 | */ 14 | var hasPathSum = function (root, targetSum) { 15 | if (root === null) { 16 | return false; 17 | } 18 | targetSum -= root.val; 19 | if (targetSum === 0) { 20 | if (root.left === null && root.right === null) { 21 | return true; 22 | } 23 | } 24 | return hasPathSum(root.left, targetSum) || hasPathSum(root.right, targetSum); 25 | }; 26 | -------------------------------------------------------------------------------- /leetcode/121买卖股票的最佳时机.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} prices 3 | * @return {number} 4 | */ 5 | var maxProfit = function (prices) { 6 | let max = 0; 7 | let minPrice = prices[0]; 8 | for (let i = 1; i < prices.length; i++) { 9 | minPrice = Math.min(minPrice, prices[i]); 10 | max = Math.max(max, prices[i] - minPrice); 11 | } 12 | return max; 13 | }; 14 | -------------------------------------------------------------------------------- /leetcode/129求根节点到叶节点数字之和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number} 12 | */ 13 | var sumNumbers = function (root) { 14 | const dfs = (node, preSum) => { 15 | if (node === null) { 16 | return 0; 17 | } 18 | const sum = preSum * 10 + node.val; 19 | if (node.left == null && node.right == null) { 20 | return sum; 21 | } else { 22 | return dfs(node.left, sum) + dfs(node.right, sum); 23 | } 24 | }; 25 | return dfs(root, 0); 26 | }; 27 | -------------------------------------------------------------------------------- /leetcode/136只出现一次的数字.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var singleNumber = function (nums) { 6 | let ans = 0; 7 | for (let i = 0; i < nums.length; i++) { 8 | ans ^= nums[i]; 9 | } 10 | return ans; 11 | }; 12 | -------------------------------------------------------------------------------- /leetcode/141环形链表.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | 9 | /** 10 | * @param {ListNode} head 11 | * @return {boolean} 12 | */ 13 | var hasCycle = function (head) { 14 | while (head) { 15 | if (head.tag) { 16 | return true; 17 | } 18 | head.tag = true; 19 | head = head.next; 20 | } 21 | return false; 22 | }; 23 | -------------------------------------------------------------------------------- /leetcode/144二叉树的前序遍历.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number[]} 12 | */ 13 | var preorderTraversal = function (root) { 14 | let result = []; 15 | preorder = (node) => { 16 | if (!node) { 17 | return; 18 | } 19 | result.push(node.val); 20 | preorder(node.left); 21 | preorder(node.right); 22 | }; 23 | preorder(root); 24 | return result; 25 | }; 26 | -------------------------------------------------------------------------------- /leetcode/14最长公共前缀.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} strs 3 | * @return {string} 4 | */ 5 | var longestCommonPrefix = function (strs) { 6 | let res = ''; 7 | if (!strs.length) { 8 | return res; 9 | } 10 | for (let j = 0; j < strs[0].length; j++) { 11 | for (let i = 1; i < strs.length; i++) { 12 | if (strs[i][j] !== strs[0][j]) { 13 | return res; 14 | } 15 | } 16 | res += strs[0][j]; 17 | } 18 | return res; 19 | }; 20 | 21 | let strs = ['flower', 'flow', 'flight']; 22 | console.log(longestCommonPrefix(strs)); 23 | -------------------------------------------------------------------------------- /leetcode/155最小栈.js: -------------------------------------------------------------------------------- 1 | /** 2 | * initialize your data structure here. 3 | */ 4 | var MinStack = function () { 5 | this.stack = []; 6 | this.min_stack = [Infinity]; 7 | }; 8 | 9 | /** 10 | * @param {number} val 11 | * @return {void} 12 | */ 13 | MinStack.prototype.push = function (val) { 14 | this.stack.push(val); 15 | this.min_stack.push(Math.min(this.min_stack[this.min_stack.length - 1], val)); 16 | }; 17 | 18 | /** 19 | * @return {void} 20 | */ 21 | MinStack.prototype.pop = function () { 22 | this.min_stack.pop(); 23 | return this.stack.pop(); 24 | }; 25 | 26 | /** 27 | * @return {number} 28 | */ 29 | MinStack.prototype.top = function () { 30 | return this.stack[this.stack.length - 1]; 31 | }; 32 | 33 | /** 34 | * @return {number} 35 | */ 36 | MinStack.prototype.getMin = function () { 37 | return this.min_stack[this.min_stack.length - 1]; 38 | }; 39 | 40 | /** 41 | * Your MinStack object will be instantiated and called as such: 42 | * var obj = new MinStack() 43 | * obj.push(val) 44 | * obj.pop() 45 | * var param_3 = obj.top() 46 | * var param_4 = obj.getMin() 47 | */ 48 | -------------------------------------------------------------------------------- /leetcode/160相交链表.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | 9 | /** 10 | * @param {ListNode} headA 11 | * @param {ListNode} headB 12 | * @return {ListNode} 13 | */ 14 | var getIntersectionNode = function (headA, headB) { 15 | if (headA === null || headB === null) { 16 | return null; 17 | } 18 | let pA = headA, 19 | pB = headB; 20 | while (pA !== pB) { 21 | if (pA === null) { 22 | pA = headB; 23 | } else { 24 | pA = pA.next; 25 | } 26 | if (pB === null) { 27 | pB = headA; 28 | } else { 29 | pB = pB.next; 30 | } 31 | } 32 | return pA; 33 | }; 34 | -------------------------------------------------------------------------------- /leetcode/165比较版本号.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} version1 3 | * @param {string} version2 4 | * @return {number} 5 | */ 6 | var compareVersion = function (version1, version2) { 7 | const v1 = version1.split("."); 8 | const v2 = version2.split("."); 9 | for (let i = 0; i < v1.length || i < v2.length; ++i) { 10 | let x = 0; 11 | y = 0; 12 | if (i < v1.length) { 13 | x = parseInt(v1[i]); 14 | } 15 | if (i < v2.length) { 16 | y = parseInt(v2[i]); 17 | } 18 | if (x > y) { 19 | return 1; 20 | } 21 | if (x < y) { 22 | return -1; 23 | } 24 | } 25 | return 0; 26 | }; 27 | -------------------------------------------------------------------------------- /leetcode/168.Excel表列名称.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} columnNumber 3 | * @return {string} 4 | */ 5 | var convertToTitle = function (columnNumber) { 6 | let res = ""; 7 | while (columnNumber) { 8 | const num = columnNumber % 26 ? columnNumber % 26 : 26; // 取模;0则取26 9 | res = String.fromCharCode(num + 65 - 1) + res; // 获取A-Z字符串 10 | columnNumber = parseInt((columnNumber - num) / 26); //取余 11 | } 12 | return res; 13 | }; 14 | -------------------------------------------------------------------------------- /leetcode/169多数元素.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var majorityElement = function (nums) { 6 | let stack = [nums[0]]; 7 | for (let i = 1; i < nums.length; i++) { 8 | // 如果栈空,则直接入栈,跳过此轮循环 9 | if (stack.length === 0) { 10 | stack.push(nums[i]); 11 | continue; 12 | } 13 | // 相等入栈,不相等出栈 14 | if (stack[stack.length - 1] === nums[i]) { 15 | stack.push(nums[i]); 16 | } else { 17 | stack.pop(); 18 | } 19 | } 20 | return stack.pop(); 21 | }; 22 | 23 | function majorityElement2(nums) { 24 | let mid = Math.floor(nums.length / 2); 25 | let map = new Map(); 26 | for (let num of nums) { 27 | if (map.has(num)) { 28 | map.set(num, map.get(num) + 1); 29 | } else { 30 | map.set(num, 1); 31 | } 32 | if (map.get(num) > mid) { 33 | return num; 34 | } 35 | } 36 | return 0; 37 | } 38 | 39 | let test1 = [2, 2, 1, 1, 1, 2, 2]; 40 | let test2 = [3, 2, 3]; 41 | let test3 = [6, 5, 5]; 42 | console.log(majorityElement2(test1)); 43 | -------------------------------------------------------------------------------- /leetcode/191位1的个数.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n - a positive integer 3 | * @return {number} 4 | */ 5 | var hammingWeight = function (n) { 6 | return n.toString(2).replace('/0/g', "").length; 7 | }; 8 | 9 | let test = 00000000000000000000000000001011; 10 | console.log(hammingWeight(test)); 11 | -------------------------------------------------------------------------------- /leetcode/1两数之和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number[]} 5 | */ 6 | var twoSum = function (nums, target) { 7 | let map = new Map(); 8 | for (let i = 0; i < nums.length; i++) { 9 | if (map.has(target - nums[i])) { 10 | return [map.get(target - nums[i]), i]; 11 | } else { 12 | map.set(nums[i], i); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /leetcode/200岛屿数量.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {character[][]} grid 3 | * @return {number} 4 | */ 5 | // 注意题意 连续大陆就是一个岛屿 6 | // 遍历二维数组,每当遇到1开启搜索模式,从当前节点向左/右/上/下,每次分别移动一步,如果是1则替换为0 7 | var numIslands = function (grid) { 8 | function dfs(grid, i, j) { 9 | // 递归终止条件 10 | if ( 11 | i < 0 || 12 | i >= grid.length || 13 | j < 0 || 14 | j >= grid[0].length || 15 | grid[i][j] === "0" 16 | ) { 17 | return; 18 | } 19 | grid[i][j] = "0"; // 走过的标记为0 20 | dfs(grid, i + 1, j); 21 | dfs(grid, i, j + 1); 22 | dfs(grid, i - 1, j); 23 | dfs(grid, i, j - 1); 24 | } 25 | let count = 0; 26 | let row = grid.length; 27 | let col = grid[0].length; 28 | 29 | for (let i = 0; i < row; i++) { 30 | for (let j = 0; j < col; j++) { 31 | if (grid[i][j] === "1") { 32 | dfs(grid, i, j); 33 | count++; 34 | } 35 | } 36 | } 37 | return count; 38 | }; 39 | 40 | let grid = [ 41 | ["1", "1", "0", "0", "0"], 42 | ["1", "1", "0", "0", "0"], 43 | ["0", "0", "1", "0", "0"], 44 | ["0", "0", "0", "1", "1"], 45 | ]; 46 | 47 | console.log(numIslands(grid)); 48 | -------------------------------------------------------------------------------- /leetcode/204计算质数.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | var countPrimes = function (n) { 6 | const isPrime = new Array(n).fill(1); 7 | let sum = 0; 8 | for (let i = 2; i < n; i++) { 9 | if (isPrime[i]) { 10 | sum += 1; 11 | for (let j = i * i; j < n; j += i) { 12 | isPrime[j] = 0; 13 | } 14 | } 15 | } 16 | return sum; 17 | }; 18 | -------------------------------------------------------------------------------- /leetcode/206反转链表.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @return {ListNode} 11 | */ 12 | var reverseList = function(head) { 13 | let pre = null; 14 | let cur = head; 15 | while (cur) { 16 | const next = cur.next; 17 | cur.next = pre; 18 | pre = cur; 19 | cur = next; 20 | } 21 | return pre; 22 | }; -------------------------------------------------------------------------------- /leetcode/20有效的括号.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {boolean} 4 | */ 5 | var isValid = function (s) { 6 | if (s.length % 2 === 1) { 7 | return false; 8 | } 9 | const map = new Map([ 10 | ['}', '{'], 11 | [']', '['], 12 | [')', '('], 13 | ]); 14 | let stack = []; 15 | for (let i = 0; i < s.length; i++) { 16 | if (map.has(s[i])) { 17 | if (!stack.length || stack[stack.length - 1] !== map.get(s[i])) { 18 | return false; 19 | } 20 | stack.pop(); 21 | } else { 22 | stack.push(s[i]); 23 | } 24 | } 25 | return !stack.length; 26 | }; 27 | -------------------------------------------------------------------------------- /leetcode/215数组中的第K个最大元素.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} k 4 | * @return {number} 5 | */ 6 | var findKthLargest = function (nums, k) { 7 | const target = nums.length - k; //因为我们快排是从小到大的,所以k就相当于从后面数,我们现在把它转换为前面数 8 | 9 | const dfs = (array, start) => { 10 | //定义一个递归函数(快排)(数组,起始下标) 11 | let left = []; //定义一个数组(左,用来存放所有比当前值小的数) 12 | let right = []; //定义一个数组(右,用来存放所有比当前值大的数) 13 | const midNum = array.splice(Math.floor(array.length / 2), 1)[0]; //截取出当前数组的当前值(原数组改变),并赋值给midNum 14 | 15 | for (let i = 0; i < array.length; i++) { 16 | if (array[i] < midNum) { 17 | left.push(array[i]); //插入到left数组 18 | } else { 19 | right.push(array[i]); //插入到right数组 20 | } 21 | } 22 | 23 | if (left.length + start === target) { 24 | //如果当前值的下标等于目标值下标target 25 | return midNum; //返回当前值 26 | } else if (left.length + start > target) { 27 | //如果当前值的下标比目标值下标大,就递归left数组 28 | return dfs(left, start); //将left数组递归(起始位置值还是start) 29 | } else { 30 | //如果当前值的下标比目标值下标小,就递归right数组 31 | return dfs(right, left.length + start + 1); //将right数组递归(起始位置值,要包含且+1) 32 | //left.length + start :是当前值下标,要+1之后才会是下一组的起始值 33 | } 34 | }; 35 | 36 | return dfs(nums, 0); //调用递归函数,并返回最后的结果 37 | }; 38 | 39 | let test = [3, 2, 1, 5, 6, 4]; 40 | console.log(findKthLargest(test, 2)); 41 | -------------------------------------------------------------------------------- /leetcode/217存在重复元素.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 给定一个整数数组,判断是否存在重复元素。 3 | * 如果存在一值在数组中出现至少两次,函数返回 true 。 4 | * 如果数组中每个元素都不相同,则返回 false 。 5 | */ 6 | /** 7 | * @param {number[]} nums 8 | * @return {boolean} 9 | */ 10 | var containsDuplicate1 = function (nums) { 11 | const set = new Set(); 12 | for (const num of nums) { 13 | if (set.has(num)) { 14 | return true; 15 | } 16 | set.add(num); 17 | } 18 | return false; 19 | }; 20 | 21 | var containsDuplicate2 = function (nums) { 22 | let setSize = new Set(nums).size; 23 | return setSize === nums.length ? false : true; 24 | }; 25 | 26 | let test1 = [1, 2, 3, 1]; 27 | let test2 = [1, 2, 3, 4]; 28 | let test3 = [1, 1, 1, 3, 3, 4, 3, 2, 4, 2]; 29 | 30 | console.log(containsDuplicate2(test1)); 31 | console.log(containsDuplicate2(test2)); 32 | console.log(containsDuplicate2(test3)); 33 | -------------------------------------------------------------------------------- /leetcode/21合并两个有序链表.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} l1 10 | * @param {ListNode} l2 11 | * @return {ListNode} 12 | */ 13 | var mergeTwoLists = function (l1, l2) { 14 | if (l1 === null) { 15 | return l2; 16 | } else if (l2 === null) { 17 | return l1; 18 | } else if (l1.val < l2.val) { 19 | l1.next = mergeTwoLists(l1.next, l2); 20 | return l1; 21 | } else { 22 | l2.next = mergeTwoLists(l1, l2.next); 23 | return l2; 24 | } 25 | }; 26 | 27 | var mergeTwoLists2 = function (list1, list2) { 28 | const prehead = new ListNode(-1); 29 | let prev = prehead; 30 | while (list1 !== null && list2 !== null) { 31 | if (list1.val <= list2.val) { 32 | prev.next = list1; 33 | list1 = list1.next; 34 | } else { 35 | prev.next = list2; 36 | list2 = list2.next; 37 | } 38 | prev = prev.next; 39 | } 40 | prev.next = list1 === null ? list2 : list1; 41 | return prehead.next; 42 | }; 43 | -------------------------------------------------------------------------------- /leetcode/226翻转二叉树.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {TreeNode} 12 | */ 13 | var invertTree = function (root) { 14 | if (root === null) { 15 | return null; 16 | } 17 | const left = invertTree(root.left); 18 | const right = invertTree(root.right); 19 | root.left = right; 20 | root.right = left; 21 | 22 | return root; 23 | }; 24 | -------------------------------------------------------------------------------- /leetcode/231二的幂.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {boolean} 4 | */ 5 | var isPowerOfTwo = function (n) { 6 | return n.toString(2).replace(/0/g, "").length === 1 ? true : false; 7 | }; 8 | 9 | let n1 = 30; 10 | let n2 = 32; 11 | console.log(isPowerOfTwo(n1)); 12 | console.log(isPowerOfTwo(n2)); 13 | -------------------------------------------------------------------------------- /leetcode/237删除链表中的节点.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} node 10 | * @return {void} Do not return anything, modify node in-place instead. 11 | */ 12 | var deleteNode = function (node) { 13 | node.val = node.next.val; 14 | node.next = node.next.next; 15 | }; 16 | -------------------------------------------------------------------------------- /leetcode/242有效的字母异位词.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @param {string} t 4 | * @return {boolean} 5 | */ 6 | var isAnagram = function (s, t) { 7 | return ( 8 | s.length == t.length && 9 | s.split("").sort().toString() === t.split("").sort().toString() 10 | ); 11 | }; 12 | 13 | var isAnagram2 = function (s, t) { 14 | if (s.length !== t.length) { 15 | return false; 16 | } 17 | const obj = {}; 18 | for (let i = 0; i < s.length; i++) { 19 | const currentS = s[i]; 20 | const currentT = t[i]; 21 | obj[currentS] ? obj[currentS]++ : (obj[currentS] = 1); 22 | obj[currentT] ? obj[currentT]-- : (obj[currentT] = -1); 23 | } 24 | return Object.values(obj).every((v) => v === 0); 25 | }; 26 | 27 | let s = "anagram", 28 | t = "nagaram"; 29 | console.log(isAnagram2(s, t)); 30 | -------------------------------------------------------------------------------- /leetcode/257二叉树的所有路径.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {string[]} 12 | */ 13 | var binaryTreePaths = function (root) { 14 | const paths = []; 15 | function construct_paths(node, path) { 16 | if (node) { 17 | path += node.val.toString(); 18 | if (node.left === null && node.right === null) { 19 | paths.push(path); 20 | } else { 21 | path += "->"; // 当前节点不是叶子节点,继续递归遍历 22 | construct_paths(node.left, path); 23 | construct_paths(node.right, path); 24 | } 25 | } 26 | } 27 | 28 | construct_paths(root, ""); 29 | return paths; 30 | }; 31 | -------------------------------------------------------------------------------- /leetcode/26删除有序数组中的重复项.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | var removeDuplicates = function (nums) { 6 | if (!nums.length) return 0; 7 | let slow = 0; 8 | for (let fast = 1; fast < nums.length; fast++) { 9 | if (nums[fast] !== nums[slow]) { 10 | nums[++slow] = nums[fast]; 11 | } 12 | } 13 | return slow + 1; 14 | }; 15 | -------------------------------------------------------------------------------- /leetcode/283移动零.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {void} Do not return anything, modify nums in-place instead. 4 | */ 5 | var moveZeroes = function (nums) { 6 | let slow = -1; 7 | for (let fast = 0; fast < nums.length; fast++) { 8 | if (nums[fast] !== 0) { 9 | slow++; 10 | [nums[slow], nums[fast]] = [nums[fast], nums[slow]]; 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /leetcode/2两数相加.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} l1 10 | * @param {ListNode} l2 11 | * @return {ListNode} 12 | */ 13 | var addTwoNumbers = function (l1, l2) { 14 | let tail = new ListNode(); 15 | let head = tail; 16 | let carry = 0; 17 | while (l1 || l2) { 18 | const n1 = l1 ? l1.val : 0; 19 | const n2 = l2 ? l2.val : 0; 20 | const sum = n1 + n2 + carry; 21 | tail.next = new ListNode(sum % 10); 22 | tail = tail.next; 23 | carry = Math.floor(sum / 10); 24 | // 移动指针 25 | if (l1) { 26 | l1 = l1.next; 27 | } 28 | if (l2) { 29 | l2 = l2.next; 30 | } 31 | } 32 | // 判断是否还有进位 33 | if (carry > 0) { 34 | tail.next = new ListNode(carry); 35 | } 36 | return head.next; 37 | }; 38 | -------------------------------------------------------------------------------- /leetcode/300最长递增子序列.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number} 4 | */ 5 | 6 | const lengthOfLIS = (nums) => { 7 | let dp = Array(nums.length).fill(1); 8 | let result = 1; 9 | 10 | for (let i = 1; i < nums.length; i++) { 11 | for (let j = 0; j < i; j++) { 12 | if (nums[i] > nums[j]) { 13 | dp[i] = Math.max(dp[i], dp[j] + 1); 14 | } 15 | } 16 | result = Math.max(result, dp[i]); 17 | } 18 | 19 | return result; 20 | }; 21 | -------------------------------------------------------------------------------- /leetcode/30在排序数组中查找元素的第一个和最后一个位置.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number[]} 5 | */ 6 | var searchRange = function (nums, target) { 7 | let left = 0; 8 | let right = nums.length - 1; 9 | let flag = -1; 10 | while (left <= right) { 11 | let mid = Math.floor((left + right) / 2); 12 | if (nums[mid] === target) { 13 | flag = mid; 14 | break; 15 | } else if (nums[mid] < target) { 16 | left = mid + 1; 17 | } else { 18 | right = mid - 1; 19 | } 20 | } 21 | if (flag === -1) { 22 | return [-1, -1]; 23 | } 24 | 25 | left = right = flag; 26 | while (nums[left - 1] === target) { 27 | left--; 28 | } 29 | while (nums[right + 1] === target) { 30 | right++; 31 | } 32 | return [left, right]; 33 | }; 34 | -------------------------------------------------------------------------------- /leetcode/319灯泡开关.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | var bulbSwitch = function (n) { 6 | return Math.floor(Math.sqrt(n)); 7 | }; 8 | 9 | console.log(bulbSwitch(100)); 10 | -------------------------------------------------------------------------------- /leetcode/322零钱兑换.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} coins 3 | * @param {number} amount 4 | * @return {number} 5 | */ 6 | var coinChange = function (coins, amount) { 7 | if (!amount) { 8 | return 0; 9 | } 10 | let dp = Array(amount + 1).fill(Infinity); 11 | dp[0] = 0; 12 | for (let coin of coins) { 13 | for (let j = coin; j <= amount; j++) { 14 | dp[j] = Math.min(dp[j - coin] + 1, dp[j]); 15 | } 16 | } 17 | return dp[amount] === Infinity ? -1 : dp[amount]; 18 | }; 19 | -------------------------------------------------------------------------------- /leetcode/326三的幂.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {boolean} 4 | */ 5 | var isPowerOfThree = function (n) { 6 | if (n <= 0) return false; 7 | while (n % 3 === 0) { 8 | n = n / 3; 9 | } 10 | return n === 1; 11 | }; 12 | -------------------------------------------------------------------------------- /leetcode/349两个数组的交集.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums1 3 | * @param {number[]} nums2 4 | * @return {number[]} 5 | */ 6 | var intersection = function (nums1, nums2) { 7 | if (nums2.length < nums1.length) { 8 | [nums1, nums2] = [nums2, nums1]; 9 | } 10 | let nums1Set = new Set(nums1); 11 | let res = new Set(); 12 | for (let i = nums2.length; i >= 0; i--) { 13 | nums1Set.has(nums2[i]) && res.add(nums2[i]); 14 | } 15 | 16 | return [...res]; 17 | }; 18 | 19 | let nums1 = [1, 2, 2, 1], 20 | nums2 = [2, 2]; 21 | let res = intersection(nums1, nums2); 22 | console.log(res); 23 | -------------------------------------------------------------------------------- /leetcode/384打乱数组.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | */ 4 | var Solution = function (nums) { 5 | this.nums = nums; 6 | }; 7 | 8 | /** 9 | * @return {number[]} 10 | */ 11 | Solution.prototype.reset = function () { 12 | return this.nums; 13 | }; 14 | 15 | /** 16 | * @return {number[]} 17 | */ 18 | Solution.prototype.shuffle = function () { 19 | let arr = [...this.nums]; 20 | let i = arr.length; 21 | while (i) { 22 | let j = Math.floor(Math.random() * i--); 23 | [arr[j], arr[i]] = [arr[i], arr[j]]; 24 | } 25 | return arr; 26 | }; 27 | 28 | /** 29 | * Your Solution object will be instantiated and called as such: 30 | * var obj = new Solution(nums) 31 | * var param_1 = obj.reset() 32 | * var param_2 = obj.shuffle() 33 | */ 34 | -------------------------------------------------------------------------------- /leetcode/387字符串中的第一个唯一字符.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 给定一个字符串,找到它的第一个不重复的字符, 3 | * 并返回它的索引。如果不存在,则返回 -1。 4 | */ 5 | /** 6 | * @param {string} s 7 | * @return {number} 8 | */ 9 | var firstUniqChar = function (s) { 10 | let map = {}; 11 | for (let v of s) { 12 | map[v] = (map[v] || 0) + 1; 13 | } 14 | s.split(""); 15 | for (let i = 0; i < s.length; i++) { 16 | if (map[s[i]] === 1) { 17 | return i; 18 | } 19 | } 20 | return -1; 21 | }; 22 | 23 | s1 = "leetcode"; 24 | s2 = "loveleetcode"; 25 | 26 | console.log(firstUniqChar(s1)); 27 | console.log(firstUniqChar(s2)); 28 | -------------------------------------------------------------------------------- /leetcode/3无重复字符的最长子串.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {number} 4 | */ 5 | var lengthOfLongestSubstring = function (s) { 6 | let len = s.length; 7 | let result = 0; 8 | let set = new Set(); 9 | // 左指针用来收缩窗口, 右指针用来扩张窗口 10 | let left = 0, 11 | right = 0; 12 | while (left < len) { 13 | // 如果不重复,就不断扩张窗口,元素添加到set中 14 | while (right < len && !set.has(s[right])) { 15 | set.add(s[right]); 16 | right++; 17 | } 18 | // 到这里说明有元素重复了,先记录子串长度,然后收缩窗口 19 | result = Math.max(result, right - left); 20 | // 收缩窗口 21 | set.delete(s[left]); 22 | left++; 23 | } 24 | return result; 25 | }; 26 | -------------------------------------------------------------------------------- /leetcode/415字符串相加.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} num1 3 | * @param {string} num2 4 | * @return {string} 5 | */ 6 | var addStrings = function (num1, num2) { 7 | let i = num1.length - 1, 8 | j = num2.length - 1, 9 | add = 0; 10 | const ans = []; 11 | while (i >= 0 || j >= 0 || add != 0) { 12 | const x = i >= 0 ? num1.charAt(i) - "0" : 0; 13 | const y = j >= 0 ? num2.charAt(j) - "0" : 0; 14 | const result = x + y + add; 15 | ans.push(result % 10); 16 | add = Math.floor(result / 10); 17 | i -= 1; 18 | j -= 1; 19 | } 20 | return ans.reverse().join(""); 21 | }; 22 | -------------------------------------------------------------------------------- /leetcode/46全排列.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[][]} 4 | */ 5 | var permute = function (nums) { 6 | const res = []; 7 | const backstack = (path) => { 8 | if (path.length === nums.length) { 9 | res.push(path); 10 | return; 11 | } 12 | nums.forEach((n) => { 13 | if (path.includes(n)) { 14 | return; 15 | } 16 | backstack(path.concat(n)); 17 | }); 18 | }; 19 | backstack([]); 20 | return res; 21 | }; 22 | 23 | let nums = [1, 2, 3]; 24 | let result = permute(nums); 25 | console.log(result); 26 | -------------------------------------------------------------------------------- /leetcode/48旋转图像.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} matrix 3 | * @return {void} Do not return anything, modify matrix in-place instead. 4 | */ 5 | var rotate = function (matrix) { 6 | let length = matrix.length; 7 | for (let i = 0; i < length; i++) { 8 | for (let j = i; j < length; j++) { 9 | [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; 10 | } 11 | } 12 | return matrix.map((item) => item.reverse()); 13 | }; 14 | 15 | matrix = [ 16 | [1, 2, 3], 17 | [4, 5, 6], 18 | [7, 8, 9], 19 | ]; 20 | 21 | console.log(rotate(matrix)); 22 | -------------------------------------------------------------------------------- /leetcode/494目标和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number} 5 | */ 6 | var findTargetSumWays = function (nums, target) { 7 | let sum = nums.reduce((sum, num) => (sum += num)); 8 | let diff = sum - target; 9 | if (diff < 0 || diff % 2 != 0) { 10 | return 0; 11 | } 12 | let neg = Math.floor(diff / 2); 13 | const dp = new Array(neg + 1).fill(0); 14 | dp[0] = 1; 15 | for (const num of nums) { 16 | for (let j = neg; j >= num; j--) { 17 | dp[j] += dp[j - num]; 18 | } 19 | } 20 | return dp[neg]; 21 | }; 22 | 23 | let nums = [1, 1, 1, 1, 1], 24 | target = 3; 25 | console.log(findTargetSumWays(nums, target)); 26 | -------------------------------------------------------------------------------- /leetcode/509斐波那契数.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | var fib = function (n) { 6 | if (n < 2) { 7 | return n; 8 | } 9 | let dp1 = 0; 10 | let dp2 = 1; 11 | for (let i = 2; i <= n; i++) { 12 | [dp1, dp2] = [dp2, dp2 + dp1]; 13 | } 14 | return dp2; 15 | }; 16 | -------------------------------------------------------------------------------- /leetcode/53最大子序和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组 3 | * (子数组最少包含一个元素),返回其最大和。 4 | */ 5 | 6 | /** 7 | * @param {number[]} nums 8 | * @return {number} 9 | */ 10 | var maxSubArray = function (nums) { 11 | let pre = 0, 12 | res = nums[0]; 13 | for (const num of nums) { 14 | if (pre > 0) { 15 | pre += num; 16 | } else { 17 | pre = num; 18 | } 19 | res = Math.max(pre, res); 20 | } 21 | return res; 22 | }; 23 | // 动态规划:若前一个元素大于0,则将其加到当前元素上。最后返回数组最大值 24 | 25 | let test1 = [-2, 1, -3, 4, -1, 2, 1, -5, 4]; 26 | console.log(maxSubArray(test1)); 27 | -------------------------------------------------------------------------------- /leetcode/54螺旋矩阵.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} matrix 3 | * @return {number[]} 4 | */ 5 | var spiralOrder = function (matrix) { 6 | if (!matrix.length || !matrix[0].length) { 7 | return []; 8 | } 9 | const rows = matrix.length, 10 | columns = matrix[0].length; 11 | let left = 0, 12 | top = 0, 13 | right = columns - 1, 14 | bottom = rows - 1; 15 | let res = []; 16 | while (left <= right && top <= bottom) { 17 | for (let i = left; i <= right; i++) { 18 | res.push(matrix[top][i]); 19 | } 20 | for (let i = top + 1; i <= bottom; i++) { 21 | res.push(matrix[i][right]); 22 | } 23 | if (left < right && top < bottom) { 24 | for (let i = right - 1; i > left; i--) { 25 | res.push(matrix[bottom][i]); 26 | } 27 | for (let i = bottom; i > top; i--) { 28 | res.push(matrix[i][left]); 29 | } 30 | } 31 | [left, right, top, bottom] = [left + 1, right - 1, top + 1, bottom - 1]; 32 | } 33 | return res; 34 | }; 35 | -------------------------------------------------------------------------------- /leetcode/5最长回文子串.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | * @return {string} 4 | */ 5 | var longestPalindrome = function (s) { 6 | if (s.length < 2) { 7 | return s; 8 | } 9 | let res = ''; 10 | const helper = (m, n) => { 11 | while (m >= 0 && n < s.length && s[m] == s[n]) { 12 | m--; 13 | n++; 14 | } // 注意此处m,n的值循环完后 是恰好不满足循环条件的时刻 15 | // 此时m到n的距离为n-m+1,但是mn两个边界不能取 所以应该取m+1到n-1的区间 长度是n-m-1 16 | if (n - m - 1 > res.length) { 17 | // slice也要取[m+1,n-1]这个区间 18 | res = s.slice(m + 1, n); 19 | } 20 | }; 21 | for (let i = 0; i < s.length; i++) { 22 | helper(i, i); // 回文子串长度是奇数 23 | helper(i, i + 1); // 回文子串长度是偶数 24 | } 25 | return res; 26 | }; 27 | 28 | s = 'babad'; 29 | let res = longestPalindrome(s); 30 | console.log(res); 31 | -------------------------------------------------------------------------------- /leetcode/64最小路径和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[][]} grid 3 | * @return {number} 4 | */ 5 | var minPathSum = function (grid) { 6 | let row = grid.length, 7 | col = grid[0].length; 8 | 9 | // calc boundary 10 | for (let i = 1; i < row; i++) 11 | // calc first col 12 | grid[i][0] += grid[i - 1][0]; 13 | 14 | for (let j = 1; j < col; j++) 15 | // calc first row 16 | grid[0][j] += grid[0][j - 1]; 17 | 18 | for (let i = 1; i < row; i++) 19 | for (let j = 1; j < col; j++) 20 | grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]); 21 | 22 | return grid[row - 1][col - 1]; 23 | }; 24 | -------------------------------------------------------------------------------- /leetcode/67二进制求和.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} a 3 | * @param {string} b 4 | * @return {string} 5 | */ 6 | var addBinary = function (a, b) { 7 | let ans = ""; 8 | let ca = 0; 9 | for (let i = a.length - 1, j = b.length - 1; i >= 0 || j >= 0; i--, j--) { 10 | let sum = ca; 11 | if (i >= 0) { 12 | sum += parseInt(a[i]); 13 | } 14 | if (j >= 0) { 15 | sum += parseInt(b[j]); 16 | } 17 | ans += sum % 2; 18 | ca = Math.floor(sum / 2); 19 | } 20 | 21 | if (ca === 1) { 22 | ans += 1; 23 | } 24 | return ans.split("").reverse().join(""); 25 | }; 26 | 27 | let a = "11"; 28 | let b = "1"; 29 | console.log(addBinary(a, b)); 30 | -------------------------------------------------------------------------------- /leetcode/704二分查找.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @param {number} target 4 | * @return {number} 5 | */ 6 | var search = function (nums, target) { 7 | let left = 0, 8 | right = nums.length - 1; 9 | while (left <= right) { 10 | let mid = (left + right) >> 1; 11 | if (nums[mid] === target) { 12 | return mid; 13 | } else if (nums[mid] < target) { 14 | left = mid + 1; 15 | } else if (nums[mid] > target) { 16 | right = mid - 1; 17 | } 18 | } 19 | return -1; 20 | }; 21 | -------------------------------------------------------------------------------- /leetcode/70爬楼梯.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | var climbStairs = function (n) { 6 | if (n < 2) { 7 | return 1; 8 | } 9 | let dp1 = 1; // 0阶 10 | let dp2 = 1; // 1阶 11 | for (let i = 2; i <= n; i++) { 12 | [dp1, dp2] = [dp2, dp1 + dp2]; 13 | } 14 | return dp2; 15 | }; 16 | 17 | var climbStairs1 = function (n) { 18 | if (n < 2) { 19 | return 1; 20 | } 21 | return climbStairs1(n - 1) + climbStairs1(n - 2); 22 | }; 23 | 24 | console.log(climbStairs(5)); 25 | -------------------------------------------------------------------------------- /leetcode/7整数反转.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} x 3 | * @return {number} 4 | */ 5 | var reverse = function (x) { 6 | let result = parseInt(x.toString().split('').reverse().join('')); 7 | result = x < 0 ? -result : result; 8 | if (result > 2 ** 31 - 1 || result < (-2) ** 31) { 9 | return 0; 10 | } 11 | return result; 12 | }; 13 | 14 | console.log(reverse(-123)); 15 | -------------------------------------------------------------------------------- /leetcode/83删除排序链表中的重复元素.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val, next) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.next = (next===undefined ? null : next) 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @return {ListNode} 11 | */ 12 | var deleteDuplicates = function (head) { 13 | if (!head) { 14 | return head; 15 | } 16 | 17 | let cur = head; 18 | while (head.next) { 19 | if (cur.val === cur.next.val) { 20 | cur.next = cur.next.next; 21 | } else { 22 | cur = cur.next; 23 | } 24 | } 25 | return head; 26 | }; 27 | -------------------------------------------------------------------------------- /leetcode/88合并两个有序数组.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums1 3 | * @param {number} m 4 | * @param {number[]} nums2 5 | * @param {number} n 6 | * @return {void} Do not return anything, modify nums1 in-place instead. 7 | */ 8 | 9 | var merge = function (nums1, m, nums2, n) { 10 | let i = m - 1; 11 | let j = n - 1; 12 | let k = m + n - 1; 13 | while (i >= 0 || j >= 0) { 14 | if (i < 0) { 15 | nums1[k--] = nums2[j--]; 16 | } else if (j < 0) { 17 | nums1[k--] = nums1[i--]; 18 | } else if (nums1[i] < nums2[j]) { 19 | nums1[k--] = nums2[j--]; 20 | } else { 21 | nums1[k--] = nums1[i--]; 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /leetcode/94二叉树的中序遍历.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val, left, right) { 4 | * this.val = (val===undefined ? 0 : val) 5 | * this.left = (left===undefined ? null : left) 6 | * this.right = (right===undefined ? null : right) 7 | * } 8 | */ 9 | /** 10 | * @param {TreeNode} root 11 | * @return {number[]} 12 | */ 13 | var inorderTraversal = function (root) { 14 | let res = []; 15 | let inorder = (node) => { 16 | if (!node) { 17 | return; 18 | } 19 | inorder(node.left); 20 | res.push(node.val); 21 | inorder(node.right); 22 | }; 23 | inorder(root); 24 | return res; 25 | }; 26 | -------------------------------------------------------------------------------- /leetcode/剑指10-I.斐波那契数列.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | var fib = function (n) { 6 | if (n < 2) { 7 | return n; 8 | } 9 | let dp1 = 0; 10 | let dp2 = 1; 11 | for (let i = 2; i <= n; i++) { 12 | [dp1, dp2] = [dp2, (dp1 + dp2) % (1e9 + 7)]; 13 | } 14 | return dp2; 15 | }; 16 | 17 | console.log(fib(4)); 18 | -------------------------------------------------------------------------------- /leetcode/剑指10-II.青蛙跳台阶问题.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @return {number} 4 | */ 5 | var numWays = function (n) { 6 | if (n <= 1) { 7 | return 1; 8 | } 9 | let dp1 = 1; 10 | let dp2 = 1; 11 | for (let i = 2; i <= n; i++) { 12 | [dp1, dp2] = [dp2, (dp1 + dp2) % (1e9 + 7)]; 13 | } 14 | return dp2; 15 | }; 16 | -------------------------------------------------------------------------------- /leetcode/剑指18删除链表的节点.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @param {number} val 11 | * @return {ListNode} 12 | */ 13 | var deleteNode = function (head, val) { 14 | if (head.val === val) { 15 | return head.next; 16 | } 17 | head.next = deleteNode(head.next, val); 18 | return head; 19 | }; 20 | -------------------------------------------------------------------------------- /leetcode/剑指21调整数组顺序使奇数位于偶数前面.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[]} 4 | */ 5 | var exchange = function (nums) { 6 | let left = 0; 7 | let right = nums.length - 1; 8 | 9 | while (left < right) { 10 | while (left < right && nums[left] % 2 == 1) { 11 | left++; 12 | } 13 | while (left < right && nums[right] % 2 == 0) { 14 | right--; 15 | } 16 | [nums[left], nums[right]] = [nums[right], nums[left]]; 17 | } 18 | return nums; 19 | }; 20 | 21 | let nums = [3, 2, 1, 5, 4]; 22 | console.log(exchange(nums)); 23 | -------------------------------------------------------------------------------- /leetcode/剑指22链表中倒数第k个节点.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * function ListNode(val) { 4 | * this.val = val; 5 | * this.next = null; 6 | * } 7 | */ 8 | /** 9 | * @param {ListNode} head 10 | * @param {number} k 11 | * @return {ListNode} 12 | */ 13 | var getKthFromEnd = function (head, k) { 14 | let fast, slow; 15 | fast = slow = head; 16 | while (k--) { 17 | fast = fast.next; 18 | } 19 | while (fast !== null) { 20 | fast = fast.next; 21 | slow = slow.next; 22 | } 23 | return slow; 24 | }; 25 | -------------------------------------------------------------------------------- /leetcode/剑指28对称的二叉树.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * function TreeNode(val) { 4 | * this.val = val; 5 | * this.left = this.right = null; 6 | * } 7 | */ 8 | /** 9 | * @param {TreeNode} root 10 | * @return {boolean} 11 | */ 12 | var isSymmetric = function (root) { 13 | if (!root) { 14 | return true; 15 | } 16 | function check(left, right) { 17 | if (!left && !right) { 18 | return true; 19 | } 20 | if (!left || !right) { 21 | return false; 22 | } 23 | if (left.val != right.val) { 24 | return false; 25 | } 26 | return check(left.left, right.right) && check(left.right, right.left); 27 | } 28 | return check(root.left, root.right); 29 | }; 30 | -------------------------------------------------------------------------------- /leetcode/剑指62圆圈中最后剩下的数字.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} n 3 | * @param {number} m 4 | * @return {number} 5 | */ 6 | var lastRemaining = function (n, m) { 7 | let res = []; 8 | for (let i = 2; i <= n; i++) { 9 | res = (res + m) % i; 10 | } 11 | return res; 12 | }; 13 | 14 | console.log(lastRemaining(5, 3)); 15 | -------------------------------------------------------------------------------- /leetcode/剑指9用两个栈实现队列.js: -------------------------------------------------------------------------------- 1 | var CQueue = function () { 2 | this.stackA = []; 3 | this.stackB = []; 4 | }; 5 | 6 | /** 7 | * @param {number} value 8 | * @return {void} 9 | */ 10 | CQueue.prototype.appendTail = function (value) { 11 | this.stackA.push(value); 12 | }; 13 | 14 | /** 15 | * @return {number} 16 | */ 17 | CQueue.prototype.deleteHead = function () { 18 | if (this.stackB.length) { 19 | return this.stackB.pop(); 20 | } else { 21 | while (this.stackA.length) { 22 | this.stackB.push(this.stackA.pop()); 23 | } 24 | if (!this.stackB.length) { 25 | return -1; 26 | } else { 27 | return this.stackB.pop(); 28 | } 29 | } 30 | }; 31 | 32 | /** 33 | * Your CQueue object will be instantiated and called as such: 34 | * var obj = new CQueue() 35 | * obj.appendTail(value) 36 | * var param_2 = obj.deleteHead() 37 | */ 38 | -------------------------------------------------------------------------------- /leetcode/面试题 08 06汉诺塔问题.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} A 3 | * @param {number[]} B 4 | * @param {number[]} C 5 | * @return {void} Do not return anything, modify C in-place instead. 6 | */ 7 | var hanota = function (A, B, C) { 8 | let n = A.length; 9 | let move = function (m, a, b, c) { 10 | if (m === 1) { 11 | // 当只有一个时直接加到c中 12 | c.push(a.pop()); 13 | } else { 14 | move(m - 1, a, c, b); // 将 a 上的 n - 1 个 通过 c 移到 b 15 | c.push(a.pop()); // 把 a 中剩下的一个直接放到 c 16 | move(m - 1, b, a, c); // 在把 b 上的 n - 1 个 通过 a 放到 c 17 | } 18 | }; 19 | move(n, A, B, C); 20 | }; 21 | -------------------------------------------------------------------------------- /markdown/Javascript相关.md: -------------------------------------------------------------------------------- 1 | ### 闭包 2 | 3 | - 闭包就是能够读取其他函数内部变量的函数 4 | - JS 中每个函数都有一个作用域,现在假设一个作用域链上有一大一小两个作用域,其中小的作用域中引用了大作用域中的变量,则在小作用域被销毁之前大作用域不能被销毁,这就是闭包 5 | - 闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能导致内存占用过多,我们建议读者只在绝对必要时再考虑使用闭包 6 | 7 | ### 数据类型判断的方法?instanceof 判断复杂数据类型的原理是什么?toString()和 constructor 有了解过吗? 8 | 9 | - 简单类型用 typeof,复杂类型用 instanceof。用 typeof 判断简单类型,除了 null 会返回 object,其他都会返回正确的结果,如果判断复杂类型通通返回 object。用 instanceof 可以检测某个实例是否是某个对象类型,如果用它来检测简单数据类型则始终返回 false,因为基本类型不是对象。 10 | - instanceof 运算符 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。 11 | - 默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中 type 是对象的类型。例如 12 | 13 | ``` 14 | var o = new Object(); 15 | o.toString(); // returns [object Object] 16 | ``` 17 | 18 | - 可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为 thisArg。 19 | 20 | ``` 21 | var toString = Object.prototype.toString; 22 | 23 | toString.call(new Date); // [object Date] 24 | toString.call(new String); // [object String] 25 | toString.call(Math); // [object Math] 26 | 27 | //Since JavaScript 1.8.5 28 | toString.call(undefined); // [object Undefined] 29 | toString.call(null); // [object Null] 30 | ``` 31 | 32 | - 所有对象都会从它的原型上继承一个 constructor 属性,这个属性指向它的构造函数,可以利用这个属性判断数据类型。 33 | 34 | ### EventLoop 事件循环机制 35 | 36 | - javascript 是一门单线程非阻塞的脚本语言,单线程意味着 javascript 代码在执行的任何时候,都只有一个主线程来处理所有的任务。 37 | - 事件循环是指主线程重复从消息队列中读取信息,执行的过程。实际上,主线程只会做一件事情,就是从消息队列里面取消息,执行消息,再取消息,再执行。 38 | - 执行栈与消息队列 39 | 40 | 1. 主线程运行的时候会产生栈和堆 41 | 2. js 从上到下解析方法,将其中的同步任务按照执行顺序排列到执行栈中 42 | 3. 当程序调用外部的 API 时,会将此类异步任务挂起,继续执行执行栈的任务。当异步任务返回结果后,再按照顺序排列到事件队列中。 43 | 4. 主线程先将执行栈中的同步任务清空,然后检查事件队列中是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行栈中遇到异步任务,则继续将这个任务排列到事件队列中。 44 | 5. 主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个循环往复的过程就是“EventLoop”事件循环 45 | 46 | ### 说一下事件委托 47 | 48 | - 事件委托指的是,不在事件的发生地(直接 dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判定事件发生元素 dom 的类型,来做出不同的响应 49 | - 举例:最经典的就是 ul 和 li 标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在 li 标签上直接添加,而是在 ul 父元素上添加。 50 | - 好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。 51 | 52 | ### 事件流 53 | 54 | - 事件捕获阶段:从 window 逐层向下传递到 目标元素,过程中遇到注册的捕获事件就会触发它 55 | - 处于目标阶段:事件到达目标元素,触发目标元素上注册的事件 56 | - 事件冒泡阶段:从目标元素向上传递到 window,过程中遇到注册的冒泡事件就会触发它 57 | 58 | ### 什么是发布-订阅模式 59 | 60 | - 发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。 61 | - 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。 62 | #### 实现思路 63 | - 创建一个对象 64 | - 在该对象上创建一个缓存列表(调度中心) 65 | - on 方法用来把函数 fn 都加到缓存列表中(订阅者注册事件到调度中心) 66 | - emit 方法取到 arguments 里第一个当做 event,根据 event 值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码) 67 | - off 方法可以根据 event 值取消订阅(取消订阅) 68 | - once 方法只监听一次,调用完毕后删除缓存函数(订阅一次) 69 | -------------------------------------------------------------------------------- /markdown/React/React学习笔记(一).md: -------------------------------------------------------------------------------- 1 | ### React 是什么 2 | 3 | - React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案。 4 | - 遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效。 5 | - 使用**虚拟 DOM**来有效地操作**DOM**,遵循从高阶组件到低阶组件的单向数据流。 6 | - 帮助我们将界面分成了各个独立的小块,每一小块就是组件,这些组件之间可以组合、嵌套,构成整体页面。 7 | - React 类组件使用一个名为**render()** 的方法或者函数组件 **return**,接收输入的数据并返回需要展示的内容。 8 | 9 | ```javascript 10 | class HelloMessage extends React.Component { 11 | render() { 12 | return
Hello {this.props.name}
; 13 | } 14 | } 15 | 16 | ReactDOM.render( 17 | , 18 | document.getElementById('hello-example') 19 | ); 20 | ``` 21 | 22 | - 上述这种类似**XML**形式就是**JSX**,最终会被**babel**编译为合法的**js**语句调用。 23 | - 被传入的数据可在组件中通过 **this.props** 在 **render()** 访问 24 | 25 | ### 特性 26 | 27 | - React 特性有很多,着重介绍下声明式编程及 Component 28 | - JSX 语法 29 | - 单向数据绑定 30 | - 虚拟 DOM 31 | - 声明式编程 32 | - Component 33 | 34 | #### 声明式编程 35 | 36 | - 声明式编程是一种编程范式,它关注的是你要做什么,而不是如何做。 37 | - 它表达逻辑而不显式的定义步骤。这意味着我们需要根据逻辑的计算来声明要显示的组件。 38 | - 如实现一个标记的地图 39 | - 通过**命令式**创建地图、创建标记、以及在地图上添加的标记的步骤如下: 40 | 41 | ```javascript 42 | // 创建地图 43 | const map = new Map.map(document.getElementById('map'), { 44 | zoom: 4, 45 | center: { lat, lng }, 46 | }); 47 | 48 | // 创建标记 49 | const marker = new Map.marker({ 50 | position: { lat, lng }, 51 | title: 'Hello Marker', 52 | }); 53 | 54 | // 地图上添加标记 55 | marker.setMap(map); 56 | ``` 57 | 58 | - 而用**React**实现上述功能则如下: 59 | 60 | ```javascript 61 | 62 | 63 | ; 64 | ``` 65 | 66 | - 声明式编程方式使得 React 组件很容易使用,最终的代码简单易于维护 67 | 68 | #### Component 69 | 70 | - 在**React**中,一切皆为组件。通常将应用程序的整个逻辑分解为小的单个部分。我们将每个单独的部分称为组件。 71 | - 组件可以是一个函数或者是一个类,接受数据输入,处理它并返回在**UI**中呈现的**React**元素 72 | - 函数式组件如下: 73 | 74 | ```javascript 75 | const Header = () => { 76 | return ( 77 | 78 |

TODO App

79 |
80 | ); 81 | }; 82 | ``` 83 | 84 | - 类组件(有状态组件)如下: 85 | 86 | ```javascript 87 | class Dashboard extends React.Component { 88 | constructor(props) { 89 | super(props); 90 | this.state = {}; 91 | } 92 | render() { 93 | return ( 94 |
95 | 96 | 97 |
98 | ); 99 | } 100 | } 101 | ``` 102 | 103 | - 一个组件该有的特点如下: 104 | - 可组合: 一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部 105 | - 可重用: 每个组件都是具有独立功能的,它可以被使用在多个 UI 场景 106 | - 可维护: 每个小的组件仅仅包含自身的逻辑,更容易被理解和维护 107 | 108 | #### 优势 109 | 110 | - 通过上面的初步了解,可以感受到 React 存在的优势: 111 | - 高效灵活 112 | - 声明式的设计,简单使用 113 | - 组件式开发,提高代码复用率 114 | - 单向响应的数据流会比双向绑定的更安全,速度更快 115 | -------------------------------------------------------------------------------- /markdown/React/React学习笔记(三).md: -------------------------------------------------------------------------------- 1 | ### State 2 | 3 | - 一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是**state**,一般在**constructor**中初始化 4 | - 当需要修改里面的值的状态需要通过调用**setState**来改变,从而达到更新组件内部数据的作用,并且重新调用组件**render**方法,如下面的例子: 5 | 6 | ```js 7 | class Button extends React.Component { 8 | constructor() { 9 | super(); 10 | this.state = { 11 | count: 0, 12 | }; 13 | } 14 | updateCount() { 15 | this.setState((prevState, props) => { 16 | return { count: prevState.count + 1 }; 17 | }); 18 | } 19 | render() { 20 | return ( 21 | 24 | ); 25 | } 26 | } 27 | ``` 28 | 29 | - **setState**还可以接受第二个参数,它是一个函数,会在**setState**调用完成并且组件开始重新渲染时被调用,可以用来监听渲染是否完成 30 | 31 | ```js 32 | this.setState( 33 | { 34 | name: 'JS', 35 | }, 36 | () => console.log('setState finished') 37 | ); 38 | ``` 39 | 40 | ### props 41 | 42 | - **React**的核心思想就是组件化思想,页面会被切分成一些独立的、可复用的组件 43 | - 组件从概念上看就是一个函数,可以接受一个参数作为输入值,这个参数就是**props**,所以可以把**props**理解为从外部传入组件内部的数据 44 | - **React**具有单向数据流的特性,所以它的主要作用是从父组件向子组件中传递数据 45 | - **props**除了可以传字符串,数字,还可以传递对象,数组甚至是回调函数,如下: 46 | 47 | ```js 48 | class Welcome extends React.Component { 49 | render() { 50 | return

Hello {this.props.name}

; 51 | } 52 | } 53 | const element = ; 54 | ``` 55 | 56 | - 上述**name**属性与**onNameChanged**方法都能在子组件的**props**变量中访问 57 | - 在子组件中,**props**在内部不可变的,如果想要改变它看,只能通过外部组件传入新的**props**来重新渲染子组件,否则子组件的**props**和展示形式不会改变 58 | 59 | ### 异同点 60 | 61 | - 相同点: 62 | - 两者都是 JavaScript 对象 63 | - 两者都是用于保存信息 64 | - props 和 state 都能触发渲染更新 65 | - 不同点: 66 | - props 是外部传递给组件的,而 state 是在组件内被组件自己管理的,一般在 constructor 中初始化 67 | - props 在组件内部是不可修改的,但 state 在组件内部可以进行修改 68 | - state 是多变的、可以修改 69 | -------------------------------------------------------------------------------- /markdown/React/React学习笔记(二).md: -------------------------------------------------------------------------------- 1 | ### 是什么 2 | 3 | - **Real DOM**,真实**DOM**, 意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实**DOM**结构,如下: 4 | ![real dom](http://101.201.140.172:7878/image/React/RealDOM.webp) 5 | - **Virtual Dom**,本质上是以**JavaScript**对象形式存在的对**DOM**的描述 6 | - 创建**虚拟 DOM**目的就是为了更好将虚拟的节点渲染到页面视图中,**虚拟 DOM**对象的节点与**真实 DOM**的属性一一照应 7 | - 在 React 中,**JSX**是其一大特性,可以让你在 JS 中通过使用 XML 的方式去直接声明界面的 DOM 结构 8 | 9 | ```javascript 10 | const vDom =

Hello World

; // 创建h1标签,右边千万不能加引号 11 | const root = document.getElementById('root'); // 找到
节点 12 | ReactDOM.render(vDom, root); // 把创建的h1标签渲染到root节点上 13 | ``` 14 | 15 | - 上述中,**ReactDOM.render()**用于将你创建好的**虚拟 DOM**节点插入到某个真实节点上,并渲染到页面上 16 | - **JSX**实际是一种语法糖,在使用过程中会被**babel**进行编译转化成 JS 代码,上述 VDOM 转化为如下: 17 | 18 | ```javascript 19 | const vDom = React.createElement( 20 | 'h1', 21 | { className: 'hClass', id: 'hId' }, 22 | 'hello world' 23 | ); 24 | ``` 25 | 26 | - 可以看到,JSX 就是为了简化直接调用**React.createElement()**方法: 27 | 28 | ``` 29 | 第一个参数是标签名,例如 h1、span、table... 30 | 第二个参数是个对象,里面存着标签的一些属性,例如 id、class 等 31 | 第三个参数是节点中的文本 32 | ``` 33 | 34 | - 通过 **console.log(VDOM)**,则能够得到虚拟 VDOM 消息 35 | - 所以可以得到,JSX 通过 babel 的方式转化成**React.createElement**执行,返回值是一个对象,也就是虚拟 DOM 36 | 37 | ### 区别 38 | 39 | - 两者的区别如下: 40 | 41 | ``` 42 | 虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘 43 | 虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘” 44 | ``` 45 | 46 | - 举例: 47 | 48 | ``` 49 | 传统的原生**api**或**jQuery**去操作**DOM**时,浏览器会从**构建 DOM 树**开始从头到尾执行一遍流程 50 | 当你在一次操作时,需要更新 10 个**DOM**节点,浏览器没这么智能,收到第一个更新**DOM**请求后,并不知道后续还有 9 次更新操作,因此会马上执行流程,最终执行 10 次流程 51 | 而通过**VNode**,同样更新 10 个**DOM**节点,**虚拟 DOM**不会立即操作**DOM**,而是将这 10 次更新的**diff**内容保存到本地的一个**js**对象中,最终将这个**js**对象一次性**attach**到**DOM**树上,避免大量的无谓计算 52 | ``` 53 | 54 | ### 优缺点 55 | 56 | - 真实 DOM 的优势: 57 | 58 | ``` 59 | 易用 60 | ``` 61 | 62 | - 真实 DOM 的缺点: 63 | 64 | ``` 65 | 效率低,解析速度慢,内存占用量过高 66 | 性能差:频繁操作真实 DOM,易于导致重绘与回流 67 | ``` 68 | 69 | - 虚拟 DOM 的优势: 70 | 71 | ``` 72 | 简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难 73 | 性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能 74 | 跨平台:React 借助虚拟 DOM, 带来了跨平台的能力,一套代码多端运行 75 | ``` 76 | 77 | - 虚拟 DOM 的缺点: 78 | 79 | ``` 80 | 在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化 81 | 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢 82 | ``` 83 | -------------------------------------------------------------------------------- /markdown/Vue相关.md: -------------------------------------------------------------------------------- 1 | ### Vue 生命周期 2 | 3 | - Vue 实例从创建到销毁的过程,就是生命周期。 4 | - 也就是:开始创建-初始化数据-编译模板-挂载 dom-数据更新重新渲染 dom-最后销毁。这一系列的过程就是 vue 的生命周期。所以 mounted 阶段真实的 dom 就已经存在了 5 | - beforeCreate:vue 实例的挂在元素 el 和数据对象 data 都没有初始化,还是一个 undefined 状态 6 | - created:此时 vue 实例的数据对象已经有了,可以访问里面的数据和方法,el 还没有,还没有挂载 dom 7 | - beforeMount:在这里 vue 实例的元素 el 和数据对象都有了,只不过在挂载之前还是虚拟的 dom 节点 8 | - mounted:vue 实例已经挂载到真实的 dom 上,可以通过对 dom 操作来获取 dom 节点 9 | - beforeUpdate:响应式数据更新时调用,发生在虚拟 dom 打补丁之前,适合在更新之前访问现有的 dom,比如移除已添加的事件监听器 10 | - updated:虚拟 dom 重新渲染和打补丁之后调用,组成新的 dom 已经更新,避免在这个钩子函数中操作数据,防止死循环 11 | - beforeDestroy:vue 实例在销毁前使用,在这里还可以使用,通过 this 也能访问到实例,可以在这里对一些不用的定时器进行清除,解绑事件 12 | - destroyed:vue 实例销毁后使用,调用后所有事件监听器都会被移除,所有的子实例都被销毁 13 | 14 | ### Vue.nextTick() 15 | 16 | - 官方解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。 17 | - 简单来说,Vue 在修改数据后,视图并不是立即更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。 18 | 19 | ### vue 数据双向绑定原理 20 | 21 | - vue 数据双向绑定原理是通过数据劫持+发布者-订阅者模式来实现的。首先是对数据进行监听,然后当监听的属性发生改变时则告诉订阅者是否要更新,若更新则会执行对应的更新函数从而更新视图。 22 | ![双向绑定示意图](https://pic2.zhimg.com/80/v2-10f94b6de4ff367903ea4c8c8aa6e865_1440w.jpg) 23 | - 我们已经知道实现数据的双向绑定,首先要对数据进行监听,所以我们需要设置一个监听器 observer,用来监听所有属性。如果属性上发生变化了,就需要告诉订阅者 watcher 是否需要进行更新。 24 | - 实现一个订阅者 watcher,每个订阅者 watcher 都绑定一个更新函数,watcher 可以接受到属性的变化通知并执行相应的函数,从而更新函数。 25 | - 实现一个解析器 compile,可以扫描每个节点的相关指令(v-model、v-on 等),如果存在这些,则解析器 compile 初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者 watcher。 26 | 27 | ### Vuex 28 | 29 | - vuex 是一个专门为 vue.js 应用程序开发的状态管理模式,通过一个集中的数据存储,方便程序中所有组件进行访问,简单来说,vuex 就是一个 vue 的状态管理工具 30 | - vuex 有五个属性:state,getters,mutations,actions,modules 31 | - state 就是数据源存放地,对应 vue 对象的 data,state 里面存储的数据是响应式的,state 发生改变,对应这个数据的组件也会改变,用 this.$store.xxx 调用 32 | - getters 相当于 store 的计算属性,主要是对 state 中数据的过滤,用 this.$store.getters.xxx 调用 33 | - mutations 存放处理数据逻辑的方法,当触发事件想改变 state 数据的时候调用 mutations,通过 this.$store.commit 调用 34 | - actionis 异步操作数据,但是通过 mutations 来操作,用 this.$store.dispatch 来触发 35 | - 使用场景:组件之间的状态,登录状态、加入购物车、音乐播放 36 | 37 | ### Axios 38 | 39 | - Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送 get、post 请求。 40 | - Axios 特性 41 | - 可以在浏览器中发送 XMLHttpRequests 42 | - 可以在 node.js 发送 http 请求 43 | - 支持 Promise API 44 | - 拦截请求和响应 45 | - 转换请求数据和响应数据 46 | - 能够取消请求 47 | - 自动转换 JSON 数据 48 | - 客户端支持保护安全免受 XSRF 攻击 49 | 50 | ### Vue-Router 51 | 52 | - Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有: 53 | - 嵌套的路由/视图表 54 | - 模块化的、基于组件的路由配置 55 | - 路由参数、查询、通配符 56 | - 基于 Vue.js 过渡系统的视图过渡效果 57 | - 细粒度的导航控制 58 | - 带有自动激活的 CSS class 的链接 59 | - HTML5 历史模式或 hash 模式,在 IE9 中自动降级 60 | - 自定义的滚动条行为 61 | - [vue-router 原理](https://zhuanlan.zhihu.com/p/57368777) 62 | 63 | ### Vue 中 data 为什么必须是一个函数 64 | 65 | - vue 中的 data 必须是个函数,因为当 data 是函数时,组件实例化的时候这个函数将会被调用,返回一个对象,计算机会给这个对象分配一个内存地址,实例化几次就分配几个内存地址,他们的地址都不一样,所以每个组件中的数据不会相互干扰,改变其中一个组件的状态,其它组件不变 66 | - 简单来说,就是为了保证组件的独立性和可复用性,如果 data 是个函数的话,每复用一次组件就会返回新的 data,类似于给每个组件实例创建一个私有的数据空间,保护各自的数据互不影响 67 | -------------------------------------------------------------------------------- /markdown/字节跳动凉经.md: -------------------------------------------------------------------------------- 1 | #### 字节跳动前端实习 一面凉经 ==时长 80min 作者 : CoderDi== 2 | 3 | - 先来个自我介绍 4 | - 为什么选择前端这个方向呢 5 | - 讲一下你的项目相关 6 | - 你的项目亮点在哪里, 7 | - 你看过什么书: ES6,红宝书。然后讲理解,什么时候用 8 | 9 | --- 10 | 11 | ### 网络相关 12 | 13 | ##### 浏览器输入 url 到渲染页面的详细过程 14 | 15 | 16 | 17 | 输入url到渲染页面
18 | 19 | 20 | ##### https 和 http 的区别,对称加密和非对称加密的了解 21 | 22 | 23 | 24 | 面试题——对称加密和非对称加密
25 | 26 | 27 | - http:超文本传输协议,是互联网上应用最为广泛的一种网络协议,建立在 TCP 之上,用于从服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。 28 | - https:是以安全为目标的 http 通道,即 http 和 tcp 之间加入 ssl 层。它的主要作用是:建立一个信息安全通道,来确保数据的传输,确保网站的安全性 29 | - 对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。对称加密有很多种算法,由于它效率很高,所以被广泛使用在很多加密协议的核心当中 30 | - 非对称加密为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人--银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。 31 | 32 | ##### TCP 和 UDP 33 | 34 | - 传输控制协议(TCP):TCP(传输控制协议)定义了两台计算机之间进行可靠的传输而交换的数据和确认信息的格式,以及计算机为了确保数据的正确到达而采取的措施。协议规定了 TCP 软件怎样识别给定计算机上的多个目的进程如何对分组重复这类差错进行恢复。协议还规定了两台计算机如何初始化一个 TCP 数据流传输以及如何结束这一传输。TCP 最大的特点就是提供的是面向连接、可靠的字节流服务。 35 | - 用户数据报协议(UDP):UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。提供的是非面向连接的、不可靠的数据流传输。UDP 不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。它只是把应用程序传给 IP 层的数据报发送出去,但是并不能保证它们能到达目的地。因此报文可能会丢失、重复以及乱序等。但由于 UDP 在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。 36 | 37 | ##### 讲一下前端缓存吧 || 主要问强缓存和协商缓存 38 | 39 | 40 | 41 | 前端缓存详解 42 | 43 | 44 | ### Vue 相关 45 | 46 | ##### 你有深入阅读过 Vue 源码吗 47 | 48 | ##### v-for 的 key 相关 49 | 50 | 51 | 52 | 在vue中v-for指令中key作用
53 | 54 | 55 | ##### Proxy 实现双向绑定 56 | 57 | 58 | 59 | Vue3.0新特性之Proxy实现双向绑定 60 | 手写vue3.0双向绑定-es6 Proxy 61 | 62 | 63 | ##### Vue 的 Computed 实现原理 64 | 65 | 66 | 67 | Vue3响应式原理与reactive、effect、computed实现 68 | 69 | 70 | ### javascript 相关 71 | 72 | ##### 跨域问题 73 | 74 | 75 | JavaScript中的跨域问题及解决办法 76 | 77 | 78 | ##### XSS, CSRF 解释, 怎么解决 79 | 80 | 81 | XSS 和 CSRF简述及预防措施 82 | 83 | 84 | ##### 基本数据类型和引用数据类型 85 | 86 | 87 | JavaScript的基本数据类型和引用数据类型 88 | 89 | 90 | ##### 原型原型链相关知识解释(好好学 constructor) 91 | 92 | 93 | javascript——原型与原型链 94 | 95 | 96 | ##### 使用 ES5 的方法, 实现继承(原型链,构造函数);手写寄生组合式继承不会 97 | 98 | 99 | ES5实现继承 100 | 101 | 102 | ##### 问会不会 promise.all, 代码实现 103 | 104 | 105 | JS手写实现Promise.all的实现方式 106 | 107 | 108 | ``` 109 | Promise.all = (promises) => { 110 | let count = 0; 111 | let list = []; 112 | let len = promises.length; 113 | return new Promise((resolve, reject) => { 114 | promises.forEach((p, i) => { 115 | p.then( 116 | (res) => { 117 | count++; 118 | list[i] = res; 119 | if (count === len) { 120 | resolve(list); 121 | } 122 | }, 123 | (err) => { 124 | reject(err); 125 | } 126 | ); 127 | }); 128 | }); 129 | }; 130 | ``` 131 | 132 | ##### 微任务宏任务的理解 133 | 134 | 135 | 前端宏任务和微任务的理解-知乎 136 | JS中EventLoop、宏任务与微任务的个人理解 137 | 138 | 139 | - setTimeout,promise 嵌套看代码讲输出,嵌套太多了, 140 | promise.then(setTimeout(console.log).then(console.log),先执行哪个,错了 141 | - this 指向问题打印,打印结果说对了,讲述错了一个 142 | 143 | ``` 144 | var length = 10; 145 | function fn() { 146 | return this.length + 1; 147 | } 148 | var obj = { 149 | length: 5, 150 | test1: function () { 151 | return fn(); 152 | }, 153 | }; 154 | obj.test2 = fn; 155 | //下面代码输出是什么 156 | console.log(obj.test1()); 157 | console.log(fn() === obj.test2()); 158 | ``` 159 | 160 | - 实现一个 repeat 方法 161 | 162 | ``` 163 | // 需要实现的函数 164 | function repeat(func, times, wait) {} 165 | 166 | // 使下面调用代码能正常工作 167 | const repeatFunc = repeat(console.log, 4, 3000); 168 | repeatFunc("hello world"); //会输出4次 hello world, 每次间隔3秒 169 | ``` 170 | 171 | 172 | 前端面试题 每隔一段时间执行一个函数 执行次数一定 setInterval 173 |
174 | 前端常见算法题 JavaScript 实现 175 | 176 | 177 | ``` 178 | // 需要实现的函数 179 | function repeat(func, times, wait) { 180 | return function () { 181 | let args = arguments; 182 | let handle = function (i) { 183 | setTimeout(() => { 184 | func.apply(null, args); 185 | }, wait * i); 186 | }; 187 | for (let i = 0; i < times; i++) { 188 | handle(i); 189 | } 190 | }; 191 | } 192 | ``` 193 | 194 | ### 数据结构相关(彻底凉凉) 195 | 196 | ##### 写出遍历二叉树,深度遍历和广度遍历 197 | 198 | ``` 199 | //深度优先递归 200 | function deep(node, res) { 201 | if (node != null) { 202 | // 该节点存在 203 | res.push(node.val); 204 | // 使用childrens变量存储node.children,提升性能,不使用node.children.length,从而不必在for循环遍历时每次都去获取子元素 205 | for (let i = 0, childrens = node.children; i < childrens.length; i++) { 206 | deep(childrens[i], res); 207 | } 208 | } 209 | return res; 210 | } 211 | console.log(deep(tree, [])); 212 | 213 | const dfs = (root) => { 214 | console.log(root.val); 215 | root.children.forEach((child) => { 216 | dfs(child); 217 | }); 218 | }; 219 | 220 | // 广度优先非递归 221 | function wide(node) { 222 | let res = []; 223 | let nodeList = []; // 存储需要被访问的节点 224 | nodeList.push(node); 225 | while (nodeList.length > 0) { 226 | let currentNode = nodeList.shift(0); 227 | res.push(currentNode.val); 228 | for ( 229 | let i = 0, childrens = currentNode.children; 230 | i < childrens.length; 231 | i++ 232 | ) { 233 | nodeList.push(childrens[i]); 234 | } 235 | } 236 | return res; 237 | } 238 | console.log(wide(tree)); 239 | 240 | const bfs = (root) => { 241 | const q = [root]; 242 | while (q.length > 0) { 243 | const n = q.shift(); 244 | console.log(n.val); 245 | n.children.forEach((child) => { 246 | q.push(child); 247 | }); 248 | } 249 | }; 250 | ``` 251 | 252 | ##### 写出快速排序 253 | 254 | ``` 255 | const quickSort = function (arr) { 256 | if (arr.length <= 1) { 257 | return arr; 258 | } 259 | let basic = arr.pop(); 260 | let left = []; 261 | let right = []; 262 | for (let i = 0; i < arr.length; i++) { 263 | if (arr[i] < basic) { 264 | left.push(arr[i]); 265 | } else { 266 | right.push(arr[i]); 267 | } 268 | } 269 | return quickSort(left).concat([basic], quickSort(right)); 270 | }; 271 | 272 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; // length = 10 273 | console.log(quickSort(arr)); 274 | ``` 275 | 276 | ##### 给你一个数组,数组长度为 n。请找出数组中第 k 大的数 277 | 278 | ``` 279 | function GetKMax(input, k) { 280 | if (k > input.length) { 281 | return []; 282 | } 283 | // write code here 284 | for (let i = 1; i < input.length; i++) { 285 | for (let j = 0; j < input.length - i; j++) { 286 | if (input[j + 1] > input[j]) { 287 | [input[j + 1], input[j]] = [input[j], input[j + 1]]; 288 | } 289 | } 290 | } 291 | return input[k - 1]; 292 | } 293 | 294 | let arr = [2, 10, 9, 13, 8, 7, 20, 1]; 295 | console.log(GetKMax(arr, 3)); 296 | ``` 297 | 298 | ### CSS 相关 299 | 300 | - 盒模型 301 | - BFC 和 IFC 相关,什么能实现 BFC 302 | - 三栏布局 303 | - 实现三角形 304 | 305 | ### 智力题 306 | 307 | - 在岛上有 100 只老虎和 1 只羊,老虎可以吃草,但他们更愿意吃羊。 308 | - 假设: 309 | - A:每次只有一只老虎可以吃羊,而且一旦他吃了羊,他自己就变成羊。 310 | - B:所有的老虎都是聪明而且完全理性的,他们的第一要务是生存。 311 | - C:羊不能被两只或者更多老虎分吃。 312 | - 问最后这只羊会不会被吃?如果是 n 只老虎和一只羊呢?3 只老虎一只羊? 313 | 314 | ### 第一次面试总结 315 | 316 | - 面试官很好,想不出来可以问,引导你的思路 317 | - 嗯,然后我很菜,可能因为我错的比较多吧,就一直给机会出新题,嘤嘤嘤 318 | - JavaScript 一些常用 API 使用不熟练,错了 319 | - 数据结构实在是没复习到,做不出来 320 | - 毕竟只复习了才半个月,接下来深入 JS 和数据结构,然后刷刷 LeetCode 321 | -------------------------------------------------------------------------------- /markdown/快手实习.md: -------------------------------------------------------------------------------- 1 | ##### 输出结果 "string" 数据类型 2 | ``` 3 | console.log(typeof typeof typeof null) 4 | ``` 5 | ##### 输出结果 "123" this指向 6 | ``` 7 | var name = '123' 8 | var obj = { 9 | name:'456', 10 | getName:function(){ 11 | function printName(){ 12 | console.log(this.name) 13 | } 14 | printName() 15 | } 16 | } 17 | obj.getName() 18 | // 结果?改写,让结果输出456 19 | 1. printName.apply(this); 20 | 2. const printName = () => 21 | ``` 22 | ##### 输出结果 事件循环 微任务宏任务 23 | ``` 24 | setTimeout(() => { 25 | console.log('quick timer'); // 4 26 | }, 0); 27 | 28 | new Promise((resolve, reject) => { 29 | console.log('init promise'); // 1 30 | process.nextTick(resolve); 31 | }).then(() => { 32 | console.log('promise.then'); // 3 33 | }); 34 | 35 | process.nextTick(() => { 36 | console.log('nextTick'); // 2 37 | }); 38 | 39 | setImmediate(() => { 40 | console.log('immediate'); // 5 41 | }); 42 | ``` 43 | ##### 数组去重 44 | - return [...new Set(arr)] 45 | - newArr.indexOf(arr[i]) === -1 46 | 47 | ##### 手写一个repeact()函数, 使每3秒打印一个helloword, 总共执行4次 48 | ``` 49 | const repeatFunc = repeact(console.log,4,3000) 50 | repeatFunc('helloword') 51 | ``` 52 | 参考答案 53 | ``` 54 | function repeat(func, times, wait) { 55 | return function (context) { 56 | for (let i = 0; i < times; i++) { 57 | setTimeout(() => { 58 | func(context, i); 59 | }, i * wait); 60 | } 61 | }; 62 | } 63 | ``` 64 | ##### 跨域产生的原因和解决方法 65 | - 浏览器的同源策略: 协议、域名、端口号相同为同源,否则不允许跨域 66 | ![跨域举例](https://img-blog.csdnimg.cn/20200527151833624.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODIzMDYzMQ==,size_16,color_FFFFFF,t_70) 67 | - 解决方案: JSONP、CORS、Nginx反向代理 68 | - Nginx举例 (前两种不做过多叙述, 面过太多次了) 69 | ``` 70 | 前端服务:http://localhost:3000, 71 | 前端页面路由:http://localhost:3000/page.html, 72 | 后端服务:http://localhost:3001, 73 | 后端接口路由:http://localhost:3001/api/test.do 74 | 修改配置文件/sites-available/default 75 | server { 76 | listen 80; 77 | server_name localhost; 78 | 79 | location = / { 80 | proxy_pass http://localhost:3000; 81 | } 82 | location /api { 83 | proxy_pass http://localhost:3001; 84 | 85 | #指定允许跨域的方法,*代表所有 86 | add_header Access-Control-Allow-Methods *; 87 | #预检命令的缓存,如果不缓存每次会发送两次请求 88 | add_header Access-Control-Max-Age 3600; 89 | #带cookie请求需要加上这个字段,并设置为true 90 | add_header Access-Control-Allow-Credentials true; 91 | 92 | #表示允许这个域跨域调用(客户端发送请求的域名和端口) 93 | #$http_origin动态获取请求客户端请求的域 不用*的原因是带cookie的请求不支持*号 94 | add_header Access-Control-Allow-Origin $http_origin; 95 | 96 | #表示请求头的字段 动态获取 97 | add_header Access-Control-Allow-Headers 98 | $http_access_control_request_headers; 99 | 100 | #OPTIONS预检命令,预检命令通过时才发送请求 101 | #检查请求的类型是不是预检命令 102 | if ($request_method = OPTIONS){ 103 | return 200; 104 | } 105 | } 106 | } 107 | ``` 108 | 109 | ##### cookie的Secure和HttpOnly 110 | - Secure: 意味着保持Cookie通信只限于加密传输,指示浏览器仅仅在通过安全/加密连接才能使用该Cookie。如果一个Web服务器从一个非安全连接里设置了一个带有secure属性的Cookie,当Cookie被发送到客户端时,它仍然能通过中间人攻击来拦截。如果一个cookie被设置了Secure=true,那么这个cookie只能用https协议发送给服务器,用http协议是不发送的。 111 | - HttpOnly: 指示浏览器不要在除HTTP(和HTTPS)请求之外暴露Cookie。一个有HttpOnly属性的Cookie,不能通过非HTTP方式来访问,例如通过调用JavaScript(例如,引用document.cookie),因此,不可能通过跨域脚本(一种非常普通的攻击技术)来偷走这种Cookie。尤其是Facebook 和 Google 正在广泛地使用HttpOnly属性 112 | 113 | ##### 301,401,403,405这些状态码都是干啥的 114 | - 301 Moved Permanently: 表明目标资源被永久的移动到了一个新的URI,任何未来对这个资源的引用都应该使用新的 URI 115 | - 401 Unauthorized:请求要求身份验证。对于需要登录的网页,服务器可能返回此响应 116 | - 403 Forbidden: 代表客户端错误。指的是服务器端有能力处理该请求,但是拒绝授权访问 117 | - 405 Method Not Allowed: 表示请求的方式不对。请求的方式(get、post、delete)方法与后台规定的方式不符合 118 | 119 | ##### JS随机生成颜色 120 | - 方法1(随机RGBA颜色值) 121 | ``` 122 | function getColorRgba(alpha) { 123 | const red = Math.floor(Math.random() * 256); 124 | const green = Math.floor(Math.random() * 256); 125 | const blue = Math.floor(Math.random() * 256); 126 | 127 | return `rgba(${red},${green},${blue},${alpha})`; 128 | } 129 | ``` 130 | - 方法2(生成十六进制的颜色值) 131 | ``` 132 | function getColor16() { 133 | const red = Math.floor(Math.random() * 256); 134 | const green = Math.floor(Math.random() * 256); 135 | const blue = Math.floor(Math.random() * 256); 136 | 137 | return '#' + red.toString(16) + green.toString(16) + blue.toString(16); 138 | } 139 | ``` 140 | ##### 关于Promise并发的题目 141 | - 有 8 个图片资源的 url,已经存储在数组 urls 中 142 | - 而且已经有一个函数 function loadImg,输入一个 url 链接 143 | - 返回一个 Promise,该 Promise 在图片下载完成的时候 resolve,下载失败则 reject 144 | - 但是我们要求,任意时刻,同时下载的链接数量不可以超过 3 个 145 | ``` 146 | var urls = [ 147 | 'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 148 | 'https://www.kkkk1000.com/images/getImgData/gray.gif', 149 | 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 150 | 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 151 | 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 152 | 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 153 | 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 154 | 'https://www.kkkk1000.com/images/wxQrCode2.png', 155 | ]; 156 | function loadImg(url) { 157 | return new Promise((resolve, reject) => { 158 | const img = new Image(); 159 | img.onload = function () { 160 | console.log('一张图片加载完成'); 161 | resolve(); 162 | }; 163 | img.onerror = reject; 164 | img.src = url; 165 | }); 166 | } 167 | ``` 168 | - 用 Promise.race来实现,先并发请求3个图片资源 169 | - 这样可以得到 3 个 Promise实例,组成一个数组promises 170 | - 然后不断的调用 Promise.race 来返回最快改变状态的 Promise 171 | - 然后从数组(promises )中删掉这个 Promise 对象实例 172 | - 再加入一个新的 Promise实例,直到全部的 url 被取完 173 | ``` 174 | function limitLoad(urls, handler, limit) { 175 | // 对数组做一个拷贝 176 | const sequence = [].concat(urls); 177 | let promises = []; 178 | 179 | //并发请求到最大数 180 | promises = sequence.splice(0, limit).map((url, index) => { 181 | // 这里返回的 index 是任务在 promises 的脚标, 182 | //用于在 Promise.race 之后找到完成的任务脚标 183 | return handler(url).then(() => { 184 | return index; 185 | }); 186 | }); 187 | 188 | (async function loop() { 189 | let p = Promise.race(promises); 190 | for (let i = 0; i < sequence.length; i++) { 191 | p = p.then((res) => { 192 | promises[res] = handler(sequence[i]).then(() => { 193 | return res; 194 | }); 195 | return Promise.race(promises); 196 | }); 197 | } 198 | })(); 199 | } 200 | limitLoad(urls, loadImg, 3); 201 | ``` 202 | -------------------------------------------------------------------------------- /markdown/排序算法.md: -------------------------------------------------------------------------------- 1 | ### 快速排序 2 | 3 | ``` 4 | function quickSort(arr) { 5 | if (arr.length <= 1) { 6 | return arr; 7 | } 8 | let newArr = [...arr]; 9 | let basic = newArr.splice(Math.floor(newArr.length / 2), 1)[0]; 10 | let left = []; 11 | let right = []; 12 | for (let i = 0; i < newArr.length; i++) { 13 | if (newArr[i] < basic) { 14 | left.push(newArr[i]); 15 | } else { 16 | right.push(newArr[i]); 17 | } 18 | } 19 | return quickSort(left).concat([basic], quickSort(right)); 20 | } 21 | 22 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 23 | console.log(quickSort(arr)); 24 | ``` 25 | 26 | ### 冒泡排序 27 | 28 | ``` 29 | function bubbleSort(arr) { 30 | for (let i = 1; i < arr.length; i++) { 31 | for (let j = 0; j < arr.length - i; j++) { 32 | if (arr[j] > arr[j + 1]) { 33 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 34 | } 35 | } 36 | } 37 | return arr; 38 | } 39 | 40 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 41 | console.log(bubbleSort(arr)); 42 | ``` 43 | 44 | ### 选择排序 45 | 46 | ``` 47 | function selectSort(arr) { 48 | let len = arr.length; 49 | let minIndex; 50 | for (let i = 0; i < len; i++) { 51 | minIndex = i; 52 | for (let j = i; j < len; j++) { 53 | if (arr[minIndex] > arr[j]) { 54 | minIndex = j; 55 | } 56 | } 57 | if (i !== minIndex) { 58 | [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; 59 | } 60 | } 61 | return arr; 62 | } 63 | 64 | let arr = [-2, 10, 9, 13, -10, 8, 7, 20, 1, -1]; 65 | console.log(selectSort(arr)); 66 | ``` 67 | -------------------------------------------------------------------------------- /markdown/百度面经.md: -------------------------------------------------------------------------------- 1 | ### 文件整理中 2 | 3 | // 进程线程区别 了解多少 4 | // 对于跨域的理解 5 | function Test() { 6 | try { 7 | // promise.try 下去研究 8 | // 解决异步抛出 error 问题 9 | const d = new Promise((resolve, reject) => { 10 | resolve(); 11 | }); 12 | d.then(() => { 13 | throw new Error("111"); 14 | }); 15 | } catch (err) { 16 | console.log(err); 17 | } 18 | } 19 | Test(); 20 | 21 | // 这个地方,能成功渲染 10 行 22 | // render( 23 | // return new Array(10).map(() => { 24 | //
25 | // }); 26 | // ) 27 | 28 | // 自减数 29 | [ 30 | { key: "鲜花", words: [1, 2, 3] }, 31 | { key: "鲜", words: [1, 2, 3] }, 32 | ]; 33 | 34 | /\* 35 | 1、左右布局 36 | float,绝对定位,flex,justfy-content:space-around 37 | 38 | 2、滚动吸底 39 | sticky 粘性布局 40 | 41 | vh? 42 | 43 | 3、深拷贝,浅拷贝 44 | const a = [{name: '111'}]; 45 | 46 | const b = [].concat(a); 47 | 48 | // b[0].name = 222; 49 | b[0] = 222; 50 | 51 | 4、箭头函数特点 52 | new,argument, 53 | 参数=》展开运算符 54 | this? 55 | bind([]); 56 | 57 | var self = this; 58 | 59 | 5、babel 原理 60 | 61 | 6、js 事件循环机制 62 | 63 | 7、function Test() { 64 | try { 65 | const d = new Promise((resolve, reject) => { 66 | resolve(); 67 | }); 68 | d.then(() => { 69 | throw new Error('111'); 70 | }); 71 | } catch (err) { 72 | console.log(err) 73 | } 74 | }; 75 | Test(); 76 | 77 | async await 78 | 79 | 【promise.try】 80 | 81 | 8、jsx key 作用 82 | 83 | 9、百度搜索输入框 84 | 防抖,节流 85 | 86 | 鲜 87 | 鲜花 88 | 鲜花饼 89 | 90 | 字典树 91 | 92 | babel 降级是语法还是方法 93 | \*/ 94 | 95 | // 关于百度面试的一些问题 96 | // 1. 手写阶乘、比较循环阶乘和递归阶乘过程、问题优缺点 Infinity 97 | // 2. 中间穿插着堆栈内存问题 98 | 99 | function counter1() { 100 | let count = 0; 101 | return function () { 102 | console.log(count++); 103 | }; 104 | } 105 | 106 | function counter2() { 107 | return function () { 108 | console.log(count++); 109 | }; 110 | let count = 0; 111 | } 112 | 113 | function counter3() { 114 | return function () { 115 | console.log(count++); 116 | }; 117 | var count = 0; 118 | } 119 | 120 | let myCounter = counter3(); 121 | myCounter(); 122 | myCounter(); 123 | myCounter(); 124 | 125 | /\* 126 | 百度正常情况是三轮连续 127 | 百度一面(45min): 128 | 129 | 1. Promise,async/await 场景题 130 | 2. 箭头函数场景题 131 | 3. 关于一个百度搜索框, 你能想到的场景, 注意点 132 | 百度二面(45min), 结束: 133 | 4. 先随便写一个闭包, 然后面试官改代码, 不同场景下的结果, 分析操作系统中的情况 134 | \*/ 135 | -------------------------------------------------------------------------------- /markdown/网络相关.md: -------------------------------------------------------------------------------- 1 | ### 说一下 http 和 https 2 | 3 | > http 和 https 的基本概念 4 | 5 | - http:超文本传输协议,是互联网上应用最为广泛的一种网络协议,建立在 TCP 之上,用于从服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。 6 | - https:是以安全为目标的 http 通道,即 http 和 tcp 之间加入 ssl 层。它的主要作用是:建立一个信息安全通道,来确保数据的传输,确保网站的安全性 7 | 8 | > http 和 https 的区别 9 | 10 | - http 的传输数据都是未加密的,也就是明文传输,网景公司设置了 ssl 协议来对 http 协议传输的数据进行加密,简单来说 https 协议是由 http 协议和 ssl 协议构建的可进行加密传输和身份认证的网络协议,比 http 协议的安全性更高 11 | - 主要的区别如下: 12 | - - Https 协议需要 ca 证书,费用较高; 13 | - - http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议; 14 | - - 使用不同的链接方式,端口也不同,一般而言,http 协议的端口为 80,https 的端口为 443; 15 | - - http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。 16 | - 在网络请求中,需要有很多服务器、路由器的转发。其中的节点都可能篡改信息,而如果使用 HTTPS,秘钥在终点站才有。HTTPS 之所以比 HTTP 更安全,是因为它使用 SSL/TLS 协议传输。它包含证书、卸载、流量转发、负载均衡、页面适配、浏览器适配、refer 传递等技术,保障了传输过程的安全性。 17 | 18 | > https 协议的优点 19 | 20 | - 使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器; 21 | - HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 http 协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。 22 | - HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。 23 | - 比起同等 HTTP 网站,采用 HTTPS 加密的网站在搜索结果中的排名将会更高” 24 | > https 协议的缺点 25 | - https 握手阶段比较费时,会使页面加载时间延长 50%,增加 10%~20%的耗电。 26 | - https 缓存不如 http 高效,会增加数据开销。 27 | - SSL 证书也需要钱,功能越强大的证书费用越高。 28 | SSL 证书需要绑定 IP,不能再同一个 ip 上绑定多个域名,ipv4 资源支持不了这种消耗。 29 | 30 | ### TCP 三次握手,一句话概括 31 | 32 | - 客户端和服务器都需要知道各自可收发,因此需要三次握手。 33 | - 简化三次握手: 34 | 35 | 36 | 三次握手 37 | 38 | 39 | - 为了准确无误的把数据送达目标处,TCP 采用了三次握手策略。用 TCP 把数据包发送出去后,TCP 不会对发送后的数据置之不理,它一定会向对方确认是否成功送达。握手过程中使用了 TCP 的标志,即 SYN 和 ACK。 40 | - 发送端首先给接收端发送一个带 SYN 标识的数据包。接收端收到后,回传一个带有 SYN-ACK 标志的数据包以表示正确传达,并确认信息。最后,发送端再回传一个带有 ACK 标志的数据包,代表握手结束。若在握手过程中的某个阶段莫名中断,TCP 会再次以相同的顺序发送相同的数据包。 41 | - 断开一个 TCP 连接则需要‘四次挥手’,当被动方收到主动方的 FIN 报文通知时,它仅仅表示主动方没有数据再发送给被动方了。但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭 SOCKET,它可能还需要发送一些数据给主动方后,再发送 FIN 报文给主动方,告诉主动方同意关闭连接,所以这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的。 42 | 43 | ### 四次挥手 44 | 45 | - 四次挥手:由于 TCP 连接是全双工的,断开一个 TCP 连接,需要客户端与服务器发送四个包来确认连接的断开,每个方向都要单独关闭。 46 | 47 | 1. Client 报文段数据传输完毕需要断开连接,向其 TCP 发出连接释放报文段(FIN=1, 序号 seq=u),并停止再发送数据,主动关闭 TCP 连接,进入 FIN_WAIT_1 状态,等待 Server 的确认; 48 | 2. Server 收到连接释放报文段后即发出确认报文段(ACK=1,确认号 ack=u+1, 序号 seq=v), Server 进入 CLOSE_WAIT 状态,此时的 TCP 处于半关闭状态,Client 到 Server 的连接释放。而 Client 收到 Server 的确认后,进入 FIN_WAIT_2 状态,等待 Server 发出的连接释放报文段。 49 | 3. 当 Server 数据传输完毕后,发出连接释放报文段(FIN=1,ACK=1,序号 seq=w,确认号 ack=u+1), Server 进入 LAST_ACK 状态,等待 Client 的再次确认。 50 | 4. Client 收到 Server 的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1, ack=w+1),Client 进入 TIME_WAIT 状态,此时 TCP 未释放掉,需要经过时间等待计时器设置的时间 2MSL 后,Client 才进入 CLOSE 状态 51 | 52 | ### HTTP 和 HTTPS 之间有什么联系,他们的端口号是什么 53 | 54 | - http 通常承载于 TCP 之上,在 HTTP 和 TCP 之间添加一个安全协议层(SSL 或 TLS),这个时候,就成了我们常说的 HTTPS,HTTP 默认端口是 80,HTTPS 默认端口号是 443 55 | 56 | ### 为什么 HTTPS 更安全 57 | 58 | - 在网络请求中,需要有很多服务器、路由器的转发。其中的节点都可能篡改信息,而如果使用 HTTPS,秘钥在终点站才有。HTTPS 之所以比 HTTP 更安全,是因为它使用 SSL/TLS 协议传输。它包含证书、卸载、流量转发、负载均衡、页面适配、浏览器适配、refer 传递等技术,保障了传输过程的安全性 59 | 60 | ### 关于 HTTP/2 你知道多少 61 | 62 | - HTTP/2 引入了‘服务器端推送’(server push)的概念,它允许服务器端在客户端需要数据前主动将数据发送到客户端缓存中,从而提高性能 63 | - HTTP/2 提供更多的加密支持 64 | - HTTP/2 使用多路技术,允许多个消息在一个连接上同时交叉 65 | - 它增加了头压缩,因此请求非常小,请求和响应的 header 只占很小的带宽比例 66 | 67 | ### 输出你知道的 HTTP 常见状态码。 68 | 69 | - 100 Continue 表示继续,一般在 post 请求时,已发送了 HTTP header 之后,服务器端将返回此消息,表示确认,之后发送具体参数信息 70 | - 200 OK 表示正常返回信息 71 | - 201 Created 表示请求成功并在服务器创建了新的资源 72 | - 202 Accepted 表示服务器已接收请求,但并未处理 73 | - 302 Found 表示临时重定向 74 | - 403 Forbidden 表示禁止访问 75 | - 404 Not Found 表示找不到如何与 URI 相匹配的资源 76 | - 500 Internal Server error 表示最常见的服务器端错误 77 | 78 | ### 完整的 HTTP 事务流程是怎样的? 79 | 80 | - 基本流程如下 81 | - 域名解析;发起 TCP 的三次握手;建立 TCP 连接后发起 HTTP 请求; 82 | - 服务器响应 HTTP 请求,浏览器得到 HTTP 代码; 83 | - 浏览器解析 HTML 代码,并请求 HTML 代码中的资源; 84 | - 浏览器对页面进行渲染并呈现给用户。 85 | 86 | ### 什么是 HTTP? 87 | 88 | - http 是客户端和服务端之间进行数据传输的格式规范,表示‘超文本传输协议’ 89 | 90 | ### 什么是 HTTP 无状态协议?如何克服 HTTP 无状态协议的缺陷? 91 | 92 | - 无状态协议对于事务处理没有记忆能力,缺少状态意味着后续如果需要处理,需要前面提供的信息; 93 | - 克服无状态协议缺陷的方法是通过 cookie 和会话保存信息 94 | 95 | ### HTTP 的请求报文和响应报文包含哪些部分? 96 | 97 | - 请求报文三部分: 98 | - 请求行,包含请求方法、URL、HTTP 版本信息;请求首部字段;请求内容实体 99 | - 响应报文三部分: 100 | - 状态行,包含 HTTP 版本、状态码、状态码的原因短语;响应首部字段、响应内容实体 101 | 102 | ### HTTP 中有哪些请求方式? 103 | 104 | - GET:请求已经被 URI(统一资源定位符)识别的资源,可以通过 URL,给服务器端传递参数数据; 105 | - POST:传输信息给服务器,主要功能与 GET 方法类似,但传输的数据量通常不受限制; 106 | - PUT:传输文件,报文主体中包含文件内容,保存到对应 URI 位置; 107 | - HEAD:活的报文首部,与 GET 方法类似,只是不返回报文主体,一般用于验证 URI 是否有效; 108 | - DELETE:删除文件,与 PUT 方法相反,删除对应 URL 位置的文件; 109 | - OPTIONS:查询相应 URI 支持的 HTTP 方法 110 | 111 | ### HTTP 协议中 1.0 版本规范与 1.1 版本规范的区别是什么? 112 | 113 | - 在 HTTP1.0 中,当建立连接后,客户端发送一个请求,服务器端返回一个信息后就关闭连接,当浏览器下次请求的时候又要建立连接。显然这种不断建立连接的方式会造成很多问题。 114 | - 在 HTTP1.1 中,引入了持续连接的概念。通过这种连接,浏览器可以在建立一个连接之后,发送请求并得到返回信息,然后再次发送请求并得到返回信息。也就是说,客户端可以连续发送多个请求,而不用等待每一个响应的到来。 115 | 116 | ### HTTP 的首部字段包括哪些类型? 117 | 118 | - (1)通用首部字段(请求报文与响应报文都会使用的首部字段)。它包括以下几部分。 119 | - Date:创建报文的时间 120 | - Connection:连接的管理 121 | - Cache- Control:缓存的控制 122 | - Transfer- Encoding:报文主体的传输编码方式 123 | - (2)请求首部字段(请求报文会使用的首部字段)。它包括以下几部分。 124 | - Host:请求资源所在服务器。 125 | - Accept:可处理的媒体类型。 126 | - Accept-Charset:可接受的字符集。 127 | - Accept- Encoding:可接受的内容编码。 128 | - Accept- Language:可接受的自然语言。 129 | - (3)响应首部字段(响应报文会使用的首部字段)。它包括以下几部分 130 | - Accept- Ranges:可接受的字节范围。 131 | - Location:令客户端重新定向到的 URL。 132 | - Server : HTTP 服务器的安装信息 133 | - (4)实体首部字段(请求报文与响应报文的实体部分使用的首部字段)。它包括以下几部分。 134 | - Allow:资源可支持的 HTTP 方法。 135 | - Content-Type:实体主体的类型。 136 | - Content- Encoding:实体主体使用的编码方式。 137 | - Content-Language:实体主体的自然语言。 138 | - Content- Length:实体主体的字节数。 139 | - Content- Range:实体主体的位置范围,一般用于发出部分请求时使用。 140 | 141 | ### 与 HTTPS 相比,HTTP 有什么缺点? 142 | 143 | - HTTP 的缺点如下。 144 | - 通信使用明文,不加密,内容可能被窃听,也就是被抓包分析 145 | - 不验证通信方身份,可能被伪装 146 | - 无法验证报文完整性,可能被篡改 147 | 148 | ### 如何优化 HTTP 请求 149 | 150 | - 利用负载均衡优化和加速 HTTP 应用请求;利用 HTTP 缓存来优化网站请求 151 | 152 | ### HTTP 协议有哪些特征? 153 | 154 | - 支持客户端、服务器模式,简单灵活,无连接、无状态 155 | 156 | ### HTTP1.1 版本的新特性有哪些? 157 | 158 | - 默认持久连接,节省通信量,只要客户端服务器端任意一端没有明确指出断开 TCP 连接,就一直保持连接,可以多次发送 HTTP 请求 159 | - 管线化,客户端可以同时发送多个 HTTP 请求,而不用一个个等待响应 160 | - 断点续传原理 161 | 162 | ### 说说 TCP 传输的三次握手、四次挥手策略 163 | 164 | - 为了准确无误的把数据送达目标处,TCP 采用了三次握手策略。用 TCP 把数据包发送出去后,TCP 不会对发送后的数据置之不理,它一定会向对方确认是否成功送达。握手过程中使用了 TCP 的标志,即 SYN 和 ACK。 165 | - 发送端首先给接收端发送一个带 SYN 标识的数据包。接收端收到后,回传一个带有 SYN-ACK 标志的数据包以表示正确传达,并确认信息。最后,发送端再回传一个带有 ACK 标志的数据包,代表握手结束。若在握手过程中的某个阶段莫名中断,TCP 会再次以相同的顺序发送相同的数据包。 166 | - 断开一个 TCP 连接则需要‘四次挥手’,当被动方收到主动方的 FIN 报文通知时,它仅仅表示主动方没有数据再发送给被动方了。但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭 SOCKET,它可能还需要发送一些数据给主动方后,再发送 FIN 报文给主动方,告诉主动方同意关闭连接,所以这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的。 167 | 168 | ### 说说 TCP 和 UDP 的区别 169 | 170 | - TCP(Transmission control protocol,传输控制协议)是基于连接的协议,也就是说,在正式发送数据前,必须和对方建立可靠的连接。一个 TCP 连接必须要经过‘三次握手’才能建立起来 171 | - UDP(User Datagram Protocol,用户数据报协议)是与 TCP 相对应的协议。它是面向非连接的协议,他不与对方建立连接,而是直接就把数据报发送出去。UDP 适用于一次只传送少量数据,对可靠性要求不高的应用环境。 172 | 173 | ### 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么? 174 | 175 | - 当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求同时在远程 DNS 服务器上启动一个 DNS 查询这能使浏览器获得对应的 IP 地址 176 | - 浏览器与远程 web 服务器通过 TCP 三次握手建立一个可靠的 TCP 连接。该握手包含一个同步报文,一个同步、应答报文,一个应答报文,这 3 个报文在浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,然后服务器应答并接受客户端的请求,最后由客户端发出已经接受该请求的报文。 177 | - 一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态码表示一个正确的响应 178 | - 此时 web 服务器提供资源服务,客户端开始下载资源。请求返回后,便进入了浏览器端模块。浏览器会解析 HTML 生成 DOM Tree,其次会根据 CSS 生成 CSS 规则树,而 JavaScript 又可以根据 DOM API 操作 DOM。 179 | 180 | ### 网络分层模型有哪七层? 181 | 182 | - 七层分别是应用( Application)层、表示( Presentation)层、会话( Session)层传输( Transport)层、网络( Network)层、数据链路(Link)层和物理( Physical)层。 183 | 184 | ### 网络七层模型中,你所熟知的协议有哪些? 185 | 186 | - ICMP,即因特网控制报文协议。它是 TCP/IP 协议族的一个子协议,用于在 IP 主机、路由器之间传递控制消息。 187 | - TFTP,即 TCPP 协议族中一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。 188 | - HTTP,即超文本传输协议,是一个属于应用层的面向对象的协议,由于其简捷快速的方式,适用于分布式超媒体信息系统。 189 | - DHCP,即动态主机配置协议,是一种让系统得以连接到网络并获取所需要配置参数的手段。 190 | 191 | ### 什么是反向代理? 192 | 193 | - 反向代理( Reverse Proxy)是指通过代理服务器来接收互联网上的连接请求,然后将请求转发给内部网络上的服务器,并把从服务器上得到的结果返回给互联网上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。 194 | 195 | ### post 与 get 请求 196 | 197 | - get 请求在浏览器回退时时无害的,而 POST 会再次提交请求 198 | - get 请求会被浏览器缓存 cache,而 post 不会,除非手动配置 199 | - get 请求在不同浏览器的地址栏里面是有长度限制的,而 post 没有 200 | - get 通过 url 传递参数,而 post 通过请求头 request body 201 | - get 相比 post 更不安全,因为是直接暴露在 url 上,所以不易传递敏感信息 202 | - get 请求一般用于请求去获取数据,post 易班发送数据到后台修改时使用 203 | 204 | ### RestFul API 规范 205 | 206 | 1. 每一个 URI 代表一种资源; 207 | 2. 同一种资源有多种表现形式(xml/json); 208 | 3. 所有的操作都是无状态的。 209 | 4. 规范统一接口。 210 | 5. 返回一致的数据格式。 211 | 6. 可缓存(客户端可以缓存响应的内容)。 212 | -------------------------------------------------------------------------------- /markdown/美团点评凉经.md: -------------------------------------------------------------------------------- 1 | ## 美团点评凉经 2 | 3 | ### 基础知识部分(未答好的) 4 | 5 | #### 进程线程详细区别 6 | 7 | - 进程是 CPU 资源分配的最小单位,线程是 CPU 调度的最小单位 8 | - 进程之间的切换开销比较大,但是线程之间的切换开销比较小。 9 | - CPU 会把资源分配给进程,但是线程几乎不拥有任何的系统资源。因为线程之间是共享同一个进程的,所以线程之间的通信几乎不需要系统的干扰。 10 | 11 | #### TCP 拥塞控制 12 | 13 | - 拥塞控制,就是在网络中发生拥塞时,减少向网络中发送数据的速度,防止造成恶性循环;同时在网络空闲时,提高发送数据的速度,最大限度地利用网络资源 14 | 15 | #### 0.1+0.2 原因+解决方法 16 | 17 | - 数字运算中的精度缺失的问题 18 | - 在计算机中,数字无论是定点数还是浮点数都是以多位二进制的方式进行存储的。在 JS 中数字采用的 IEEE 754 的双精度标准进行存储 19 | 20 | ``` 21 | console.log(0.1000000000000001) 22 | // 0.1000000000000001 (中间 14 个 0,会打印出它本身) 23 | console.log(0.10000000000000001) 24 | // 0.1 (中间 15 个 0,js 会认为这两个值足够接近,所以会显示 0.1) 25 | ``` 26 | 27 | - 就是说由于 0.1 转换成二进制时是无限循环的,所以在计算机中 0.1 只能存储成一个近似值。 28 | - 在 0.1 + 0.2 这个式子中,0.1 和 0.2 都是近似表示的,在他们相加的时候,两个近似值进行了计算,导致最后得到的值是 0.30000000000000004,此时对于 JS 来说,其不够近似于 0.3,于是就出现了 0.1 + 0.2 != 0.3 这个现象。 29 | 30 | #### 431 状态码怎么解决 31 | 32 | - HTTP 431 Request Header Fields Too Large 响应状态码指示服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。 33 | - 它可以在请求头字段的总数太大或单个头字段太大时使用。 34 | - 这个错误不应该发生在经过良好测试的生产系统上,但在测试新系统时可以更频繁地发现。 35 | - 请求头超过了 tomcat 的限值。本来 post 请求是没有参数大小限制,但是服务器有自己的默认大小。 36 | - 修改服务器的 header 的大小, 改 tomcat 配置:maxHttpHeaderSize =”102400”,SpringBoot 项目更方便了,在 application.properties 文件中添加 37 | - ngnix.conf 的修改 38 | - 修改 tomcat 中的 server.xml 39 | 40 | #### http2 二进制分帧详情 41 | 42 | 43 | 44 | HTTP----HTTP2.0新特性
45 | 46 | 47 | #### vnode render 48 | 49 | 50 | 51 | new Vue({ render: h => h(App), }).$mount('#app')
52 | 53 | 54 | #### commonjs 和 esModule 55 | 56 | 57 | 58 | commonJS 和 ES Module 区别
59 | 60 | 61 | ### 代码部分 62 | 63 | #### 闭包实现计数器 64 | 65 | function counter() { 66 | let num = 0; 67 | return function () { 68 | console.log(++num); 69 | }; 70 | } 71 | 72 | #### 奇偶数组拼接, 求时间复杂度(先用了 filter, 面试官不满意) 73 | 74 | // 改写成双指针方法(当时测试用例有些不成功,让我结束补) 75 | // 现在写的这个还是会乱序啊...面试官明明要求了保证顺序 76 | // LeetCode 也这样, 求大佬解法 77 | function func(arr) { 78 | let left = 0; 79 | let right = arr.length - 1; 80 | 81 | while (left < right) { 82 | while (left < right && arr[left] % 2 == 1) { 83 | left++; 84 | } 85 | while (left < right && arr[right] % 2 == 0) { 86 | right--; 87 | } 88 | [arr[left], arr[right]] = [arr[right], arr[left]]; 89 | } 90 | return arr; 91 | } 92 | let arr = [1, 2, 3, 4, 5, 6, 7]; 93 | console.log(func(arr)); 94 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dike1999/front-end-interview/9319364ebf508d036ae62602bbe069329165a3e6/test/index.js --------------------------------------------------------------------------------