├── 2025 ├── 2025前端技术知识图谱-精简版.md ├── @2025前端技术知识图谱.md ├── AI 赋能与前沿探索 │ ├── AI 赋能前端开发与前沿探索.md │ ├── 一、核心概念与技术.md │ ├── 三、前端集成 AI 能力.md │ └── 二、AI辅助开发实践.md ├── Node.js与服务端开发 │ ├── Node.js与服务端开发.md │ ├── 一、核心技术与原理.md │ ├── 三、(工程化) 工具链与 CLI 开发.md │ └── 二、Web 服务与 API 开发.md ├── roadMap.md ├── 工程化与效能提升 │ ├── 一、构建系统与工具链.md │ ├── 三、部署与运维.md │ ├── 二、开发流程与规范化.md │ ├── 前端工程化与效能提升.md │ └── 构建速度优化.md ├── 性能优化与安全保障 │ ├── web安全.md │ ├── 前端性能优化与安全保障.md │ ├── 性能优化.md │ └── 页面加载速度优化.md ├── 框架与架构设计 │ ├── 一、框架核心原理深度剖析.md │ ├── 三、前端架构设计与模式演进.md │ ├── 二、主流框架生态与最佳实践.md │ └── 前端框架精通与架构设计.md ├── 计算机原理与网络 │ ├── 【一】计算机基础与数据表示.md │ ├── 【七】编程语言、编译与前端工程化.md │ ├── 【三】操作系统核心概念.md │ ├── 【二】数据结构与算法基础.md │ ├── 【五】浏览器工作原理.md │ ├── 【六】JavaScript 核心机制.md │ ├── 【四】网络通信原理.md │ ├── 前端开发应掌握的计算机原理.md │ ├── 计算机处理器与大模型算力.md │ └── 进程和线程.md └── 语言基础 │ ├── 【一】HTML:内容结构与语义化.md │ ├── 【三】JavaScript 核心:从基础到现代特性与函数式思想.md │ ├── 【二】CSS 核心:布局、样式与现代工程化实践.md │ ├── 【四】TypeScript:静态类型系统与工程实践.md │ └── 前端基础核心:语言与标准.md ├── .cursor ├── Dockerfile ├── environment.json └── install.sh ├── .gitignore ├── .vscode └── launch.json ├── JavaScript ├── Promise │ ├── 1.js │ ├── 2.js │ ├── promise.md │ ├── promise1.js │ ├── promise2.js │ ├── promise3.js │ ├── promise4.js │ ├── promise5.js │ ├── promise6.js │ └── promise7.js ├── Proxy │ └── 1.js ├── defineProperty │ ├── 1.html │ ├── 1.js │ ├── 2.js │ └── 3.js ├── es6基础 │ ├── 1.js │ ├── 1.xml │ ├── Generator.md │ ├── Iterator.md │ ├── Set,Map.md │ ├── Symbol.md │ ├── async、await.js │ ├── async、await.md │ └── 语法扩展.md ├── js基础 │ ├── 原型和原型链 │ │ ├── regCode.js │ │ ├── 原型和原型链.html │ │ ├── 原型和原型链.js │ │ └── 原型和原型链.md │ ├── 执行原理 │ │ ├── 1.html │ │ ├── 1.js │ │ └── 1.md │ └── 正则 │ │ ├── 1.js │ │ └── 1.md ├── notes │ ├── this.js │ ├── 作用域.js │ ├── 类型转换.js │ └── 闭包.js ├── websocket │ └── socket.js ├── 函数式编程 │ ├── 1.md │ ├── index.js │ └── re.js ├── 节流和防抖 │ ├── 1.html │ ├── debounce.js │ └── throttle.js ├── 跨域 │ ├── 1.md │ ├── download.js │ └── jsonp.js └── 面试总结 │ ├── 1.html │ ├── 1.js │ ├── 1.md │ ├── 2020.md │ └── 强制类型转换.md ├── README.md ├── WebRTC ├── 1.md ├── 3.html ├── 5.html ├── iceServer.json ├── vcWebrtc.js ├── webrtc.html ├── webrtc1.md ├── webrtc2.md └── webrtc3.md ├── browser ├── 1.html ├── 1.md ├── 2.md ├── 3.md └── beforeRequest │ ├── background.html │ ├── background.js │ ├── content-script │ └── resource-inject.js │ ├── icon-default.png │ └── manifest.json ├── fe-performance ├── cache.md ├── event-loop.md ├── image.md ├── ssr.md ├── timIng.md └── 前端异常处理.md ├── node ├── koa │ ├── .gitignore │ ├── businesscard.pdf │ ├── certificate.pem │ ├── certrequest.csr │ ├── fn.js │ ├── google.png │ ├── image.png │ ├── kkk.png │ ├── package.json │ ├── privatekey.pem │ ├── public │ │ ├── 1.css │ │ ├── index.html │ │ ├── jsonp.js │ │ └── module.js │ ├── route.js │ ├── server.js │ ├── test-1.png │ └── test.js └── xlsx │ ├── .gitignore │ ├── 1.json │ ├── 1.xlsx │ ├── 2.xlsx │ ├── index.js │ ├── package.json │ └── yarn.lock ├── other ├── 1.html ├── 2.html ├── JavaScriptStyleGuide.md ├── index.html ├── list.md ├── qa.md ├── scroll.html ├── share.md ├── test.html └── test.js ├── questions ├── ModalHelperFn.js ├── arrange.md ├── notes.md ├── passive-EventListener.md └── useModalPenetration.js ├── react ├── core-old │ ├── component.js │ ├── demo.html │ ├── demo.js │ ├── element.js │ ├── index.js │ ├── server.js │ ├── start.sh │ ├── types.js │ └── unit.js ├── demo │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── Ability │ │ └── index.js │ │ ├── About │ │ └── index.js │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── Calendar │ │ └── index.js │ │ ├── Carousel │ │ ├── index.js │ │ └── index.scss │ │ ├── Game │ │ ├── index.css │ │ └── index.js │ │ ├── Lazy │ │ ├── index.css │ │ └── index.js │ │ ├── Test │ │ └── index.js │ │ ├── ToScroll │ │ ├── index.js │ │ └── index.scss │ │ ├── Touch │ │ └── index.js │ │ ├── core-old │ │ ├── component.js │ │ ├── element.js │ │ ├── index.js │ │ └── unit.js │ │ ├── hooks │ │ └── useImmerState.js │ │ ├── index.css │ │ ├── index.js │ │ ├── serviceWorker.js │ │ └── test.js ├── fiber │ └── index.js ├── hooks │ ├── hooks.html │ ├── hooks.js │ ├── 深入 React hooks — useState.md │ ├── 深入 React hooks — 原理 & 实现.md │ └── 深入 React hooks — useEffect.md ├── redux-model │ ├── dva │ │ └── index.js │ ├── react-redux │ │ ├── connect.js │ │ ├── index.js │ │ └── mapDispatchToProps.js │ ├── redux-saga │ │ └── index.js │ └── redux │ │ ├── applyMiddleware.js │ │ ├── bindActionCreators.js │ │ ├── combineReducers.js │ │ ├── compose.js │ │ ├── createStore.js │ │ ├── index.js │ │ ├── store_enhancer.js │ │ └── thunk.js └── router │ ├── BrowserRouter.js │ ├── HashRouter.js │ ├── HistoryContext.js │ ├── Link.js │ ├── Redirect.js │ ├── Route.js │ ├── Router.js │ ├── RouterContext.js │ ├── Switch.js │ ├── createNamedContext .js │ ├── history.js │ ├── hook.js │ ├── index.js │ └── matchPath.js ├── service_worker.js ├── test.js ├── test1.js ├── tiku ├── 0419.js ├── 0430.js ├── 1.html ├── 1.js ├── 1.md ├── 2.js ├── async-code │ ├── async-fnList.js │ ├── async-task.js │ ├── await-fetch.js │ ├── cache-fetch.js │ ├── co.js │ ├── concurrent.js │ ├── createFlow.js │ ├── fetch-shaking.js │ ├── middleware-Task.js │ └── person.js ├── design-pattern │ ├── desc.md │ └── index.js ├── leetCode │ ├── 102-二叉树的层次遍历.js │ ├── 104-二叉树的最大深度.js │ ├── 107-二叉树的层次遍历2.js │ ├── 112-路径总和.js │ ├── 113-路径总和二.js │ ├── 138-复制带随机指针的链表.js │ ├── 1436-旅行终点站.js │ ├── 15-3数之和.js │ ├── 160-两个链表的第一个公共节点.js │ ├── 172-阶乘后的尾数0.js │ ├── 198-打家窃舍.js │ ├── 2-两数相加.js │ ├── 20-有效的括号.js │ ├── 206-反转链表.js │ ├── 21-合并两个有序链表.js │ ├── 213-打家劫舍二.js │ ├── 23-合并k个升序链表.js │ ├── 230-二叉搜索树第k小的数.js │ ├── 237-删除链表中的节点.js │ ├── 239-滑动窗口最大值.js │ ├── 258-各位相加.js │ ├── 32-最长有效字符串.js │ ├── 337-打家劫舍三.js │ ├── 34-在排序数组中查找元素的第一个和最后一个位置.js │ ├── 415-字符串相加.js │ ├── 43-字符串相乘.js │ ├── 5-最长子回文串.js │ ├── 54-螺旋矩阵.js │ ├── 59-螺旋矩阵II.js │ ├── 69-x 的平方根.js │ ├── 7-整数翻转.js │ ├── 70-爬楼梯.js │ ├── 746-最小花费爬楼梯.js │ ├── 796-旋转字符串.js │ ├── 8-字符串转整数.js │ ├── 82- 删除排序链表中的重复元素 II.js │ ├── 83-删除排序链表中的重复元素.js │ ├── 867-转置矩阵.js │ ├── 88-合并两个有序数组.js │ ├── 9-回文数.js │ ├── 91-解码方法.js │ ├── 92-反转链表2.js │ ├── 95-不同的二叉搜索树二.js │ ├── 96-不同的二叉搜索树.js │ ├── isPalindrome.js │ ├── maximum-sum-subarray.js │ ├── symmetric-tree.js │ ├── two-sum.js │ ├── 剑指 Offer 32 - I. 从上到下打印二叉树.js │ ├── 剑指 Offer 32 - III. 从上到下打印二叉树 III.js │ ├── 剑指 Offer 33. 二叉搜索树的后序遍历序列.js │ ├── 剑指 Offer 59 - I. 滑动窗口的最大值.js │ ├── 剑指offer-18-删除链表的节点.js │ ├── 剑指offer34二叉树指定路径和的所有路径.js │ ├── 剑指offer54-二叉搜索树第k大的节点.js │ ├── 旅行商问题.js │ └── 连续子数组的最大和.js ├── polyfill │ └── es5-class.js ├── sort │ ├── bubbleSort.js │ ├── insertSort.js │ ├── mergeSort.js │ ├── quickSort.js │ └── selectSort.js └── structure │ ├── binarySearchTree.js │ ├── circulateList.js │ ├── doublyLinkedList.js │ ├── info.md │ ├── linkedList.js │ ├── list.js │ ├── loselose.js │ ├── queue.js │ └── stack.js ├── ts ├── hello.js ├── hello.ts ├── package-lock.json ├── package.json └── tsconfig.json ├── tsconfig.json ├── typings.json ├── typings ├── globals │ └── node │ │ ├── index.d.ts │ │ └── typings.json └── index.d.ts ├── user.jpg ├── vue ├── ast │ ├── .gitignore │ ├── ast.js │ ├── ast.md │ ├── package.json │ └── read.js ├── mvvm │ ├── 1.html │ ├── 1.md │ ├── 2.html │ ├── MVVM.js │ └── Observer.js ├── rollup │ ├── 1.md │ ├── bundle.js │ ├── foo.js │ ├── main.js │ └── rollup.config.js └── 组件间通信 │ ├── 1.md │ └── eventBus.js └── webpack ├── 1.md ├── ast └── 1.js ├── modules ├── common.js ├── es6.js ├── require.js └── test.js ├── tapable ├── AsyncParallelHook.js ├── AsyncSeriseHook.js ├── SyncHook.js ├── SyncLoopHook.js └── SyncWaterHook.js ├── w-pack ├── dist │ └── ddd.js ├── index.html ├── package.json ├── src │ ├── index.js │ └── output.js ├── test │ ├── index.js │ ├── log.js │ ├── name.js │ └── style.css ├── util │ └── composeName.js └── w.config.js └── webpack-demo ├── .gitignore ├── dev-server.js ├── index.html ├── loaders └── test-loader.js ├── package.json ├── plugins └── inline-script-plugin.js ├── server.js ├── src ├── common │ └── utils.js └── pages │ ├── app │ ├── app.js │ ├── fn.js │ ├── main.js │ ├── play.js │ ├── style.css │ └── test.js │ ├── home │ └── index.js │ └── test │ └── index.js ├── static └── img │ ├── 1.jpg │ └── 2.jpg ├── vendor.manifest.json ├── webpack.config.js └── webpack.dll.conf.js /.cursor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | # 避免交互式提示 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | 6 | # 安装基础工具 7 | RUN apt-get update && apt-get install -y \ 8 | curl \ 9 | git \ 10 | nodejs \ 11 | npm \ 12 | sudo \ 13 | python3 \ 14 | python3-pip \ 15 | build-essential \ 16 | && apt-get clean \ 17 | && rm -rf /var/lib/apt/lists/* 18 | 19 | # 安装 Node.js 版本管理器 20 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 21 | 22 | # 配置 nvm 环境变量 23 | ENV NVM_DIR=/root/.nvm 24 | RUN . "$NVM_DIR/nvm.sh" && nvm install 16 && nvm use 16 && nvm alias default 16 25 | 26 | # 全局配置 27 | RUN echo "source $NVM_DIR/nvm.sh" >> /root/.bashrc 28 | RUN npm install -g yarn 29 | 30 | # 设置工作目录 31 | WORKDIR /workspace 32 | -------------------------------------------------------------------------------- /.cursor/environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "Dockerfile", 4 | "context": "." 5 | }, 6 | "user": "root", 7 | "install": "./.cursor/install.sh", 8 | "start": "", 9 | "terminals": [ 10 | { 11 | "name": "dev", 12 | "command": "cd /workspace && npm run dev || echo '没有找到 dev 脚本,请查看 package.json'", 13 | "description": "启动前端开发服务器" 14 | }, 15 | { 16 | "name": "test", 17 | "command": "cd /workspace && npm run test || echo '没有找到 test 脚本,请查看 package.json'", 18 | "description": "运行测试" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /.cursor/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "开始安装依赖..." 5 | 6 | # 确保工作目录存在 7 | if [ ! -d "/workspace" ]; then 8 | echo "错误: 工作目录 /workspace 不存在" 9 | exit 1 10 | fi 11 | 12 | # 进入工作目录 13 | cd /workspace 14 | echo "当前目录: $(pwd)" 15 | 16 | # 如果存在 package.json 则安装依赖 17 | if [ -f "package.json" ]; then 18 | echo "检测到 package.json,开始安装依赖" 19 | 20 | # 优先使用 yarn,如果失败则使用 npm 21 | if command -v yarn &> /dev/null; then 22 | echo "使用 yarn 安装依赖" 23 | yarn install --frozen-lockfile || npm install 24 | else 25 | echo "使用 npm 安装依赖" 26 | npm install 27 | fi 28 | else 29 | echo "未检测到 package.json,跳过依赖安装" 30 | fi 31 | 32 | # 查看安装的 node 和 npm 版本 33 | node -v 34 | npm -v 35 | 36 | echo "安装依赖完成" 37 | 38 | # 构建项目(如果需要) 39 | # npm run build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .history/ 8 | # testing 9 | /coverage 10 | /ts/node_modules 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Current TS File", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/ts/node_modules/ts-node/dist/bin.js", 9 | "args": [ 10 | "${relativeFile}" 11 | ], 12 | "cwd": "${workspaceRoot}", 13 | "protocol": "inspector" 14 | }, 15 | { 16 | 17 |             "type": "chrome", 18 |             "request": "launch", 19 |             "name": "Launch Chrome against localhost", 20 |             "url": "http://localhost:8080", 21 |             "file": "${workspaceFolder}/index.html", 22 |             "sourceMaps": true, 23 |             "webRoot": "${workspaceFolder}" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /JavaScript/Promise/2.js: -------------------------------------------------------------------------------- 1 | new Promise((resolve, reject) => { 2 | reject(1) 3 | }).then(r => { 4 | return 2; 5 | }).finally(m => { 6 | console.log(m); 7 | }); 8 | 9 | new Promise((resolve, reject) => { 10 | reject(1) 11 | }).then(r => { 12 | return new Promise((resolve, reject) => { 13 | resolve(3); 14 | }); 15 | }).finally(m => { 16 | console.log(m); 17 | }); 18 | -------------------------------------------------------------------------------- /JavaScript/Promise/promise1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2018/12/17. 3 | */ 4 | function Promise(fn){ 5 | 6 | let resolveCall = function() {console.log('我是默认的');}; 7 | 8 | this.then = (onFulfilled) => { 9 | resolveCall = onFulfilled; 10 | }; 11 | function resolve(v){ 12 | resolveCall(v); 13 | } 14 | fn(resolve); 15 | } 16 | 17 | new Promise((resolve, reject) => { 18 | setTimeout(_ => { 19 | resolve('success'); 20 | }, 200) 21 | }).then(r => { 22 | console.log(r); 23 | }); -------------------------------------------------------------------------------- /JavaScript/Promise/promise2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2018/12/17. 3 | */ 4 | function Promise(fn){ 5 | 6 | let resolveCall = function() {console.log('我是默认的');}; 7 | 8 | this.then = (onFulfilled) => { 9 | resolveCall = onFulfilled; 10 | }; 11 | function resolve(v){ 12 | setTimeout(_ => { 13 | resolveCall(v); 14 | }); 15 | } 16 | fn(resolve); 17 | } 18 | 19 | new Promise((resolve, reject) => { 20 | resolve('success'); 21 | }).then(r => { 22 | console.log(r); 23 | }); -------------------------------------------------------------------------------- /JavaScript/Promise/promise3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2018/12/17. 3 | */ 4 | function Promise(fn){ 5 | this.resolves = []; 6 | 7 | this.then = (onFulfilled) => { 8 | this.resolves.push(onFulfilled); 9 | return this; 10 | }; 11 | let resolve = (value) =>{ 12 | setTimeout(_ => { 13 | this.resolves.forEach(fn => value = fn(value) || value); 14 | }); 15 | }; 16 | fn(resolve); 17 | } 18 | 19 | new Promise((resolve, reject) => { 20 | resolve('success'); 21 | }).then(r => { 22 | console.log(r); 23 | return r + ' ---> Vchat'; 24 | }).then(r => { 25 | console.log(r); 26 | }); -------------------------------------------------------------------------------- /JavaScript/Promise/promise4.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2018/12/17. 3 | */ 4 | function Promise(fn){ 5 | this.resolves = []; 6 | this.status = 'PENDING'; 7 | this.value; 8 | 9 | this.then = (onFulfilled) => { 10 | if (this.status === 'PENDING') { 11 | this.resolves.push(onFulfilled); 12 | } else if (this.status === 'FULFILLED') { 13 | console.log('isFULFILLED'); 14 | onFulfilled(this.value); 15 | } 16 | return this; 17 | }; 18 | let resolve = (value) =>{ 19 | if (this.status === 'PENDING') { 20 | setTimeout(_ => { 21 | this.status = 'FULFILLED'; 22 | this.resolves.forEach(fn => value = fn(value) || value); 23 | this.value = value; 24 | }); 25 | } 26 | }; 27 | fn(resolve); 28 | } 29 | 30 | let getInfor = new Promise((resolve, reject) => { 31 | resolve('success'); 32 | }).then(r => { 33 | console.log('hahah'); 34 | }); 35 | setTimeout(_ => { 36 | getInfor.then(r => { 37 | console.log(r); 38 | }) 39 | }); 40 | 41 | //现在我们在then方法中return的是他本身,这样一来所有的操作都是在一个Promise上,而Promise的状态又是不能随意转化的,所以都是以第一个Promise的状态为准 -------------------------------------------------------------------------------- /JavaScript/defineProperty/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Document 9 | 10 | 11 |
12 |

hi Vchat!

13 |
14 | 15 |
       16 | 17 | 18 | -------------------------------------------------------------------------------- /JavaScript/defineProperty/1.js: -------------------------------------------------------------------------------- 1 | // let VchatInfo = {}; 2 | // let Input = document.getElementById('input'); 3 | // let introduceDom = document.getElementById('introduce'); 4 | // Object.defineProperty(VchatInfo, "introduce", { 5 | // get: function(){ 6 | // return introduceDom.innerHTML; 7 | // }, 8 | // set: function(introduce){ 9 | // introduceDom.innerHTML = introduce; 10 | // } 11 | // }); 12 | // VchatInfo.introduce = Input.value = "vchat是一个社交聊天系统"; 13 | // Input.oninput = function() { 14 | // VchatInfo.introduce = Input.value; 15 | // console.log(VchatInfo.introduce); 16 | // }; -------------------------------------------------------------------------------- /JavaScript/defineProperty/3.js: -------------------------------------------------------------------------------- 1 | function defineReactive(obj, key, val) { 2 | Object.defineProperty(obj, key, { 3 | enumerable: true, 4 | configurable: true, 5 | get: function() { 6 | console.log('get'); 7 | return val; 8 | }, 9 | set: function(newVal) { 10 | console.log('set'); 11 | val += newVal; 12 | } 13 | }); 14 | } 15 | let obj = {name: '成龙大哥', say: ':其实我之前是拒绝拍这个游戏广告的,'}; 16 | Object.keys(obj).forEach(k => { 17 | defineReactive(obj, k, obj[k]); 18 | }); 19 | console.log(obj.name + obj.say); 20 | 21 | // obj.say = '后来我试玩了一下,哇,好热血,蛮好玩的'; 22 | // console.log(obj.name + obj.say); -------------------------------------------------------------------------------- /JavaScript/es6基础/Generator.md: -------------------------------------------------------------------------------- 1 | ### Generator 2 | * 概述 -------------------------------------------------------------------------------- /JavaScript/es6基础/Iterator.md: -------------------------------------------------------------------------------- 1 | es6引入了[Symbol.iterator]属性,Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器,即iterator对象。同时提供了for...of循环,用来遍历任意具有iterator接口的数据结构。 2 | 3 | 遍历时会调用Iterator的next()方法,每次调用会返回一个有value和done属性的对象。value是当前成员的值,done表示遍历是否结束。这也是遍历器对象的根本特征。原生默认具有iterator接口的数据结构有Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象。对象是不具备iterator接口的,但是可以如下模拟: 4 | ``` javascript 5 | let obj = { 6 | [Symbol.iterator]() { 7 | return { 8 | next: function () { 9 | return { 10 | value: 'hahaha', 11 | done: false 12 | }; 13 | } 14 | }; 15 | } 16 | }; 17 | for (let v of obj) { 18 | console.log(v) // 'hahaha' 19 | } 20 | // 这个例子将会无限循环下去,因为迭代器没有返回结束的信号 21 | 22 | let iterable = { 23 | 0: 'a', 24 | 1: 'b', 25 | 2: 'c', 26 | length: 3, 27 | [Symbol.iterator]: Array.prototype[Symbol.iterator] 28 | }; 29 | for (let v of iterable) { 30 | console.log(v); // 'a', 'b', 'c' 31 | } 32 | // 也可以直接绑定数组的迭代器,或者通过Array.from()将其转换为真正的数组 33 | ``` 34 | 解构赋值、扩展运算、for...of、等方法都会默认调用迭代器接口。对象没有迭代器,但是依然可以进行扩展运算。 -------------------------------------------------------------------------------- /JavaScript/es6基础/async、await.md: -------------------------------------------------------------------------------- 1 | ### 异步遍历器(ES2018) 2 | * 概述 3 | 4 | es2018引入了异步遍历器接口`Symbol.asyncIterator`。调用遍历器的next方法,返回的是一个 Promise 对象。 5 | ``` javascript 6 | asyncIterator 7 | .next() 8 | .then( 9 | ({ value, done }) => /* ... */ 10 | ); 11 | ``` 12 | * for await...of 13 | 14 | 用于遍历异步的 Iterator 接口。 15 | ``` javascript 16 | async function f() { 17 | for await (let x of createAsyncIterable(['a', 'b'])) { 18 | console.log(x); 19 | } 20 | } 21 | // a b 22 | ``` 23 | ### 异步 Generator 函数 24 | 异步Generator,自然返回一个异步遍历器。 25 | ``` javascript 26 | async function* gen() { 27 | yield 'hello'; 28 | } 29 | const genObj = gen(); 30 | genObj.next().then(x => console.log(x)); 31 | // { value: 'hello', done: false } 32 | ``` -------------------------------------------------------------------------------- /JavaScript/js基础/原型和原型链/原型和原型链.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 32 | -------------------------------------------------------------------------------- /JavaScript/js基础/执行原理/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /JavaScript/js基础/执行原理/1.js: -------------------------------------------------------------------------------- 1 | // function bar() { 2 | // console.log(n); 3 | // } 4 | // function foo() { 5 | // var n = 1; 6 | // bar(); 7 | // } 8 | // var n = 2; 9 | // foo(); // 2 10 | 11 | // function foo() { 12 | // function bar() { 13 | // console.log(n); 14 | // } 15 | // var n = 1; 16 | // bar(); 17 | // } 18 | // var n = 2; 19 | // foo(); // 1 20 | 21 | const a = { 22 | a: 1 23 | } 24 | Object.defineProperty(a, 'a', { 25 | // enumerable: false 26 | get() { 27 | return 2 28 | } 29 | }) 30 | // for (let k in a) { 31 | // console.log("k", k); 32 | // } 33 | // console.log(Object.keys(a)) 34 | // console.log("Object.getOwnPropertyNames(a)", Object.getOwnPropertyNames(a)) 35 | // console.log("Reflect.ownKeys(a)", Reflect.ownKeys(a)) 36 | a.a = 3; 37 | console.log(a.a) -------------------------------------------------------------------------------- /JavaScript/js基础/正则/1.js: -------------------------------------------------------------------------------- 1 | // let reg = /12{3,4}5/g; 2 | // console.log('12225'.match(reg)); 3 | 4 | 5 | // let reg = /12[34]5/g; 6 | // console.log('12225'.match(reg)); // null 7 | // console.log('1235'.match(reg)); // [ '1235' ] 8 | 9 | let reg = /2{0,}/g; 10 | // console.log('62225'.match(reg)); 11 | console.log('1'.match(reg)); -------------------------------------------------------------------------------- /JavaScript/notes/this.js: -------------------------------------------------------------------------------- 1 | // const a = { 2 | // c: 1, 3 | // b: function b() { 4 | // console.log(this.c) 5 | // } 6 | // } 7 | // const c = {c: 2}; 8 | // (c.f = a.b)(); // undefined 9 | // c.f(); // 2 10 | // a.b(); // 1 11 | 12 | // const d = { 13 | // b() { 14 | // console.log(this) 15 | // } 16 | // } 17 | // const e = {} 18 | // e.f = d.b; 19 | // e.f(); 20 | // d.b(); 21 | 22 | // const k = { 23 | // b: () => { 24 | // console.log(this) 25 | // } 26 | // } 27 | // const j = {} 28 | // j.f = k.b; 29 | // j.f(); 30 | // k.b(); 31 | 32 | // const h = { 33 | // b() { 34 | // console.log(this) 35 | // } 36 | // } 37 | // const i = {} 38 | // i.f = h.b.bind(h); 39 | // i.f(); 40 | // h.b(); 41 | 42 | /* 43 | 1. 由 new 调用?绑定到新创建的对象。 44 | 2. 显式绑定,由 call 或者 apply(或者 bind)调用?绑定到指定的对象。 45 | 3. 隐式绑定由上下文对象调用?绑定到那个上下文对象。 46 | 4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。 47 | 5. 箭头函数根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。 48 | 这其实和 ES6 之前代码中的 self = this 机制一样。 49 | */ -------------------------------------------------------------------------------- /JavaScript/notes/作用域.js: -------------------------------------------------------------------------------- 1 | /* 2 | 作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。 3 | */ 4 | /* 5 | 赋值操作符会导致 LHS 查询。=操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。 6 | */ 7 | /* 8 | 左查询和右查询都是从当前作用域开始查找,没找到则向上继续查找,没找到最终会找到全局作用域,此时找没找到都会停止查找 9 | */ 10 | /* 11 | 不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛出 ReferenceError 异常(严格模式下)。 12 | */ 13 | function foo(a) { 14 | var b = a; 15 | return a + b; 16 | } 17 | var c = foo( 2 ); 18 | // LHS 3次 c= a= b= 19 | // RHS 4次 foo =a a b 20 | 21 | 22 | /* 词法作用域 */ 23 | /* 24 | 词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段基本能够知道全部标识符在哪里以及是如何声明的,从而能够预测在执行过程中如何对它们进行查找。 25 | */ 26 | /* 27 | 避免使用eval(..) 和 with,都可以动态的修改已有的词法作用域。 28 | 并且他们都是引擎无法在编译时对作用域查找进行优化,因为引擎只能谨慎地认为这样的优化是无效的。使用这其中任何一个机制都将导致代码运行变慢。 29 | */ 30 | 31 | 32 | 33 | /* 34 | 作用域和块作用域 35 | IIFE 36 | try/catch、let、const 37 | */ 38 | 39 | /* 40 | 变量、函数声明提升,函数声明先于变量 41 | foo(); // 1 42 | function foo() { console.log( 1 ); } 43 | var foo = function() { console.log( 2 ); }; 44 | 45 | 只是变量提升优先,赋值是可以成功的 46 | function foo() { console.log( 1 ); } 47 | var foo = function() { console.log( 2 ); }; 48 | foo(); // 2 49 | */ -------------------------------------------------------------------------------- /JavaScript/notes/类型转换.js: -------------------------------------------------------------------------------- 1 | /* 2 | toString 3 | 1. 基本类型值的字符串化规则为:null 转换为 "null",undefined 转换为 "undefined",true转换为 "true"。数字的字符串化则遵循通用规则,极大值或极小值例外(指数)。 4 | 2. 普通对象的 toString/Object.prototype.toString,返回 [[class]] 值, 如"[object Object]",最终用的是 ToPrimitive 方法。 5 | 3. 自己重新定义了 toString ,则用自己定义的。 6 | */ 7 | 8 | /* 9 | JSON.stringify() 10 | 1.如果对象定义了 toJson ,JSON.stringify 会先调用 toJson 得到返回值再将返回值序列化。 11 | 2.第二个参数 replacer,可以传字符串数组或函数,指定序列化时忽略哪些属性或者只要哪些属性。 12 | */ 13 | 14 | 15 | /* 16 | ToNumber 17 | 1. true 转换为 1,false 转换为 0。undefined 转换为 NaN,null 转换为 0。 18 | 2. 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字(ToPrimitive)。 19 | */ -------------------------------------------------------------------------------- /JavaScript/notes/闭包.js: -------------------------------------------------------------------------------- 1 | /* 2 | 当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。 3 | 1. 函数 4 | 2. 作用域外引用未销毁 5 | */ 6 | 7 | // for (var i=1; i<=5; i++) { 8 | // try { 9 | // throw i; 10 | // } catch (j) { 11 | // console.log(j) 12 | // setTimeout( 13 | // function timer() { 14 | // console.log( j ); 15 | // }, 16 | // j*1000 ); 17 | // } 18 | // } 19 | 20 | // for (var i=1; i<=5; i++) { 21 | // let j = i; 22 | // setTimeout( 23 | // function timer() { 24 | // console.log( j ); 25 | // }, 26 | // j*1000 ); 27 | // } 28 | 29 | // for (let i=1; i<=5; i++) { 30 | // setTimeout( 31 | // function timer() { 32 | // console.log( i ); 33 | // }, 34 | // i*1000 ); 35 | // } 36 | 37 | // for (var i=1; i<=5; i++) { 38 | // const j = i; 39 | // setTimeout( 40 | // function timer() { 41 | // console.log( j ); 42 | // }, 43 | // j*1000 ); 44 | // } 45 | 46 | // for (var i=1; i<=5; i++) { 47 | // (function(j) { 48 | // setTimeout( 49 | // function timer() { 50 | // console.log( j ); 51 | // }, 52 | // j*1000 ) 53 | // })(i) 54 | // } -------------------------------------------------------------------------------- /JavaScript/节流和防抖/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 35 | -------------------------------------------------------------------------------- /JavaScript/节流和防抖/debounce.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc 函数防抖 3 | * @param fn Function 事件处理函数 4 | * @param delay Number 设定延时时间 5 | * @param isImmediate Boolean 事件触发时是否立刻执行 6 | */ 7 | function debounce(fn, delay, isImmediate) { 8 | var timer = null; 9 | return function() { 10 | var that = this; 11 | var args = [].slice.call(arguments); 12 | var callNow = !timer && isImmediate; 13 | 14 | if(timer) clearTimeout(timer); 15 | 16 | // delay执行 17 | timer = setTimeout(function() { 18 | timer = null; 19 | if(!isImmediate) fn.apply(that, args); 20 | }, delay); 21 | // 立即执行 22 | if(callNow) fn.apply(that, args); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /JavaScript/节流和防抖/throttle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc 函数节流 3 | * @param fn Function 事件处理函数 4 | * @param delay Number 设定执行时间 5 | */ 6 | function throttle(fn, delay) { 7 | var timer = null; 8 | var prev = +new Date(); 9 | return function() { 10 | var that = this; 11 | var args = [].slice.call(arguments); 12 | var now = +new Date(); 13 | var diff = now - prev; 14 | if(timer) clearTimeout(timer); 15 | if(diff >= delay) { 16 | fn.apply(that, args); 17 | prev = now; 18 | }else { 19 | timer = setTimeout(function() { 20 | fn.apply(that, args); 21 | prev = Date.now(); 22 | timer = null; 23 | }, delay) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /JavaScript/跨域/1.md: -------------------------------------------------------------------------------- 1 | ## 同源策略 2 | 如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。 3 | * 同源策略会隔离不同源的 DOM、页面数据和网络通信,进而实现 Web 页面的安全性。 4 | * 页面中可以引用第三方资源,不过这也暴露了很多诸如 XSS 的安全问题,因此又在这种开放的基础之上引入了 CSP 来限制其自由程度。 5 | * 使用 XMLHttpRequest 和 Fetch 都是无法直接进行跨域请求的,因此浏览器又在这种严格策略的基础之上引入了跨域资源共享策略,让其可以安全地进行跨域操作。 6 | * 两个不同源的 DOM 是不能相互操纵的,因此,浏览器中又实现了跨文档消息机制,让其可以比较安全地通信。 -------------------------------------------------------------------------------- /JavaScript/跨域/download.js: -------------------------------------------------------------------------------- 1 | const downloadQr = () => { 2 | // 下载跨域问题(同源图片可以直接下载) 3 | const src = 'https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png'; 4 | const canvas = document.createElement('canvas'); 5 | const img = document.createElement('img'); 6 | img.onload = function() { 7 | canvas.width = img.width; 8 | canvas.height = img.height; 9 | const context = canvas.getContext('2d'); 10 | context.drawImage(img, 0, 0, img.width, img.height); 11 | canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height); 12 | canvas.toBlob((blob) => { 13 | const link = document.createElement('a'); 14 | link.href = window.URL.createObjectURL(blob); 15 | link.download = 'aaa'; 16 | link.click(); 17 | }, 'image/jpeg'); 18 | }; 19 | img.setAttribute('crossOrigin', 'anonymous'); 20 | img.src = src; 21 | }; -------------------------------------------------------------------------------- /JavaScript/跨域/jsonp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2019/1/26. 3 | */ 4 | const jsonp = function ({ url, params, callback }) { 5 | const script = document.createElement('script'); 6 | return new Promise((resolve, reject) => { 7 | window[callback] = function (data) { 8 | resolve(data); 9 | document.body.removeChild(script); 10 | }; 11 | params = { ...params, callback }; 12 | const arrs = []; 13 | Object.keys(params).forEach((k) => { 14 | arrs.push(`${k}=${params[k]}`); 15 | }); 16 | script.src = `${url}?${arrs.join('&')}`; 17 | document.body.appendChild(script); 18 | }); 19 | }; 20 | 21 | if (typeof module !== 'undefined' && !module.nodeType && module.exports) { 22 | module.exports = jsonp; 23 | } -------------------------------------------------------------------------------- /JavaScript/面试总结/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /JavaScript/面试总结/强制类型转换.md: -------------------------------------------------------------------------------- 1 | ## Boolean 转换 2 | |数据类型|转换为 true|转换为 false| 3 | | --- | --- | --- | 4 | |Boolean|true|false| 5 | |String|非空字符串|''(空字符串)| 6 | |Number|非 0 (包括无穷大、负无穷大)|+0/0/-0 和 NaN| 7 | |Object|任何对象|无(不考虑 null)| 8 | |undefined|无|undefined| 9 | |null|无|null| 10 | 11 | ## Number 转换 12 | |数据类型|转换为 number| 13 | | --- | --- | 14 | |Boolean|0/1| 15 | |null|0| 16 | |undefined|NaN| 17 | |String|1.字符串为纯数字,如:'123','012','0.12',忽略前置 0;
2.空字符串,转成 0;
3.包含有效的十六进制,如:'0xf',转成对应的十进制;('0xf' == 15)
4.其他的均为 NaN;| 18 | |Object|如果是对象,优先调用对象的 valueOf() 方法,然后判断其是否能转换成数值;
如果转换结果是 NaN 或者拿到的不是原始值,继续调用对象的 toString() 方法,再次依规则转换;
| 19 | 20 | ## == 转换规则 21 | * 有一个操作数是布尔值,在比较之前先将其转换为数值,0/1; 22 | * 如果一个是字符串,另一个是数值,先将字符串转为数值; 23 | * 如果其中一个是对象,另一个不是,则调用对象的 valueOf() 方法,用得到的基本类型值(得不到就调用 toString()),再进行比较; 24 | * null 和 undefined 相等; 25 | * 比较相等性之前,不用将 null 和 undefined 转换成其他值,如:null == false // false; 26 | * 如果其中一个是 NaN,直接返回 false,因为就连 NaN 也不等于 NaN; 27 | * 如果两个操作数都是对象,先比较引用,看是否是同一个对象;是就返回 true,否则 false; 28 | 29 | 思考:[] == ![] 30 | 31 | ## 其它 32 | ``` javascript 33 | 0.2 + 0.1 === 0.3 // false js 采用 IEEE754 精度 34 | // js 机器精度/误差范围 Number.EPSILON 即 2^-52 35 | isNaN('a') // true 判断是否是一个数 36 | Number.isNaN('a') // false 判断是否是NaN 37 | 38 | // JSON.stringify(-0) 返回 "0",而 JSON.parse("-0") 返回 -0 39 | // 第二个参数 40 | var a = { 41 | b: 42, 42 | c: "42", 43 | d: [1,2,3] 44 | }; 45 | JSON.stringify( a, ["b","c"] ); // "{"b":42,"c":"42"}" 46 | JSON.stringify( a, function(k,v){ 47 | if (k !== "c") return v; 48 | } ); 49 | // "{"b":42,"d":[1,2,3]}" 50 | 51 | // a == 2 && a == 3 52 | 53 | 2 == [2]; // true 54 | "" == [null]; // true 55 | 0 == "\n"; // true 56 | ``` 57 | * 如果两边的值中有 true 或者 false,千万不要使用 ==。 58 | * 如果两边的值中有 []、"" 或者 0,尽量不要使用 ==。 59 | * 当然,大多数时候 === 即可避免这些问题 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | 主要是平时学习的代码、及心得文章。 3 | ## 文章列表 4 | ### 【前端发动机】 5 | * [「前端发动机」 js原型和原型链 及 canvas验证码实践](https://github.com/wuyawei/fe-code/blob/master/JavaScript/js%E5%9F%BA%E7%A1%80/%E5%8E%9F%E5%9E%8B%E5%92%8C%E5%8E%9F%E5%9E%8B%E9%93%BE/%E5%8E%9F%E5%9E%8B%E5%92%8C%E5%8E%9F%E5%9E%8B%E9%93%BE.md) 6 | * [「前端发动机」 Vue 组件间通信方式完整版](https://github.com/wuyawei/fe-code/blob/master/vue/%E7%BB%84%E4%BB%B6%E9%97%B4%E9%80%9A%E4%BF%A1/1.md) 7 | * [「前端发动机」 深入 Vue 响应式原理,活捉一个 MVVM](https://github.com/wuyawei/fe-code/blob/master/vue/mvvm/1.md) 8 | * [「前端发动机」原生 JavaScript 实现各种 数组 API 的 Polyfill 版](https://github.com/wuyawei/fe-code/blob/master/tiku/1.md) 9 | * [「前端发动机」从 bind 聊到 curry (柯里化)](https://github.com/wuyawei/fe-code/blob/master/JavaScript/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/1.md) 10 | * [「前端发动机」Touchmove 禁止默认滚动的几种方案](https://github.com/wuyawei/fe-code/blob/master/questions/passive-EventListener.md) 11 | * [「前端发动机」深入 React hooks — useState](https://github.com/wuyawei/fe-code/blob/master/react/hooks/%E6%B7%B1%E5%85%A5%20React%20hooks%20%20%E2%80%94%20%20useState.md) 12 | * [「前端发动机」深入 React hooks — useEffect](https://github.com/wuyawei/fe-code/blob/master/react/hooks/%E6%B7%B1%E5%85%A5%20React%20hooks%20%E2%80%94%20useEffect.md) 13 | * [「前端发动机」深入 React hooks — 原理 & 实现](https://github.com/wuyawei/fe-code/blob/master/react/hooks/%E6%B7%B1%E5%85%A5%20React%20hooks%20%20%E2%80%94%20%E5%8E%9F%E7%90%86%20%26%20%E5%AE%9E%E7%8E%B0.md) 14 | ### 【从头到脚】 15 | * [【从头到脚】撸一个多人视频聊天 — WebRTC 实战(一)](https://github.com/wuyawei/fe-code/blob/master/WebRTC/webrtc1.md) 16 | * [【从头到脚】WebRTC + Canvas 实现一个双人协作的共享画板](https://github.com/wuyawei/fe-code/blob/master/WebRTC/webrtc2.md) 17 | * [【从头到脚】前端实现多人视频聊天— WebRTC 实战(多人篇)](https://github.com/wuyawei/fe-code/blob/master/WebRTC/webrtc3.md) 18 | 19 | ## 公众号 20 | 感兴趣的同学可以关注下我的公众号 **前端发动机**,好玩又有料。 21 | 22 | ![](https://user-gold-cdn.xitu.io/2019/7/21/16c14d1d0f3be11e?w=400&h=400&f=jpeg&s=34646) 23 | -------------------------------------------------------------------------------- /WebRTC/iceServer.json: -------------------------------------------------------------------------------- 1 | { 2 | "iceServers": [ 3 | { 4 | "url": "stun:stun.l.google.com:19302" 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /WebRTC/webrtc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 13 | 14 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /browser/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 |
1
11 | 32 |
test
33 | 34 | 35 | -------------------------------------------------------------------------------- /browser/3.md: -------------------------------------------------------------------------------- 1 | ## HTTP 协议 2 | ### HTTP/0.9 3 | * 只有一个请求行,并没有HTTP 请求头和请求体 4 | * 服务器也没有返回头信息 5 | * 返回的文件内容都是 ASCII 字符流 6 | ### HTTP/1.0 7 | * 引入请求头和响应头 8 | * 状态码、缓存、客户端信息(都存在于请求头和响应头) 9 | ### HTTP/1.1 10 | * 改进持久连接,keep-alive。它的特点是在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直保持。目前浏览器中对于同一个域名,默认允许同时建立 6 个 TCP 持久连接。 11 | * cookie等 12 | 13 | ## xss 攻击 14 | ### 存储型 XSS 攻击 15 | * 多为编辑、输入文字时关键字过滤不全,存储到服务器,从而影响到用户。 16 | ### 反射型 XSS 攻击 17 | * 通过地址请求将参数传给服务器,服务器又返回给用户,所以不要随便乱点链接。 18 | * Web 服务器不会存储反射型 XSS 攻击的恶意脚本,这是和存储型 XSS 攻击不同的地方。 19 | ### 基于 DOM 的 XSS 攻击 20 | * 通过网络劫持在页面传输过程中修改 HTML 页面的内容,这种劫持类型很多,有通过 WiFi 路由器劫持的,有通过本地恶意软件来劫持的,它们的共同点是在 Web 资源传输过程或者在用户使用页面的过程中修改 Web 页面的数据。 21 | * 不涉及 web 服务器 22 | ### 防止 xss 攻击 23 | 1. 服务器对输入脚本进行过滤或转码。 24 | 2. 充分利用 CSP 25 | > CSP指的是内容安全策略,为了缓解很大一部分潜在的跨站脚本问题,浏览器的扩展程序系统引入了内容安全策略(CSP)的一般概念。 26 | * 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的; 27 | * 禁止向第三方域提交数据,这样用户数据也不会外泄; 28 | * 禁止执行内联脚本和未授权的脚本; 29 | * 提供上报机制,这样可以帮助我们尽快发现有哪些 XSS 攻击,以便尽快修复问题。 30 | * 使用 31 | * 服务器返回 Content-Security-Policy HTTP头部。或者设置 meta 标签。 32 | ```html 33 | 34 | ``` 35 | 3. 使用 HttpOnly 属性,有效保护 cookie。 36 | ## CSRF 攻击 37 | > CSRF 跨站点请求伪造(Cross—Site Request Forgery)。通过伪造用户登录信息,骗取服务器的信任。 38 | ### 防止 CSRF 攻击 39 | * 验证 HTTP Referer 字段 40 | * 请求地址中添加 token 并验证 41 | ## HTTPS 42 | * 对称加密 43 | * 非对称加密 44 | * 混合加密 45 | * 数字证书 -------------------------------------------------------------------------------- /browser/beforeRequest/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 请求拦截 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /browser/beforeRequest/background.js: -------------------------------------------------------------------------------- 1 | chrome.webRequest.onBeforeRequest.addListener( 2 | function(details) { 3 | // 需要替换的 url 4 | return {redirectUrl: chrome.extension.getURL(details.url)}; 5 | }, 6 | { 7 | urls: ["*://*"], //你要拦截的url地址 8 | types: ["script"] //拦截类型为script, 9 | }, 10 | ["blocking"] //类型blocking为拦截, 11 | 12 | ); -------------------------------------------------------------------------------- /browser/beforeRequest/content-script/resource-inject.js: -------------------------------------------------------------------------------- 1 | /* global chrome*/ 2 | 3 | // noinspection JSUnresolvedVariable 4 | chrome.runtime.onMessage.addListener(function(request) { // background返回的回调消息 5 | console.log('request', request) 6 | const addScript = (link) => { 7 | const theScript = document.createElement('script'); 8 | theScript.src = link; 9 | document.documentElement.appendChild(theScript); 10 | }; 11 | const addStyle = (link) => { 12 | const d = document.createElement('link'); 13 | d.href = link; 14 | d.rel="stylesheet"; 15 | document.documentElement.appendChild(d); 16 | }; 17 | if (request.type === 'fetch-resource') { 18 | console.log('fetch-resource'); 19 | request.contentType === "stylesheet" 20 | ? request.links.forEach(addStyle) 21 | : request.links.forEach(addScript) 22 | } 23 | }); -------------------------------------------------------------------------------- /browser/beforeRequest/icon-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/browser/beforeRequest/icon-default.png -------------------------------------------------------------------------------- /browser/beforeRequest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beforeRequest", 3 | "version": "0.1.0", 4 | "manifest_version": 2, 5 | "description": "请求拦截", 6 | "browser_action": { 7 | "default_icon": { 8 | "160": "icon-default.png" 9 | } 10 | }, 11 | "background": { 12 | "page": "background.html" 13 | }, 14 | "permissions": [ 15 | "webRequest", 16 | "http://*/*", 17 | "https://*/*", 18 | "webRequestBlocking", 19 | "tabs" 20 | ], 21 | "content_scripts": [{ 22 | "matches": [ 23 | "http://*/*", 24 | "https://*/*" 25 | ], 26 | "js": [ 27 | "content-script/resource-inject.js" 28 | ] 29 | }] 30 | } -------------------------------------------------------------------------------- /fe-performance/cache.md: -------------------------------------------------------------------------------- 1 | ### 前端缓存 -------------------------------------------------------------------------------- /fe-performance/image.md: -------------------------------------------------------------------------------- 1 | ## 图片优化 2 | > 不同业务场景下选择不一样的图片类型 3 | ### jpg 4 | > jpg 是有损压缩,相对体积较小。适用于大的背景图、轮播图或 Banner 图。但是处理矢量图形或字体等线条感强的图形时,会有明显模糊感,且不支持透明度。 5 | ### png 6 | > png 分为PNG-8 与 PNG-24,特点:无损压缩、质量高、体积大、支持透明。应用场景一般是logo 7 | 8 | > 一个二进制位表示两种颜色(0|1 对应黑|白),如果一种图片格式对应的二进制位数有 n 个,那么它就可以呈现 2^n 种颜色。 9 | 10 | > 8 位的 PNG 最多支持 256 种颜色,而 24 位的可以呈现约 1600 万种颜色。 11 | ### svg 12 | > SVG(可缩放矢量图形)是一种基于 XML 语法的图像格式。它和其它图片种类有着本质的不同:SVG 对图像的处理不是基于像素点,而是是基于对图像的形状描述。特点:文本文件、体积小、不失真、兼容性好 13 | ### Base64 14 | > Base64 并非一种图片格式,而是一种浏览器能识别的编码方式。 15 | 所以是以文本形式存在,可以直接放在代码里,从而减少http请求,但是不适用于大图,因为本身base64就已经编译成原体积的4/3了 16 | ### 雪碧图 17 | > 雪碧图 也叫图像精灵,是小图标的解决方案,减少http请求 18 | ### WebP 19 | > 是 Google 专为 Web 开发的一种旨在加快图片加载速度的图片格式,它支持有损压缩和无损压缩。他有其他图片的各种优点,甚至支持动图。但是现阶段浏览器兼容性太低。 -------------------------------------------------------------------------------- /fe-performance/ssr.md: -------------------------------------------------------------------------------- 1 | ## 服务端渲染 2 | ### 客户端渲染 3 | * vue 等 spa 页面一般就是典型的客户端渲染,页面上只有很少的html内容,都靠 JavaScript 加载。 4 | ### 为什么使用服务端渲染 (SSR)? 5 | 优点 6 | * 更利于 seo - 搜索引擎抓取网页内容 7 | * 加快首屏渲染速度,不需要等到所有的JavaScript脚本加载完成 8 | 限制 9 | * 服务器资源消耗 10 | * 需要node server 11 | * 浏览器特定的代码,只能在某些生命周期钩子函数 (lifecycle hook) 中使用 12 | -------------------------------------------------------------------------------- /fe-performance/timIng.md: -------------------------------------------------------------------------------- 1 | ## http 请求的时间线各个时间指标的优化 2 | * 排队(Queuing)时间过久 3 | 排队时间过久,大概率是由浏览器为 **每个域名** 最多维护 6 个连接导致的。 4 | > 可以将资源分布在多个域名下,或者把站点升级到 HTTP2,因为 HTTP2 已经没有每个域名最多维护 6 个 TCP 连接的限制了。 5 | * 第一字节时间(TTFB)时间过久 6 | 服务器生成页面数据的时间过久。 7 | 网络的原因。 8 | 发送请求头时带上了多余的用户信息(服务器处理需要时间)。 9 | > 面对第一种服务器的问题,你可以想办法去提高服务器的处理速度,比如通过增加各种缓存的技术;针对第二种网络问题,你可以使用 CDN 来缓存一些静态文件;至于第三种,你在发送请求时就去尽可能地减少一些不必要的 Cookie 数据信息。 10 | * Content Download 时间过久 11 | 单个请求的 Content Download 花费了大量时间,有可能是字节数太多的原因导致的。 12 | > 可以减少文件大小,比如压缩、去掉源码中不必要的注释等方法。 13 | ## 性能指标 14 | * 首次内容绘制 (First Contentful Paint, FCP) 15 | 白屏时间 16 | * 首次有效绘制 (First Meaningful Paint, FMP) 17 | 首屏渲染完成 18 | * 可交互时间 (Time to Interactive, TTI) 19 | js 等加载完成 20 | ## 关键渲染路径 21 | > 优先显示与用户操作相关的内容 22 | ## DOMContentLoaded 23 | > 以下 js,默认指 同步加载的情况。且都指代普通脚本,非 ES modules。 24 | * 没有外链 css 和 js 的情况下,DOMContentLoaded 即 html 的加载和解析时间 25 | * 只有外链 css 的时候,会等 css 加载完成并解析成 Cssom,最终和 Dom 树一起生成布局树,不阻塞 Dom 解析,影响 DOMContentLoaded 响应。 26 | * 有外链 css 和 js 的时候,因为 js 可能会操作 dom 及其 css,所以浏览器会等外链 css 加载并解析完后,才会执行 js;所以尽管 js 加载和 css 加载是同时开始的,但就算 js 先下载完,也会被阻塞,而不是立即执行;等 js 执行完,dom 继续解析; 27 | * 只有外链 js 的情况下,js 的加载和执行也会阻塞 dom 的解析。多个 script 标签会并行下载,但是会依次执行。script 只会影响在 它后面的 Dom 解析。script 执行完,Dom 也会重绘或重排。同样也说明浏览器生成 Dom 树,不需要等所有 Dom 解析完成,如果没有 css 阻塞,会优先绘制不受 script 影响的 Dom。 28 | * script 标签设置成 async 时,该 js 的下载不会阻塞 dom 解析,下载完后立即执行,但是也不会影响 DOMContentLoaded 完成。 29 | * script 标签设置成 defer 时,该 js 下载不会阻塞 dom 解析,dom 解析完后执行该 js,js 执行完后,DOMContentLoaded 才会完成。 30 | * 对于 ES modules 模块脚本,默认 defer,如果被设置成 async,该模块及其依赖的所有模块,都将并行下载,并尽快解析和执行。 -------------------------------------------------------------------------------- /fe-performance/前端异常处理.md: -------------------------------------------------------------------------------- 1 | ### 常见的前端异常 2 | * js 语法错误,代码异常 3 | * ajax 等接口请求异常 4 | * 静态资源加载异常 5 | * 跨域脚本异常 Script error 6 | * promise 异常 7 | ### 常见处理 8 | > let a = '11 ,类似于此种语法错误,目前测试似乎都捕捉不到,不过语法错误一般开发就发现了,不会留到线上。 9 | > **直接在控制台输入和在 script 标签中测试结果不一致,以下结果为 script 标签处** 10 | 11 | * try-catch 12 | try-catch 只能捕获到同步的运行时错误,对语法和异步错误却无能为力,捕获不到。而且,我们也很难给所有的代码都加上 try catch。 13 | * window.onerror 14 | 只能绑定一个回调函数,如果想在不同文件中想绑定不同的回调函数,window.onerror 显然无法完成。 15 | window.onerror return true 可以阻止控制台抛出错误。 16 | 捕捉不到静态资源加载异常。 17 | * addEventListener 18 | 可以捕捉到静态资源加载的异常 19 | ``` javascript 20 | /** 21 | * @param event 事件名 22 | * @param function 回调函数 23 | * @param useCapture 回调函数是否在捕获阶段执行,默认是false,在冒泡阶段执行 24 | */ 25 | window.addEventListener('error', (event) => { 26 | // addEventListener 回调函数的离散参数全部聚合在 error 对象中 27 | // 上报错误 28 | }, true) 29 | 30 | // aa 31 | // setTimeout(() => { 32 | // aa; 33 | // }); 34 | ``` 35 | * unhandledrejection 36 | 当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。 37 | 当一个 Promise 错误最初未被处理,但是稍后又得到了处理,则会触发rejectionhandled事件。 38 | ```javascript 39 | window.addEventListener('rejectionhandled', (event) => { 40 | console.log(event) 41 | }, true); 42 | window.addEventListener('unhandledrejection', (event) => { 43 | console.log(event) 44 | }, true); 45 | let a = Promise.reject('eee'); 46 | setTimeout(() => { 47 | a.catch(() => '456'); 48 | }) 49 | ``` -------------------------------------------------------------------------------- /node/koa/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /node/koa/businesscard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/koa/businesscard.pdf -------------------------------------------------------------------------------- /node/koa/certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIChTCCAe4CCQDFzpcEW1s77zANBgkqhkiG9w0BAQsFADCBhjELMAkGA1UEBhMC 3 | Q04xDzANBgNVBAgMBkJlamluZzEVMBMGA1UEBwwMG1tBG1tCQmVqaW5nMQ0wCwYD 4 | VQQKDAR3dXl3MQ0wCwYDVQQLDAR3dXl3MQ0wCwYDVQQDDAR3dXl3MSIwIAYJKoZI 5 | hvcNAQkBFhN3MjQ5OTExNTM2NkAxMjYuY29tMB4XDTE5MDEyNjA5MzgwMFoXDTE5 6 | MDIyNTA5MzgwMFowgYYxCzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAZCZWppbmcxFTAT 7 | BgNVBAcMDBtbQRtbQkJlamluZzENMAsGA1UECgwEd3V5dzENMAsGA1UECwwEd3V5 8 | dzENMAsGA1UEAwwEd3V5dzEiMCAGCSqGSIb3DQEJARYTdzI0OTkxMTUzNjZAMTI2 9 | LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAq6KAGmwmnsjHbaihYjfC 10 | 2Oexhf+7JwGl7274TTZKO39HeU/WCA+IDwh9NextnYiM8YTJe+xZzuk9kjbVSHKM 11 | ECobFFg/j/6+rm0+1aPVb2/TRp9+DS9+dHbJZU19Gy95Gq93Ea/pzU9AxzdgKYny 12 | GrbQ0yMqppJM+va7RVwrHqkCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCIMKL3he17 13 | T6rSnOaxWz1WlWLyjf4FHJPtfaAxg2wIWAo1AofprhuZLNL41wwoZHF36+8oijju 14 | MJqtTp0rpFrDP4VvVaIcF0sbsXiYABv+wjPje+ObzXQyt0thMmaoZmGrOn7XrHUR 15 | N9LlQZCe3IxRO3409NMpPaIQXL5pwjylyg== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /node/koa/certrequest.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB8zCCAVwCAQAwgYYxCzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAZCZWppbmcxFTAT 3 | BgNVBAcMDBtbQRtbQkJlamluZzENMAsGA1UECgwEd3V5dzENMAsGA1UECwwEd3V5 4 | dzENMAsGA1UEAwwEd3V5dzEiMCAGCSqGSIb3DQEJARYTdzI0OTkxMTUzNjZAMTI2 5 | LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAq6KAGmwmnsjHbaihYjfC 6 | 2Oexhf+7JwGl7274TTZKO39HeU/WCA+IDwh9NextnYiM8YTJe+xZzuk9kjbVSHKM 7 | ECobFFg/j/6+rm0+1aPVb2/TRp9+DS9+dHbJZU19Gy95Gq93Ea/pzU9AxzdgKYny 8 | GrbQ0yMqppJM+va7RVwrHqkCAwEAAaAsMBMGCSqGSIb3DQEJAjEGDAR3dXl3MBUG 9 | CSqGSIb3DQEJBzEIDAYxMjM0NTYwDQYJKoZIhvcNAQELBQADgYEApqKhg4GLPDW4 10 | 7jOZDauA5DyuX/1VoF5ieqgtxa9jLDo8a4n1HuHO8J+zEV0EYnGrYMYtk0OYc5IT 11 | aaHnHzrLbGyWVF6BHg5pq1eOmAMfdd95QjBrTs/IEHbctsDI5vZ33MJGt/N1sHAA 12 | jCemuA5tS2yWWsAy71zryTOLMqoMTo4= 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /node/koa/fn.js: -------------------------------------------------------------------------------- 1 | let a = 1; 2 | const setA = () => ++a; 3 | const b = {b: 1}; 4 | const setB = () => b.b++; 5 | module.exports = { 6 | a, 7 | setA, 8 | b, 9 | setB 10 | } -------------------------------------------------------------------------------- /node/koa/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/koa/google.png -------------------------------------------------------------------------------- /node/koa/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/koa/image.png -------------------------------------------------------------------------------- /node/koa/kkk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/koa/kkk.png -------------------------------------------------------------------------------- /node/koa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-demo", 3 | "version": "1.0.0", 4 | "description": "server-demo", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "nodemon server.js" 8 | }, 9 | "keywords": [ 10 | "koa", 11 | "async" 12 | ], 13 | "author": "wuyw", 14 | "license": "Apache-2.0", 15 | "repository": { 16 | "type": "git", 17 | "url": "" 18 | }, 19 | "dependencies": { 20 | "dom-to-image": "^2.6.0", 21 | "handlebars": "^4.7.7", 22 | "html-pdf": "^3.0.1", 23 | "jsdom": "^16.5.3", 24 | "koa": "^2.0.0", 25 | "koa-router": "^10.0.0", 26 | "koa-send": "^5.0.0", 27 | "koa-static": "^5.0.0", 28 | "node-canvas": "^2.7.0", 29 | "node-html-to-image": "^3.1.0", 30 | "nodemon": "^2.0.7", 31 | "pdf2pic": "^2.1.4", 32 | "pdfcrowd": "^5.1.1", 33 | "pem": "^1.14.1", 34 | "puppeteer": "^9.1.1", 35 | "puppeteer-cluster": "^0.22.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /node/koa/privatekey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQCrooAabCaeyMdtqKFiN8LY57GF/7snAaXvbvhNNko7f0d5T9YI 3 | D4gPCH017G2diIzxhMl77FnO6T2SNtVIcowQKhsUWD+P/r6ubT7Vo9Vvb9NGn34N 4 | L350dsllTX0bL3kar3cRr+nNT0DHN2ApifIattDTIyqmkkz69rtFXCseqQIDAQAB 5 | AoGAE9SOoH7mZ2ojQJQ/MS3oIU+Dv0uwv+z80xErNyCSqxUlmOqUfBHvUm1UqipK 6 | 7+ya/DyvWtYP+kwQhRg4xEoXzIXgxQUZZOM9u6rS8SEremJvBnJ6KEGZZEdYe89i 7 | PipINJsQM20jXN4lil0evY4EYtfuFxeCaT9bNtDASlr39VECQQDSVZGq2l7F6lin 8 | nkkdtC5Lcgwt0gQIekgk65/zyKcmoLtMisgUsuwiPv0LsCfywlCvW796K5PG37XR 9 | MPVkWBQzAkEA0OYBDxawUkzdzLTV8+uUybmBzB+0XkWQO8wVaf+6EY4qiML052SF 10 | b3YTG46l4CXjTrHUliXcPDKBMTNi7RAFswJBAKnvMnp8ZXe3x5rdOquCCKkx4n8e 11 | n92Nlj4mFJwssEd4Mvs7YT2/9SISC/Xmg9ARiIi1MrVtAb76vXXe/VPuCZsCQDH8 12 | d5Bmu3ZWElTjiLa/TnEexKJVi9FrD+JfKVid6qY8wIzNKdEcypkrRdM4RRTnvAae 13 | ouHy2UpGQ4fEZg2X4eUCQDRaZVj/9nWpppBhkRERZz8wzxMYKbjrAmjyUFdT7ffK 14 | OeUMwGEVGIARPzvkSWBzFFuPmBj0kg2sjDtm9uHKDbo= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /node/koa/public/1.css: -------------------------------------------------------------------------------- 1 | div{ 2 | color: blue; 3 | } -------------------------------------------------------------------------------- /node/koa/public/jsonp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2019/1/26. 3 | */ 4 | const jsonp = function ({url, params, callback}) { 5 | let script = document.createElement('script'); 6 | return new Promise((resolve, reject) => { 7 | window[callback] = function (data) { 8 | resolve(data); 9 | document.body.removeChild(script); 10 | }; 11 | params = {...params, callback}; 12 | let arrs = []; 13 | for (let key in params) { 14 | arrs.push(`${key}=${params[key]}`); 15 | } 16 | script.src = `${url}?${arrs.join('&')}`; 17 | document.body.appendChild(script); 18 | }); 19 | }; 20 | 21 | if (typeof module !== 'undefined' && !module.nodeType && module.exports) { 22 | module.exports = jsonp; 23 | } 24 | console.log('123') 25 | const dom = document.querySelector('.red') 26 | dom.style.color='red'; 27 | dom.style.marginLeft = '100px'; -------------------------------------------------------------------------------- /node/koa/public/module.js: -------------------------------------------------------------------------------- 1 | export let a = 1; 2 | export const setA = () => { 3 | ++a; 4 | }; 5 | -------------------------------------------------------------------------------- /node/koa/test-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/koa/test-1.png -------------------------------------------------------------------------------- /node/koa/test.js: -------------------------------------------------------------------------------- 1 | const {a, setA, b, setB} = require('./fn'); 2 | console.log("b", b) 3 | console.log("a", a); 4 | setTimeout(() => { 5 | setA(); 6 | console.log("a", a); 7 | setB(); 8 | console.log("b", b) 9 | }, 1000); -------------------------------------------------------------------------------- /node/xlsx/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /node/xlsx/1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/xlsx/1.xlsx -------------------------------------------------------------------------------- /node/xlsx/2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/node/xlsx/2.xlsx -------------------------------------------------------------------------------- /node/xlsx/index.js: -------------------------------------------------------------------------------- 1 | const XLSX = require('xlsx'); 2 | const fs = require('fs'); 3 | const workbook = XLSX.readFile('./2.xlsx', {}); 4 | const sheetNames = workbook.SheetNames; 5 | const worksheet = workbook.Sheets[sheetNames[0]]; 6 | const obj = {} 7 | const map = {A: '学校名称(中文)', B: '学校名称(英文)', C: '学校简介', D: '地理位置', E: '世界排名', F: '专业名称', G: '专业排名', H: '专业简介', I: '专业方向', J: '申请截止日期', K: '录取/入学人数', L: '学费(年)', M: '申请费', N: '平均薪资(年)', O: '就业率', P: '全职教师', Q: '师生比例' } 8 | for(let k in worksheet) { 9 | if (k.indexOf('!') === -1) { 10 | for (let i = 2; i < 66; i++) { 11 | if (k.match(/[a-zA-Z]+(\d+)/)[1] == i) { 12 | if(!obj[i]) { 13 | obj[i] = {} 14 | } 15 | obj[i][map[k.match(/([a-zA-Z]+)/)[1]]] = worksheet[k].v 16 | } 17 | } 18 | } 19 | } 20 | fs.writeFile('./1.json', JSON.stringify(obj), { 'flag': 'a' }, function(err) { 21 | if (err) { 22 | throw err; 23 | } 24 | }); -------------------------------------------------------------------------------- /node/xlsx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xlsx", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "xlsx": "^0.15.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /other/qa.md: -------------------------------------------------------------------------------- 1 | 1. 华为 mate9 自带浏览器不支持 es6 、fetch 及 css 的 flex语法(注:transform 支持) 2 | 2. 关于禁止 touchmove 滚动的几种处理方式 3 | 3. 移动端路由动画(translateY(0) 和 top 0 位置不一致及移动端状态栏影响,顶部出现下沉闪烁现象,暂用position: fixed 处理) 4 | 4. 楼层跳转滚动事件监听兼容性(document.body.scrollTop/document.documentElement.scrollTop)、元素可视区域计算、导航联动的附加数据处理。 5 | 5. 组件拆分需要更细致,有相同功能的 hooks 组合在一起(state/effect等) 6 | 6. ui 细节问题需要注意 -------------------------------------------------------------------------------- /other/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 |
26 | 27 | 28 |
主区域
29 |
30 | 31 | -------------------------------------------------------------------------------- /questions/ModalHelperFn.js: -------------------------------------------------------------------------------- 1 | // 解决滚动穿透问题 2 | /* 3 | 前置条件 4 | body.modal-open{ 5 | position: fixed; 6 | width: 100%; 7 | } 8 | */ 9 | export const ModalHelperFn = function (bodyCls) { 10 | let scrollTop; 11 | return { 12 | afterOpen() { 13 | scrollTop = document.scrollingElement.scrollTop; 14 | document.body.classList.add(bodyCls); 15 | document.body.style.top = `${-scrollTop}px`; 16 | }, 17 | beforeClose() { 18 | document.body.classList.remove(bodyCls); 19 | document.scrollingElement.scrollTop = scrollTop; 20 | document.body.style.top = ''; 21 | } 22 | }; 23 | }; 24 | 25 | // scrollingElement-polyfill 26 | export const scrollingElementPolyfill = function () { 27 | if (document.scrollingElement) { 28 | return; 29 | } 30 | let element = null; 31 | function scrollingElement() { 32 | if (element) { 33 | return element; 34 | } if (document.body.scrollTop) { 35 | // speed up if scrollTop > 0 36 | return (element = document.body); 37 | } 38 | const iframe = document.createElement('iframe'); 39 | iframe.style.height = '1px'; 40 | document.documentElement.appendChild(iframe); 41 | const doc = iframe.contentWindow.document; 42 | doc.write('
x
'); 43 | doc.close(); 44 | const isCompliant = doc.documentElement.scrollHeight > doc.body.scrollHeight; 45 | iframe.parentNode.removeChild(iframe); 46 | return (element = isCompliant ? document.documentElement : document.body); 47 | } 48 | Object.defineProperty(document, 'scrollingElement', { 49 | get: scrollingElement 50 | }); 51 | }; -------------------------------------------------------------------------------- /questions/notes.md: -------------------------------------------------------------------------------- 1 | ### Todo 2 | * 虚拟滚动 3 | 固定高度和不固定高度 4 | * 移动端兼容问题 5 | * ios: input 自动唤起键盘不能有延迟(promise/setTimeout) input.focus() 必须在事件触发中(如; touchStart)写才有效 且调用 focus 的input 不能是临时创建的 6 | * ios: 键盘弹起,底部固定菜单被遮挡(只能保证input在视口,全屏input+底部菜单会有此问题) 7 | * android: 部分机型垂直居中问题 8 | * 滚动穿透问题 9 | * 红米部分机型 overflow-hidden 异常诡异的问题,盒子内定位元素会被诡异的隐藏掉(无解决办法,不使用overflow-hidden就正常了) 10 | * sticky 实现网易云歌单列表滚动动画 11 | * redux/react-redux/dva/redux-saga 12 | * react-router-dom 13 | * react 14 | * vue3.0 15 | * webpack 16 | * http 17 | * ... -------------------------------------------------------------------------------- /questions/useModalPenetration.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useMemo } from 'react'; 2 | import { ModalHelperFn, scrollingElementPolyfill } from './ModalHelperFn'; 3 | 4 | // 处理 滚动穿透 hooks 5 | export default function useModalPenetration(flag, className = 'modal-open') { 6 | const ModalHelper = useMemo(() => ModalHelperFn(className), [className]); 7 | useEffect(() => { 8 | // 兼容 scrollingElement 9 | scrollingElementPolyfill(); 10 | }, []); 11 | useEffect(() => { 12 | if (flag) { 13 | ModalHelper.afterOpen(); 14 | } else { 15 | ModalHelper.beforeClose(); 16 | } 17 | }, [flag]); 18 | } 19 | -------------------------------------------------------------------------------- /react/core-old/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 简易版 React Demo 7 | 44 | 45 | 46 |
47 | 48 | 49 | 65 | 66 | -------------------------------------------------------------------------------- /react/core-old/element.js: -------------------------------------------------------------------------------- 1 | class Element { 2 | constructor(type, props) { 3 | this.type = type; 4 | this.props = props; 5 | } 6 | } 7 | 8 | const createElement = (type, props, ...children) => { 9 | props = props || {}; 10 | props.children = children; 11 | return new Element(type, props); 12 | } 13 | 14 | export { Element }; 15 | export default createElement; -------------------------------------------------------------------------------- /react/core-old/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 检查是否有先前运行的服务器进程 4 | pkill -f "node server.js" 2>/dev/null 5 | 6 | # 等待一会儿确保进程被终止 7 | sleep 1 8 | 9 | # 启动 Node.js 服务器 10 | # cd /Users/wuyawei02/Desktop/wuyw/fe-code/react/core-old && ./start.sh 11 | echo "启动简易版 React 演示服务器..." 12 | echo "请在浏览器访问: http://localhost:3000/demo.html" 13 | node server.js -------------------------------------------------------------------------------- /react/core-old/types.js: -------------------------------------------------------------------------------- 1 | // 定义React元素类型常量 2 | 3 | export const types = { 4 | // 元素类型 5 | ELEMENT_TEXT: 'TEXT_ELEMENT', // 文本元素 6 | ELEMENT_NATIVE: 'NATIVE_ELEMENT', // 原生DOM元素 7 | ELEMENT_COMPONENT: 'COMPONENT_ELEMENT', // 自定义组件元素 8 | 9 | // 更新类型 10 | UPDATE_PROPS: 'UPDATE_PROPS', // 更新属性 11 | UPDATE_TEXT: 'UPDATE_TEXT', // 更新文本内容 12 | UPDATE_CHILDREN: 'UPDATE_CHILDREN', // 更新子元素 13 | 14 | // 操作类型 15 | PLACEMENT: 'PLACEMENT', // 新增节点 16 | UPDATE: 'UPDATE', // 更新节点 17 | DELETION: 'DELETION' // 删除节点 18 | }; -------------------------------------------------------------------------------- /react/demo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // ./node_modules/.bin/eslint --init 2 | // 初始化 eslint 配置文件 3 | module.exports = { 4 | "env": { 5 | "browser": true, 6 | "es2021": true 7 | }, 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:react/recommended" 11 | ], 12 | "parserOptions": { 13 | "ecmaFeatures": { 14 | "jsx": true 15 | }, 16 | "ecmaVersion": 12, 17 | "sourceType": "module" 18 | }, 19 | "plugins": [ 20 | "react" 21 | ], 22 | "rules": { 23 | "semi": ["error", 'always'] 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /react/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /react/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@redux-saga/core": "^1.1.3", 7 | "antd": "^3.19.3", 8 | "antd-mobile": "^2.3.1", 9 | "better-scroll": "^1.15.2", 10 | "node-sass": "^4.12.0", 11 | "ramda": "^0.26.1", 12 | "react": "^16.8.6", 13 | "react-dom": "^16.8.6", 14 | "react-modal": "^3.8.1", 15 | "react-router-dom": "^5.0.1", 16 | "react-scripts": "3.0.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "eslint": "^5.16.0", 41 | "eslint-config-airbnb": "^18.2.1", 42 | "eslint-plugin-react": "^7.21.5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /react/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/react/demo/public/favicon.ico -------------------------------------------------------------------------------- /react/demo/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /react/demo/src/About/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | // import Modal from 'react-modal'; 3 | 4 | function useMousePostion () { 5 | const [position, setPosition] = useState({x:0, y:0}); 6 | 7 | function handlePosition(e) { 8 | setPosition({x: e.clientX, y: e.clientX}); 9 | console.log(`现在的坐标是---> x: ${e.clientX}; y: ${e.clientX}`) 10 | } 11 | 12 | useEffect(() => { 13 | document.addEventListener('mousemove', handlePosition); 14 | document.title = `x: ${position.x}; y: ${position.y}`; 15 | 16 | return () => { 17 | document.removeEventListener('mousemove', handlePosition); 18 | } 19 | }, [position]); 20 | 21 | return position; 22 | } 23 | 24 | function About() { 25 | const {x, y} = useMousePostion(); 26 | const [modalIsOpen, setModalIsOpen] = useState(false); 27 | return ( 28 |
29 |

about

30 |
31 |
32 |
33 | {`现在的坐标是---> x: ${x}; y: ${y}`} 34 |
35 |
36 | {/* */} 37 | {/* setModalIsOpen(false)} 45 | > 46 |

Alert

47 |
48 |

Description goes here.

49 |
50 | 51 |
*/} 52 |
53 | ) 54 | } 55 | export default About; -------------------------------------------------------------------------------- /react/demo/src/App.css: -------------------------------------------------------------------------------- 1 | .one{ 2 | display:inline-block; 3 | position:relative; 4 | width: 100px; 5 | height: 20px; 6 | } 7 | .one::before { 8 | content:''; 9 | position:absolute; 10 | border-radius: 5px 5px 0 0; 11 | top:0; right:0; bottom:0; left:0; 12 | background:#F6DEE9; 13 | z-index:-1; 14 | transform: scaleY(1.95) perspective(12px) rotateX(25deg);; 15 | transform-origin: bottom; 16 | } 17 | .two{ 18 | width: 250px; 19 | height: 55px; 20 | background-color: #F6DEE9; 21 | border-radius: 8px; 22 | } 23 | .box{ 24 | width: 250px; 25 | display: flex; 26 | flex-flow: column; 27 | justify-content: center; 28 | align-items: center; 29 | margin-left: 100px; 30 | } 31 | ul, li{ 32 | margin: 0; 33 | padding: 0; 34 | list-style-type: none; 35 | } -------------------------------------------------------------------------------- /react/demo/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense, lazy } from "react"; 2 | import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom"; 3 | import 'antd-mobile/dist/antd-mobile.css'; 4 | import './App.css'; 5 | const About = lazy(() => import('./About')) 6 | const Game = lazy(() => import('./Game')); 7 | const Calendar = lazy(() => import('./Calendar/index')); 8 | const Touch = lazy(() => import('./Touch')); 9 | const Carousel = lazy(() => import('./Carousel')); 10 | const Test = lazy(() => import('./Test/index')); 11 | const ToScroll = lazy(() => import('./ToScroll/index')); 12 | const Lazy = lazy(() => import('./Lazy')); 13 | const Ability = lazy(() => import('./Ability')); 14 | 15 | function AppRouter() { 16 | return ( 17 | 18 | {/* about 19 |
20 | calendar 21 |
22 | game 23 |
24 | touch 25 |
26 | carousel 27 |
28 | test */} 29 | Loading...}> 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | ); 44 | } 45 | 46 | export default AppRouter; -------------------------------------------------------------------------------- /react/demo/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /react/demo/src/Carousel/index.scss: -------------------------------------------------------------------------------- 1 | .my-carousel { 2 | width: 7.5rem; 3 | overflow: hidden; 4 | // touch-action: none; 5 | } 6 | .my-carousel-item-wrapper { 7 | position: relative; 8 | margin: 0 auto; 9 | white-space: nowrap; 10 | } 11 | .my-carousel-item { 12 | width: 100%; 13 | height: 100%; 14 | color: #fff; 15 | display: inline-block; 16 | transition:ease 0.5s; 17 | } 18 | 19 | .my-carousel-slider-wrapper { 20 | margin: 0.4rem auto 0; 21 | height: 0.12rem; 22 | position: relative; 23 | left: 50%; 24 | transform: translateX(-50%); 25 | display: inline-block; 26 | } 27 | .my-carousel-slider-item { 28 | width: 0.12rem; 29 | height: 0.12rem; 30 | border-radius: 50%; 31 | background-color: #e7ecf6; 32 | margin-right: 0.1rem; 33 | float: left; 34 | } 35 | .my-carousel-slider-item.active { 36 | width: 0.28rem; 37 | height: 0.12rem; 38 | background-color: #424966; 39 | border-radius: 0.06rem; 40 | } 41 | -------------------------------------------------------------------------------- /react/demo/src/Game/index.css: -------------------------------------------------------------------------------- 1 | .square{ 2 | width:50px; 3 | height: 50px; 4 | border: 1px solid #00ccff; 5 | box-sizing: border-box; 6 | text-align: center; 7 | } 8 | .Board{ 9 | display: grid; 10 | grid-template-columns: 50px 50px 50px; 11 | } 12 | .Game{ 13 | display: flex; 14 | } -------------------------------------------------------------------------------- /react/demo/src/Lazy/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/react/demo/src/Lazy/index.css -------------------------------------------------------------------------------- /react/demo/src/Lazy/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, Suspense } from 'react'; 2 | import './index.css'; 3 | let newData = {}; 4 | const cache = {} 5 | const fetch = async() => { 6 | const fn = () => new Promise((resolve, reject) => { 7 | setTimeout(() => resolve({name: 'Jsf'}), 1000); 8 | }) 9 | if (!cache.k) { 10 | cache.k = fn(); 11 | } 12 | newData = await cache.k 13 | cache.k = newData; 14 | return true; 15 | }; 16 | const resource = () => { 17 | if (!cache.k) { 18 | fetch(); 19 | } 20 | console.log("resource -> cache.k", cache.k); 21 | if (cache.k && typeof cache.k.then === 'function') { 22 | throw cache.k 23 | } 24 | return newData; 25 | } 26 | function Board(props) { 27 | // 尝试读取用户信息,尽管该数据可能尚未加载 28 | const user = resource(); 29 | console.log("Board -> user", user) 30 | return

{user.name}

; 31 | } 32 | function Lazy() { 33 | return ( 34 |
35 |
36 |

Lazy

37 |
38 | loading...
}> 39 | 40 | 41 | 42 | ) 43 | } 44 | 45 | export default Lazy; 46 | -------------------------------------------------------------------------------- /react/demo/src/ToScroll/index.scss: -------------------------------------------------------------------------------- 1 | .wrapper{ 2 | height: 500px; 3 | overflow: scroll; 4 | position: relative; 5 | border: 2px solid #eee; 6 | .wrapper-box-item{ 7 | width: 750px; 8 | line-height: 100px; 9 | text-align: center; 10 | background-color: red; 11 | margin-bottom: 20px; 12 | color: #fff; 13 | } 14 | .pullup-wrapper{ 15 | line-height: 50px; 16 | height: 50px; 17 | background-color: #57b2a9; 18 | color: #fff; 19 | text-align: center; 20 | } 21 | } -------------------------------------------------------------------------------- /react/demo/src/core-old/component.js: -------------------------------------------------------------------------------- 1 | class Component { 2 | constructor(props){ 3 | this.props = props; 4 | } 5 | } 6 | 7 | export default Component; -------------------------------------------------------------------------------- /react/demo/src/core-old/element.js: -------------------------------------------------------------------------------- 1 | class Element { 2 | constructor(type, props) { 3 | this.type = type; 4 | this.props = props; 5 | } 6 | } 7 | 8 | const createElement = (type, props, ...children) => { 9 | props = props || {}; 10 | props.children = children; 11 | return new Element(type, props); 12 | } 13 | 14 | export default createElement; -------------------------------------------------------------------------------- /react/demo/src/core-old/index.js: -------------------------------------------------------------------------------- 1 | import createReactUnit from './unit'; 2 | import createElement from './element'; 3 | import Component from './component'; 4 | 5 | const React = { 6 | nextRootIndex: '0', 7 | createElement, 8 | Component 9 | } 10 | const render = (element, container) => { 11 | const createReactUnitInstance = createReactUnit(element); 12 | const markUp = createReactUnitInstance.getMarkUp(React.nextRootIndex); 13 | container.innerHTML = markUp; 14 | } 15 | React.render = render; 16 | export default React; -------------------------------------------------------------------------------- /react/demo/src/hooks/useImmerState.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef, useCallback } from 'react'; 2 | import produce from "immer" 3 | 4 | const useImmerState = (initialValue) => { 5 | const [value, setValue] = useState(initialValue); 6 | const setState = useCallback((callback) => { 7 | const nextState = produce(value, draftState => { 8 | callback(draftState); 9 | }) 10 | setValue(nextState); 11 | }, [value]) 12 | return [value, setState] 13 | } 14 | 15 | export default useImmerState; -------------------------------------------------------------------------------- /react/demo/src/index.css: -------------------------------------------------------------------------------- 1 | @import '~antd/dist/antd.css'; 2 | body { 3 | margin: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /react/demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | console.log("serviceWorker", serviceWorker) 7 | 8 | ReactDOM.render(, document.getElementById('root')); 9 | 10 | // // If you want your app to work offline and load faster, you can change 11 | // // unregister() to register() below. Note this comes with some pitfalls. 12 | // // Learn more about service workers: https://bit.ly/CRA-PWA 13 | // serviceWorker.unregister(); 14 | // import React from './core-old/index'; 15 | // React.render('ohnanan', document.getElementById('root')) 16 | 17 | // const onClick = (e) => { 18 | // alert(e.target.id); 19 | // } 20 | // React.render(React.createElement('div', {id: 'test', onClick}, '点我'), document.getElementById('root')) 21 | 22 | // class Count extends React.Component{ 23 | // constructor(props) { 24 | // super(props); 25 | // this.state = { 26 | // text: '呵呵' 27 | // } 28 | // } 29 | // render() { 30 | // return React.createElement('div', this.props, this.state.text); 31 | // } 32 | // } 33 | // React.render(React.createElement(Count, {id: 'test', onClick}), document.getElementById('root')) 34 | 35 | // import pathToRegexp from "path-to-regexp"; 36 | // const keys = []; 37 | // // exact 全匹配 strict 末尾斜杠是否精确匹配 sensitive 区分大小写 38 | // const regexp = pathToRegexp('/learn/:id/:name/', keys, {end:true, strict: false, sensitive: false}); 39 | // const match = regexp.exec('/Learn/123/peter'); 40 | // console.log("match", match, keys) -------------------------------------------------------------------------------- /react/demo/src/test.js: -------------------------------------------------------------------------------- 1 | let treeData = [ 2 | { 3 | title: '1', 4 | key: '1', 5 | children: [ 6 | { 7 | title: '1-1', 8 | key: '1-1', 9 | children: [ 10 | { 11 | title: '1-1-1', 12 | key: '1-1-1' 13 | } 14 | ] 15 | } 16 | ] 17 | }, 18 | { 19 | title: '2', 20 | key: '2' 21 | } 22 | ] 23 | 24 | let tableData = [ 25 | 26 | ] 27 | 28 | const getParentKey = (key, tree) => { 29 | let parentKey; 30 | for (let i = 0; i < tree.length; i++) { 31 | const node = tree[i]; 32 | if (node.children) { 33 | if (node.children.some(item => item.key === key)) { 34 | parentKey = node.key; 35 | } else if (getParentKey(key, node.children)) { 36 | parentKey = getParentKey(key, node.children); 37 | } 38 | } 39 | } 40 | return parentKey; 41 | }; -------------------------------------------------------------------------------- /react/fiber/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/react/fiber/index.js -------------------------------------------------------------------------------- /react/redux-model/dva/index.js: -------------------------------------------------------------------------------- 1 | const app = dva({ 2 | initialState: { count: 1 }, 3 | }); 4 | 5 | app.model({ 6 | namespace: 'todo', 7 | state: [], 8 | reducers: { 9 | add(state, { payload: todo }) { 10 | // 保存数据到 state 11 | return [...state, todo]; 12 | }, 13 | }, 14 | effects: { 15 | *save({ payload: todo }, { put, call }) { 16 | // 调用 saveTodoToServer,成功后触发 `add` action 保存到 state 17 | yield call(saveTodoToServer, todo); 18 | yield put({ type: 'add', payload: todo }); 19 | }, 20 | }, 21 | subscriptions: { 22 | setup({ history, dispatch }) { 23 | // 监听 history 变化,当进入 `/` 时触发 `load` action 24 | return history.listen(({ pathname }) => { 25 | if (pathname === '/') { 26 | dispatch({ type: 'load' }); 27 | } 28 | }); 29 | }, 30 | }, 31 | }); 32 | 33 | const App = app.start(); 34 | 35 | // 集成了 redux/react-redux/redux-saga/react-router-dom/connected-react-router 36 | 37 | // 完全定义了model层的写法,在 app.start() 方法中,将 model 拆解, 38 | // 并调用 redux 的 createStore 方法 创建 store。其中包括各种中间件、增强器的创建 39 | // 将 effects 拆解组合成 sagas 并调用 redux-saga 的 run 方法启动 40 | // 路由创建 41 | 42 | // 通过 onEffect 创建 model.loading,从而可以拿到所有 effect 的 loading 状态 -------------------------------------------------------------------------------- /react/redux-model/react-redux/connect.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/react/redux-model/react-redux/connect.js -------------------------------------------------------------------------------- /react/redux-model/react-redux/index.js: -------------------------------------------------------------------------------- 1 | // const mapStateToProps = (state, ownProps) => { 2 | // console.log(state); // state 3 | // console.log(ownProps); // ownProps 4 | // } 5 | 6 | // function mapDispatchToProps(dispatch) { 7 | // return { actions: bindActionCreators(actionCreators, dispatch) } 8 | // } 9 | 10 | // connect(mapStateToProps, mapDispatchToProps)(WrappedComponent) 11 | 12 | // connect => mapStateToProps => selectorFactory(dispatch) => mapStateToProps(state) 13 | 14 | // connect 接收 mapStateToProps 后,构建 selectorFactory 函数 15 | // const selectorFactory = (dispatch, options) => (state, props) => mapStateToProps(state); 16 | // 创建 wrapWithConnect 接收 WrappedComponent 17 | // 一系列状态和构建等操作 18 | // 最终将 selectorFactory 组合好的 props 传递给 WrappedComponent 19 | // 之所以能拿到 store 是因为 connect 里引入了同一个 Context 20 | 21 | // dispatch 更新 store 的 state,但是 store 和 react 本身是没有关系的,dispatch 并不会直接触发 react 的更新 22 | // react-redux 通过 subscribeUpdates 订阅了 store 的 state 的更新,即 dispatch 更新 store 后,store 会触发 subscribeUpdates 23 | // 而 subscribeUpdates 会关联 react 的 setState 或 useState,从而触发 react 的更新 24 | // 所以 react-redux 通过发布订阅把 react 和 redux 联系在一起 25 | 26 | // react 的 Subscription 和 store 的 subscribe,被同一个 onStateChange(即 subscribeUpdates)订阅 -------------------------------------------------------------------------------- /react/redux-model/react-redux/mapDispatchToProps.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/react/redux-model/react-redux/mapDispatchToProps.js -------------------------------------------------------------------------------- /react/redux-model/redux-saga/index.js: -------------------------------------------------------------------------------- 1 | // saga.run 接收 effect 或者 all([effect]) 启动 saga (多个 effect 需要 rootSaga 即 all([effect]),每个 effect 需要对应的 take) 2 | // 通过 runEffect 判断 是何种类型的 effect,执行相应的代码 3 | // 通过 takeEvery/takeLatest 不断地监听 action 的派发,从而执行相应的 effect 4 | -------------------------------------------------------------------------------- /react/redux-model/redux/applyMiddleware.js: -------------------------------------------------------------------------------- 1 | import compose from './compose'; 2 | 3 | // 中间件形式 4 | const logger = ({getState, dispatch}) => next => action => { 5 | const result = next(action); 6 | console.log('dispatch', getState()); 7 | return result; 8 | // 最终是 dispatch 的返回值 result = dispatch(...) 9 | // 但是如果某个中间件没有 return,也就拿不到了 10 | } 11 | // 构建中间件 12 | const applyMiddleware = (...middlewares) => createStore => (reducer, ...args) => { 13 | const store = createStore(reducer, ...args); 14 | let dispatch = () => { 15 | throw new Error('不能在构建中间件时派发!!!'); 16 | } 17 | const middlewareAPI = { 18 | getState: store.getState, 19 | dispatch: (actions, ...args) => dispatch(actions, ...args) // 引用 dispatch 20 | } 21 | const chain = middlewares.map(middleware => middlewares(middlewareAPI)); // 初始化中间件链 22 | dispatch = compose(...chain)(store.dispatch); // 构建强化的 dispatch,即中间件的 next,即下一个中间件 23 | // 此时的 dispatch,是 middlewares 的函数从右到左依次将执行返回值(也是函数)作为参数传入的结果 24 | // 执行的时候从左边开始执行,就像洋葱,左边第一个就是最外层的函数,一层一层执行 25 | // 之所以一层层执行的原因是中间件调用了 next,next 就是 compose 时接收的右边的返回函数 26 | return { 27 | ...store, 28 | dispatch 29 | } 30 | } -------------------------------------------------------------------------------- /react/redux-model/redux/bindActionCreators.js: -------------------------------------------------------------------------------- 1 | const bindActionCreator = (actionCreator, dispatch) => { 2 | return (...args) => dispatch(actionCreator.apply(this, args)); 3 | } 4 | 5 | const bindActionCreators = (actionCreators, dispatch) => { 6 | if (typeof actionCreators === 'function') { 7 | return bindActionCreator(actionCreators, dispatch); 8 | } 9 | if (typeof actionCreators !== 'object' || actionCreators === null) { 10 | throw new Error('参数不合法!!!'); 11 | } 12 | const boundActionCreators = {}; 13 | Object.keys(actionCreators).forEach(k => { 14 | const actionCreator = actionCreators[k]; 15 | if (typeof actionCreator === 'function') { 16 | boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); 17 | } 18 | }) 19 | return boundActionCreators; 20 | } -------------------------------------------------------------------------------- /react/redux-model/redux/combineReducers.js: -------------------------------------------------------------------------------- 1 | const combineReducers = (reducers) => { 2 | const resultReducers = {}; 3 | Object.keys(reducers).forEach(K => { 4 | if (typeof reducers[k] === 'function') { 5 | resultReducers[k] = reducers[k]; 6 | } 7 | }); 8 | return (state, action) => { 9 | const newState = {}; 10 | let hasChanged = false; 11 | Object.keys(resultReducers).forEach(k => { 12 | const reducer = resultReducers[k]; 13 | const cacheStateForKey = state[k]; // 缓存上一次的 state for key 14 | const newStateForKey = reducer(cacheStateForKey, action); 15 | if (typeof newStateForKey === 'undefined') { 16 | throw new Error('reducer 必须有默认返回值'); 17 | } 18 | newState[k] = newStateForKey; 19 | hasChanged = hasChanged || newStateForKey !== cacheStateForKey; // hasChanged 判断每个 state 是否有变化,有一个变化则需要更新 20 | }) 21 | hasChanged = hasChanged || Object.keys(resultReducers).length !== Object.keys(state).length; // 判断是否有给了state 默认值,但reducer不合法的情况,有的话用最新的 22 | return hasChanged ? newState : state; 23 | } 24 | } -------------------------------------------------------------------------------- /react/redux-model/redux/compose.js: -------------------------------------------------------------------------------- 1 | const compose = (...fns) => { 2 | if (fns.length === 0) { 3 | return arg => arg; 4 | } 5 | if (fns.length === 1) { 6 | return fns[0]; 7 | } 8 | return fns.reduce((pre, cur) => (...args) => pre(cur(...args))); 9 | } -------------------------------------------------------------------------------- /react/redux-model/redux/index.js: -------------------------------------------------------------------------------- 1 | const createStore = (reducer,initState) => { 2 | let currentState = initState; 3 | let currentListeners = []; 4 | const getState = () => currentState; 5 | const subscribe = (listener) => { 6 | currentListeners.push(listener); 7 | const unSubscribe = () => { 8 | currentListeners = currentListeners.filter(lis => lis !== listener); 9 | } 10 | return unSubscribe; 11 | } 12 | const dispatch = (action) => { 13 | currentState = reducer(currentState, action); 14 | currentListeners.forEach(fn => fn()); 15 | } 16 | return { 17 | getState, 18 | dispatch, 19 | subscribe 20 | } 21 | } -------------------------------------------------------------------------------- /react/redux-model/redux/thunk.js: -------------------------------------------------------------------------------- 1 | const createThunkMiddleware = (extraArgument) => ({dispatch, getState}) => next => action => { 2 | if (typeof action === 'function') { 3 | return action(dispatch, getState, extraArgument); 4 | } 5 | return next(action); 6 | } 7 | 8 | const thunk = createThunkMiddleware(); 9 | thunk.withExtraArgument = createThunkMiddleware; 10 | 11 | 12 | // const api = "http://www.example.com/sandwiches/"; 13 | // const whatever = 42; 14 | // const store = createStore( 15 | // reducer, 16 | // applyMiddleware(thunk.withExtraArgument({ api, whatever })), 17 | // ); 18 | // function fetchUser(id) { 19 | // return (dispatch, getState, { api, whatever }) => { 20 | // // ... 21 | // }; 22 | // } -------------------------------------------------------------------------------- /react/router/BrowserRouter.js: -------------------------------------------------------------------------------- 1 | import Router from './Router'; 2 | import {createBrowserHistory} from './history'; 3 | const BrowserRouter = ({children}) => { 4 | return 5 | } 6 | export default BrowserRouter; -------------------------------------------------------------------------------- /react/router/HashRouter.js: -------------------------------------------------------------------------------- 1 | import Router from './Router'; 2 | import {createHashHistory} from './history'; 3 | const HashRouter = ({children}) => { 4 | return 5 | } 6 | export default HashRouter; -------------------------------------------------------------------------------- /react/router/HistoryContext.js: -------------------------------------------------------------------------------- 1 | import createNamedContext from './createNamedContext'; 2 | const HistoryContext = createNamedContext('Router-History'); 3 | export default HistoryContext; -------------------------------------------------------------------------------- /react/router/Link.js: -------------------------------------------------------------------------------- 1 | import React, {useContext} from 'react'; 2 | import RouterContext from './RouterContext'; 3 | 4 | const resolveToLocation = (to, location) => { 5 | return typeof to === 'function' ? to(location) : to; 6 | } 7 | const isModifiedEvent = (event) => { 8 | return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); 9 | } 10 | const LinkAnchor = ({ 11 | navigate, 12 | onClick, 13 | ...rest 14 | }) => { 15 | let props = { 16 | ...rest, 17 | onClick: event => { 18 | try { 19 | if (onClick) onClick(event); 20 | } catch (ex) { 21 | event.preventDefault(); 22 | throw ex; 23 | } 24 | if ( 25 | !event.defaultPrevented && // 阻止 click 26 | event.button === 0 && // 忽略 左键 27 | (!target || target === "_self") && // 浏览器自己处理 "target=_blank" 等 28 | !isModifiedEvent(event) // 忽略带有 修饰键(ctrl/ALT...) 的点击 29 | ) { 30 | event.preventDefault(); 31 | navigate(); 32 | } 33 | } 34 | }; 35 | return 36 | } 37 | const Link = ({replace, to, component = LinkAnchor, ...rest}) => { 38 | const { history, location } = useContext(RouterContext); 39 | const href = location ? history.createHref(location) : ""; 40 | const props = { 41 | ...rest, 42 | href, 43 | navigate() { 44 | const _location = resolveToLocation(to, location); 45 | const method = replace ? history.replace : history.push; 46 | method(_location); 47 | } 48 | }; 49 | return React.createElement(component, props); 50 | } -------------------------------------------------------------------------------- /react/router/Redirect.js: -------------------------------------------------------------------------------- 1 | import React, {useContext} from 'react'; 2 | import RouterContext from './RouterContext'; 3 | 4 | const resolveToLocation = (to, location) => { 5 | return typeof to === 'function' ? to(location) : to; 6 | } 7 | const Redirect = ({to, push, computedMatch}) => { 8 | const context = useContext(RouterContext); 9 | const method = push ? history.push : history.replace; 10 | const _location = resolveToLocation(to, location); 11 | method(_location); 12 | return null; 13 | } 14 | export default Redirect; -------------------------------------------------------------------------------- /react/router/Route.js: -------------------------------------------------------------------------------- 1 | import React, {useContext} from 'react'; 2 | import RouterContext from './RouterContext'; 3 | import matchPath from './matchPath'; 4 | 5 | const Route = ({computedMatch, component, render, children, ...args}) => { 6 | const context = useContext(RouterContext); 7 | const location = args.location || context.location; 8 | // computedMatch Switch 中计算好的 match 9 | const props = { 10 | ...context, 11 | location, 12 | match: computedMatch ? computedMatch : args.path ? matchPath(location.pathname, args) : context.match 13 | } 14 | // 同步 更新 RouterContext中的 props 15 | return 16 | {props.match 17 | ? children 18 | ? typeof children === "function" 19 | ? children(props) 20 | : children 21 | : component 22 | ? React.createElement(component, props) 23 | : render ? render(props) : null 24 | : null 25 | } 26 | 27 | } 28 | export default Route; -------------------------------------------------------------------------------- /react/router/Router.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from 'react'; 2 | import RouterContext from './RouterContext'; 3 | import HistoryContext from './HistoryContext'; 4 | 5 | const computeRootMatch = (pathname) => { 6 | return { path: "/", url: "/", params: {}, isExact: pathname === "/" }; 7 | } 8 | const Router = ({history, children}) => { 9 | const [location, setlocation] = useState({}) 10 | useEffect(() => { 11 | setlocation(history.location); 12 | const unListhen = history.listhen(({location}) => { 13 | setlocation(location); 14 | }); 15 | return () => { 16 | unListhen && unListhen(); 17 | } 18 | }, []) 19 | return 24 | 25 | 26 | } 27 | 28 | export default Router; -------------------------------------------------------------------------------- /react/router/RouterContext.js: -------------------------------------------------------------------------------- 1 | import createNamedContext from './createNamedContext'; 2 | const RouterContext = createNamedContext('Router'); 3 | export default RouterContext; -------------------------------------------------------------------------------- /react/router/Switch.js: -------------------------------------------------------------------------------- 1 | import React, {useContext} from 'react'; 2 | import RouterContext from './RouterContext'; 3 | import matchPath from './matchPath'; 4 | 5 | const Switch = ({children, ...props}) => { 6 | const context = useContext(RouterContext); 7 | const location = props.location || context.location; 8 | 9 | let element, match; 10 | // 匹配到第一个成功,就停止 11 | React.children.forEach((child) => { 12 | if(!match && React.isValidElement(child)) { 13 | element = child; 14 | // Redirect props.from 15 | const path = child.props.path || child.props.from; 16 | match = path ? matchPath(location.pathname, {...child.props, path}) : context.match; 17 | } 18 | }); 19 | return <> 20 | {match 21 | ? React.cloneElement(element, { location, computedMatch: match }) 22 | : null 23 | } 24 | 25 | } 26 | export default Switch; -------------------------------------------------------------------------------- /react/router/createNamedContext .js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const createNamedContext = name => { 4 | const context = createContext(); 5 | context.displayName = name; 6 | return context; 7 | }; 8 | 9 | export default createNamedContext; -------------------------------------------------------------------------------- /react/router/hook.js: -------------------------------------------------------------------------------- 1 | import React, {useContext} from 'react'; 2 | import RouterContext from './RouterContext'; 3 | import HistoryContext from './HistoryContext'; 4 | import matchPath from './matchPath'; 5 | 6 | export const useHistory = () => { 7 | return useContext(HistoryContext); 8 | } 9 | 10 | export const useLocation = () => { 11 | const {location} = useContext(RouterContext); 12 | return location; 13 | } 14 | 15 | export const useParams = () => { 16 | const {match} = useContext(RouterContext); 17 | return match ? match.params : {}; 18 | } 19 | 20 | export const useRouteMatch = (path) => { 21 | const {match, location} = useContext(RouterContext); 22 | return path ? matchPath(location.pathname, path) : match; 23 | } -------------------------------------------------------------------------------- /react/router/index.js: -------------------------------------------------------------------------------- 1 | // https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/matchPath.js 2 | export {default as Router} from './Router'; 3 | export {default as BrowserRouter} from './BrowserRouter'; 4 | export {default as HashRouter} from './HashRouter'; 5 | export {default as Switch} from './Switch'; 6 | export {default as Redirect} from './Redirect'; 7 | export {default as Route} from './Route'; 8 | export {default as Link} from './Link'; 9 | export { useHistory, useLocation, useParams, useRouteMatch } from "./hooks.js"; -------------------------------------------------------------------------------- /react/router/matchPath.js: -------------------------------------------------------------------------------- 1 | import pathToRegexp from "path-to-regexp"; 2 | 3 | const compilePath = (path, options) => { 4 | // 生成路由规则 path 的正则表达式 5 | // 记录动态路由参数 6 | const keys = []; 7 | const regexp = pathToRegexp(path, keys, options); 8 | const result = { regexp, keys }; 9 | return result; 10 | } 11 | 12 | const matchPath = (pathname, options = {}) => { 13 | // 用于统一匹配多个 path 14 | if (typeof options === "string" || Array.isArray(options)) { 15 | options = { path: options }; 16 | } 17 | // exact 全匹配 strict 末尾斜杠是否精确匹配 sensitive 区分大小写 18 | const { path, exact = false, strict = false, sensitive = false } = options; 19 | const paths = [].concat(path); 20 | return paths.reduce((matched, path) => { 21 | if (!path && path !== "") return null; 22 | // 多个 path , 有一个命中就停止 23 | if (matched) return matched; 24 | // keys 匹配到的动态参数,如 /:id 25 | const { regexp, keys } = compilePath(path, { 26 | end: exact, 27 | strict, 28 | sensitive 29 | }) 30 | // 匹配当前路由 31 | const match = regexp.exec(pathname); 32 | if (!match) return null; // 没有匹配到 33 | const [url, ...values] = match; 34 | const isExact = pathname === url; 35 | if (exact && !isExact) return null; // 非全匹配 36 | return { 37 | path, 38 | url, 39 | isExact, 40 | params: keys.reduce((memo, value, index) => { 41 | // 组合路由动态参数 42 | // {id: '123'...} 43 | memo[value.name] = values[index]; 44 | return memo; 45 | }, {}) 46 | } 47 | }, null) 48 | } 49 | 50 | export default matchPath; -------------------------------------------------------------------------------- /service_worker.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/service_worker.js -------------------------------------------------------------------------------- /tiku/0430.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 3 | 4 | 示例 1: 5 | 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 6 | 输出:[1,2,3,6,9,8,7,4,5] 7 | 8 | 123 9 | 456 10 | 789 11 | 12 | */ 13 | 14 | 15 | 16 | 17 | /* 18 | 给你一个正整数 n ,生成一个包含 1 到 n² 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 19 | 20 | 示例 1: 21 | 输入:n = 3 22 | 输出:[[1,2,3],[8,9,4],[7,6,5]] 23 | 24 | 示例 2: 25 | 输入:n = 1 26 | 输出:[[1]] 27 | 28 | 提示: 29 | 1 <= n <= 20 30 | */ 31 | 32 | 33 | -------------------------------------------------------------------------------- /tiku/2.js: -------------------------------------------------------------------------------- 1 | // 每隔 1s 输出 index,0、1、2、3、4 2 | // for(let i = 0; i < 5; i++) { 3 | // setTimeout(() => { 4 | // console.log(new Date(), i) 5 | // }, i * 1000) 6 | // } 7 | // for(var i = 0; i < 5; i++) { 8 | // (n => setTimeout(() => { 9 | // console.log(new Date(), n) 10 | // }, n * 1000))(i) 11 | // } 12 | 13 | // 间隔 0、1、2、3、4 s 并输出对应值 14 | const p = (n) => { 15 | return new Promise((resolve, reject) => setTimeout(() => { 16 | console.log(new Date(), n) 17 | resolve(n + 1); 18 | }, n * 1000)) 19 | } 20 | function *fn() { 21 | const res = yield p(0); 22 | const res1 = yield p(res); 23 | const res2 = yield p(res1); 24 | const res3 = yield p(res2); 25 | yield p(res3); 26 | } 27 | const co = (fn) => { 28 | const gen = fn(); 29 | const next = (res) => { 30 | const {value, done} = gen.next(res); 31 | if (done) return; 32 | value.then(next); 33 | } 34 | next(); 35 | } 36 | // co(fn) 37 | 38 | // 输入中文字符串,统计中文种类及出现字数 39 | const echocount = (str) => { 40 | const map = {}; 41 | for (let i = 0; i < str.length; i++) { 42 | if(map[str[i]]) { 43 | map[str[i]]++; 44 | } else { 45 | map[str[i]] = 1; 46 | } 47 | } 48 | return map; 49 | } 50 | // console.log("echocount('所产所生的的监察局范德萨')", echocount('所产所生的的监察局范德萨')); 51 | -------------------------------------------------------------------------------- /tiku/async-code/async-fnList.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 有一个 函数数组,里面可能是同步,也可能是异步的 Promise, 3 | * 怎么保持顺序执行并且让前一个函数的返回值作为后一个函数执行的参数。 4 | */ 5 | 6 | const run0 = async (list, result) => { 7 | for (let i = 0; i < list.length; i++) { 8 | const fn = list[i]; 9 | result = await fn(result); 10 | } 11 | console.log("run -> result", result); 12 | return result; 13 | }; 14 | 15 | const run1 = (list) => { 16 | let index = 0; 17 | let result; 18 | const eva = (i, result) => { 19 | const fn = Promise.resolve(list[i](result)); 20 | fn.then((r) => { 21 | console.log("eva -> r", r, i); 22 | if (i >= list.length - 1) return; 23 | eva(++i, r); 24 | }); 25 | }; 26 | eva(index); 27 | }; 28 | 29 | const run2 = (list) => { 30 | let p = Promise.resolve(); 31 | list.forEach((fn) => { 32 | p = p.then((r) => fn(r)); 33 | }); 34 | p.then((r) => { 35 | console.log("r", r); 36 | }); 37 | }; 38 | 39 | const excute = (list) => { 40 | let p = Promise.resolve(); 41 | list.forEach((fn) => { 42 | p = p.then(async (a) => { 43 | return await fn(a); 44 | }); 45 | }); 46 | p.then((r) => { 47 | console.log("r", r); 48 | }); 49 | }; 50 | 51 | const fnList = [ 52 | () => 1, 53 | async (p) => { 54 | await new Promise((resolve) => { 55 | setTimeout(resolve, 3000); 56 | }); 57 | return p + 2; 58 | }, 59 | (p) => p + 2, 60 | (p) => Promise.resolve(p + 3), 61 | (p) => Promise.resolve(p + 4), 62 | (p) => p + 5, 63 | (p) => p, 64 | ]; 65 | 66 | // run(fnList) 67 | excute(fnList); 68 | -------------------------------------------------------------------------------- /tiku/async-code/cache-fetch.js: -------------------------------------------------------------------------------- 1 | const delay = (time) => new Promise((resolve) => setTimeout(resolve, time)); 2 | function fetch(data) { 3 | return delay(3000).then(() => { 4 | console.log(2); 5 | return data; 6 | }); 7 | } 8 | 9 | // 并发缓存同一请求 10 | // 非并发可以继续发 11 | const getData = fn => { 12 | const cache = {}; 13 | return (url) => { 14 | if(cache[url]) { 15 | return cache[url]; 16 | } 17 | cache[url] = fn(url); 18 | Promise.resolve().then(() => { 19 | delete cache[url]; 20 | }) 21 | return cache[url]; 22 | } 23 | } 24 | 25 | const cachePromise = getData(fetch) 26 | 27 | const a = cachePromise('3').then((a) => { 28 | console.log(a); 29 | }); 30 | cachePromise('3').then((a) => { 31 | console.log(a); 32 | }); 33 | 34 | // delay(3000).then(() => { 35 | // cachePromise('get').then((a) => { 36 | // console.log(a); 37 | // }); 38 | // cachePromise('get').then((a) => { 39 | // console.log(a); 40 | // }); 41 | // }) -------------------------------------------------------------------------------- /tiku/async-code/co.js: -------------------------------------------------------------------------------- 1 | function co(fn) { 2 | return new Promise((resolve, reject) => { 3 | const gen = fn(); 4 | const onFulfilled = (res) => { 5 | try { 6 | const value = gen.next(res); 7 | next(value); 8 | } catch (error) { 9 | reject(error); 10 | } 11 | } 12 | const onRejected = (err) => { 13 | try { 14 | const reason = gen.throw(err); 15 | } catch (error) { 16 | reject(error); 17 | } 18 | } 19 | 20 | function next(res) { 21 | if(res.done) return resolve(res.value); 22 | Promise.resolve(res.value).then(onFulfilled, onRejected); 23 | } 24 | onFulfilled(); 25 | }) 26 | } 27 | function* test() { 28 | const e = yield new Promise(resolve => { 29 | setTimeout(() => { 30 | resolve('e'); 31 | }, 1000); 32 | }); 33 | const a = yield Promise.reject('a'); 34 | const d = yield 'd'; 35 | const b = yield Promise.resolve('b'); 36 | const c = yield Promise.resolve('c'); 37 | return [a, b, c, d, e]; 38 | } 39 | co(test).then(r => { 40 | console.log(r) 41 | }) -------------------------------------------------------------------------------- /tiku/async-code/concurrent.js: -------------------------------------------------------------------------------- 1 | /* 2 | run(fetchList, num, callback); 3 | fetchList 接口请求list 4 | num 最大并发数 5 | callback 所有接口返回后,执行callback,并把接口返回值回传 6 | */ 7 | const fetch = (val, time) => { 8 | return () => new Promise((resolve, reject) => { 9 | setTimeout(() => {resolve(val)}, time) 10 | }) 11 | } 12 | const fetchList = [fetch(1, 1000), fetch(2, 2500), fetch(3, 1000), fetch(4, 2000), fetch(5, 1000)]; 13 | // 1, 3, 2, 5, 4 14 | 15 | const run = (list, num = 2, callback) => { 16 | let index = 0; 17 | const res = []; 18 | const excute = () => { 19 | while(index < 2 && list.length) { 20 | const fn = list.shift(); 21 | fn().then(r => { 22 | index--; 23 | res.push(r); 24 | if(index === 0 && !list.length) { 25 | callback(res); 26 | } 27 | excute(); 28 | }) 29 | index++; 30 | } 31 | } 32 | excute(); 33 | } 34 | 35 | run(fetchList, 2, (result) => { 36 | console.log("result", result); 37 | // 1, 3, 2, 5, 4 38 | }) -------------------------------------------------------------------------------- /tiku/async-code/createFlow.js: -------------------------------------------------------------------------------- 1 | /* 2 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 3 | 4 | const subFlow = createFlow([() => delay(1000).then(() => log("c"))]); 5 | 6 | createFlow([ 7 | () => log("a"), 8 | () => log("b"), 9 | subFlow, 10 | [() => delay(1000).then(() => log("d")), () => log("e")], 11 | ]).run(() => { 12 | console.log("done"); 13 | }); 14 | 15 | // 需要按照 a,b,延迟1秒,c,延迟1秒,d,e, done 的顺序打印 16 | */ 17 | var { log } = console; 18 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 19 | 20 | const createFlow = (arr) => { 21 | const queue = arr.flat(); 22 | const run = async (done) => { 23 | done && queue.push(done); 24 | for(let i = 0; i < queue.length; i++) { 25 | const fn = queue[i]; 26 | if (typeof fn.run === 'function') { 27 | await fn.run(); 28 | } else { 29 | await fn(); 30 | } 31 | } 32 | } 33 | return { 34 | run 35 | }; 36 | }; 37 | const createFlow1 = (arr) => { 38 | const queue = arr.flat(); 39 | const run = (done) => { 40 | done && queue.push(done); 41 | let p = Promise.resolve(); 42 | for(let i = 0; i < queue.length; i++) { 43 | const fn = queue[i]; 44 | if (typeof fn.run === 'function') { 45 | p = p.then(() => fn.run()) 46 | } else { 47 | p = p.then(() => fn()); 48 | } 49 | } 50 | return p; 51 | } 52 | return { 53 | run 54 | }; 55 | }; 56 | 57 | 58 | const subFlow = createFlow([() => delay(1000).then(() => log("c"))]); 59 | createFlow([ 60 | () => log("a"), 61 | () => log("b"), 62 | subFlow, 63 | [() => delay(1000).then(() => log("d")), () => log("e")], 64 | ]).run(() => { 65 | console.log("done"); 66 | }); -------------------------------------------------------------------------------- /tiku/async-code/middleware-Task.js: -------------------------------------------------------------------------------- 1 | /* 2 | let task1 = function (next) { 3 | setTimeout(() => { 4 | console.log(1); 5 | next(); 6 | }, 1000); 7 | }; 8 | 9 | let tast2 = function (next, num) { 10 | setTimeout(() => { 11 | console.log(num); 12 | next(); 13 | }, 3000); 14 | }; 15 | 16 | let task = new Task(); 17 | task.add(task1).add(tast2, null, 2); 18 | task.run(); 19 | // 其中 next 表示执行下一个方法 20 | // 一秒后输出1,再过三秒后输出2 21 | */ 22 | 23 | const delay = timeout => new Promise((resolve, reject) => setTimeout(resolve, timeout)); 24 | class Task { 25 | constructor() { 26 | this.queue = []; 27 | } 28 | add(fn, context, ...args) { 29 | this.queue.push((next = () => {}) => fn.bind(context, next, ...args)); 30 | return this; 31 | } 32 | stop(time) { 33 | this.queue.push((next = () => {}) => () => { 34 | console.log(`现在暂停${time}`); 35 | setTimeout(next, time); 36 | }) 37 | return this; 38 | } 39 | run(fn) { 40 | const compose = arr => arr.reduce((pre, cur) => (...args) => pre(cur(...args))); 41 | const task = compose(this.queue)(fn); 42 | task(); 43 | } 44 | } 45 | let task1 = function (next) { 46 | console.log('task1') 47 | setTimeout(() => { 48 | console.log(1); 49 | next(); 50 | }, 1000); 51 | }; 52 | 53 | let tast2 = function (next, num) { 54 | console.log('tast2') 55 | setTimeout(() => { 56 | console.log(num); 57 | next(); 58 | }, 3000); 59 | }; 60 | 61 | let task = new Task(); 62 | task.add(task1).stop(2000).add(tast2, null, 2); 63 | task.run(() => { 64 | console.log('done'); 65 | }); 66 | -------------------------------------------------------------------------------- /tiku/async-code/person.js: -------------------------------------------------------------------------------- 1 | // new Person('lisi').say('hello').sleep(5000).age(10).getAge() 2 | // 输出 3 | // lisi 4 | // hello 5 | // 等待5s 6 | // getAge 输出 10 7 | 8 | class Person{ 9 | constructor(name) { 10 | console.log("Person -> constructor -> name", name); 11 | } 12 | say(val) { 13 | console.log("Person -> say -> val", val); 14 | return this; 15 | } 16 | sleep(time) { 17 | const pre = Date.now(); 18 | while(Date.now() - pre <= time) { 19 | 20 | } 21 | return this; 22 | } 23 | age(age) { 24 | this.age=age; 25 | return this; 26 | } 27 | getAge() { 28 | console.log(this.age); 29 | } 30 | } 31 | 32 | new Person('lisi').say('hello').sleep(5000).age(10).getAge() -------------------------------------------------------------------------------- /tiku/design-pattern/desc.md: -------------------------------------------------------------------------------- 1 | ### 如何写出好的代码 2 | * 从哪些方面去评价代码的好坏? 3 | * 可读性 4 | * 可扩展性 5 | * 可维护 6 | * 易用性 7 | * 易复用性 8 | * 简洁性 9 | * 灵活性 ... 10 | 多个方面综合评价一段代码,而不是单一维度。 11 | * 从哪些方面去提高编写好代码的能力? 12 | * 设计思想 13 | * 设计模式 14 | * 设计原则 15 | * 编码规范 16 | * 重构技巧 17 | 个人来看,相对于其他标准,代码的可读性会更加重要。写给机器看的代码简单,写给人看的代码难。 18 | 19 | ### 什么是面向对象编程? 20 | > 面向对象编程是一种编程范式或者编程风格,以类或对象作为代码编写的基本单元,以封装、抽象、继承、多态作为代码设计实现的基石。类和对象,是函数和数据的集合。 21 | * 封装 22 | 把数据和方法封装成有限的接口供外部访问,隐私信息进行隐藏。保护数据不被随意修改,提高可维护性和易用性。 23 | * 抽象 24 | * 继承 25 | 子类继承父类的数据和方法。 26 | * 多态 27 | 子类可以对父类方法进行覆盖或重载,提高代码的可复用和可扩展性 -------------------------------------------------------------------------------- /tiku/design-pattern/index.js: -------------------------------------------------------------------------------- 1 | // 单例模式 new Vue 2 | // 策略模式 if else --> map 3 | // 代理模式 (虚拟代理、缓存代理、保护代理) img 懒加载loading占位 4 | // 迭代器模式 5 | // 发布订阅模式 event-emitter 6 | // 命令模式 7 | // 组合模式 组合模式除了要求组合对象和叶对象拥有相同的接口之外,还有一个必要条件,就是对一组叶对象的操作必须具有一致性。 8 | // 享元模式 9 | // 装饰者模式(AOP) 表单验证,先验证后提交 10 | // 状态模式 11 | // 适配器模式 12 | 13 | 14 | // 单一职责原则 15 | // 开闭原则 :当需要改变一个程序的功能或者给这个程序增加新功 能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。 -------------------------------------------------------------------------------- /tiku/leetCode/102-二叉树的层次遍历.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。 3 | 4 |   5 | 6 | 示例: 7 | 二叉树:[3,9,20,null,null,15,7], 8 | 9 | 3 10 | / \ 11 | 9 20 12 | / \ 13 | 15 7 14 | 返回其层次遍历结果: 15 | 16 | [ 17 | [3], 18 | [9,20], 19 | [15,7] 20 | ] 21 | 22 | */ 23 | 24 | /** 25 | * Definition for a binary tree node. 26 | * function TreeNode(val) { 27 | * this.val = val; 28 | * this.left = this.right = null; 29 | * } 30 | */ 31 | /** 32 | * @param {TreeNode} root 33 | * @return {number[][]} 34 | */ 35 | // 迭代 36 | var levelOrder = function(root) { 37 | if(root === null) return []; 38 | const result = []; 39 | const queue = [root]; 40 | while(queue.length) { 41 | // 用 currLevel 做出队 42 | // 因为 queue 会继续添加子节点,length 会变 43 | // 44 | let currLevel = queue.length; 45 | const currNodes = []; 46 | while(currLevel > 0) { 47 | const node = queue.shift(); 48 | currNodes.push(node.val); 49 | if(node.left) { 50 | queue.push(node.left); 51 | } 52 | if(node.right) { 53 | queue.push(node.right); 54 | } 55 | currLevel--; 56 | } 57 | result.push(currNodes); 58 | } 59 | return result; 60 | }; 61 | 62 | 63 | // 递归 64 | var levelOrder = function(root) { 65 | if(root === null) return []; 66 | const result = []; 67 | const travers = (node, res, idx) => { 68 | if(node === null) return; 69 | if(!res[idx]) { 70 | res[idx] = []; 71 | } 72 | res[idx++].push(node.val); 73 | travers(node.left, res, idx); 74 | travers(node.right, res, idx); 75 | } 76 | travers(root, result, 0); 77 | return result; 78 | }; -------------------------------------------------------------------------------- /tiku/leetCode/104-二叉树的最大深度.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个二叉树,找出其最大深度。 3 | 4 | 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 5 | 6 | 说明: 叶子节点是指没有子节点的节点。 7 | 8 | 示例: 9 | 给定二叉树 [3,9,20,null,null,15,7], 10 | 11 | 3 12 | / \ 13 | 9 20 14 | / \ 15 | 15 7 16 | 返回它的最大深度 3 。 17 | 18 | */ 19 | 20 | /** 21 | * Definition for a binary tree node. 22 | * function TreeNode(val) { 23 | * this.val = val; 24 | * this.left = this.right = null; 25 | * } 26 | */ 27 | const findDeep = (root) => { 28 | let maxLen = 0; 29 | let level = 0; 30 | const travers = (node) => { 31 | if(!node) { 32 | maxLen = Math.max(level, maxLen) 33 | } 34 | level++ 35 | travers(node.left) 36 | travers(node.right) 37 | level-- 38 | } 39 | travers(root) 40 | return maxLen 41 | } 42 | 43 | 44 | 45 | /** 46 | * @param {TreeNode} root 47 | * @return {number} 48 | */ 49 | var maxDepth = function(root) { 50 | let len = 0; 51 | let level = 0; 52 | const travers = node => { 53 | if(!node) { 54 | len = Math.max(len, level); 55 | return; 56 | }; 57 | level++; 58 | travers(node.left); 59 | travers(node.right); 60 | level--; 61 | } 62 | travers(root); 63 | return len; 64 | }; -------------------------------------------------------------------------------- /tiku/leetCode/107-二叉树的层次遍历2.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 3 | 4 | 例如: 5 | 给定二叉树 [3,9,20,null,null,15,7], 6 | 7 | 3 8 | / \ 9 | 9 20 10 | / \ 11 | 15 7 12 | 返回其自底向上的层次遍历为: 13 | 14 | [ 15 | [15,7], 16 | [9,20], 17 | [3] 18 | ] 19 | 20 | */ 21 | 22 | /** 23 | * Definition for a binary tree node. 24 | * function TreeNode(val) { 25 | * this.val = val; 26 | * this.left = this.right = null; 27 | * } 28 | */ 29 | /** 30 | * @param {TreeNode} root 31 | * @return {number[][]} 32 | */ 33 | var levelOrderBottom = function(root) { 34 | if(root === null) return []; 35 | const queue = [root]; 36 | const result = []; 37 | while(queue.length) { 38 | let level = queue.length; 39 | let res = []; 40 | while(level > 0) { 41 | const node = queue.shift(); 42 | res.push(node.val); 43 | if(node.left) { 44 | queue.push(node.left); 45 | } 46 | if(node.right) { 47 | queue.push(node.right); 48 | } 49 | level--; 50 | } 51 | // 开头添加 52 | result.unshift(res); 53 | } 54 | return result; 55 | }; -------------------------------------------------------------------------------- /tiku/leetCode/112-路径总和.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=112 lang=javascript 3 | * 4 | * [112] 路径总和 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * Definition for a binary tree node. 10 | * function TreeNode(val) { 11 | * this.val = val; 12 | * this.left = this.right = null; 13 | * } 14 | */ 15 | /** 16 | * @param {TreeNode} root 17 | * @param {number} sum 18 | * @return {boolean} 19 | */ 20 | var hasPathSum1 = function(root, sum) { 21 | let _sum = 0; 22 | let has = false; 23 | const computedSum = (node) => { 24 | if(node === null) return; 25 | _sum += node.val; 26 | computedSum(node.left); 27 | computedSum(node.right); 28 | if(node.left === null && node.right === null && _sum === sum) { 29 | has = true; 30 | } 31 | if(!has){ 32 | _sum -= node.val; 33 | } 34 | } 35 | computedSum(root); 36 | return has; 37 | }; 38 | var hasPathSum = function(root, sum) { 39 | if(root === null) return false; 40 | sum -= root.val; 41 | if(root.left === null && root.right === null) { 42 | return sum === 0; 43 | } else { 44 | return hasPathSum(root.left, sum) || hasPathSum(root.right, sum); 45 | } 46 | }; 47 | 48 | -------------------------------------------------------------------------------- /tiku/leetCode/113-路径总和二.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=113 lang=javascript 3 | * 4 | * [113] 路径总和 II 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * Definition for a binary tree node. 10 | * function TreeNode(val) { 11 | * this.val = val; 12 | * this.left = this.right = null; 13 | * } 14 | */ 15 | /** 16 | * @param {TreeNode} root 17 | * @param {number} sum 18 | * @return {number[][]} 19 | */ 20 | var pathSum = function(root, sum) { 21 | const targetArr = []; 22 | const computedArr = []; 23 | const selectArr = (node) => { 24 | if(node === null) return; 25 | computedArr.push(node.val); 26 | selectArr(node.left); 27 | selectArr(node.right); 28 | if(node.left === null && node.right === null && computedArr.reduce((pre, cur) => pre + cur) === sum) { 29 | targetArr.push([...computedArr]); 30 | } 31 | computedArr.pop(); 32 | } 33 | selectArr(root); 34 | return targetArr; 35 | }; 36 | // @lc code=end 37 | 38 | -------------------------------------------------------------------------------- /tiku/leetCode/138-复制带随机指针的链表.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=138 lang=javascript 3 | * 4 | * [138] 复制带随机指针的链表 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * // Definition for a Node. 10 | * function Node(val, next, random) { 11 | * this.val = val; 12 | * this.next = next; 13 | * this.random = random; 14 | * }; 15 | */ 16 | 17 | /** 18 | * 难点在于 random 的处理 19 | * 通过 map 的映射记录 node -> copyNode 之间的关系 20 | * @param {Node} head 21 | * @return {Node} 22 | */ 23 | var copyRandomList = function(head) { 24 | if(head === null) return head; 25 | let origin = head; 26 | let copyHead = new Node(0); 27 | let copy = copyHead; 28 | const copyMap = new Map(); 29 | // 先拷贝一份 30 | while(origin) { 31 | let copyNode = new Node(origin.val); 32 | copyNode.next = origin.next; 33 | // 记录每个原来的节点对应的拷贝节点 34 | copyMap.set(origin, copyNode); 35 | 36 | copy.next = copyNode; 37 | origin = copyNode.next; 38 | copy = copyNode; 39 | } 40 | origin = head; 41 | copy = copyHead.next; 42 | // 处理random 43 | while(origin){ 44 | if(origin.random) { 45 | copy.random = copyMap.get(origin.random); 46 | } else { 47 | copy.random = null; 48 | } 49 | origin = origin.next; 50 | copy = copy.next; 51 | } 52 | return copyHead.next; 53 | }; 54 | // @lc code=end 55 | 56 | -------------------------------------------------------------------------------- /tiku/leetCode/1436-旅行终点站.js: -------------------------------------------------------------------------------- 1 | // 给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。 2 | 3 | // 题目数据保证线路图会形成一条不存在循环的线路,因此恰有一个旅行终点站。 4 | 5 | 6 | 7 | // 示例 1: 8 | 9 | // 输入:paths = [["London","New York"],["New York","Lima"],["Lima","Sao Paulo"]] 10 | // 输出:"Sao Paulo" 11 | // 解释:从 "London" 出发,最后抵达终点站 "Sao Paulo" 。本次旅行的路线是 "London" -> "New York" -> "Lima" -> "Sao Paulo" 。 12 | // 示例 2: 13 | 14 | // 输入:paths = [["B","C"],["D","B"],["C","A"]] 15 | // 输出:"A" 16 | // 解释:所有可能的线路是: 17 | // "D" -> "B" -> "C" -> "A". 18 | // "B" -> "C" -> "A". 19 | // "C" -> "A". 20 | // "A". 21 | // 显然,旅行终点站是 "A" 。 22 | // 示例 3: 23 | 24 | // 输入:paths = [["A","Z"]] 25 | // 输出:"Z" 26 | 27 | 28 | var destCity = function(paths) { 29 | const result = paths.find(p => paths.every(v => v[0] !== p[1])); 30 | if (result) { 31 | return result[1]; // 返回目标城市 32 | } 33 | }; -------------------------------------------------------------------------------- /tiku/leetCode/15-3数之和.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 3 | 4 | 注意:答案中不可以包含重复的三元组。 5 | 6 |   7 | 8 | 示例: 9 | 10 | 给定数组 nums = [-1, 0, 1, 2, -1, -4], 11 | 12 | 满足要求的三元组集合为: 13 | [ 14 | [-1, 0, 1], 15 | [-1, -1, 2] 16 | ] 17 | 18 | */ 19 | 20 | /** 21 | * @param {number[]} nums 22 | * @return {number[][]} 23 | */ 24 | var threeSum = function(nums) { 25 | const res = []; 26 | if(!nums || nums.length < 3) return res; 27 | // 排序 28 | nums = nums.sort((a,b) => a-b); 29 | for(let i =0; i< nums.length; i++) { 30 | // 去重 31 | if(i>0 && nums[i] === nums[i-1]) continue; 32 | let l = i + 1; 33 | let r = nums.length - 1; 34 | while(l < r) { 35 | const sum = nums[i] + nums[l] + nums[r]; 36 | if(sum > 0) { 37 | r--; 38 | } else if(sum < 0) { 39 | l++; 40 | } else { 41 | res.push([nums[i], nums[l], nums[r]]); 42 | // 去重 43 | while(nums[l] === nums[++l]); 44 | while(nums[r] === nums[--r]); 45 | } 46 | } 47 | } 48 | return res; 49 | }; -------------------------------------------------------------------------------- /tiku/leetCode/160-两个链表的第一个公共节点.js: -------------------------------------------------------------------------------- 1 | // 输入两个链表,找出它们的第一个公共节点。 2 | 3 | /** 4 | * Definition for singly-linked list. 5 | * function ListNode(val) { 6 | * this.val = val; 7 | * this.next = null; 8 | * } 9 | */ 10 | 11 | /** 12 | * 原理是由于共享一段路程。所以A=a+c+b,而B=b+c+a,所以假设是个环路,则当俩指针在这环形链表不断移动时,最后会相遇。 13 | 思路:快慢指针同时走,当其中一个到达自己的尽头时,就切换到另一个所在链表头节点。有相交,会相等,没有则最终都会指向null,也相等。 14 | * @param {ListNode} headA 15 | * @param {ListNode} headB 16 | * @return {ListNode} 17 | */ 18 | var getIntersectionNode = function(headA, headB) { 19 | if(!headA || !headB) return null; 20 | let node1 = headA; 21 | let node2 = headB; 22 | while(node1 !== node2) { 23 | node1 = node1 === null ? headB : node1.next; 24 | node2 = node2 === null ? headA : node2.next; 25 | } 26 | return node1; 27 | }; -------------------------------------------------------------------------------- /tiku/leetCode/172-阶乘后的尾数0.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个整数 n,返回 n! 结果尾数中零的数量。 3 | 4 | 示例 1: 5 | 6 | 输入: 3 7 | 输出: 0 8 | 解释: 3! = 6, 尾数中没有零。 9 | 示例 2: 10 | 11 | 输入: 5 12 | 输出: 1 13 | 解释: 5! = 120, 尾数中有 1 个零. 14 | 说明: 你算法的时间复杂度应为 O(log n) 。 15 | 16 | // 2*5 或者 2和5的倍数 乘积有0 17 | // 换言之 有多少5的倍数 就有多少 0 18 | // 求n的阶乘中,5的倍数出现了多少次 19 | // 22! 中 5、10、15、20 20 | // 30! 中 5、10、15、20、25、30,30整除5后倍数6也大于5,会多一个0 21 | 22 | /** 23 | * @param {number} n 24 | * @return {number} 25 | */ 26 | var trailingZeroes = function(n) { 27 | let index = 0; 28 | while(n >= 5) { 29 | index += n / 5 | 0; 30 | n /= 5; 31 | } 32 | return index; 33 | }; 34 | 35 | 36 | /** 37 | * @param {number} n 38 | * @return {number} 39 | */ 40 | // 错误解法 数字太大后无法计算且 复杂度高 41 | var trailingZeroes111 = function(n) { 42 | if(n < 1) return 0; 43 | let multiplyNum = 1; 44 | for(let i = 2; i <= n; i++){ 45 | multiplyNum *= i; 46 | } 47 | let index = 0; 48 | while(multiplyNum % 10 === 0) { 49 | multiplyNum /= 10; 50 | index ++; 51 | } 52 | return index; 53 | }; -------------------------------------------------------------------------------- /tiku/leetCode/198-打家窃舍.js: -------------------------------------------------------------------------------- 1 | /* 2 | 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 3 | 4 | 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 5 | 6 |   7 | 8 | 示例 1: 9 | 10 | 输入:[1,2,3,1] 11 | 输出:4 12 | 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 13 |   偷窃到的最高金额 = 1 + 3 = 4 。 14 | 示例 2: 15 | 16 | 输入:[2,7,9,3,1] 17 | 输出:12 18 | 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 19 |   偷窃到的最高金额 = 2 + 9 + 1 = 12 。 20 |   21 | 22 | 提示: 23 | 24 | 0 <= nums.length <= 100 25 | 0 <= nums[i] <= 400 26 | 27 | */ 28 | 29 | /** 30 | * @param {number[]} nums 31 | * @return {number} 32 | */ 33 | var rob = function(nums) { 34 | const len = nums.length; 35 | if(len === 0) return 0; 36 | const dp = [[]]; 37 | dp[0][0] = nums[0]; // 当前这个偷 38 | dp[0][1] = 0; // 当前这个不偷 39 | for(let i = 1; i < len; i++) { 40 | dp[i] = []; 41 | dp[i][0] = nums[i] + dp[i-1][1]; // 当前偷的话,上一家必须不偷 42 | dp[i][1] = Math.max(dp[i-1][0], dp[i-1][1]); // 当前不偷,上一家偷不偷都行 43 | } 44 | return Math.max(dp[len - 1][0], dp[len - 1][1]); 45 | }; 46 | 47 | 48 | var rob1 = function(nums) { 49 | const len = nums.length; 50 | if(len === 0) return 0; 51 | const dp = []; 52 | dp[0] = 0; 53 | dp[1] = nums[0]; 54 | for(let i = 2; i <= len; i++) { 55 | // 当前等于上家偷的与当前家+前两家偷得的最大值 56 | dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]); 57 | } 58 | return dp[len]; 59 | }; 60 | 61 | 62 | // 思路同 rob1 只是用变量代替了 dp 63 | var rob2 = function(nums) { 64 | const len = nums.length; 65 | let pre = 0, tmp = 0, now = 0; 66 | for(let i = 0; i < len; i++) { 67 | pre = tmp; 68 | tmp = now; 69 | now = Math.max(now, pre + nums[i]); 70 | 71 | } 72 | return now; 73 | }; -------------------------------------------------------------------------------- /tiku/leetCode/2-两数相加.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 3 | 4 | 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 5 | 6 | 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 7 | 8 | 示例: 9 | 10 | 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 11 | 输出:7 -> 0 -> 8 12 | 原因:342 + 465 = 807 13 | 14 | */ 15 | 16 | // 相加后也为逆序 17 | function ListNode(val) { 18 | this.val = val; 19 | this.next = null; 20 | } 21 | 22 | const addTwoNumbers = function(l1, l2) { 23 | const node = new ListNode('head'); 24 | let temp = node; 25 | let add = 0; // 相加后思否需要进 1 26 | while(l1 || l2) { 27 | let sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + add; 28 | temp.next = new ListNode(sum % 10); // 两数相加后取余,再向前进 1 29 | add = sum >= 10 ? 1 : 0; 30 | l1 && (l1 = l1.next); 31 | l2 && (l2 = l2.next); 32 | temp = temp.next; 33 | } 34 | // 最后如果还超出了10位,依然需要进 1 35 | add && (temp.next = new ListNode(add)); 36 | return node.next; 37 | }; 38 | 39 | 40 | // 递归解法 41 | const addTwoNumbers2 = function(l1, l2, add = 0) { 42 | if(l1 || l2) { 43 | let sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + add; 44 | add = sum >= 10 ? 1 : 0; 45 | const noop = new ListNode(null); 46 | l1 = l1 ? l1.next : noop.next; 47 | l2 = l2 ? l2.next : noop.next; 48 | return { 49 | val: sum % 10, 50 | next: addTwoNumbers2(l1, l2, add ) 51 | } 52 | } else { 53 | return add ? new ListNode(add) : null; 54 | } 55 | } -------------------------------------------------------------------------------- /tiku/leetCode/20-有效的括号.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 3 | 4 | 有效字符串需满足: 5 | 6 | 左括号必须用相同类型的右括号闭合。 7 | 左括号必须以正确的顺序闭合。 8 | 注意空字符串可被认为是有效字符串。 9 | 10 | 示例 1: 11 | 12 | 输入: "()" 13 | 输出: true 14 | 示例 2: 15 | 16 | 输入: "()[]{}" 17 | 输出: true 18 | 示例 3: 19 | 20 | 输入: "(]" 21 | 输出: false 22 | 示例 4: 23 | 24 | 输入: "([)]" 25 | 输出: false 26 | 示例 5: 27 | 28 | 输入: "{[]}" 29 | 输出: true 30 | */ 31 | 32 | /** 33 | * @param {string} s 34 | * @return {boolean} 35 | */ 36 | var isValid = function(s) { 37 | if(s === '') return true; 38 | if (s.length % 2) return false; 39 | const stack = []; 40 | const map = { 41 | '(': ')', 42 | '[': ']', 43 | '{': '}' 44 | } 45 | for(let i = 0; i < s.length; i++) { 46 | if(map[s[i]]) { 47 | stack.push(s[i]); 48 | }else { 49 | const top = stack[stack.length-1]; // 栈顶元素等于当前即可出栈 50 | if(map[top] === s[i]) { 51 | stack.pop(); 52 | } else { 53 | return false; 54 | } 55 | } 56 | } 57 | return stack.length === 0; 58 | }; 59 | isValid("()[]{}"); -------------------------------------------------------------------------------- /tiku/leetCode/206-反转链表.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=206 lang=javascript 3 | * 4 | * [206] 反转链表 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * Definition for singly-linked list. 10 | * function ListNode(val) { 11 | * this.val = val; 12 | * this.next = null; 13 | * } 14 | */ 15 | /** 16 | * @param {ListNode} head 17 | * @return {ListNode} 18 | */ 19 | 20 | // 新建节点,不改变原链表 21 | const reverseListI = (oldList) => { 22 | let newList = null; 23 | let curr = oldList; 24 | while(curr) { 25 | const node = new ListNode(curr.val); 26 | node.next = newList; 27 | newList = node; 28 | curr = curr.next; 29 | } 30 | return newList; 31 | } 32 | 33 | // 迭代 34 | const reverseList = (head) => { 35 | let prev = null; 36 | let curr = head; 37 | while (curr) { 38 | let temp = curr.next; 39 | curr.next = prev; 40 | prev = curr; 41 | curr = temp; 42 | } 43 | return prev; 44 | } 45 | 46 | const reverseListR = (head) => { 47 | let prev = null; 48 | let curr = head; 49 | const _reverse = (p, c) => { 50 | if(!c) return p; 51 | let temp = c.next; 52 | c.next = p; 53 | return _reverse(temp, c) 54 | } 55 | return _reverse(prev, curr) 56 | } 57 | 58 | 59 | 60 | // 递归 61 | var reverseListO = function(head) { 62 | let curr = head; 63 | let result = null; 64 | const reverse = (cur, res) => { 65 | if(!cur) return res; 66 | let next = cur.next; 67 | cur.next = res; 68 | return reverse(next, cur); 69 | } 70 | return reverse(curr, result) 71 | }; 72 | // @lc code=end 73 | 74 | -------------------------------------------------------------------------------- /tiku/leetCode/21-合并两个有序链表.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=21 lang=javascript 3 | * 4 | * [21] 合并两个有序链表 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * Definition for singly-linked list. 10 | * function ListNode(val, next) { 11 | * this.val = (val===undefined ? 0 : val) 12 | * this.next = (next===undefined ? null : next) 13 | * } 14 | */ 15 | /** 16 | * @param {ListNode} l1 17 | * @param {ListNode} l2 18 | * @return {ListNode} 19 | */ 20 | // 递归 21 | var mergeTwoLists1 = function(l1, l2) { 22 | if(l1 === null) return l2; 23 | if(l2 === null) return l1; 24 | if(l1.val < l2.val) { 25 | l1.next = mergeTwoLists1(l1.next, l2); 26 | return l1; 27 | } else { 28 | l2.next = mergeTwoLists1(l2.next, l1); 29 | return l2; 30 | } 31 | }; 32 | function ListNode(val, next) { 33 | this.val = (val===undefined ? 0 : val) 34 | this.next = (next===undefined ? null : next) 35 | } 36 | // 迭代 37 | var mergeTwoLists = function(l1, l2) { 38 | if(l1 === null) return l2; 39 | if(l2 === null) return l1; 40 | let head = new ListNode(0); 41 | let cur = head; 42 | while(l1 && l2) { 43 | if(l1.val < l2.val) { 44 | cur.next = l1; 45 | l1 = l1.next; 46 | cur = cur.next; 47 | } else{ 48 | cur.next = l2; 49 | l2 = l2.next; 50 | cur = cur.next; 51 | } 52 | } 53 | if(l2 === null) { 54 | cur.next = l1; 55 | } 56 | if(l1 === null) { 57 | cur.next = l2; 58 | } 59 | return head.next; 60 | }; 61 | // @lc code=end 62 | 63 | -------------------------------------------------------------------------------- /tiku/leetCode/213-打家劫舍二.js: -------------------------------------------------------------------------------- 1 | /* 2 | 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 3 | 4 | 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 5 | 6 | 示例 1: 7 | 8 | 输入: [2,3,2] 9 | 输出: 3 10 | 解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。 11 | 示例 2: 12 | 13 | 输入: [1,2,3,1] 14 | 输出: 4 15 | 解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 16 |   偷窃到的最高金额 = 1 + 3 = 4 。 17 | */ 18 | 19 | /** 20 | * @param {number[]} nums 21 | * @return {number} 22 | */ 23 | 24 | 25 | // 粗暴解法,去除首尾,各走一次 26 | var robchunk = function(nums) { 27 | const len = nums.length; 28 | let pre = 0, tmp = 0, now = 0; 29 | for(let i = 0; i < len; i++) { 30 | pre = tmp; 31 | tmp = now; 32 | now = Math.max(now, pre + nums[i]); 33 | 34 | } 35 | return now; 36 | }; 37 | var rob = function(nums) { 38 | if(nums.length === 1) return nums[0]; 39 | return Math.max(robchunk(nums.slice(1)), robchunk(nums.slice(0, -1))); 40 | }; -------------------------------------------------------------------------------- /tiku/leetCode/23-合并k个升序链表.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=23 lang=javascript 3 | * 4 | * [23] 合并K个升序链表 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * Definition for singly-linked list. 10 | * function ListNode(val, next) { 11 | * this.val = (val===undefined ? 0 : val) 12 | * this.next = (next===undefined ? null : next) 13 | * } 14 | */ 15 | /** 16 | * @param {ListNode[]} lists 17 | * @return {ListNode} 18 | */ 19 | var mergeTwoLists = function(l1, l2) { 20 | if(l1 === null) return l2; 21 | if(l2 === null) return l1; 22 | if(l1.val < l2.val) { 23 | l1.next = mergeTwoLists(l1.next, l2); 24 | return l1; 25 | } else { 26 | l2.next = mergeTwoLists(l2.next, l1); 27 | return l2; 28 | } 29 | }; 30 | var mergeKLists = function(lists) { 31 | if(lists.length === 0) return null; 32 | let l1 = lists.shift(); 33 | let l2; 34 | while(lists.length) { 35 | l2 = lists.shift(); 36 | l1 = mergeTwoLists(l1, l2); 37 | } 38 | return l1; 39 | }; 40 | // @lc code=end 41 | 42 | -------------------------------------------------------------------------------- /tiku/leetCode/230-二叉搜索树第k小的数.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} k 12 | * @return {number} 13 | */ 14 | var kthSmallest = function(root, k) { 15 | const res = []; 16 | const stack = []; 17 | while(root || stack.length){ 18 | while(root) { 19 | stack.push(root); 20 | root = root.left; 21 | } 22 | const node = stack.pop(); 23 | res.push(node.val); 24 | root = node.right; 25 | } 26 | return res[k - 1]; 27 | }; -------------------------------------------------------------------------------- /tiku/leetCode/237-删除链表中的节点.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=237 lang=javascript 3 | * 4 | * [237] 删除链表中的节点 5 | * 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。 6 | 现有一个链表 -- head = [4,5,1,9],它可以表示为: 7 | 8 | 示例 1: 9 | 10 | 输入:head = [4,5,1,9], node = 5 11 | 输出:[4,1,9] 12 | 解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 13 | 示例 2: 14 | 15 | 输入:head = [4,5,1,9], node = 1 16 | 输出:[4,5,9] 17 | 解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. 18 | 19 | 20 | 提示: 21 | 22 | 链表至少包含两个节点。 23 | 链表中所有节点的值都是唯一的。 24 | 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 25 | 不要从你的函数中返回任何结果。 26 | */ 27 | 28 | // @lc code=start 29 | /** 30 | * Definition for singly-linked list. 31 | * function ListNode(val) { 32 | * this.val = val; 33 | * this.next = null; 34 | * } 35 | */ 36 | /** 37 | * @param {ListNode} node 38 | * @return {void} Do not return anything, modify node in-place instead. 39 | */ 40 | var deleteNode = function(node) { 41 | let next = node.next; 42 | node.val = next.val; 43 | node.next = next.next; 44 | }; 45 | // @lc code=end 46 | 47 | -------------------------------------------------------------------------------- /tiku/leetCode/239-滑动窗口最大值.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 3 | 4 | 返回滑动窗口中的最大值。 5 | 6 |   7 | 8 | 进阶: 9 | 10 | 你能在线性时间复杂度内解决此题吗? 11 | 12 |   13 | 14 | 示例: 15 | 16 | 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 17 | 输出: [3,3,5,5,6,7] 18 | 解释: 19 | 20 | 滑动窗口的位置 最大值 21 | --------------- ----- 22 | [1 3 -1] -3 5 3 6 7 3 23 | 1 [3 -1 -3] 5 3 6 7 3 24 | 1 3 [-1 -3 5] 3 6 7 5 25 | 1 3 -1 [-3 5 3] 6 7 5 26 | 1 3 -1 -3 [5 3 6] 7 6 27 | 1 3 -1 -3 5 [3 6 7] 7 28 |   29 | 30 | 提示: 31 | 32 | 1 <= nums.length <= 10^5 33 | -10^4 <= nums[i] <= 10^4 34 | 1 <= k <= nums.length 35 | */ 36 | /** 37 | * @param {number[]} nums 38 | * @param {number} k 39 | * @return {number[]} 40 | */ 41 | // 前置: 42 | // 如果一个一直最大值的队列新增一个数字,只要判断最大值和此值就行了 43 | // 但是如果减掉一个,就需要判断减少的是否是最大值,如果不是,同上 44 | // 如果减少的是最大值,就需要从头查找最大值了 45 | // 所以维护一个最大值的单调递减队列,最大值在queue[0],如果减少的是最大值,直接shift就好,第二大值就会到[0]的位置,后需添加继续比较就好 46 | var maxSlidingWindow = function(nums, k) { 47 | const res = []; 48 | const maxQueue = []; 49 | for(let i = 0; i < nums.length; i++) { 50 | // 已装填的最后一个如果小于将要填充这个,将其删除 51 | // 循环到队列中元素都判断完 52 | // 保证队列是单调递减的 53 | while(maxQueue.length > 0 && maxQueue[maxQueue.length-1] < nums[i]) { 54 | maxQueue.pop(); 55 | } 56 | maxQueue.push(nums[i]); 57 | if(i >= k -1) { 58 | // 此时每次都是新窗口 59 | // maxQueue[0]刚好是这个窗口的最大值,maxQueue[1]是第二大的,如果有的话 60 | // 第一个为当前窗口最大值 61 | res.push(maxQueue[0]); 62 | // 此时要判断是否需要移除一位 63 | // 如果此时的最大值,刚好是当前窗口的第一位,则需要将这个最大值移除掉 64 | // 当前窗口第二大的值,将会移动到 maxQueue[0] 65 | if(maxQueue.length > 0 && maxQueue[0] === nums[i - k + 1]) { 66 | maxQueue.shift(); 67 | } 68 | } 69 | } 70 | return res; 71 | }; -------------------------------------------------------------------------------- /tiku/leetCode/258-各位相加.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。 3 | 4 | 示例: 5 | 6 | 输入: 38 7 | 输出: 2 8 | 解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。 9 | 进阶: 10 | 你可以不使用循环或者递归,且在 O(1) 时间复杂度内解决这个问题吗? 11 | 12 | */ 13 | 14 | /** 15 | * 数学解法 16 | * 对9取余,整除为9,0为0 17 | * @param {number} num 18 | * @return {number} 19 | */ 20 | var addDigits = function(num) { 21 | return num && ((num % 9) ? (num % 9) : 9) || 0; 22 | }; 23 | 24 | /** 25 | * 循环解法 26 | * @param {number} num 27 | * @return {number} 28 | */ 29 | var addDigits1 = function(num) { 30 | while(num > 9) { 31 | num = `${num}`.split('').reduce((pre, cur) => pre+ +cur, 0); 32 | } 33 | return num; 34 | }; 35 | 36 | 37 | /** 38 | * 递归解法 39 | * @param {number} num 40 | * @return {number} 41 | */ 42 | var addDigits2 = function(num) { 43 | if(num < 10) { 44 | return num; 45 | } 46 | return addDigits(`${num}`.split('').reduce((pre, cur) => pre+ +cur, 0)); 47 | }; 48 | -------------------------------------------------------------------------------- /tiku/leetCode/32-最长有效字符串.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 3 | 4 | 示例 1: 5 | 6 | 输入: "(()" 7 | 输出: 2 8 | 解释: 最长有效括号子串为 "()" 9 | 示例 2: 10 | 11 | 输入: ")()())" 12 | 输出: 4 13 | 解释: 最长有效括号子串为 "()()" 14 | 15 | */ 16 | 17 | /** 18 | * @param {string} s 19 | * @return {number} 20 | */ 21 | var longestValidParentheses = function(s) { 22 | // 哨兵 23 | const stack = [-1]; 24 | let max = 0; 25 | for (let i = 0; i < s.length; i++) { 26 | if(s[i] === '(') { 27 | // 左括号,下标入栈 28 | stack.push(i); 29 | } else { 30 | const node = stack.pop(); 31 | // 匹配到的出完后,第一个为接下来的哨兵 32 | if(stack.length === 0) { 33 | stack.push(i); 34 | } 35 | // 和栈顶下标相减,得到现有长度 36 | max = Math.max(max, i- stack[stack.length -1]); 37 | } 38 | } 39 | return max; 40 | }; -------------------------------------------------------------------------------- /tiku/leetCode/337-打家劫舍三.js: -------------------------------------------------------------------------------- 1 | /* 2 | 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。 3 | 4 | 计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。 5 | 6 | 示例 1: 7 | 8 | 输入: [3,2,3,null,3,null,1] 9 | 10 | 3 11 | / \ 12 | 2 3 13 | \ \ 14 | 3 1 15 | 16 | 输出: 7 17 | 解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7. 18 | 示例 2: 19 | 20 | 输入: [3,4,5,1,3,null,1] 21 | 22 |   3 23 | / \ 24 | 4 5 25 | / \ \ 26 | 1 3 1 27 | 28 | 输出: 9 29 | 解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9. 30 | 31 | */ 32 | 33 | /** 34 | * Definition for a binary tree node. 35 | * function TreeNode(val) { 36 | * this.val = val; 37 | * this.left = this.right = null; 38 | * } 39 | */ 40 | /** 41 | * @param {TreeNode} root 42 | * @return {number} 43 | */ 44 | var rob = function(root) { 45 | const dfs = (node) => { 46 | if(node === null) { 47 | // 第一个表示选择当前的最大值 48 | // 第二个表示不选当前的最大值 49 | return [0, 0]; 50 | } 51 | const l = dfs(node.left); 52 | const r = dfs(node.right); 53 | // 选了父的,不能选子的 54 | const selected = node.val + l[1] + r[1]; 55 | // 不选父的,子的可选可不选 56 | const notSelected = Math.max(l[0], l[1]) + Math.max(r[0], r[1]); 57 | return [selected, notSelected]; 58 | } 59 | const rootDfs = dfs(root); 60 | return Math.max(rootDfs[0], rootDfs[1]); 61 | }; -------------------------------------------------------------------------------- /tiku/leetCode/34-在排序数组中查找元素的第一个和最后一个位置.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 3 | 4 | 你的算法时间复杂度必须是 O(log n) 级别。 5 | 6 | 如果数组中不存在目标值,返回 [-1, -1]。 7 | 8 | 示例 1: 9 | 10 | 输入: nums = [5,7,7,8,8,10], target = 8 11 | 输出: [3,4] 12 | 示例 2: 13 | 14 | 输入: nums = [5,7,7,8,8,10], target = 6 15 | 输出: [-1,-1] 16 | */ 17 | 18 | // O(n) 解法 19 | var searchRange = function(nums, target) { 20 | let res = []; 21 | for(let i = 0; i < nums.length; i++) { 22 | if(nums[i] === target) { 23 | if(!res.length) { 24 | res = [i, i]; 25 | } else { 26 | res[1] = i; 27 | } 28 | } 29 | } 30 | return res.length ? res : [-1, -1]; 31 | }; 32 | 33 | /** 34 | * @param {number[]} nums 35 | * @param {number} target 36 | * @return {number[]} 37 | */ 38 | // 二分查找 39 | var searchRange2 = function(nums, target) { 40 | let left = 0; 41 | let right = nums.length - 1; 42 | while(left <= right) { 43 | let pointIndex = Math.floor((right + left) / 2); 44 | if(target < nums[pointIndex]) { 45 | right = pointIndex - 1; 46 | } else if(target > nums[pointIndex]) { 47 | left = pointIndex + 1; 48 | } else { 49 | left = right = pointIndex; 50 | while(target === nums[--left]){}; 51 | while(target === nums[++right]){}; 52 | return [left + 1, right - 1]; 53 | } 54 | } 55 | return [-1, -1]; 56 | }; -------------------------------------------------------------------------------- /tiku/leetCode/415-字符串相加.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。 3 | 4 |   5 | 6 | 提示: 7 | 8 | num1 和num2 的长度都小于 5100 9 | num1 和num2 都只包含数字 0-9 10 | num1 和num2 都不包含任何前导零 11 | 你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式 12 | 13 | */ 14 | 15 | // 暴躁解法 16 | var addStrings = function(num1, num2) { 17 | let i = num1.length - 1; 18 | let j = num2.length - 1; 19 | let step = 0; // 进位数 20 | let arr = []; 21 | while(i >= 0 || j >= 0 || step !== 0) { 22 | let result = 0; 23 | let a = num1.charAt(i) - 0; 24 | let b = num2.charAt(j) - 0; 25 | result = a + b + step; 26 | step = result / 10 | 0; 27 | arr.push(result % 10); 28 | i--; 29 | j--; 30 | } 31 | return arr.reverse().join(''); 32 | }; 33 | console.log("addStrings", addStrings('98','9')); -------------------------------------------------------------------------------- /tiku/leetCode/43-字符串相乘.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 3 | 4 | 示例 1: 5 | 6 | 输入: num1 = "2", num2 = "3" 7 | 输出: "6" 8 | 示例 2: 9 | 10 | 输入: num1 = "123", num2 = "456" 11 | 输出: "56088" 12 | 说明: 13 | 14 | num1 和 num2 的长度小于110。 15 | num1 和 num2 只包含数字 0-9。 16 | num1 和 num2 均不以零开头,除非是数字 0 本身。 17 | 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。 18 | */ 19 | 20 | /** 21 | * @param {string} num1 22 | * @param {string} num2 23 | * @return {string} 24 | */ 25 | var multiply = function(num1, num2) { 26 | if(num1 === '0' || num2 === '0') return '0'; 27 | const len1 = num1.length; 28 | const len2 = num2.length; 29 | let arr = new Array(len1 + len2).fill(0); 30 | for(let i = len1-1; i >= 0; i--) { 31 | for(let j = len2-1; j >= 0; j--) { 32 | // 相乘后与之前的乘积相加 33 | arr[i+j+1] = num1[i] * num2[j] + arr[i+j+1]; 34 | console.log("multiply -> arr[i+j+1]", arr[i+j+1]); 35 | } 36 | } 37 | console.log("multiply -> arr", arr) 38 | let step = 0; // 进位数 39 | for(let i = arr.length - 1; i >=0; i--) { 40 | // 求进 41 | arr[i] = arr[i] + step; 42 | // 计算下一个进位 43 | step = arr[i] / 10 | 0; 44 | // 保存余数 45 | arr[i] = arr[i] % 10; 46 | } 47 | // 首位不能是0 48 | if(arr[0] === 0) { 49 | arr = arr.slice(1); 50 | } 51 | return arr.join(''); 52 | }; 53 | // console.log("multiply([9,9,9], [9,9,9])", multiply('999', '999')); 54 | 55 | -------------------------------------------------------------------------------- /tiku/leetCode/5-最长子回文串.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 3 | 4 | 示例 1: 5 | 6 | 输入: "babad" 7 | 输出: "bab" 8 | 注意: "aba" 也是一个有效答案。 9 | 示例 2: 10 | 11 | 输入: "cbbd" 12 | 输出: "bb" 13 | 14 | */ 15 | 16 | 17 | /** 18 | * @param {string} s 19 | * @return {string} 20 | */ 21 | // 最长回文数 22 | // 回文数特征 23 | // 奇数回文中心,下两个必定和它相等。此时开始左右一起查找,找到该回文的起止点。 24 | // 偶数回文中心,下一个必定和它相等。此时开始左右一起查找,找到该回文的起止点。 25 | var longestPalindrome = function(s) { 26 | let result = s[0] || ""; 27 | for(var i = 0; i < s.length; i++){ 28 | for(var j = 1; j <= 2; j++){ //奇数偶数回文 29 | var left = i; 30 | var right = i + j; 31 | console.log("longestPalindrome -> right", left, right) 32 | while(left >= 0 && right < s.length && s[left] === s[right]){ 33 | left--; 34 | right++; 35 | } 36 | var len = (right - left) - 1; 37 | console.log("longestPalindrome -> right", left, right) 38 | if(len > result.length){ 39 | result = s.substr(left + 1, len); 40 | console.log("longestPalindrome -> result", result) 41 | } 42 | } 43 | } 44 | return result; 45 | }; 46 | longestPalindrome('adadabbcadada1'); 47 | 48 | /** 49 | * 动态规划 50 | * @param {string} s 51 | * @return {string} 52 | */ 53 | var longestPalindrome1 = function(s) { 54 | const n = s.length; 55 | const dp = []; 56 | let res = ''; 57 | for(let i = n - 1; i >= 0; i--) { 58 | dp[i] = []; 59 | for(let j = i; j < n; j++) { 60 | dp[i][j] = s[i]==s[j] && (j - i < 2 || dp[i+1][j-1]); 61 | if(dp[i][j] && j - i + 1 > res.length) { 62 | res = s.slice(i, j+1); 63 | } 64 | } 65 | } 66 | return res; 67 | }; -------------------------------------------------------------------------------- /tiku/leetCode/54-螺旋矩阵.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 3 | 4 | 示例 1: 5 | 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 6 | 输出:[1,2,3,6,9,8,7,4,5] 7 | 8 | 123 9 | 456 10 | 789 11 | 12 | 示例 2: 13 | 输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] 14 | 输出:[1,2,3,4,8,12,11,10,9,5,6,7] 15 | 16 | 提示: 17 | m == matrix.length 18 | n == matrix[i].length 19 | 1 <= m, n <= 10 20 | -100 <= matrix[i][j] <= 100 21 | */ 22 | 23 | /** 24 | * @param {number[][]} matrix 25 | * @return {number[]} 26 | */ 27 | var spiralOrder = function(matrix) { 28 | if (!matrix.length || !matrix[0].length) { 29 | return []; 30 | } 31 | 32 | const result = []; 33 | let top = 0; 34 | let bottom = matrix.length - 1; 35 | let left = 0; 36 | let right = matrix[0].length - 1; 37 | 38 | while (top <= bottom && left <= right) { 39 | // 从左到右遍历上边界 40 | for (let i = left; i <= right; i++) { 41 | result.push(matrix[top][i]); 42 | } 43 | top++; 44 | 45 | // 从上到下遍历右边界 46 | for (let i = top; i <= bottom; i++) { 47 | result.push(matrix[i][right]); 48 | } 49 | right--; 50 | 51 | // 从右到左遍历下边界 52 | if (top <= bottom) { 53 | for (let i = right; i >= left; i--) { 54 | result.push(matrix[bottom][i]); 55 | } 56 | bottom--; 57 | } 58 | 59 | // 从下到上遍历左边界 60 | if (left <= right) { 61 | for (let i = bottom; i >= top; i--) { 62 | result.push(matrix[i][left]); 63 | } 64 | left++; 65 | } 66 | } 67 | 68 | return result; 69 | }; -------------------------------------------------------------------------------- /tiku/leetCode/59-螺旋矩阵II.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给你一个正整数 n ,生成一个包含 1 到 n² 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 3 | 4 | 示例 1: 5 | 输入:n = 3 6 | 输出:[[1,2,3],[8,9,4],[7,6,5]] 7 | 8 | 示例 2: 9 | 输入:n = 1 10 | 输出:[[1]] 11 | 12 | 提示: 13 | 1 <= n <= 20 14 | */ 15 | 16 | /** 17 | * @param {number} n 18 | * @return {number[][]} 19 | */ 20 | var generateMatrix = function(n) { 21 | // 创建一个 n x n 的二维数组 22 | const matrix = Array.from({ length: n }, () => Array(n).fill(0)); 23 | 24 | let num = 1; // 当前要填入的数字 25 | let top = 0; 26 | let bottom = n - 1; 27 | let left = 0; 28 | let right = n - 1; 29 | 30 | while (num <= n * n) { 31 | // 从左到右填充上边界 32 | for (let i = left; i <= right; i++) { 33 | matrix[top][i] = num++; 34 | } 35 | top++; 36 | 37 | // 从上到下填充右边界 38 | for (let i = top; i <= bottom; i++) { 39 | matrix[i][right] = num++; 40 | } 41 | right--; 42 | 43 | // 从右到左填充下边界 44 | for (let i = right; i >= left; i--) { 45 | matrix[bottom][i] = num++; 46 | } 47 | bottom--; 48 | 49 | // 从下到上填充左边界 50 | for (let i = bottom; i >= top; i--) { 51 | matrix[i][left] = num++; 52 | } 53 | left++; 54 | } 55 | 56 | return matrix; 57 | }; -------------------------------------------------------------------------------- /tiku/leetCode/69-x 的平方根.js: -------------------------------------------------------------------------------- 1 | /* 2 | 实现 int sqrt(int x) 函数。 3 | 4 | 计算并返回 x 的平方根,其中 x 是非负整数。 5 | 6 | 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 7 | 8 | 示例 1: 9 | 10 | 输入: 4 11 | 输出: 2 12 | 示例 2: 13 | 14 | 输入: 8 15 | 输出: 2 16 | 说明: 8 的平方根是 2.82842..., 17 |   由于返回类型是整数,小数部分将被舍去。 18 | */ 19 | 20 | var mySqrt = function(x) { 21 | if(x < 2) return x; 22 | let l = 1, r = x / 2 + 1, mid = 1; 23 | while(l <= r) { 24 | mid = (l+r) >> 1; 25 | if(mid ** 2 > x) { 26 | r = mid -1; 27 | } else if(mid ** 2 < x) { 28 | l = mid + 1; 29 | } else { 30 | return mid 31 | } 32 | } 33 | return r; 34 | }; 35 | 36 | console.log("mySqrt(10)", mySqrt(10)); -------------------------------------------------------------------------------- /tiku/leetCode/7-整数翻转.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。 3 | 4 | 示例 1: 5 | 6 | 输入: 123 7 | 输出: 321 8 |  示例 2: 9 | 10 | 输入: -123 11 | 输出: -321 12 | 示例 3: 13 | 14 | 输入: 120 15 | 输出: 21 16 | 注意: 17 | 18 | 假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231,  231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。 19 | */ 20 | var reverse = function(x) { 21 | let result = 0; 22 | while(x !== 0) { 23 | result = result * 10 + x % 10; 24 | x = x / 10 | 0; // 不分正负 返回去掉小数点后的数 25 | } 26 | if(result < -(2 ** 31) || result > 2 ** 31 - 1) { 27 | result = 0; 28 | } 29 | return result; 30 | }; -------------------------------------------------------------------------------- /tiku/leetCode/70-爬楼梯.js: -------------------------------------------------------------------------------- 1 | /* 2 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 3 | 4 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 5 | 6 | 注意:给定 n 是一个正整数。 7 | 8 | 示例 1: 9 | 10 | 输入: 2 11 | 输出: 2 12 | 解释: 有两种方法可以爬到楼顶。 13 | 1. 1 阶 + 1 阶 14 | 2. 2 阶 15 | 示例 2: 16 | 17 | 输入: 3 18 | 输出: 3 19 | 解释: 有三种方法可以爬到楼顶。 20 | 1. 1 阶 + 1 阶 + 1 阶 21 | 2. 1 阶 + 2 阶 22 | 3. 2 阶 + 1 阶 23 | */ 24 | 25 | /** 26 | * 暴力解法 27 | * @param {number} n 28 | * @return {number} 29 | */ 30 | var climbStairs1 = function(n) { 31 | if(n === 1 || n === 2) return n; 32 | return climbStairs(n-1) + climbStairs(n-2); 33 | }; 34 | 35 | 36 | /** 37 | * 动态规划 38 | * @param {number} n 39 | * @return {number} 40 | */ 41 | var climbStairs = function(n) { 42 | const dp = []; 43 | dp[0] = 1; 44 | dp[1] = 2; 45 | for(let i = 2; i < n; i++) { 46 | dp[i] = dp[i-1] + dp[i-2]; 47 | } 48 | return dp[n-1]; 49 | }; -------------------------------------------------------------------------------- /tiku/leetCode/796-旋转字符串.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定两个字符串, A 和 B。 3 | 4 | A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = 'abcde',在移动一次之后结果就是'bcdea' 。如果在若干次旋转操作之后,A 能变成B,那么返回True。 5 | 6 | 示例 1: 7 | 输入: A = 'abcde', B = 'cdeab' 8 | 输出: true 9 | 10 | 示例 2: 11 | 输入: A = 'abcde', B = 'abced' 12 | 输出: false 13 | 注意: 14 | 15 | A 和 B 长度不超过 100。 16 | 17 | */ 18 | 19 | /** 20 | * @param {string} A 21 | * @param {string} B 22 | * @return {boolean} 23 | */ 24 | var rotateString = function(A, B) { 25 | if(A===B) return true; 26 | let len = A.length; 27 | let s = A; 28 | for (let i = 0; i < len; i++) { 29 | s = s.substr(1) + s[0]; 30 | if(s === B) { 31 | return true; 32 | } 33 | } 34 | return false; 35 | }; 36 | 37 | 38 | var rotateString1 = function(A, B) { 39 | return A.length === B.length && (A+A).includes(B); 40 | }; -------------------------------------------------------------------------------- /tiku/leetCode/8-字符串转整数.js: -------------------------------------------------------------------------------- 1 | /* 2 | 请你来实现一个 atoi 函数,使其能将字符串转换成整数。 3 | 4 | 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下: 5 | 6 | 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。 7 | 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。 8 | 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。 9 | 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。 10 | 11 | 在任何情况下,若函数不能进行有效的转换时,请返回 0 。 12 | 13 | 提示: 14 | 15 | 本题中的空白字符只包括空格字符 ' ' 。 16 | 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。 17 |   18 | 19 | 示例 1: 20 | 21 | 输入: "42" 22 | 输出: 42 23 | 示例 2: 24 | 25 | 输入: " -42" 26 | 输出: -42 27 | 解释: 第一个非空白字符为 '-', 它是一个负号。 28 |   我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。 29 | 示例 3: 30 | 31 | 输入: "4193 with words" 32 | 输出: 4193 33 | 解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。 34 | 示例 4: 35 | 36 | 输入: "words and 987" 37 | 输出: 0 38 | 解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 39 | 因此无法执行有效的转换。 40 | 示例 5: 41 | 42 | 输入: "-91283472332" 43 | 输出: -2147483648 44 | 解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 45 |   因此返回 INT_MIN (−231) 。 46 | */ 47 | 48 | /** 49 | * @param {string} str 50 | * @return {number} 51 | */ 52 | // 正则 53 | var myAtoi = function(str) { 54 | str = str.trim().match(/^[+-]?\d+/g); 55 | if(!str) return 0; 56 | return Math.max(Math.min(str[0], 2 ** 31-1), -(2**31)) || 0; 57 | }; 58 | 59 | // parseInt 60 | /** 61 | * 无视开头空格(满足) 62 | * 返回有符号整数(满足) 63 | * 无视整数部分后的字符(满足) 64 | * 范围在32位内(含)(需处理) 65 | * 其他情况返回0(需处理) 66 | * 67 | * @param {*} str 68 | * @returns 69 | */ 70 | var myAtoi1 = function(str) { 71 | str = parseInt(str, 10); 72 | return Math.max(Math.min(str, 2 ** 31-1), -(2**31)) || 0; 73 | }; -------------------------------------------------------------------------------- /tiku/leetCode/82- 删除排序链表中的重复元素 II.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。 3 | 4 | 示例 1: 5 | 6 | 输入: 1->2->3->3->4->4->5 7 | 输出: 1->2->5 8 | 示例 2: 9 | 10 | 输入: 1->1->1->2->3 11 | 输出: 2->3 12 | */ 13 | 14 | /** 15 | * Definition for singly-linked list. 16 | * function ListNode(val) { 17 | * this.val = val; 18 | * this.next = null; 19 | * } 20 | */ 21 | /** 22 | * 排序链表,即所有重复节点都是连续的 23 | * 如果下一个不是重复,说明该节点没有重复 24 | * @param {ListNode} head 25 | * @return {ListNode} 26 | */ 27 | var deleteDuplicates = function(head) { 28 | let node = head; 29 | let lastHead = new ListNode(0); 30 | lastHead.next = head; 31 | let last = lastHead; 32 | while(node && node.next){ 33 | if(node.val === node.next.val) { 34 | let val = node.val; 35 | while(node && node.val === val) { 36 | node = node.next; 37 | } 38 | last.next = node; 39 | }else{ 40 | last = last.next; 41 | node = node.next; 42 | } 43 | } 44 | return lastHead.next; 45 | }; -------------------------------------------------------------------------------- /tiku/leetCode/83-删除排序链表中的重复元素.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=83 lang=javascript 3 | * 4 | * [83] 删除排序链表中的重复元素 5 | */ 6 | 7 | // @lc code=start 8 | /** 9 | * Definition for singly-linked list. 10 | * function ListNode(val) { 11 | * this.val = val; 12 | * this.next = null; 13 | * } 14 | */ 15 | /** 16 | * @param {ListNode} head 17 | * @return {ListNode} 18 | */ 19 | var deleteDuplicates = function(head) { 20 | const cache = new Set(); 21 | let node = head; 22 | while(node){ 23 | const val = node.val; 24 | // 连续下一个相等直接移走 25 | while(node.next && node.next.val === val) { 26 | node.val = node.next.val; 27 | node.next = node.next.next; 28 | } 29 | // 非连续相等移除 30 | if(cache.has(node.val)){ 31 | if(node.next) { 32 | node.val = node.next.val; 33 | node.next = node.next.next; 34 | } else { 35 | // 处理最后一个 36 | node = null; 37 | } 38 | } else { 39 | // 首次出现 40 | cache.add(node.val); 41 | node = node.next; 42 | } 43 | } 44 | return head; 45 | }; 46 | // @lc code=end 47 | 48 | -------------------------------------------------------------------------------- /tiku/leetCode/867-转置矩阵.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个矩阵 A, 返回 A 的转置矩阵。 3 | 4 | 矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。 5 | 6 | 示例 1: 7 | 8 | 输入:[[1,2,3],[4,5,6],[7,8,9]] 9 | 输出:[[1,4,7],[2,5,8],[3,6,9]] 10 | 示例 2: 11 | 12 | 输入:[[1,2,3],[4,5,6]] 13 | 输出:[[1,4],[2,5],[3,6]] 14 | 123 15 | 456 16 | -- 17 | 14 18 | 25 19 | 36 20 | */ 21 | 22 | 23 | /** 24 | * 类似于螺旋矩阵打印 25 | * 本质就是拿子数组的长度作为新数组的长度 26 | * 拿原数组的长度作为新的子数组的长度 2,3 -> 3,2 27 | * 利用 3 循环构造3个子数组 28 | * 以第一次循环为例,第一个子数组取的值为 A[0][0], A[1][0] 29 | * 以此类推,A[0][1], A[1][1] 30 | * A[0][2],A[1][2] 31 | * @param {number[][]} A 32 | * @return {number[][]} 33 | */ 34 | var transpose = function(A) { 35 | const rows = A.length; // 2 36 | const cols = A[0].length; // 3 37 | const result = []; 38 | 39 | for (let c = 0; c < cols; c++) { 40 | result[c] = []; 41 | for (let r = 0; r < rows; r++) { 42 | result[c].push(A[r][c]); 43 | } 44 | } 45 | 46 | return result; 47 | }; -------------------------------------------------------------------------------- /tiku/leetCode/88-合并两个有序数组.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 3 | 4 |   5 | 6 | 说明: 7 | 8 | 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。 9 | 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 10 |   11 | 12 | 示例: 13 | 14 | 输入: 15 | nums1 = [1,2,3,0,0,0], m = 3 16 | nums2 = [2,5,6], n = 3 17 | 18 | 输出: [1,2,2,3,5,6] 19 | 20 | */ 21 | 22 | /** 23 | * @param {number[]} nums1 24 | * @param {number} m 25 | * @param {number[]} nums2 26 | * @param {number} n 27 | * @return {void} Do not return anything, modify nums1 in-place instead. 28 | */ 29 | var merge = function(nums1, m, nums2, n) { 30 | let len = m+n-1; 31 | m--; 32 | n--; 33 | while(n >= 0) { 34 | if(nums1[m] > nums2[n]) { 35 | nums1[len--] = nums1[m--]; 36 | }else{ 37 | nums1[len--] = nums2[n--]; 38 | } 39 | } 40 | }; -------------------------------------------------------------------------------- /tiku/leetCode/9-回文数.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 回文数 3 | * @param {number} x 789 4 | * @return {boolean} 5 | */ 6 | 7 | // 不用字符串解法 8 | var isPalindrome = function(x) { 9 | if(x<0) return false; 10 | let target = 0; 11 | let source = x; 12 | while(source > 0) { 13 | target = target * 10 + source % 10; 14 | source = Math.floor(source / 10); 15 | } 16 | return target === x; 17 | }; 18 | 19 | // 字符串解法 20 | var isPalindrome1 = function(x) { 21 | x = `${x}`; 22 | let left = 0; 23 | let right = x.length - 1; 24 | while(left < right) { 25 | if(x[left] !== x[right]) { 26 | return false; 27 | } 28 | left ++; 29 | right --; 30 | } 31 | return true; 32 | }; -------------------------------------------------------------------------------- /tiku/leetCode/91-解码方法.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/tiku/leetCode/91-解码方法.js -------------------------------------------------------------------------------- /tiku/leetCode/92-反转链表2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @lc app=leetcode.cn id=92 lang=javascript 3 | * 4 | * [92] 反转链表 II 5 | * 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 6 | 说明: 7 | 1 ≤ m ≤ n ≤ 链表长度。 8 | 9 | 示例: 10 | 11 | 输入: 1->2->3->4->5->NULL, m = 2, n = 4 12 | 输出: 1->4->3->2->5->NULL 13 | */ 14 | 15 | // @lc code=start 16 | /** 17 | * Definition for singly-linked list. 18 | * function ListNode(val) { 19 | * this.val = val; 20 | * this.next = null; 21 | * } 22 | */ 23 | /** 24 | * @param {ListNode} head 25 | * @param {number} m 26 | * @param {number} n 27 | * @return {ListNode} 28 | */ 29 | var reverseBetween = function(head, m, n) { 30 | if(!m || !n || !head) { 31 | return head; 32 | } 33 | let start = null; 34 | let end = null; 35 | let center = null; 36 | let centerRight = null; 37 | let curr = head; 38 | let index = 0; 39 | while(curr) { 40 | index++; 41 | if(index < m) { 42 | start = curr; 43 | curr = curr.next; 44 | } 45 | if(index >= m && index <= n) { 46 | let tmp = curr.next; 47 | curr.next = center; 48 | center = curr; 49 | curr = tmp; 50 | if(index === m) { 51 | centerRight = center; 52 | } 53 | } 54 | if(index > n) { 55 | end = curr; 56 | break; 57 | } 58 | } 59 | start ? start.next = center : head = center; 60 | centerRight && (centerRight.next = end); 61 | return head; 62 | }; 63 | // @lc code=end 64 | 65 | -------------------------------------------------------------------------------- /tiku/leetCode/95-不同的二叉搜索树二.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个整数 n,生成所有由 1 ... n 为节点所组成的 二叉搜索树 。 3 | 4 |   5 | 6 | 示例: 7 | 8 | 输入:3 9 | 输出: 10 | [ 11 |   [1,null,3,2], 12 |   [3,2,null,1], 13 |   [3,1,null,null,2], 14 |   [2,1,3], 15 |   [1,null,2,null,3] 16 | ] 17 | 解释: 18 | 以上的输出对应以下 5 种不同结构的二叉搜索树: 19 | 20 | 1 3 3 2 1 21 | \ / / / \ \ 22 | 3 2 1 1 3 2 23 | / / \ \ 24 | 2 1 2 3 25 |   26 | 27 | 提示: 28 | 29 | 0 <= n <= 8 30 | */ 31 | 32 | /** 33 | * Definition for a binary tree node. 34 | * function TreeNode(val, left, right) { 35 | * this.val = (val===undefined ? 0 : val) 36 | * this.left = (left===undefined ? null : left) 37 | * this.right = (right===undefined ? null : right) 38 | * } 39 | */ 40 | /** 41 | * @param {number} n 42 | * @return {TreeNode[]} 43 | */ 44 | function TreeNode(val, left, right) { 45 | this.val = (val===undefined ? 0 : val) 46 | this.left = (left===undefined ? null : left) 47 | this.right = (right===undefined ? null : right) 48 | } 49 | var generateTrees = function(n) { 50 | if(n === 0) return []; 51 | function buildTree(start, end) { 52 | const res = []; 53 | // 核心,没有子节点时,left或right为null 54 | if (start > end) { 55 | return [null]; 56 | }; 57 | for(let i = start; i<=end; i++) { 58 | const left = buildTree(start, i - 1); 59 | const right = buildTree(i + 1, end); 60 | for(let leftNode of left) { 61 | for(let rightNode of right) { 62 | const node = new TreeNode(i); 63 | node.left = leftNode; 64 | node.right = rightNode; 65 | res.push(node); 66 | } 67 | } 68 | } 69 | return res; 70 | } 71 | return buildTree(1, n); 72 | }; -------------------------------------------------------------------------------- /tiku/leetCode/96-不同的二叉搜索树.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 3 | 4 | 示例: 5 | 6 | 输入: 3 7 | 输出: 5 8 | 解释: 9 | 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 10 | 11 | 1 3 3 2 1 12 | \ / / / \ \ 13 | 3 2 1 1 3 2 14 | / / \ \ 15 | 2 1 2 3 16 | 17 | */ 18 | 19 | /** 20 | * @param {number} n 21 | * @return {number} 22 | * 根节点为i,左子树+右子树 23 | * dp[0]*dp[i-1] + dp[1]*dp[i-2] + ... + dp[i-1] * dp[0] 24 | */ 25 | var numTrees = function(n) { 26 | const dp = new Array(n+1).fill(0); 27 | dp[0] = 1; 28 | dp[1] = 1; 29 | for(let i = 2; i <= n; i++) { 30 | // j是左子树 31 | // 单个 dp[i] = dp[j] * dp[i - 1 - j]; 32 | // 最后需要累加当前 i 所有的dp[i]; 33 | for(let j = 0; j <= i - 1; j++) { 34 | dp[i] += dp[j] * dp[i - 1 - j]; 35 | } 36 | } 37 | return dp[n]; 38 | }; 39 | 40 | 41 | // 卡塔兰数 公式 42 | // C0=1, Cn+1=(2*(2n+1))/(n+2)*Cn 43 | 44 | var numTrees1 = function(n) { 45 | let c = 1; 46 | for(let i = 0; i < n; i++) { 47 | c = 2 * (2 * i + 1) / (i + 2) * c 48 | } 49 | return c; 50 | }; 51 | ​ -------------------------------------------------------------------------------- /tiku/leetCode/isPalindrome.js: -------------------------------------------------------------------------------- 1 | // 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。 2 | // 说明:本题中,我们将空字符串定义为有效的回文串。 3 | // 输入: "A man, a plan, a canal: Panama" 4 | // 输出: true 5 | 6 | const str = "A man, a plan, a canal: Panama"; 7 | // const isPalindrome = (s) => { 8 | // const str = s.replace(/\W|_/g, '').toLowerCase(); 9 | // return str.split('').reverse().join('') === str; 10 | // } 11 | 12 | const isPalindrome = (s) => { 13 | const str = s.replace(/\W|_/g, '').toLowerCase(); 14 | let left = 0; 15 | let right = str.length -1; 16 | while(left < right) { 17 | if(str[left] !== str[right]) { 18 | return false; 19 | } 20 | left ++; 21 | right --; 22 | } 23 | return true; 24 | } 25 | 26 | console.log(isPalindrome(str)); -------------------------------------------------------------------------------- /tiku/leetCode/maximum-sum-subarray.js: -------------------------------------------------------------------------------- 1 | /* 2 | 求解连续最大子序列和 3 | Input: [-2,1,-3,4,-1,2,1,-5,4], 4 | Output: 6 5 | Explanation: [4,-1,2,1] has the largest sum = 6. 6 | */ 7 | 8 | const maximumSum = (arr) => { 9 | let maximumSumArr = []; 10 | let max = -Number.MAX_VALUE; 11 | for(let i = 0; i < arr.length; i++) { 12 | for(let j = i+1; j < arr.length; j++) { 13 | const _arr = arr.slice(i, j+1); 14 | const sum= _arr.reduce((pre, cur) => pre + cur); 15 | if(sum > max) { 16 | maximumSumArr = _arr; 17 | max = sum; 18 | } 19 | } 20 | } 21 | console.log("maximumSum -> maximumSumArr", max) 22 | return max; 23 | } 24 | maximumSum([-2,1,-3,4,-1,2,1,-5,4]) 25 | 26 | const maximumSum2 = (arr) => { 27 | let maximumSumArr = []; 28 | let max = -Number.MAX_VALUE; 29 | for(let i = 0; i < arr.length; i++) { 30 | let sum = 0; 31 | let sumArr = []; 32 | for(let j = i+1; j < arr.length; j++) { 33 | sum += arr[j]; 34 | sumArr.push(arr[j]); 35 | if (sum > max) { 36 | max = sum; 37 | maximumSumArr = [...sumArr]; 38 | } 39 | } 40 | } 41 | console.log("max", max, maximumSumArr); 42 | return max; 43 | } 44 | maximumSum2([-2,1,-3,4,-1,2,1,-5,4]) -------------------------------------------------------------------------------- /tiku/leetCode/symmetric-tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个二叉树,检查它是否是镜像对称的。 3 | 4 | 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。{left, val, right} 5 | 6 | 1 7 | / \ 8 | 2 2 9 | / \ / \ 10 | 3 4 4 3 11 | 12 | 但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的: 13 | 14 | 1 15 | / \ 16 | 2 2 17 | \ \ 18 | 3 3 19 | 20 | 进阶: 21 | 22 | 你可以运用递归和迭代两种方法解决这个问题吗? 23 | */ 24 | 25 | 26 | // 递归 27 | var isSymmetric = function(root) { 28 | if(root === null) return true; 29 | const isMirror = (t1, t2) => { 30 | if(t1 === null && t2 === null) { 31 | return true; 32 | } 33 | if(t1 === null || t2 === null) { 34 | return false; 35 | } 36 | return t1.val === t2.val && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left); 37 | } 38 | return isMirror(root, root); 39 | }; 40 | 41 | 42 | // 迭代 43 | var isSymmetric1 = function(root) { 44 | const queue = [root, root]; 45 | while(queue.length) { 46 | const t1 = queue.pop(); 47 | const t2 = queue.pop(); 48 | if (t1 === null && t2 === null) continue; 49 | if(t1 === null || t2 === null) return false; 50 | if (t1.val === t2.val) { 51 | queue.push(t1.left, t2.right, t1.right, t2.left); 52 | } else { 53 | return false; 54 | } 55 | } 56 | return true; 57 | }; -------------------------------------------------------------------------------- /tiku/leetCode/two-sum.js: -------------------------------------------------------------------------------- 1 | // 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 2 | // 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 3 | // 示例: 4 | // 给定 nums = [2, 7, 11, 15], target = 9 5 | // 因为 nums[0] + nums[1] = 2 + 7 = 9 6 | // 所以返回 [0, 1] 7 | const nums = [11, 15, 3, 6, 7, 2, 7]; 8 | const target = 9; 9 | 10 | // const twoSum = (nums, target) => { 11 | // const cache = {}; 12 | // for(let i = 0; i <= nums.length -1; i++) { 13 | // const diff = target - nums[i]; 14 | // if (cache[diff] !== undefined) { 15 | // return [cache[diff], i]; 16 | // } 17 | // cache[nums[i]] = i; 18 | // } 19 | // } 20 | 21 | // const twoSum = (nums, target) => { 22 | // const cache = {}; 23 | // for(let i = 0; i <= nums.length -1; i++) { 24 | // const diff = target - nums[i]; 25 | // if (cache[nums[i]] !== undefined) { 26 | // return [cache[nums[i]], i]; 27 | // } 28 | // cache[diff] = i; 29 | // } 30 | // } 31 | 32 | // const twoSum = function (nums, target) { 33 | // const map = new Map(); 34 | // for (let i = 0; i < nums.length; i++) { 35 | // const diff = target - nums[i]; 36 | // if (map.has(diff)) { 37 | // return [map.get(diff), i]; 38 | // } 39 | // map.set(nums[i], i); 40 | // } 41 | // }; 42 | 43 | console.log("twoSum(nums, target)", twoSum(nums, target)); -------------------------------------------------------------------------------- /tiku/leetCode/剑指 Offer 32 - I. 从上到下打印二叉树.js: -------------------------------------------------------------------------------- 1 | /* 2 | 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。 3 | 4 |   5 | 6 | 例如: 7 | 给定二叉树: [3,9,20,null,null,15,7], 8 | 9 | 3 10 | / \ 11 | 9 20 12 | / \ 13 | 15 7 14 | 返回: 15 | 16 | [3,9,20,15,7] 17 |   18 | */ 19 | 20 | 21 | /** 22 | * Definition for a binary tree node. 23 | * function TreeNode(val) { 24 | * this.val = val; 25 | * this.left = this.right = null; 26 | * } 27 | */ 28 | /** 29 | * @param {TreeNode} root 30 | * @return {number[]} 31 | */ 32 | var levelOrder = function(root) { 33 | if(root === null) return []; 34 | const queue = [root]; 35 | const result = []; 36 | // 一层遍历,不需要存queue.length 37 | while(queue.length) { 38 | const node = queue.shift(); 39 | result.push(node.val); 40 | if(node.left) { 41 | queue.push(node.left); 42 | } 43 | if(node.right) { 44 | queue.push(node.right); 45 | } 46 | } 47 | return result; 48 | }; -------------------------------------------------------------------------------- /tiku/leetCode/剑指 Offer 32 - III. 从上到下打印二叉树 III.js: -------------------------------------------------------------------------------- 1 | /* 2 | 请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。 3 | 4 |   5 | 6 | 例如: 7 | 给定二叉树: [3,9,20,null,null,15,7], 8 | 9 | 3 10 | / \ 11 | 9 20 12 | / \ 13 | 15 7 14 | 返回其层次遍历结果: 15 | 16 | [ 17 | [3], 18 | [20,9], 19 | [15,7] 20 | ] 21 | 22 | */ 23 | 24 | /** 25 | * Definition for a binary tree node. 26 | * function TreeNode(val) { 27 | * this.val = val; 28 | * this.left = this.right = null; 29 | * } 30 | */ 31 | /** 32 | * @param {TreeNode} root 33 | * @return {number[][]} 34 | */ 35 | var levelOrder = function(root) { 36 | if(root === null) return []; 37 | const result = []; 38 | const travers = (node, res, idx) => { 39 | if(node === null) return; 40 | if(!res[idx]) { 41 | res[idx] = []; 42 | } 43 | // 奇数列开头添加即可 44 | if(idx % 2 === 0) { 45 | res[idx++].push(node.val); 46 | } else { 47 | res[idx++].unshift(node.val); 48 | } 49 | travers(node.left, res, idx); 50 | travers(node.right, res, idx); 51 | } 52 | travers(root, result, 0); 53 | return result; 54 | }; -------------------------------------------------------------------------------- /tiku/leetCode/剑指 Offer 33. 二叉搜索树的后序遍历序列.js: -------------------------------------------------------------------------------- 1 | /* 2 | 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 3 | 4 |   5 | 6 | 参考以下这颗二叉搜索树: 7 | 8 | 5 9 | / \ 10 | 2 6 11 | / \ 12 | 1 3 13 | 示例 1: 14 | 15 | 输入: [1,6,3,2,5] 16 | 输出: false 17 | 示例 2: 18 | 19 | 输入: [1,3,2,6,5] 20 | 输出: true 21 | 22 | */ 23 | 24 | /** 25 | * @param {number[]} postorder 26 | * @return {boolean} 27 | */ 28 | var verifyPostorder = function(postorder) { 29 | const len = postorder.length; 30 | if(len < 2) return true; 31 | const root = postorder[len -1]; 32 | let i = 0; 33 | while(i < len-1) { 34 | if(postorder[i] > root) { 35 | break; 36 | } 37 | i++; 38 | } 39 | const result = postorder.slice(i, len-1).every(v => v > root); 40 | if(result) { 41 | return verifyPostorder(postorder.slice(0, i)) && verifyPostorder(postorder.slice(i, len-1)) 42 | } else { 43 | return false; 44 | } 45 | }; -------------------------------------------------------------------------------- /tiku/leetCode/剑指offer-18-删除链表的节点.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。 3 | 4 | 返回删除后的链表的头节点。 5 | 6 | 注意:此题对比原题有改动 7 | 8 | 示例 1: 9 | 10 | 输入: head = [4,5,1,9], val = 5 11 | 输出: [4,1,9] 12 | 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 13 | 示例 2: 14 | 15 | 输入: head = [4,5,1,9], val = 1 16 | 输出: [4,5,9] 17 | 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. 18 | 19 | */ 20 | 21 | /** 22 | * Definition for singly-linked list. 23 | * function ListNode(val) { 24 | * this.val = val; 25 | * this.next = null; 26 | * } 27 | */ 28 | /** 29 | * @param {ListNode} head 30 | * @param {number} val 31 | * @return {ListNode} 32 | */ 33 | 34 | var deleteNode = function(head, val) { 35 | let start = new ListNode(0); 36 | start.next = head; 37 | let node = start; 38 | while (node.next) { 39 | if (node.next.val === val) { 40 | node.next = node.next.next; 41 | break; 42 | } 43 | node = node.next; 44 | } 45 | return start.next; 46 | }; 47 | 48 | // var deleteNode = function(head, val) { 49 | // let node = head; 50 | // while(node) { 51 | // if(node.val === val) { 52 | // node.val = node.next.val; 53 | // node.next = node.next.next; 54 | // } 55 | // let next = node.next; 56 | // if(next && next.next === null && next.val === val) { 57 | // node.next = null; 58 | // } 59 | // node = node.next; 60 | // } 61 | // return head; 62 | // }; 63 | -------------------------------------------------------------------------------- /tiku/leetCode/剑指offer34二叉树指定路径和的所有路径.js: -------------------------------------------------------------------------------- 1 | /* 2 | 输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。 3 | 4 |   5 | 6 | 示例: 7 | 给定如下二叉树,以及目标和 sum = 22, 8 | 9 | 5 10 | / \ 11 | 4 8 12 | / / \ 13 | 11 13 4 14 | / \ / \ 15 | 7 2 5 1 16 | 返回: 17 | 18 | [ 19 | [5,4,11,2], 20 | [5,8,4,5] 21 | ] 22 | 23 | /** 24 | * Definition for a binary tree node. 25 | * function TreeNode(val) { 26 | * this.val = val; 27 | * this.left = this.right = null; 28 | * } 29 | */ 30 | /** 31 | * @param {TreeNode} root 32 | * @param {number} sum 33 | * @return {number[][]} 34 | */ 35 | var pathSum = function(root, sum) { 36 | const res = []; 37 | const arr = []; 38 | const travers = (node) => { 39 | if(node === null) { 40 | return; 41 | }; 42 | arr.push(node.val); 43 | if(arr.reduce((pre, cur) => pre+cur) === sum && node.left === null && node.right === null) { 44 | res.push([...arr]); 45 | } 46 | travers(node.left); 47 | travers(node.right); 48 | arr.pop(); 49 | } 50 | travers(root); 51 | return res; 52 | }; -------------------------------------------------------------------------------- /tiku/leetCode/剑指offer54-二叉搜索树第k大的节点.js: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一棵二叉搜索树,请找出其中第k大的节点。 3 | 4 |   5 | 6 | 示例 1: 7 | 8 | 输入: root = [3,1,4,null,2], k = 1 9 | 3 10 | / \ 11 | 1 4 12 | \ 13 |   2 14 | 输出: 4 15 | 示例 2: 16 | 17 | 输入: root = [5,3,6,2,4,null,null,1], k = 3 18 | 5 19 | / \ 20 | 3 6 21 | / \ 22 | 2 4 23 | / 24 | 1 25 | 输出: 4 26 |   27 | 28 | 限制: 29 | 30 | 1 ≤ k ≤ 二叉搜索树元素个数 31 | /** 32 | * Definition for a binary tree node. 33 | * function TreeNode(val) { 34 | * this.val = val; 35 | * this.left = this.right = null; 36 | * } 37 | */ 38 | /** 39 | * @param {TreeNode} root 40 | * @param {number} k 41 | * @return {number} 42 | */ 43 | // 二处搜索树的中序遍历为排序后的节点 44 | // 递归 45 | var kthLargest = function(root, k) { 46 | const res = []; 47 | const travers = (node) => { 48 | if(!node) return null; 49 | travers(node.left); 50 | res.push(node.val); 51 | travers(node.right); 52 | } 53 | travers(root); 54 | return res[res.length - k]; 55 | }; 56 | 57 | 58 | // 迭代 59 | var kthLargest1 = function(root, k) { 60 | const res = []; 61 | const stack = []; 62 | while(root || stack.length){ 63 | while(root) { 64 | stack.push(root); 65 | root = root.right; 66 | } 67 | const node = stack.pop(); 68 | res.push(node.val); 69 | root = node.left; 70 | } 71 | return res[k - 1]; 72 | }; -------------------------------------------------------------------------------- /tiku/leetCode/旅行商问题.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/tiku/leetCode/旅行商问题.js -------------------------------------------------------------------------------- /tiku/leetCode/连续子数组的最大和.js: -------------------------------------------------------------------------------- 1 | /* 2 | 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 3 | 4 | 要求时间复杂度为O(n)。 5 | 6 | 示例1: 7 | 8 | 输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 9 | 输出: 6 10 | 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 11 |   12 | 13 | 提示: 14 | 15 | 1 <= arr.length <= 10^5 16 | -100 <= arr[i] <= 100 17 | 18 | */ 19 | 20 | 21 | // 假定 list[j] ~ list[k] 最大 22 | // 那么 list[k]的累加 - list[j] 的累加,就等于 list[j] ~ list[k] 23 | // 且 list[j] 的累加 必定是 list[k]的累加过程中的 最小值 24 | function LSS(list) { 25 | const len = list.length; 26 | let max = list[0]; 27 | let min = 0; 28 | let sum = 0; 29 | for (let i = 0; i < len; i++) { 30 | sum += list[i]; 31 | if (sum - min > max) max = sum - min; 32 | if (sum < min) { 33 | min = sum; 34 | } 35 | } 36 | 37 | return max; 38 | } 39 | // console.log(LSS([-2,-3,1,4])) 40 | // console.log(LSS([1,-2,-3,4])) 41 | // console.log(LSS([-2,1,4,-3])) 42 | 43 | 44 | // 暴力解法 45 | function LSS1(nums) { 46 | const len = nums.length; 47 | let max = -Number.MAX_VALUE; 48 | for (let i = 0; i < len; i++) { 49 | let sum = 0; 50 | for (let j = i; j < len; j++) { 51 | sum += nums[j]; 52 | max = Math.max(sum, max); 53 | } 54 | } 55 | return max; 56 | } 57 | // console.log(LSS1([-1])); 58 | // console.log(LSS1([-2,1,-3,4,-1,2,1,-5,4])); 59 | 60 | 61 | // 动态规划 62 | function maxSubArray2(nums) { 63 | const len = nums.length; 64 | let sum = max = nums[0]; 65 | for (let i = 1; i < len; i++) { 66 | if(sum > 0) { 67 | sum += nums[i]; 68 | } else { 69 | sum = nums[i]; 70 | } 71 | max = Math.max(sum, max); 72 | } 73 | return max; 74 | } 75 | console.log(maxSubArray2([-1])); 76 | console.log(maxSubArray2([-2,1,-3,4,-1,2,1,-5,4])); -------------------------------------------------------------------------------- /tiku/sort/bubbleSort.js: -------------------------------------------------------------------------------- 1 | const bubbleSort = (arr) => { 2 | for(let i = 0; i < arr.length; i++) { 3 | for(let j = 0; j < arr.length - i - 1; j++) { 4 | if(arr[j] > arr[j+1]) { 5 | let tmp = arr[j]; 6 | arr[j] = arr[j+1]; 7 | arr[j+1] = tmp; 8 | } 9 | } 10 | } 11 | return arr; 12 | } 13 | 14 | const bubbleSort1 = (arr) => { 15 | for(let i = 0; i < arr.length; i++) { 16 | let finish = true; 17 | for(let j = 0; j < arr.length - i - 1; j++) { 18 | if(arr[j] > arr[j+1]) { 19 | [arr[j], arr[j+1]] = [arr[j+1], arr[j]]; 20 | finish = false; 21 | } 22 | } 23 | if(finish) { 24 | break; 25 | } 26 | } 27 | return arr; 28 | } 29 | 30 | const bubbleSort3 = (arr) => { 31 | let i = arr.length - 1; 32 | // 换成while也一样 33 | for(; i > 0;) { 34 | let pos = 0; 35 | for(let j = 0; j < i; j++) { 36 | if(arr[j] > arr[j+1]) { 37 | pos = j; 38 | [arr[j], arr[j+1]] = [arr[j+1], arr[j]]; 39 | finish = false; 40 | } 41 | } 42 | i = pos; 43 | } 44 | return arr; 45 | } 46 | 47 | const arr = [12, 1,3,6,8,7,5,2, 1]; 48 | console.log(bubbleSort(arr)); 49 | // console.log(bubbleSort1(arr)); 50 | // console.log(bubbleSort3(arr)); -------------------------------------------------------------------------------- /tiku/sort/insertSort.js: -------------------------------------------------------------------------------- 1 | const insertSort = (arr) => { 2 | for(let i = 1; i < arr.length; i++) { 3 | // 选择一个标的 4 | let target = i; 5 | //已排列表循环 6 | // 一轮循环以后,说明 i(包括 i)之前的已经排序好了 7 | for(let j = i-1; j >= 0; j--) { 8 | // 插入已排列表 9 | if(arr[target] < arr[j]) { 10 | // 小的插到前面 11 | [arr[target], arr[j]] = [arr[j], arr[target]]; 12 | // 交换后,原来的 target 的下标,变成了j 13 | // 起始下标是 i,第一次交换,是将 i-1 移动到了 i 14 | // target 变为了 i - 1,即 j 15 | // 所以每次 arr[target] 是同一个值依次和前面已排序的值做比较 16 | // 找到最终位置 17 | target = j; 18 | } else { 19 | // 如果已排列表中,最大的一个都比 target 小,那本次循环可以跳过 20 | break; 21 | } 22 | } 23 | } 24 | return arr; 25 | } 26 | 27 | 28 | const arr = [1,3,6,8,7,5,2, 1, 12]; 29 | console.log(insertSort(arr)) -------------------------------------------------------------------------------- /tiku/sort/selectSort.js: -------------------------------------------------------------------------------- 1 | // 选择排序 2 | // 选择一个最小的,按循环放到前面 3 | const selectSort = arr => { 4 | for(let i = 0; i < arr.length; i++) { 5 | // i 为最小的数字的下标 6 | let minIndex = i; 7 | for(let j = i + 1; j < arr.length; j++) { 8 | // 找到最小值的下标 9 | if(arr[j] < arr[minIndex]) { 10 | minIndex = j; 11 | } 12 | } 13 | [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]] 14 | } 15 | return arr; 16 | } 17 | 18 | 19 | const arr = [1,3,6,8,7,5,2, 1, 12]; 20 | console.log(selectSort(arr)) -------------------------------------------------------------------------------- /tiku/structure/circulateList.js: -------------------------------------------------------------------------------- 1 | // 单项循环链表 2 | // 类似单链表,只不过最后一个元素(tail)的next指向第一个元素(head) 3 | 4 | // 双向循环链表 5 | // 类似双向链表,除了最后一个元素(tail)的next指向第一个元素(head)以外 6 | // 第一个元素(head)的prev,将指向最后一个元素(tail) -------------------------------------------------------------------------------- /tiku/structure/info.md: -------------------------------------------------------------------------------- 1 | ### 基本数据结构 2 | * 列表 3 | * 栈 4 | > 后进先出 5 | * 队列 6 | > 先进先出 7 | * 链表 8 | * 单向列表 9 | * 双向列表 10 | * 循环列表 -------------------------------------------------------------------------------- /tiku/structure/list.js: -------------------------------------------------------------------------------- 1 | class List{ 2 | constructor() { 3 | this.dataStore = []; 4 | this.listSize = 0; 5 | this.pos = 0; 6 | } 7 | get length() { 8 | return this.listSize; 9 | } 10 | clear() { 11 | this.dataStore = []; 12 | this.listSize = 0; 13 | this.pos = 0; 14 | } 15 | find(ele) { 16 | return this.dataStore.indexOf(ele); 17 | } 18 | append(ele) { 19 | this.dataStore[this.listSize++] = ele; 20 | } 21 | remove(ele) { 22 | this.dataStore = this.dataStore.filter(e => e !== ele); 23 | } 24 | insert(ele, after) { 25 | const index = this.find(after); 26 | if (index > -1) { 27 | this.dataStore.splice(index+1, 0, ele); 28 | return true; 29 | } 30 | return false; 31 | } 32 | contains(ele) { 33 | return this.find(ele) !== -1; 34 | } 35 | front() { 36 | this.pos = 0; 37 | } 38 | end() { 39 | this.pos = this.listSize - 1; 40 | } 41 | prev() { 42 | if (this.pos >= 0) { 43 | this.pos--; 44 | } 45 | } 46 | hasPrev() { 47 | return this.pos > 0; 48 | } 49 | next() { 50 | if (this.pos < this.listSize) { 51 | this.pos++; 52 | } 53 | } 54 | hasNext() { 55 | return this.pos < this.listSize; 56 | } 57 | moveTo(to){ 58 | this.pos = to; 59 | } 60 | currPos() { 61 | return this.pos; 62 | } 63 | getElement() { 64 | return this.dataStore[this.pos]; 65 | } 66 | } 67 | 68 | const list = new List(); 69 | const arr = new Array(10).fill(''); 70 | arr.forEach(() => list.append(Math.random().toString(16).slice(1))); 71 | 72 | for(list.front(); list.hasNext(); list.next()) { 73 | console.log(list.currPos(), list.getElement()) 74 | } 75 | console.log(list.currPos(), list.getElement()) -------------------------------------------------------------------------------- /tiku/structure/loselose.js: -------------------------------------------------------------------------------- 1 | // 散列函数 2 | 3 | // 优化方式(本质都是为了解决key值存储位置的冲突情况),分离链接和线性探查做的是万一出现冲突该怎么存储,而 djb2 则是尽可能不让设置出现冲突 4 | 5 | // 分离链接 6 | // 利用链表处理相同 key值的情况 7 | 8 | 9 | // 线性探查 10 | // 不停的 index++ 直到有合适的空位存储该value 11 | 12 | 13 | // djb2 -------------------------------------------------------------------------------- /tiku/structure/queue.js: -------------------------------------------------------------------------------- 1 | // 先进先出 2 | class Queue { 3 | constructor() { 4 | this.dataStore = []; 5 | } 6 | enqueue(ele) { 7 | this.dataStore.push(ele); 8 | } 9 | dequeue() { 10 | return this.dataStore.shift(); 11 | } 12 | isEmpty() { 13 | return this.dataStore.length === 0; 14 | } 15 | front(){ 16 | // 队列首元素 17 | return this.dataStore[0] 18 | } 19 | get size() { 20 | return this.dataStore.length; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tiku/structure/stack.js: -------------------------------------------------------------------------------- 1 | // 后进先出 2 | class Stack { 3 | constructor() { 4 | this.dataStore = []; 5 | } 6 | push(ele) { 7 | this.dataStore.push(ele); 8 | } 9 | pop() { 10 | return this.dataStore.pop(); 11 | } 12 | peek() { 13 | return this.dataStore[this.dataStore.length - 1]; // 获取栈顶元素 14 | } 15 | clear() { 16 | this.dataStore = []; 17 | } 18 | isEmpty() { 19 | return this.dataStore.length === 0; 20 | } 21 | get size() { 22 | return this.dataStore.length; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "ts-node": "^8.4.1", 13 | "typescript": "^3.6.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "removeComments": false, 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true, 11 | "outDir": "lib", 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "strictNullChecks": true, 15 | "sourceMap": true, 16 | "baseUrl": ".", 17 | "rootDir": ".", 18 | "jsx": "preserve", 19 | "jsxFactory": "Taro.createElement", 20 | "allowJs": true, 21 | "resolveJsonModule": true, 22 | "typeRoots": [ 23 | "node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "dist" 29 | ], 30 | "compileOnSave": false 31 | } 32 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "node": "registry:dt/node#7.0.0+20170322231424" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /typings/globals/node/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/a4a912a0cd1849fa7df0e5d909c8625fba04e49d/node/index.d.ts", 5 | "raw": "registry:dt/node#7.0.0+20170322231424", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/a4a912a0cd1849fa7df0e5d909c8625fba04e49d/node/index.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/user.jpg -------------------------------------------------------------------------------- /vue/ast/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /vue/ast/ast.js: -------------------------------------------------------------------------------- 1 | function add() { // 柯里化函数 2 | let arg = [...arguments]; 3 | let rt = 0; 4 | let fn = function() { 5 | arg.push(...arguments); 6 | rt = arg.reduce((a, b) => a + b); 7 | return fn; 8 | }; 9 | fn.toString = function() { 10 | return rt; 11 | }; 12 | return fn; 13 | } 14 | 15 | let a = { 16 | n: 1, 17 | toString() { 18 | return this.n ++; 19 | } 20 | }; 21 | if (a == 1 && a == 2 && a ==3) { 22 | console.log('oh nanana'); 23 | } -------------------------------------------------------------------------------- /vue/ast/ast.md: -------------------------------------------------------------------------------- 1 | 在线解析 http://esprima.org/demo/parse.html# 2 | 相关文章 https://segmentfault.com/a/1190000016231512 3 | ## ast 抽象语法树 4 | > Babel、webpack等许多前端工具都用到了ast 5 | 6 | Babel的编译过程和大多数其他语言的编译器大致相同,可以分为三个阶段。 7 | 8 | 1. 解析(PARSE):将代码字符串解析成抽象语法树。 9 | 2. 转换(TRANSFORM):对抽象语法树进行转换操作。 10 | 3. 生成(GENERATE): 根据变换后的抽象语法树再生成代码字符串。 11 | 12 | ### ast的常用包 recast 13 | * 安装 14 | ``` 15 | npm i recast -S 16 | ``` 17 | * 使用 运行 node read ast 查看ast中的js语法树 18 | ``` javascript 19 | recast.parse(code); // 解析器 20 | recast.visit // 遍历 21 | const {variableDeclaration, variableDeclarator, functionExpression} = recast.types.builders; // 模具 22 | recast.print(ast).code; // 还原成code 23 | recast.prettyPrint(ast, { tabWidth: 2 }).code; //还原时可以指定参数 24 | // 即 25 | recast.print(recast.parse(source)).code === source 26 | // TNT,即recast.types.namedTypes 判断AST对象类型 27 | // TNT.Node.assert() 类型不匹配 28 | // TNT.Node.check() 判断类型是否一致,并输出False和True 29 | // Node可以替换成任意AST对象,例如TNT.ExpressionStatement.check(),TNT.FunctionDeclaration.assert() 30 | ``` 31 | -------------------------------------------------------------------------------- /vue/ast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "recast": "^0.17.3" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /vue/ast/read.js: -------------------------------------------------------------------------------- 1 | const recast = require('recast'); 2 | const TNT = recast.types.namedTypes; 3 | recast.run( function(ast, printSource){ // 读取js语法树 4 | // printSource(ast) // 控制台打印 5 | recast.visit(ast, { // 遍历语法树 6 | visitExpressionStatement: function({node}) { 7 | // console.log(node); // AST对象 8 | // printSource(node); // AST对象对应的源码 9 | // 判断是否为ExpressionStatement,正确则输出一行字。 10 | // if(TNT.ExpressionStatement.check(node)){ 11 | // console.log('这是一个ExpressionStatement') 12 | // } 13 | // 判断是否为ExpressionStatement,正确不输出,错误则全局报错 14 | TNT.ExpressionStatement.assert(node); 15 | return false 16 | // return false 或者以下写法 17 | // this.traverse(path) // path.node 18 | } 19 | }); 20 | }); -------------------------------------------------------------------------------- /vue/mvvm/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |
9 | 10 | 11 |
12 |

待办:{{doingList.length}}

13 |
    14 |
  • 15 | {{v.title}} 16 | 17 |
  • 18 |
19 |

已完成:{{doneList.length}}

20 |
    21 |
  • 22 | {{v.title}} 23 | 24 |
  • 25 |
26 |
27 | 28 | 29 | 50 | -------------------------------------------------------------------------------- /vue/mvvm/2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |
9 | 姓名:
10 | 年龄:
11 | 职业:
12 |

13 | 输出:{{info}} 14 |

15 | 16 |
17 | 18 | 19 | 41 | -------------------------------------------------------------------------------- /vue/mvvm/Observer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2019/3/10. 3 | */ 4 | // 在需要订阅的地方(如:模版编译),添加观察者(watcher),并立刻通过取值触发指定属性的get方法,从而将观察者添加进订阅系统Dep,然后在 Set 的时候,进行 notify,通知给所有观察者进行响应的update 5 | class Dep{ 6 | constructor() { 7 | this.subs = []; 8 | } 9 | addSub(sub) { 10 | this.subs.push(sub); 11 | } 12 | notify() { 13 | console.log(this.subs); 14 | } 15 | } 16 | function observer(data, key, val) { 17 | let dep = new Dep(); 18 | Object.defineProperty(data, key, { 19 | get() { 20 | Dep.target && dep.addSub(Dep.target); 21 | return val; 22 | }, 23 | set(newval) { 24 | val = newval; 25 | dep.notify(); 26 | } 27 | }) 28 | } 29 | 30 | let obj = {name: 'a', age: 18, sex: 'man'}; 31 | Object.keys(obj).forEach(k => { 32 | observer(obj, k, obj[k]); 33 | }); 34 | 35 | Dep.target = 1; 36 | console.log(obj.name); 37 | Dep.target = null; 38 | 39 | Dep.target = 2; 40 | console.log(obj.age); 41 | Dep.target = null; 42 | 43 | obj.name = 'b'; 44 | obj.age = 32; 45 | obj.name = 'cccccc'; 46 | console.log(obj.name, obj.age); -------------------------------------------------------------------------------- /vue/rollup/1.md: -------------------------------------------------------------------------------- 1 | ## Rollup 是一个 JavaScript 模块打包器 2 | ### 安装 3 | npm install rollup --global # or `npm i rollup -g` for short 4 | 5 | rollup --help 或 rollup -h 查看帮助 6 | 7 | ### 创建项目 8 | * mkdir -p my-rollup-project/src 9 | 10 | cd my-rollup-project 11 | * ``` 12 | // src/main.js 13 | import foo from './foo.js'; 14 | export default function () { 15 | console.log(foo); 16 | } 17 | ``` 18 | * ``` 19 | // src/foo.js 20 | export default 'hello world!'; 21 | ``` 22 | * rollup main.js -f cjs 23 | > -f 选项(--output.format 的缩写)指定了所创建 bundle 的类型——这里是 CommonJS(在 Node.js 中运行)。由于没有指定输出文件,所以会直接打印在 命令面板 中: 24 | * rollup main.js -o bundle.js -f cjs 25 | > 生成为bundle.js文件 26 | * rollup main.js -f cjs > bundle.js 27 | > 生成为bundle.js文件,但是效率不高 28 | * rollup -c 29 | > 运行rollup.config.js中的默认配置,实际这种方式速度较快 30 | * rollup -c -o bundle-2.js 31 | > 相同的命令行选项会覆盖掉默认文件的配置 -------------------------------------------------------------------------------- /vue/rollup/bundle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Created by wyw on 2019/3/6. 5 | */ 6 | var foo = 'hello world!'; 7 | 8 | /** 9 | * Created by wyw on 2019/3/6. 10 | */ 11 | function main () { 12 | console.log(foo); 13 | } 14 | 15 | module.exports = main; 16 | -------------------------------------------------------------------------------- /vue/rollup/foo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2019/3/6. 3 | */ 4 | export default 'hello world!'; -------------------------------------------------------------------------------- /vue/rollup/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyw on 2019/3/6. 3 | */ 4 | import foo from './foo.js'; 5 | export default function () { 6 | console.log(foo); 7 | } -------------------------------------------------------------------------------- /vue/rollup/rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | input: 'main.js', 3 | output: { 4 | file: 'bundle.js', 5 | format: 'cjs' 6 | } 7 | }; -------------------------------------------------------------------------------- /vue/组件间通信/eventBus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | const bus = new Vue({ 3 | data () { 4 | return { 5 | wsData: {} 6 | } 7 | }, 8 | created () { 9 | this.$on('onMessage', val => { 10 | this.wsData = val; 11 | }) 12 | }, 13 | computed: { 14 | stateWs() { 15 | if (this.wsData.type === 'updateState') { 16 | return this.wsData; 17 | } 18 | } 19 | } 20 | }); 21 | export default bus; -------------------------------------------------------------------------------- /webpack/1.md: -------------------------------------------------------------------------------- 1 | ### scripts 2 | ```javascript 3 | // "build": "set NODE_ENV=production&& webpack" production&&之间不能有空格 4 | // 如果要兼容Windows和Mac 可以安装 cross-env 5 | // "build": " cross-env NODE_ENV=production webpack" 6 | ``` -------------------------------------------------------------------------------- /webpack/ast/1.js: -------------------------------------------------------------------------------- 1 | // https://astexplorer.net/ -------------------------------------------------------------------------------- /webpack/modules/common.js: -------------------------------------------------------------------------------- 1 | // vscode 配置node提示 2 | // cnpm i typings -g 3 | // typings install dt~node --global --save 4 | // CommonJS 规范 module.exports 主要用于node 5 | const fs = require('fs'); 6 | const req = (modulePath) => { 7 | const module = { 8 | exports: {} 9 | } 10 | const content = fs.readFileSync(modulePath, 'utf-8'); 11 | const fun = (module, exports, req) => { 12 | eval(content); 13 | return module.exports; 14 | } 15 | return fun(module, module.exports); 16 | } 17 | const log = req('./test.js'); 18 | log(1); // 1 -------------------------------------------------------------------------------- /webpack/modules/es6.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/webpack/modules/es6.js -------------------------------------------------------------------------------- /webpack/modules/require.js: -------------------------------------------------------------------------------- 1 | // AMD 规范 RequireJS 2 | const factories = {}; 3 | const _require = (deps, factor) => { 4 | depsFn = deps.map(dep => { 5 | const _deps = factories[dep]._deps; 6 | _deps.forEach((_dep, i) => _require([_dep], (d) => { 7 | _deps[i] = d; 8 | })); 9 | return factories[dep].apply(null, _deps); 10 | }); 11 | factor.apply(null, depsFn); 12 | } 13 | const define = (name, deps, factor) => { 14 | factories[name] = factor; 15 | factories[name]._deps = deps; 16 | } 17 | define('name', [], () => { 18 | return 'zs'; 19 | }); 20 | define('age', [], () => { 21 | return 18; 22 | }); 23 | define('say', ['name', 'age'], (name, age) => { 24 | return () => {console.log(`我的名字是${name}, 今年${age}`)}; 25 | }); 26 | _require(['name', 'age', 'say'], (name, age, say) => { 27 | console.log("name", name); 28 | console.log("age", age); 29 | say(); 30 | }) 31 | 32 | // CMD 规范 SeaJS 33 | // ES6 export/import 34 | // es6 export 变量 导出的还是引用;commonjs 则是值的拷贝 -------------------------------------------------------------------------------- /webpack/modules/test.js: -------------------------------------------------------------------------------- 1 | const log = (p) => { 2 | console.log(p); 3 | } 4 | module.exports = log; -------------------------------------------------------------------------------- /webpack/tapable/AsyncParallelHook.js: -------------------------------------------------------------------------------- 1 | // 异步并行 -------------------------------------------------------------------------------- /webpack/tapable/AsyncSeriseHook.js: -------------------------------------------------------------------------------- 1 | // 异步串行 -------------------------------------------------------------------------------- /webpack/tapable/SyncHook.js: -------------------------------------------------------------------------------- 1 | class SyncHook{ 2 | constructor(parmas) { 3 | this.hooks = []; 4 | } 5 | tap(name, fn) { 6 | this.hooks.push(fn); 7 | } 8 | call(...arg) { 9 | this.hooks.forEach(fn => fn(...arg)); 10 | } 11 | } 12 | // 串行同步执行,不关心返回值 13 | const hooks = new SyncHook(['num']); 14 | hooks.tap('one', (num) => { 15 | console.log(num+1); 16 | }) 17 | hooks.tap('two', (num) => { 18 | console.log(num+2); 19 | }) 20 | hooks.tap('three', (num) => { 21 | console.log(num+3); 22 | }) 23 | hooks.call(1); -------------------------------------------------------------------------------- /webpack/tapable/SyncLoopHook.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/webpack/tapable/SyncLoopHook.js -------------------------------------------------------------------------------- /webpack/tapable/SyncWaterHook.js: -------------------------------------------------------------------------------- 1 | const _compose = arr => (...arg) => arr.reduce((pre,cur) => cur(pre), arg); 2 | const queue = arr => (...arg) => { 3 | let result = null; 4 | arr.forEach((fn, i) => { 5 | if (i===0) { 6 | result = fn(...arg); 7 | }else{ 8 | result = fn(result); 9 | } 10 | }) 11 | } 12 | class SyncHook{ 13 | constructor(parmas) { 14 | this.hooks = []; 15 | } 16 | tap(name, fn) { 17 | this.hooks.push(fn); 18 | } 19 | call(...arg) { 20 | queue(this.hooks)(...arg) 21 | } 22 | } 23 | // 将执行完的返回值传给下一个函数 24 | const hooks = new SyncHook(['num']); 25 | hooks.tap('one', (num) => { 26 | console.log(num); 27 | return num+1; 28 | }) 29 | hooks.tap('two', (num) => { 30 | console.log(num); 31 | return num+2; 32 | }) 33 | hooks.tap('three', (num) => { 34 | console.log(num); 35 | return num+3 36 | }) 37 | hooks.call(1); -------------------------------------------------------------------------------- /webpack/w-pack/dist/ddd.js: -------------------------------------------------------------------------------- 1 | !function(modules) { 2 | const installModules = {}; 3 | function __w_require__(moduleId) { 4 | // 是否缓存 5 | if(installModules[moduleId]){ 6 | return installModules[moduleId].exports 7 | }; 8 | let module = installModules[moduleId] = { 9 | exports: {} 10 | }; 11 | modules[moduleId].call(modules.exports, module, module.exports, __w_require__); 12 | return module.exports; 13 | } 14 | return __w_require__('./test/index.js'); 15 | }({"./test/index.js":function(module, exports, __w_require__){ 16 | eval(`const logName = __w_require__("./test/log.js"); 17 | const name = __w_require__("./test/name.js"); 18 | const composeName = __w_require__("./util/composeName.js"); 19 | __w_require__("./test/style.css"); 20 | logName(name); 21 | const createComponent = () => { 22 | const div = document.createElement('div'); 23 | div.classList.add('name'); 24 | div.innerText = composeName(name); 25 | document.body.appendChild(div); 26 | } 27 | createComponent();`) 28 | },"./test/log.js":function(module, exports, __w_require__){ 29 | eval(`const composeName = __w_require__("./util/composeName.js"); 30 | const log = (name) => { 31 | console.log(composeName(name)); 32 | } 33 | module.exports = log;`) 34 | },"./util/composeName.js":function(module, exports, __w_require__){ 35 | eval(`module.exports = (name) => { 36 | return '我的名字叫'+name; 37 | }`) 38 | },"./test/name.js":function(module, exports, __w_require__){ 39 | eval(`module.exports = '张三';`) 40 | },"./test/style.css":function(module, exports, __w_require__){ 41 | eval(` 42 | let style = document.createElement("style"); 43 | style.innerText = ".name{ color: red;}"; 44 | document.head.appendChild(style); 45 | `) 46 | },}); -------------------------------------------------------------------------------- /webpack/w-pack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /webpack/w-pack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "w-pack", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "w-pack": "./src/index.js" 8 | }, 9 | "scripts": { 10 | "build": "node ./src/index.js", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "", 14 | "license": "ISC" 15 | } 16 | -------------------------------------------------------------------------------- /webpack/w-pack/src/output.js: -------------------------------------------------------------------------------- 1 | !function(modules) { 2 | const installModules = {}; 3 | function __w_require__(moduleId) { 4 | // 是否缓存 5 | if(installModules[moduleId]){ 6 | return installModules[moduleId].exports 7 | }; 8 | let module = installModules[moduleId] = { 9 | exports: {} 10 | }; 11 | modules[moduleId].call(modules.exports, module, module.exports, __w_require__); 12 | return module.exports; 13 | } 14 | return __w_require__('__entry__'); 15 | }({__depsContent__}); -------------------------------------------------------------------------------- /webpack/w-pack/test/index.js: -------------------------------------------------------------------------------- 1 | const logName = require('./log.js'); 2 | const name = require('./name.js'); 3 | const composeName = require('../util/composeName.js'); 4 | require('./style.css'); 5 | logName(name); 6 | const createComponent = () => { 7 | const div = document.createElement('div'); 8 | div.classList.add('name'); 9 | div.innerText = composeName(name); 10 | document.body.appendChild(div); 11 | } 12 | createComponent(); -------------------------------------------------------------------------------- /webpack/w-pack/test/log.js: -------------------------------------------------------------------------------- 1 | const composeName = require('../util/composeName.js'); 2 | const log = (name) => { 3 | console.log(composeName(name)); 4 | } 5 | module.exports = log; -------------------------------------------------------------------------------- /webpack/w-pack/test/name.js: -------------------------------------------------------------------------------- 1 | module.exports = '张三'; -------------------------------------------------------------------------------- /webpack/w-pack/test/style.css: -------------------------------------------------------------------------------- 1 | .name{ 2 | color: red; 3 | } -------------------------------------------------------------------------------- /webpack/w-pack/util/composeName.js: -------------------------------------------------------------------------------- 1 | module.exports = (name) => { 2 | return '我的名字叫'+name; 3 | } -------------------------------------------------------------------------------- /webpack/w-pack/w.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 项目入口 3 | entry:"./test/index.js", 4 | // 项目出口 5 | output:{ 6 | filename:'ddd.js' 7 | } 8 | } -------------------------------------------------------------------------------- /webpack/webpack-demo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | assets/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | -------------------------------------------------------------------------------- /webpack/webpack-demo/dev-server.js: -------------------------------------------------------------------------------- 1 | const webpackDevServer = require('webpack-dev-server'); 2 | const webpack = require('webpack'); 3 | 4 | const config = require('./webpack.config.js'); 5 | const options = { 6 | contentBase: '/', 7 | hot: true, 8 | host: 'localhost' 9 | }; 10 | 11 | webpackDevServer.addDevServerEntrypoints(config, options); 12 | const compiler = webpack(config); 13 | const server = new webpackDevServer(compiler, options); 14 | 15 | server.listen(5000, 'localhost', () => { 16 | console.log('dev server listening on port 5000'); 17 | }); -------------------------------------------------------------------------------- /webpack/webpack-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /webpack/webpack-demo/loaders/test-loader.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const loaderUtils = require('loader-utils'); // 获取loader配置参数 options 3 | const schemaUtils = require('schema-utils'); // 校验options参数类型 4 | module.exports = function(source) { 5 | // source 源内容 或者是上个loader处理后的内容 6 | const options = loaderUtils.getOptions(this); // 获取options 7 | return source; 8 | } -------------------------------------------------------------------------------- /webpack/webpack-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-demo", 3 | "sideEffects": [ 4 | "./src/pages/app/fn.js" 5 | ], 6 | "version": "1.0.0", 7 | "description": "", 8 | "main": "main.js", 9 | "scripts": { 10 | "build": "set NODE_ENV=production&& webpack", 11 | "test": "echo \"Error: no test specified\" && exit 1", 12 | "dll": "webpack -p --progress --config webpack.dll.conf.js", 13 | "watch": "webpack --watch", 14 | "start": "webpack-dev-server --open", 15 | "server": "node dev-server.js" 16 | }, 17 | "author": "", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "@babel/core": "^7.8.6", 21 | "@babel/preset-env": "^7.8.6", 22 | "@babel/preset-react": "^7.8.3", 23 | "babel-loader": "8.0.0-beta.0", 24 | "clean-webpack-plugin": "^1.0.0", 25 | "css-loader": "^1.0.1", 26 | "express": "^4.16.4", 27 | "file-loader": "^2.0.0", 28 | "html-webpack-plugin": "^3.2.0", 29 | "mini-css-extract-plugin": "^0.9.0", 30 | "node-sass": "^4.13.1", 31 | "react-hot-loader": "^4.12.19", 32 | "sass-loader": "^8.0.2", 33 | "style-loader": "^0.23.1", 34 | "uglifyjs-webpack-plugin": "^2.2.0", 35 | "url-loader": "^1.1.2", 36 | "webpack": "^4.26.1", 37 | "webpack-cli": "^3.3.11", 38 | "webpack-dev-middleware": "^3.4.0", 39 | "webpack-dev-server": "^3.1.10", 40 | "loader-utils": "^1.4.0", 41 | "schema-utils": "^2.6.4" 42 | }, 43 | "dependencies": { 44 | "dplayer": "^1.25.0", 45 | "react": "^16.13.0", 46 | "react-dom": "^16.13.0", 47 | "react-router-dom": "5.1.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /webpack/webpack-demo/plugins/inline-script-plugin.js: -------------------------------------------------------------------------------- 1 | class InlineScriptPlugin{ 2 | constructor(options) { 3 | this.options = options; 4 | } 5 | apply(compiler){ 6 | const that = this; 7 | compiler.hooks.compilation.tap('compilation', function(compilation) { 8 | // htmlWebpackPluginAlertAssetTags 是 html-webpack-plugin 添加到 compilation 的钩子 9 | compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync('changeTags', function(data, callback){ 10 | data.body.forEach(function(bodyTag){ 11 | const src = bodyTag.attributes.src; 12 | delete bodyTag.attributes.src; 13 | const souceKey = src.replace(that.options.regSrc, '$1'); 14 | const source = compilation.assets[souceKey].source(); 15 | bodyTag.innerHTML = source; 16 | }); 17 | callback(null, data) // 将处理后的内容返回去 18 | }) 19 | }) 20 | } 21 | } 22 | module.exports = InlineScriptPlugin; -------------------------------------------------------------------------------- /webpack/webpack-demo/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const webpack = require('webpack'); 3 | const webpackDevMiddleware = require('webpack-dev-middleware'); 4 | 5 | const app = express(); 6 | const config = require('./webpack.config.js'); 7 | const compiler = webpack(config); 8 | 9 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 10 | // configuration file as a base. 11 | app.use(webpackDevMiddleware(compiler, { 12 | publicPath: config.output.publicPath 13 | })); 14 | 15 | // Serve the files on port 3000. 16 | app.listen(3000, function () { 17 | console.log('Example app listening on port 3000!\n'); 18 | }); -------------------------------------------------------------------------------- /webpack/webpack-demo/src/common/utils.js: -------------------------------------------------------------------------------- 1 | export const createImg = (src, parent) => { 2 | const img = document.createElement('img'); 3 | img.src= src; 4 | img.style.width= '300px'; 5 | parent.appendChild(img); 6 | } -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/app/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './style.css'; 3 | export default function App() { 4 | return
5 | Hi Vchat! hihihihi 6 |
7 | }; -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/app/fn.js: -------------------------------------------------------------------------------- 1 | export const log = (val) => { 2 | log(val); 3 | } 4 | 5 | export const getDate = () => { 6 | return Date.now(); 7 | } -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/app/main.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { hot } from 'react-hot-loader/root'; 3 | import { 4 | BrowserRouter as Router, Route, Switch 5 | } from 'react-router-dom'; 6 | import { render } from 'react-dom'; 7 | import App from './app'; 8 | import Test from './test'; 9 | import Play from './play'; 10 | import img from 'static/img/2.jpg'; 11 | 12 | const Main = hot(() => { 13 | useEffect(() => { 14 | // util.createImg(img, document.querySelector("#app")); 15 | }, []) 16 | return 17 | 18 | 19 | 20 | 21 | 22 | 23 | }); 24 | export default render(
, document.getElementById('app')); -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/app/play.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import 'dplayer/dist/DPlayer.min.css'; 3 | import DPlayer from 'dplayer'; 4 | import './style.css'; 5 | import flv from '../../../assets/react/01-react-props【瑞客论坛 www.ruike1.com】.flv'; 6 | export default function Play() { 7 | useEffect(() => { 8 | const dp = new DPlayer({ 9 | container: document.getElementById('paly'), 10 | video: { 11 | url: flv, 12 | type: 'flv' 13 | }, 14 | }); 15 | }, []) 16 | return
17 | paly 18 |
19 | }; -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/app/style.css: -------------------------------------------------------------------------------- 1 | .red{ 2 | color: red; 3 | background: url('~static/img/1.jpg'); 4 | /* css中用alias别名前面要加~ */ 5 | background-position: center; 6 | background-size: cover; 7 | } 8 | #map{ 9 | width:300px; 10 | height: 300px; 11 | } 12 | #paly{ 13 | width:1200px; 14 | height: 600px; 15 | margin: 0 auto; 16 | } -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/app/test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './style.css'; 3 | import {log} from './fn' 4 | export default function Test() { 5 | log('999'); 6 | return
7 | test 8 |
9 | }; -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/home/index.js: -------------------------------------------------------------------------------- 1 | import img from 'static/img/1.jpg'; 2 | util.createImg(img, document.querySelector("#app")) -------------------------------------------------------------------------------- /webpack/webpack-demo/src/pages/test/index.js: -------------------------------------------------------------------------------- 1 | console.log('test') -------------------------------------------------------------------------------- /webpack/webpack-demo/static/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/webpack/webpack-demo/static/img/1.jpg -------------------------------------------------------------------------------- /webpack/webpack-demo/static/img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuyawei/fe-code/fa6b5dc749b44876d6e27b0b760e9e88566ffcd1/webpack/webpack-demo/static/img/2.jpg -------------------------------------------------------------------------------- /webpack/webpack-demo/vendor.manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"vendor_8d15a5","content":{"./node_modules/_react@16.13.0@react/index.js":{"id":0,"buildMeta":{"providedExports":true}},"./node_modules/_object-assign@4.1.1@object-assign/index.js":{"id":1,"buildMeta":{"providedExports":true}},"./node_modules/_react@16.13.0@react/cjs/react.production.min.js":{"id":3,"buildMeta":{"providedExports":true}},"./node_modules/_react-dom@16.13.0@react-dom/index.js":{"id":4,"buildMeta":{"providedExports":true}},"./node_modules/_react-dom@16.13.0@react-dom/cjs/react-dom.production.min.js":{"id":5,"buildMeta":{"providedExports":true}},"./node_modules/_scheduler@0.19.0@scheduler/index.js":{"id":6,"buildMeta":{"providedExports":true}},"./node_modules/_scheduler@0.19.0@scheduler/cjs/scheduler.production.min.js":{"id":7,"buildMeta":{"providedExports":true}}}} -------------------------------------------------------------------------------- /webpack/webpack-demo/webpack.dll.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | module.exports = { 4 | entry: { 5 | vendor: ['react', 'react-dom'] 6 | }, 7 | output: { 8 | path: path.join(__dirname, 'dist'), 9 | filename: 'dll.[name]_[hash:6].js', 10 | library: '[name]_[hash:6]' 11 | }, 12 | plugins: [ 13 | new webpack.DllPlugin({ 14 | path: path.join(__dirname, '[name].manifest.json'), 15 | name: '[name]_[hash:6]' 16 | }) 17 | ] 18 | } --------------------------------------------------------------------------------