├── docs ├── js │ ├── func │ │ ├── call │ │ │ ├── index.js │ │ │ └── index.md │ │ └── apply │ │ │ └── index.md │ ├── node │ │ └── eventemitter │ │ │ └── index.md │ ├── browser │ │ └── getURLParameters │ │ │ └── index.md │ ├── utils │ │ ├── sleep │ │ │ ├── index.js │ │ │ └── index.md │ │ └── curry │ │ │ ├── index.js │ │ │ └── index.md │ └── array │ │ ├── forEach │ │ ├── index.js │ │ ├── index.test.js │ │ └── index.md │ │ ├── map │ │ ├── index.test.js │ │ ├── index.js │ │ └── index.md │ │ ├── filter │ │ ├── index.js │ │ └── index.md │ │ └── reduce │ │ ├── index.js │ │ ├── index.test.js │ │ └── index.md ├── public │ ├── CNAME │ ├── robots.txt │ ├── logo.png │ ├── ios-180x180.png │ ├── pwa-128x128.png │ ├── pwa-144x144.png │ ├── pwa-192x192.png │ ├── pwa-256x256.png │ ├── pwa-512x512.png │ └── logo.svg ├── posts │ ├── this │ │ └── index.md │ └── guide │ │ └── index.md ├── code.png ├── .vitepress │ ├── theme │ │ ├── main.scss │ │ ├── index.js │ │ └── Layout.vue │ ├── components │ │ └── ScrollProgress.vue │ └── config.js ├── index.md ├── scss │ └── mixin │ │ └── index.md ├── vite.config.ts ├── react │ └── send_code │ │ └── index.md └── vue │ └── scroll_progress_bar │ └── index.md ├── .gitignore ├── README.md ├── .github └── workflows │ └── deploy.yml ├── scripts └── pwa.ts ├── package.json ├── logo.svg └── LICENSE /docs/js/func/call/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/public/CNAME: -------------------------------------------------------------------------------- 1 | code.lencx.tech -------------------------------------------------------------------------------- /docs/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / -------------------------------------------------------------------------------- /docs/posts/this/index.md: -------------------------------------------------------------------------------- 1 | # This 关键字详解 2 | 3 | > TODO 4 | -------------------------------------------------------------------------------- /docs/js/func/apply/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 👉 apply 3 | 4 | > TODO 5 | -------------------------------------------------------------------------------- /docs/js/node/eventemitter/index.md: -------------------------------------------------------------------------------- 1 | ## 👉 EventEmitter 2 | 3 | > TODO 4 | -------------------------------------------------------------------------------- /docs/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/code.png -------------------------------------------------------------------------------- /docs/js/browser/getURLParameters/index.md: -------------------------------------------------------------------------------- 1 | # 👉 getURLParameters 2 | 3 | > TODO 4 | -------------------------------------------------------------------------------- /docs/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/logo.png -------------------------------------------------------------------------------- /docs/public/ios-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/ios-180x180.png -------------------------------------------------------------------------------- /docs/public/pwa-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/pwa-128x128.png -------------------------------------------------------------------------------- /docs/public/pwa-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/pwa-144x144.png -------------------------------------------------------------------------------- /docs/public/pwa-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/pwa-192x192.png -------------------------------------------------------------------------------- /docs/public/pwa-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/pwa-256x256.png -------------------------------------------------------------------------------- /docs/public/pwa-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lencx/code-snippets/HEAD/docs/public/pwa-512x512.png -------------------------------------------------------------------------------- /docs/js/utils/sleep/index.js: -------------------------------------------------------------------------------- 1 | function sleep(ms) { 2 | return new Promise(resolve => setTimeout(resolve, ms)); 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .history 4 | 5 | node_modules 6 | yarn.lock 7 | package-lock.json 8 | 9 | # build 10 | docs/.vitepress/dist 11 | 12 | draft/ 13 | -------------------------------------------------------------------------------- /docs/js/array/forEach/index.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myEach = function(callback) { 2 | for (var i = 0; i < this.length; i++) { 3 | callback(this[i], i, this); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/js/array/map/index.test.js: -------------------------------------------------------------------------------- 1 | require('./index'); 2 | 3 | test('map', () => { 4 | const newData = [1, 2, 3, 4].myMap((item) => item * 2); 5 | expect(newData).toStrictEqual([2, 4, 6, 8]); 6 | }) 7 | -------------------------------------------------------------------------------- /docs/js/array/map/index.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myMap = function(callback) { 2 | arr = []; 3 | for (var i = 0; i < this.length; i++) { 4 | arr[i] = callback(this[i], i, this); 5 | } 6 | return arr; 7 | } 8 | -------------------------------------------------------------------------------- /docs/js/array/filter/index.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myFilter = function (callback, context) { 2 | var arr = []; 3 | for (var i = 0; i < this.length; i++) { 4 | if (callback.call(context, this[i], i, this)) { 5 | arr.push(this[i]); 6 | } 7 | } 8 | return arr; 9 | }; -------------------------------------------------------------------------------- /docs/js/array/forEach/index.test.js: -------------------------------------------------------------------------------- 1 | require('./index'); 2 | 3 | const testData = [1, 2, 3, 4]; 4 | 5 | test('forEach', () => { 6 | testData.myEach((item, index, self) => { 7 | expect(item).toBe(testData[index]); 8 | expect(self).toBe(testData); 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /docs/js/utils/curry/index.js: -------------------------------------------------------------------------------- 1 | function curry(func) { 2 | return function curried(...args) { 3 | if (args.length >= func.length) { 4 | return func.apply(this, args); 5 | } 6 | return function(...args2) { 7 | return curried.apply(this, args.concat(args2)); 8 | }; 9 | }; 10 | } -------------------------------------------------------------------------------- /docs/posts/guide/index.md: -------------------------------------------------------------------------------- 1 | # 🪧 指南 2 | 3 | ## 类别 4 | 5 | - [Article](../../posts/this/) - 瞎折腾 / 笔记 6 | - [JavaScript](../../js/array/forEach/) - JS 实现系列 7 | - [CSS](../../scss/mixin/) - CSS 实现系列 8 | 9 | ## 参考 10 | 11 | - [[MDN] JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript) 12 | - [The Modern JavaScript Tutorial](https://javascript.info) 13 | - [You Don't Know JS Yet (book series) - 2nd Edition](https://github.com/getify/You-Dont-Know-JS) 14 | - [Node.js Docs](https://nodejs.org/zh-cn/docs/) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | [“What I Cannot create, I do not understand”](https://www.quora.com/What-did-Richard-Feynman-mean-when-he-said-What-I-cannot-create-I-do-not-understand) — Richard Feynman 6 | 7 | --- 8 | 9 | ## Features 10 | 11 | - JavaScript 12 | - array 13 | - browser 14 | - function 15 | - node 16 | - object 17 | - utils 18 | - CSS 19 | - mixin 20 | - function 21 | 22 | ## License 23 | 24 | CC0 1.0 License © 2021 [lencx](https://github.com/lencx) 25 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v2 15 | with: 16 | node-version: '14' 17 | - run: yarn 18 | - run: yarn build 19 | 20 | - name: Deploy 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GH_TOKEN }} 24 | publish_dir: ./docs/.vitepress/dist 25 | force_orphan: true -------------------------------------------------------------------------------- /docs/js/array/reduce/index.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myReduce = function(callback, initialVal) { 2 | // 初始值是一个可选参数 3 | var accumulator = initialVal || undefined; 4 | // TODO: 未处理边界情况,详情可查看 MDN 文档 5 | // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#polyfill 6 | // 1. 如果 callback 为 null 或非 function,则报错 7 | // 2. 如果数组为空,并且不存在初始值,则报错 8 | for (var i = 0; i < this.length; i++) { 9 | if (accumulator) { 10 | accumulator = callback.call(accumulator, accumulator, this[i], i, this); 11 | } else { 12 | accumulator = this[i]; 13 | } 14 | } 15 | return accumulator; 16 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/main.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --font-family-base: -apple-system, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 3 | } 4 | 5 | .home-hero { 6 | img { 7 | width: 160px !important; 8 | } 9 | } 10 | 11 | .home-content { 12 | padding: 0 20px !important; 13 | text-align: center; 14 | } 15 | 16 | hr { 17 | margin: 40px 0; 18 | border: none; 19 | border-top: 2px dashed #d8d8d8; 20 | } 21 | 22 | .giscus { 23 | background-color: #f8f8f8; 24 | padding: 20px; 25 | margin-top: 50px; 26 | border-radius: 8px; 27 | border: solid 3px rgba(#000, 0.03); 28 | } 29 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /logo.svg 4 | heroAlt: JS 5 | actionText: 👉 开始探索 6 | tagline: 手写 Code Snippets 系列 7 | # actionText: Get Started 8 | actionLink: /posts/guide/ 9 | features: 10 | - title: 瞎折腾 / 笔记 11 | details: 在折腾中学习,在记录中成长 12 | - title: JS 实现系列 13 | details: 包含 Array,Browser,Node.js 等 14 | - title: CSS 实现系列 15 | details: 包含 Animation,Layout,Mixin 等 16 | footer: CC0 1.0 Licensed | Copyright © 2021-present lencx 17 | --- 18 | 19 | [“What I Cannot create, I do not understand”](https://www.quora.com/What-did-Richard-Feynman-mean-when-he-said-What-I-cannot-create-I-do-not-understand) — Richard Feynman 20 | -------------------------------------------------------------------------------- /docs/js/array/reduce/index.test.js: -------------------------------------------------------------------------------- 1 | require('./index'); 2 | 3 | test('reduce', () => { 4 | const indexArr = []; 5 | const newData = [1, 2, 3, 4].myReduce((acc, cur, index) => { 6 | indexArr.push(index); 7 | return acc + cur; 8 | }); 9 | expect(newData).toStrictEqual(10); 10 | expect(indexArr).toStrictEqual([1, 2, 3]); 11 | }) 12 | 13 | test('reduce initialValue', () => { 14 | const indexArr = []; 15 | const newData = [1, 2, 3, 4].myReduce((acc, cur, index) => { 16 | indexArr.push(index); 17 | return acc + cur; 18 | }, 2); 19 | expect(newData).toStrictEqual(12); 20 | expect(indexArr).toStrictEqual([0, 1, 2, 3]); 21 | }) 22 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from "vitepress/theme"; 2 | import { watch } from "vue"; 3 | import Layout from "./Layout.vue"; 4 | import "./main.scss"; 5 | 6 | export default { 7 | ...DefaultTheme, 8 | Layout, 9 | enhanceApp({ router }) { 10 | watch( 11 | () => router.route.path, 12 | (path) => { 13 | if (typeof window !== 'undefined') { 14 | const _path = localStorage.getItem("URL_PATH", path); 15 | if (_path !== path) { 16 | localStorage.setItem("URL_PATH", path); 17 | window.location.reload(); 18 | } 19 | } 20 | } 21 | ); 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/Layout.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /docs/scss/mixin/index.md: -------------------------------------------------------------------------------- 1 | # Mixin 2 | 3 | ## 清除浮动 4 | 5 | ```scss 6 | @mixin clearfix { 7 | &::after { 8 | content: ''; 9 | display: table; 10 | clear: both; 11 | } 12 | } 13 | ``` 14 | 15 | ## 文本省略 16 | 17 | ```scss 18 | // 单行文本省略 19 | @mixin single-text($width) { 20 | -o-text-overflow: ellipsis; 21 | text-overflow: ellipsis; 22 | overflow: hidden; 23 | white-space: nowrap; 24 | max-width: $width; 25 | } 26 | 27 | // 多行文本省略 28 | @mixin multi-text($width, $row) { 29 | max-width: $width; 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | display: -webkit-box; 33 | -webkit-line-clamp: $row; 34 | /*! autoprefixer: off */ 35 | -webkit-box-orient: vertical; 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /scripts/pwa.ts: -------------------------------------------------------------------------------- 1 | import fg from 'fast-glob' 2 | import fs from 'fs-extra' 3 | 4 | export async function fix() { 5 | const names = await fg('docs/.vitepress/dist/**/*.html', { onlyFiles: true }) 6 | 7 | await Promise.all(names.map(async(i) => { 8 | let html = await fs.readFile(i, 'utf-8') 9 | 10 | html = html.replace('', 11 | ` 12 | 19 | `).trim() 20 | 21 | await fs.writeFile(i, html, 'utf-8') 22 | })) 23 | } 24 | 25 | fix() -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-snippets", 3 | "version": "1.0.0", 4 | "description": "✍️ code snippets - 手写系列", 5 | "author": "lencx ", 6 | "license": "CC0 1.0", 7 | "scripts": { 8 | "dev": "vitepress dev docs", 9 | "build": "vitepress build docs && yarn pwa", 10 | "pwa": "esno scripts/pwa.ts", 11 | "test": "jest", 12 | "watch": "jest --watchAll", 13 | "coverage": "jest --coverage" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/lencx/code-snippets" 18 | }, 19 | "homepage": "https://github.com/lencx/code-snippets", 20 | "devDependencies": { 21 | "@vitejs/plugin-vue-jsx": "^1.1.8", 22 | "esno": "^0.9.1", 23 | "jest": "^27.0.6", 24 | "sass": "^1.38.1", 25 | "vite-plugin-pwa": "^0.11.2", 26 | "vitepress": "^0.18.0" 27 | }, 28 | "dependencies": { 29 | "@giscus/vue": "^1.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/js/array/map/index.md: -------------------------------------------------------------------------------- 1 | # 👉 map 2 | 3 | > map() 方法创建一个新数组,其结果是该数组中的每个元素调用一次给定函数后的返回值 4 | 5 | ## 💠 语法 6 | 7 | ```js 8 | var new_array = arr.map(function callback(currentValue[, index[, array]]) { 9 | // Return element for new_array 10 | }[, thisArg]) 11 | ``` 12 | 13 | - `callback` - 生成新数组元素的函数,接收一至三个参数: 14 | - `currentValue` - 数组中当前正在处理的元素 15 | - `index` [可选] - 正在处理的元素在数组中的索引 16 | - `array` [可选] - 调用 map 方法的数组本身 17 | - `thisArg` [可选] - 当执行回调函数 callback 时,用作 this 的值 18 | - 返回值 - 一个由原数组每个元素执行回调函数的结果组成的新数组 19 | 20 | ## ✍️ 实现 21 | 22 | ```js 23 | Array.prototype.myMap = function(callback) { 24 | arr = []; 25 | for (var i = 0; i < this.length; i++) { 26 | arr[i] = callback(this[i], i, this); 27 | } 28 | return arr; 29 | } 30 | ``` 31 | 32 | ## 📌 测试 33 | 34 | ```js 35 | const arr = [1, 2, 3]; 36 | const newVal = arr.myMap((i) => Math.pow(i, 2)); 37 | console.log(newVal); // [1, 9, 16] 38 | ``` 39 | 40 | --- 41 | 42 | ## 🔗 参考 43 | 44 | - [[MDN] Array.prototype.map()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 45 | -------------------------------------------------------------------------------- /docs/js/array/forEach/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 👉 forEach 3 | 4 | > forEach() 方法对数组的每个元素执行一次给定的函数 5 | 6 | ## 💠 语法 7 | 8 | ```js 9 | arr.forEach(callback(currentValue [, index [, array]])[, thisArg]) 10 | ``` 11 | 12 | - `callback` - 为数组中每个元素执行的函数,接收一至三个参数: 13 | - `currentValue` - 数组中当前正在处理的元素 14 | - `index` [可选] - 正在处理的元素在数组中的索引 15 | - `array` [可选] - 调用 forEach 方法的数组本身 16 | - `thisArg` [可选] - 当执行回调函数 callback 时,用作 this 的值。 17 | - 返回值 - `undefined` 18 | 19 | ## ⚠️ 注意 20 | 21 | 除了抛出异常以外,没有办法中止或跳出 forEach() 循环 22 | 23 | ## ✍️ 实现 24 | 25 | ```js 26 | Array.prototype.myEach = function(callback) { 27 | for (var i = 0; i < this.length; i++) { 28 | callback(this[i], i, this); 29 | } 30 | } 31 | ``` 32 | 33 | ## 📌 测试 34 | 35 | ```js 36 | const arr = ['js', 'css', 'html']; 37 | arr.myEach((item) => console.log(item)) 38 | ``` 39 | 40 | --- 41 | 42 | ## 🔗 参考 43 | 44 | - [[MDN] NodeList.prototype.forEach()](https://developer.mozilla.org/zh-CN/docs/Web/API/NodeList/forEach) 45 | - [[MDN] Array.prototype.forEach()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) 46 | -------------------------------------------------------------------------------- /docs/js/array/filter/index.md: -------------------------------------------------------------------------------- 1 | # 👉 filter 2 | 3 | > filter() 方法创建一个新数组, 返回符合函数的条件的所有元素。 4 | 5 | ## 💠 语法 6 | 7 | ```js 8 | var newArray = arr.filter(callback(element[, index[, array]])[, thisArg]) 9 | ``` 10 | 11 | - `callback` - 用来检测数组的每个元素的函数,返回 `true` 表示该元素通过检测,保留元素,`false` 则舍弃,接收一至三个参数: 12 | - `element` - 数组中当前正在处理的元素 13 | - `index` [可选] - 正在处理的元素在数组中的索引 14 | - `array` [可选] - 调用 filter 方法的数组本身 15 | - `thisArg` [可选] - 当执行回调函数 callback 时,用作 this 的值 16 | - 返回值 - 一个新的、由通过检测的元素组成的数组,如果没有任何数组元素通过检测,则返回空数组。 17 | 18 | ## ✍️ 实现 19 | 20 | ```js 21 | Array.prototype.myFilter = function (callback, context) { 22 | var arr = []; 23 | for (var i = 0; i < this.length; i++) { 24 | if (callback.call(context, this[i], i, this)) { 25 | arr.push(this[i]); 26 | } 27 | } 28 | return arr; 29 | }; 30 | ``` 31 | 32 | ## 📌 测试 33 | 34 | ```js 35 | const arr = [1, 2, 3, 4, 5]; 36 | const newVal = arr.myFilter((i) => i > 3); 37 | console.log(newVal); // [4, 5] 38 | ``` 39 | 40 | --- 41 | 42 | ## 🔗 参考 43 | 44 | - [[MDN] Array.prototype.filter()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) 45 | -------------------------------------------------------------------------------- /docs/js/func/call/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 👉 call 3 | 4 | > call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数 5 | 6 | ## 💠 语法 7 | 8 | ```js 9 | function.call(thisArg, arg1, arg2, ...) 10 | ``` 11 | 12 | ## 🟠 示例 13 | 14 | ```js 15 | function sayHello(message) { 16 | console.log(message, this.name); 17 | } 18 | const obj = { 19 | name: 'lencx' 20 | }; 21 | sayHello.call(obj, 'welcome'); // welcome lencx 22 | ``` 23 | 24 | 示例输出 `welcome lencx`,以上面例子分析一个调用函数的基本原理,我们就会注意到这些: 25 | 26 | 1. 调用原型函数 `call` 改变了 `this` 的指向。即调用函数变成了 `obj.sayHello` 27 | 28 | ```js 29 | function sayHello(message) { 30 | console.log(message, this.name); 31 | } 32 | const obj = { 33 | name: 'lencx', 34 | sayHello: function(message) { 35 | console.log(message, this.name); 36 | } 37 | }; 38 | obj.sayHello('welcome'); // welcome lencx 39 | ``` 40 | 41 | 2. 传递给 `sayHello.call` 的任意参数作为 `arg1, arg2, ...` 传递给原始 `sayHello` 42 | 3. 不会对 `obj` 和 `sayHello` 产生副作用。即调用 `call` 不会以任何方式修改原始 `obj` 或 `sayHello` 43 | 44 | ## 🤔 思考 45 | 46 | > TODO 47 | 48 | ## ✍️ 实现 49 | 50 | ```js 51 | 52 | ``` 53 | 54 | ## 📌 测试 55 | 56 | ```js 57 | 58 | ``` 59 | 60 | --- 61 | 62 | ## 🔗 参考 63 | -------------------------------------------------------------------------------- /docs/vite.config.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/vuejs/vitepress/issues/241#issuecomment-780167185 2 | import vueJsx from '@vitejs/plugin-vue-jsx' 3 | import { VitePWA } from 'vite-plugin-pwa' 4 | 5 | export default { 6 | plugins: [ 7 | vueJsx(), 8 | VitePWA({ 9 | outDir: '.vitepress/dist', 10 | registerType: 'autoUpdate', 11 | injectRegister: 'inline', 12 | manifest: { 13 | name: '✍️ ', 14 | short_name: '✍️ ', 15 | theme_color: '#3eaf7c', 16 | icons: [ 17 | { 18 | src: '/pwa-128x128.png', 19 | sizes: '128x128', 20 | type: 'image/png', 21 | }, 22 | { 23 | src: '/pwa-144x144.png', 24 | sizes: '144x144', 25 | type: 'image/png', 26 | }, 27 | { 28 | src: '/pwa-192x192.png', 29 | sizes: '192x192', 30 | type: 'image/png', 31 | }, 32 | { 33 | src: '/pwa-256x256.png', 34 | sizes: '256x256', 35 | type: 'image/png', 36 | }, 37 | { 38 | src: '/pwa-512x512.png', 39 | sizes: '512x512', 40 | type: 'image/png', 41 | }, 42 | ], 43 | }, 44 | }), 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /docs/js/utils/curry/index.md: -------------------------------------------------------------------------------- 1 | # 👉 curry 2 | 3 | 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是 Moses Schönfinkel 和戈特洛布·弗雷格发明的。 4 | 5 | `柯里化是将一个接受多个参数的函数分解成一系列函数,每个函数只接受一个参数。` 例如:将 `f(a,b,c)` 变换为 `f(a)(b)(c)` 。 6 | 7 | ## ✍️ 实现 8 | 9 | ```js 10 | function curry(func) { 11 | return function curried(...args) { 12 | // 如果参数的数量(args.length)大于或等于原函数中定义的参数数量(func.length), 13 | // 则直接使用 func.apply 将参数传递。 14 | if (args.length >= func.length) { 15 | return func.apply(this, args); 16 | } 17 | // 否则,我们只得到一部分参数,此时还未调用 func, 18 | // 则返回一个新的匿名函数,重新柯里化,提供之前的参数(args)和当前匿名函数参数(args2)。 19 | return function(...args2) { 20 | return curried.apply(this, args.concat(args2)); 21 | }; 22 | }; 23 | } 24 | ``` 25 | 26 | ## 📌 测试 27 | 28 | ```js 29 | function sum(a, b, c) { 30 | return a + b + c; 31 | } 32 | 33 | const currySum = curry(sum); 34 | 35 | currySum(1, 2, 3); // 6 - 未柯里 36 | currySum(1)(2)(3); // 6 - 完全柯里 37 | currySum(1)(2, 3); // 6 - 第一个参数柯里 38 | ``` 39 | 40 | --- 41 | 42 | ## 🔗 参考 43 | 44 | - [[wiki] 柯里化](https://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96) 45 | - [What is 'Currying'?](https://stackoverflow.com/questions/36314/what-is-currying) 46 | - [[javascript.info] currying](https://javascript.info/currying-partials) 47 | -------------------------------------------------------------------------------- /docs/js/utils/sleep/index.md: -------------------------------------------------------------------------------- 1 | # 👉 sleep 2 | 3 | 指定时间内函数暂停执行。 在 C 或 PHP 等编程语言中,可以调用 `sleep(2)` 使程序暂停 2 秒。 Java 有 `Thread.sleep(2000)`,Python 有 `time.sleep(2)`,Go 有 `time.Sleep(2 * time.Second)`。 4 | 5 | JavaScript 没有原生的休眠功能,但由于引入了 promises(以及 ES2018 中的 `async/await`),我们可以用一种优雅地方式来实现此功能。 6 | 7 | ## ✍️ 实现 8 | 9 | ```js 10 | function sleep(ms) { 11 | return new Promise(resolve => setTimeout(resolve, ms)); 12 | } 13 | ``` 14 | 15 | ## 📌 测试 16 | 17 | ```js 18 | async function testSleep() { 19 | console.log('Taking a break...'); 20 | await sleep(2000); 21 | console.log('Two seconds later, showing sleep in a loop...'); 22 | 23 | // Sleep in loop 24 | for (let i = 0; i < 5; i++) { 25 | if (i === 3) await sleep(2000); 26 | console.log(i); 27 | } 28 | } 29 | 30 | testSleep(); 31 | ``` 32 | 33 | --- 34 | 35 | ## 🔗 参考 36 | 37 | - [What is the JavaScript version of sleep()?](https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep) 38 | - [[C] sleep(3) - Linux man page](https://linux.die.net/man/3/sleep) 39 | - [[PHP] sleep](https://www.php.net/manual/en/function.sleep.php) 40 | - [[Java] Pausing Execution with Sleep](https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html) 41 | - [[Python] time.sleep(secs)](https://docs.python.org/3/library/time.html#time.sleep) 42 | - [[Go] func Sleep](https://pkg.go.dev/time#Sleep) 43 | -------------------------------------------------------------------------------- /docs/js/array/reduce/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 👉 reduce 3 | 4 | > reduce() 方法对数组中的每个元素执行一次 reducer 函数(升序执行),将其结果汇总为单个返回值 5 | 6 | ## 💠 语法 7 | 8 | ```js 9 | arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue]) 10 | ``` 11 | 12 | - `callback` - 为数组中每个元素(如果没有提供 `initialValue` 则使用数组中第一个元素)执行的函数,包含四个参数: 13 | - `accumulator` - 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或 `initialValue`(见于下方) 14 | - `currentValue` - 数组中当前正在处理的元素 15 | - `index` [可选] - 正在处理的元素在数组中的索引,如果提供了 `initialValue`,则起始索引号为 `0`,否则从索引 `1` 起始 16 | - `array` [可选] - 调用 reduce 方法的数组本身 17 | - `initialValue` [可选] - 作为第一次调用 `callback` 函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 `reduce` 将报错。 18 | - 返回值 - 函数累计处理的结果 19 | 20 | ## ✍️ 实现 21 | 22 | ```js 23 | Array.prototype.myReduce = function(callback, initialVal) { 24 | // 初始值是一个可选参数 25 | var accumulator = initialVal || undefined; 26 | // TODO: 未处理边界情况,详情可查看 MDN 文档 27 | // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#polyfill 28 | // 1. 如果 callback 为 null 或非 function,则报错 29 | // 2. 如果数组为空,并且不存在初始值,则报错 30 | for (var i = 0; i < this.length; i++) { 31 | if (accumulator) { 32 | accumulator = callback.call(accumulator, accumulator, this[i], i, this); 33 | } else { 34 | accumulator = this[i]; 35 | } 36 | } 37 | return accumulator; 38 | } 39 | ``` 40 | 41 | ## 📌 测试 42 | 43 | ```js 44 | const arr = [1, 2, 3, 4]; 45 | const val = arr.myReduce((acc, cur) => acc + cur); 46 | console.log(val) // 10 47 | ``` 48 | 49 | --- 50 | 51 | ## 🔗 参考 52 | 53 | - [[MDN] NodeList.prototype.reduce()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) 54 | -------------------------------------------------------------------------------- /docs/react/send_code/index.md: -------------------------------------------------------------------------------- 1 | # 发送验证码 2 | 3 | ## ✍️ 实现 4 | 5 | `SendCode.tsx` 6 | 7 | ```tsx 8 | import React, { FC, useEffect, useRef, useState } from "react"; 9 | 10 | function useInterval(callback: () => void, delay: number) { 11 | const savedCallback = useRef(null); 12 | savedCallback.current = callback; 13 | 14 | useEffect(() => { 15 | function tick() { 16 | savedCallback.current(); 17 | } 18 | if (delay !== null) { 19 | const id = setInterval(tick, delay); 20 | return () => clearInterval(id); 21 | } 22 | }, [delay]); 23 | } 24 | 25 | export interface SendCodeProps { 26 | time?: number; 27 | onSend?: () => void; 28 | initText?: string; 29 | runText?: (v: string) => string; 30 | endText?: string; 31 | } 32 | 33 | const SendCode: FC = ({ time, onSend, initText, runText, endText }) => { 34 | const [text, setText] = useState(initText); 35 | const [second, setSecond] = useState(0); 36 | const [isRunning, setRunning] = useState(false); 37 | 38 | const handleSend = () => { 39 | setRunning(true); 40 | setText(runText(time)); 41 | onSend && onSend(); 42 | }; 43 | 44 | useInterval( 45 | () => { 46 | const val = second + 1; 47 | if (time === val) { 48 | setSecond(0); 49 | setRunning(false); 50 | setText(endText); 51 | } else { 52 | setSecond(val); 53 | setText(runText(time - val)); 54 | } 55 | }, 56 | isRunning ? 1000 : null 57 | ); 58 | 59 | return ( 60 | 63 | ); 64 | }; 65 | 66 | SendCode.defaultProps = { 67 | time: 60, 68 | initText: '获取验证码', 69 | runText: (v: string) => `${v}秒后重发`, 70 | endText: '重新获取验证码', 71 | }; 72 | 73 | export default SendCode; 74 | ``` 75 | 76 | --- 77 | 78 | ## 🔗 参考 79 | 80 | - [Making setInterval Declarative with React Hooks](https://overreacted.io/making-setinterval-declarative-with-react-hooks/) 81 | -------------------------------------------------------------------------------- /docs/.vitepress/components/ScrollProgress.vue: -------------------------------------------------------------------------------- 1 | 89 | 90 | 93 | 94 | 101 | -------------------------------------------------------------------------------- /docs/.vitepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: '✍️ ', 3 | base: '/', 4 | 5 | head: [ 6 | ['link', { rel: 'icon', href: '/logo.svg' }], 7 | // https://stackoverflow.com/questions/49568333/pwa-icons-are-not-used-in-ios-11-3 8 | ['link', { rel: 'apple-touch-icon', size: '180x180', href: '/ios-180x180.png' }], 9 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 10 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], 11 | ['meta', { name: 'msapplication-TileColor', content: '#3eaf7c' }], 12 | ['meta', { name: 'viewport', content: 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover' }], 13 | ['meta', { name: 'author', content: 'lencx' }], 14 | ['meta', { property: 'og:title', content: 'Code Snippets' }], 15 | ['meta', { property: 'og:image', content: 'https://code.lencx.tech/logo.png' }], 16 | ['meta', { property: 'og:description', content: '✍️ code snippets - 手写系列' }], 17 | ['meta', { name: 'twitter:card', content: 'summary_large_image' }], 18 | ['meta', { name: 'twitter:image', content: 'https://code.lencx.tech/logo.png' }], 19 | ], 20 | 21 | themeConfig: { 22 | docsDir: 'docs', 23 | repo: 'lencx/code-snippets', 24 | repoLabel: '点 ⭐️ 不迷路', 25 | docsBranch: 'main', 26 | editLinks: true, 27 | editLinkText: 'Edit this page', 28 | lastUpdated: 'Last Updated', 29 | 30 | nav: [ 31 | { text: 'Article', link: '/posts/guide/', activeMatch: '^/posts/' }, 32 | { text: 'JavaScript', link: '/js/array/forEach/', activeMatch: '^/js/' }, 33 | { text: 'React', link: '/react/send_code/', activeMatch: '^/react/' }, 34 | { text: 'Vue', link: '/vue/scroll_progress_bar/', activeMatch: '^/vue/' }, 35 | { text: 'CSS', link: '/scss/mixin/', activeMatch: '^/s?css/' }, 36 | ], 37 | 38 | sidebar: { 39 | '/posts/': getPosts(), 40 | '/js/': getJS(), 41 | '/react/': getReact(), 42 | '/vue/': getVue(), 43 | '/css/': getCss(), 44 | '/scss/': getCss(), 45 | } 46 | } 47 | } 48 | 49 | function getPosts() { 50 | return [ 51 | { text: 'Guide', link: '/posts/guide/' }, 52 | { 53 | text: 'Article', 54 | // collapsable: true, 55 | children: [ 56 | { text: 'This 关键字详解', link: '/posts/this/' }, 57 | ], 58 | } 59 | ] 60 | } 61 | 62 | function getJS() { 63 | return [ 64 | { 65 | text: 'Array', 66 | children: [ 67 | { text: 'forEach', link: '/js/array/forEach/' }, 68 | { text: 'map', link: '/js/array/map/' }, 69 | { text: 'filter', link: '/js/array/filter/' }, 70 | { text: 'reduce', link: '/js/array/reduce/' }, 71 | ], 72 | }, 73 | { 74 | text: 'Function', 75 | children: [ 76 | { text: 'call', link: '/js/func/call/' }, 77 | { text: 'apply', link: '/js/func/apply/' }, 78 | ], 79 | }, 80 | { 81 | text: 'Node.js', 82 | children: [ 83 | { text: 'Event Emitter', link: '/js/node/eventemitter/' }, 84 | ], 85 | }, 86 | { 87 | text: 'Utils', 88 | children: [ 89 | { text: 'curry', link: '/js/utils/curry/' }, 90 | { text: 'sleep', link: '/js/utils/sleep/' }, 91 | ], 92 | }, 93 | { 94 | text: 'Browser', 95 | children: [ 96 | { text: 'getURLParameters', link: '/js/browser/getURLParameters/' }, 97 | ], 98 | } 99 | ] 100 | } 101 | 102 | function getReact() { 103 | return [ 104 | { 105 | text: 'React', 106 | children: [ 107 | { text: 'SendCode', link: '/react/send_code/' }, 108 | ], 109 | }, 110 | ] 111 | } 112 | 113 | function getVue() { 114 | return [ 115 | { 116 | text: 'Vue3', 117 | children: [ 118 | { text: 'ScrollProgress', link: '/vue/scroll_progress_bar/' }, 119 | ], 120 | }, 121 | ] 122 | } 123 | 124 | function getCss() { 125 | return [ 126 | // { 127 | // text: 'CSS', 128 | // children: [], 129 | // }, 130 | { 131 | text: 'SCSS', 132 | children: [ 133 | { text: 'Mixin', link: '/scss/mixin/' }, 134 | ], 135 | }, 136 | ] 137 | } -------------------------------------------------------------------------------- /docs/public/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/vue/scroll_progress_bar/index.md: -------------------------------------------------------------------------------- 1 | # 滚动进度条 2 | 3 | 创建一个进度条,用来指示页面滚动百分比,主要有两点注意事项及一个思考: 4 | 5 | - 使用 `position: fixed` 将滚动进度条置于页面顶部,`z-index` 设置一个较大的值是为了保证元素在页面内容的最上层。 6 | - 使用 `EventTarget.addEventListener()` 和 `Element.scrollTop` 来确定文档的滚动百分比并将其设置为滚动进度条的宽度。 7 | - 思考 - 如果页面容器高度发生变化,会发生什么? 8 | 9 | ## 💠 核心 10 | 11 | ```html 12 |
13 | ``` 14 | 15 | ```css 16 | #scroll_progress_bar { 17 | position: fixed; 18 | top: 0; 19 | width: 0%; 20 | height: 4px; 21 | background: #7983ff; 22 | z-index: 10000; 23 | } 24 | ``` 25 | 26 | ```js 27 | const scrollProgress = document.getElementById('scroll_progress_bar'); 28 | // 滚动条高度 29 | const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; 30 | 31 | window.addEventListener('scroll', () => { 32 | const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; 33 | // 当前进度条进度 = 当前滚动条位置 / 滚动条高度 34 | scrollProgress.style.width = `${(scrollTop / height) * 100}%`; 35 | }); 36 | ``` 37 | 38 | ## ✍️ 实现 39 | 40 | `ScrollProgress.vue` 41 | 42 | ```vue 43 | 140 | 141 | 144 | 145 | 152 | ``` 153 | 154 | --- 155 | 156 | ## 🔗 参考 157 | 158 | - [[MDN] Document: scroll event](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/scroll_event) - 文档视图或者一个元素在滚动时,会触发元素的scroll事件。 159 | - [[MDN] MutationObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver) - 接口提供了监视对 `DOM树` 所做更改的能力。它被设计为旧的 `Mutation Events` 功能的替代品,该功能是 `DOM3 Events` 规范的一部分。 160 | - [Vue3 API](https://v3.vuejs.org/) 161 | - [props](https://v3.vuejs.org/api/options-data.html#props) 162 | - [\