# Lg-study **Repository Path**: yang-xiaowei2/lg-study ## Basic Information - **Project Name**: Lg-study - **Description**: js基础知识的进阶 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2022-10-22 - **Last Updated**: 2022-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 无敌的李鹏玺 ## javascipt深入理解 ### 函数式编程 #### 什么是高阶函数 * 函数作为参数 * 函数作为另一个函数的返回值 ##### 函数作为参数 ###### lx_forEach ```javascript function lx_forEach(array, fn) { for (var i = 0; i < array.length; i++) { fn(arr[i]); } } let arr = [1, 2, 4, 5, 6]; lx_forEach(arr, function (item) { console.log(item); }); ``` ###### lx_filter ``` function lx_filter(array, fn) { let results = []; for (var i = 0; i < array.length; i++) { if (fn(arr[i])) { results.push(arr[i]); } } return results; } let arr = [1, 2, 4, 5, 6]; console.log( lx_filter(arr, function (item) { return item % 2 == 0; }) ); ``` ###### lx_map ``` function lx_map(array, fn) { let results = []; for (const value of array) { results.push(fn(value)); } return results; } let arr = [1, 3, 5, 7, 9]; let map = lx_map(arr, (item) => item * 2); console.log(map); ``` ###### lx_some ``` function lx_some(array, fn) { let flag = false; for (const value of array) { if (fn(value)) { flag = true; break; } } return flag; } let arr = [1, 3, 5, 6, 9]; let some = lx_some(arr, (item) => item % 2 === 0); console.log(some); ``` ###### lx_every ```javascript function lx_every(array, fn) { let flag = true; for (const value of array) { if (!fn(value)) { flag = false; break; } } return flag; } let arr = [1, 3, 5, 7, 9]; let every = lx_every(arr, (item) => item % 2 === 1); console.log(every); ``` ##### 函数作为返回值 ###### base ``` function base() { let msg = "hello hanghang"; return function () { console.log(msg); } } ``` ###### lx_once ``` function lx_once(fn) { let done = false; return function () { if (!done) { done = true; return fn.apply(this, arguments); } }; } let pay = lx_once((money) => { console.log("支付了" + money + "元"); return money; }); const money = pay(1000); console.log(money, "moneymoney"); pay(1000); ``` ### 闭包 闭包:函数和其周围的状态(词法环境)的引用捆绑在一起形成闭包。 闭包的本质:函数在执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上移除;但是堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员。 ``` function makePower(power) { return function (number) { return Math.pow(number, power); }; } const power2 = makePower(2); console.log(power2(3)) ``` ### 纯函数 纯函数:相同的输入得到相同的输出 ,即映射。 优点:可以通过memorize减少 复杂代码二次执行的事件。 ``` // getNumber为纯函数 let getNumber = (a) => { console.log(new Date().getTime()); let e = 0; for (var i = 0; i < 1000000; i++) { e += i; e * i; } return a; }; // memorize做缓存处理 let memorize = (fn) => { let obj = {}; return function () { let key = JSON.stringify(arguments); obj[key] = obj[key] || fn.apply(fn, arguments); return obj[key]; }; }; const _memo = memorize(getNumber); console.log(_memo(1)); console.log(_memo(1)); console.log(_memo(1)); console.log(_memo(1)); ``` 纯函数的副作用:当一个变量在外面,纯函数中用到外部变量,外部变量的改变会导致纯函数不是纯函数。 ``` let num = 18; function is(n) { return n >= num; } ``` ### 函数柯里化 底层就是 函数嵌套函数 return一个函数 调用的时候形成闭包, 可以使得参数一个一个传 ,可形成不同的闭包。 ``` const getSum = (a, b, c) => a + b + c; function curry(fn) { return function curried(...args) { // 判断形参的个数和传参的个数 if (args.length < fn.length) { return function () { return curried(...args.concat(Array.from(arguments))); }; } return fn(...args); }; } const testCurry = curry(getSum); console.log(testCurry(1)(2, 3)); console.log(testCurry(1)(2)(3)); ``` ### 函数组合 ``` function compose(...args) { return function (a) { return args.reverse().reduce((pre, current) => current(pre), a); }; } let str = "hello lsq"; const split = (a) => a.split(" "); const reverse = (a) => a.reverse(); const log = (res) => { console.log(res); return res; }; const composeFn = compose(reverse, log, split); ``` ### 函子 函子中存储一个值,这个值就是我们要处理的一个值, 如果想要处理这个值,只能通过 map 方法传入一个处理函数来处理(这个处理函数必须是纯函数) #### base ``` class Container { static Of(value) { return new Container(value); } constructor(value) { this._value = value; } map(fn) { return Container.Of(fn(this._value)); } } const c = new Container(5).map((i) => i * i); console.log(c); ``` #### Maybe 如果value是null或者undefined直接返回,否则执行方法。 ``` class Maybe { static Of(value) { return new Maybe(value); } constructor(value) { this._value = value; } isNull() { return this._value === undefined || this._value === null; } map(fn) { return Maybe.Of(this.isNull() ? null : fn(this._value)); } } const m = new Maybe(null).map((i) => i * i); console.log(m); ``` #### Either 函子 Either 函子需要声明两个对象,一种用于处理正常情况,一种用于处理异常情况。 ``` class Left { static Of(value) { return new Left(value); } constructor(value) { this._value = value; } map() { return this; } } class Right { static Of(value) { return new Right(value); } constructor(value) { this._value = value; } map(fn) { return Right.Of(fn(this._value)); } } const jsonparse = (str) => { try { return Right.Of(JSON.parse(str)); } catch (e) { return Left.Of({error: e.message}); } }; let r = jsonparse('{ "name": "zs" }').map((x) => x.name.toUpperCase()); console.log(r); ``` ### ECMAScript ECMAScript 为 Javascript 提供了一些规则。 * web中 js包括 bom dom js * nodejs中 js包括nodeapi js #### ES6(ES2015)新增 * let 块级作用域 只能在所声明的代码块中访问到 * const 常量 块级作用域 * 数组的解构 按照位置解构 * 对象的解构 按照属性名字解构 * 模版字符串 * 带标签的模版字符串 ``` fun`str${name}` fun有多个参数,如事例第一个参数为["str"],第二个为name ``` * 字符串扩展方法 includes startsWith以...开头 endWith以...结尾 * 参数默认值 * Proxy对象 删除属性也可以监测到,修改数组可以监测到 * Reflect对象 ``` 方法1:Reflect.ownKeys() 方法2:Reflect.has() 方法3:Reflect.get() 方法4:Reflect.set() ``` * Promise对象 解决回调地狱 * class 类 * Set 集合对象 ``` 可以使用forEach循环或者 for...of 循环 add() delete() has() clear() size属性获取长度 ``` * Map对象 ``` set() get() forEach((key,value)=>{}) clear() ``` * symbol/bigint ``` Symbol.for()比较字符串是否相同 会默认转化为字符串比较 for in 循环拿不到sysbol属性的key Object.keys() JSON.stringify() symbol属性会被忽略 getOwnPropetySysbols() 可以获取到symbol的key Reflect.ownKeys() 可以获取到所有的key值 ``` * for of 循环 * iterable * generator * ESmodule #### ES7 * arr.includes() * **=== Math.pow() #### ES8 * Object.values() * Object.entries() * Object.getOwnPropertyDescriptors() * async/await ### 异步编程 #### generator ``` function* fn() { let s = yield "hello"; yield "hi"; console.log(s); } let f = fn(); f.next(); // 函数开始执行 在第一个yield停止 获取yield后面的值 f.next("lpx"); // 第二哥yield停止,并把括号中的值传给第一个yield的返回值。 f.next(); // 打印出s ``` #### 手写promise ``` const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; class LxPromise { value = undefined; reason = undefined; status = PENDING; successCallback = []; failCallback = []; constructor(executor) { executor(this.resolve, this.reject); } static resolve(value) { if (value instanceof LxPromise) return value; return new LxPromise((resolve) => resolve(value)); } static reject(reason) { if (reason instanceof LxPromise) return reason; return new LxPromise((resolve, reject) => reject(reason)); } static all(array) { return new LxPromise((resolve) => { let arr = []; let index = 0; function setArr(key, value) { arr[key] = value; index++; if (index === array.length) { resolve(arr); } } for (let i = 0; i < array.length; i++) { const current = array[i]; if (current instanceof LxPromise) { current.then((res) => { setArr(i, res); }); } else { setArr(i, current); } } }); } resolve = (value) => { if (this.status !== PENDING) return false; this.value = value; this.status = FULFILLED; while (this.successCallback.length) this.successCallback.shift()(value); }; reject = (reason) => { if (this.status !== REJECTED) return false; this.reason = reason; this.status = REJECTED; while (this.failCallback.length) this.failCallback.shift()(reason); }; then = (successCallback, failCallback) => { successCallback = successCallback ? successCallback : (value) => value; failCallback = failCallback ? failCallback : (reason) => { throw reason; }; let promise2 = new LxPromise((resolve, reject) => { if (this.status === FULFILLED) { let x = successCallback(this.value); disposePromiseThen(x, resolve, reject); // resolve(successCallback(this.value)); } else if (this.status === REJECTED) { let x = failCallback(this.reason); disposePromiseThen(x, resolve, reject); reject(failCallback(this.reason)); } else { this.successCallback.push(() => { let x = successCallback(this.value); disposePromiseThen(x, resolve, reject); }); this.failCallback.push(() => { let x = failCallback(this.reason); disposePromiseThen(x, resolve, reject); }); } }); return promise2; }; catch(failCallback) { return this.then(undefined, failCallback); } } /** * 判断上一个promise返回的是 promise对象 还是 变量值 * */ function disposePromiseThen(x, resolve, reject) { if (x instanceof LxPromise) { x.then(resolve, reject); } else { resolve(x); } } let lx_promise = new LxPromise((resolve, reject) => { console.log("开始"); setTimeout(() => { resolve(1); }, 2000); }); lx_promise .then((res) => { console.log(res); let p1 = new LxPromise((resolve) => { resolve("测试p1"); }); return p1; }) .then((res) => { console.log(res); }); ``` ## 前端工程化 常见的脚手架工具 create-react-app vue-cli 等。 脚手架的本质作用: - 创建项目基础结构,提供项目规范和约定 - 相同的组织结构 - 相同的开发范式 - 相同的模块依赖 - 相同的工具配置 - 相同的代码基础 ### Yeoman 官方文档 https://yeoman.io/ #### Yeoman的安装 ``` yarn add global yo ``` #### Yeoman的基本使用 // 使用generator-node yarn add global generator-node 运行 yo node 生成 yo node:cli yarn link #### Yeoman的常规使用步骤 1. 确定需求 2. 找到合适的Generator 3. 全局范围安装Generator 4. 通过yo 运行 Generator 5. 通过命令行交互填写选项 6. 生成所需要的项目结构 #### Yoeman 自定义 generator-lx ``` // 目录 generators app templates lx.txt index.js ``` > index.js文件为 generator 的核心入口 > > templates下的文件为ejs模版文件 第一步:安装 yeoman-generator ``` yarn add yeoman-generator ``` 第二步:在index.js文件引入,并且导出继承于yeoman-generator的类 ``` const Genrator = require('yeoman-generator'); module.exports = class extends Genrator { write(){ this.fs.write("./lx.txt",Math.random()+"") } } ``` 第三步:npm link 将自定义generator储存在全局 第四步:yo lx 执行 generator-lx,可在文件中看到新增一个lx.txt的文件,文件中有一个随机数 第五步:使用ejs模版生成文件 ``` module.exports = class extends Genrator { write() { // 获取文件所在路径 默认指向templates文件夹 const tml = this.templatePath('lx.txt') // 编写文件输出路径 const output = this.destinationPath('lx.txt') // 传入模版的变量 const context = { username: "lx", age: "18" } // 生成文件 this.fs.copyTpl(tml, output, context) } } ``` 第六步:询问用户问题 ``` // 询问用户,可在prompting中return返回this.prompt()方法 prompting(){ return this.prompt([ { type:"input", name:"username", message:"请输入你的姓名", default:this.appname // appname 为项目生成的目录名称 }, { type:"input", name:"age", message:"请输入你的年龄" }, ]).then(answer=>{ this.answer = answer }) } ``` 第七步:发布 ``` nrm use npm //换源 yarn publish 或 npm publish username:xizige email:2899952565@qq.com password:lpx666888. ``` 附加:yeoman-gennerator除prompting,wirting外的api ``` module.exports = class extends Generator{ initianlizing(){ //获取当前项目状态,获取基本配置参数等 } prompting(){ //向用户展示交互式问题收集关键参数 } configuring(){ //保存配置相关信息且生成配置文件(名称多为'.'开头的配置文件,例如.editorconfig) } default(){ //未匹配任何生命周期方法的非私有方法均在此环节*自动*执行 } writing(){ //依据模板进行新项目结构的写操作 } conflicts(){ //处理冲突(内部调用,一般不用处理) } install(){ //使用指定的包管理工具进行依赖安装(支持npm,bower,yarn) } end(){ //结束动作,例如清屏,输出结束信息,say GoodBye等等 } } ``` ### Plop 小型脚手架工具,类似于yeoman中的sub generator,重复创建相同文件的时候可以使用。 第一步:安装plop ``` yarn add plop ``` 第二步:根目录新建plopfile.js ``` module.exports = plop => { plop.setGenerator("component",{ description:"create a component", prompts:[ { type:"input", name:"name", message:"component name", default:"LxComponent" } ], actions:[ { type:"add", path:"src/components/{{name}/{{name}.js", templateFile:"template/js.hbs" } ] }) } ``` 第三步:模版文件为 .hbs文件 ``` import react from "react"; const {{name}} = ()=>{ return

lx

} ``` 第四步:yarn plop + plopfile.js文件中通过setGenerator定义的名字 ``` yarn plop component ``` ### 脚手架的工作原理 第一步:初始化package.json文件,并且修改bin对应的入口文件 ``` yarn init -y ``` package.json文件 ``` { "name": "lx-cli", "version": "1.0.0", "main": "index.js", "license": "MIT", "bin":"cli.js" } ``` 第二步:cli.js文件, ``` #!/usr/bin/env node // mac或linux需要 chmod 755 cli.js 修改文件的读写权限 // 安装inquirer8.0版本,并且引入 const inquirer = require("inquirer"); // 安装ejs模版,并且引入 yarn add ejs const path = require("path") const fs = require("fs") const ejs = require("ejs") // 通过inquirer.prompt询问,ejs渲染,fs写入并输出文件 inquirer.prompt([ { type:"input", name:"name", message:"project name" } ]).then(answer=>{ console.log(answer) const tmlDir = path.join(__dirname,"templates") const outDir = process.cwd() fs.readdir(tmlDir,(err,files)=>{ if(err) throw err; files.forEach(file=>{ ejs.renderFile(path.join(tmlDir,file),answer,(error,res)=>{ if(error) throw error; fs.writeFileSync(path.join(outDir,file),res) }) }) }) }) ``` ### 自动化构建 #### sass_demo index.scss文件 ``` $body-color:red; $body-size:12px; body{ color: $body-color; font-size: $body-size; } ``` > 由于scss文件不被浏览器识别,所以要对文件进行编译。 安装 sass ``` yarn add sass -D ``` 通过sass,把scss文件编译成css文件 ``` yarn sass index.css lx.css ``` > 由于每次输入命令使开发繁琐,可以通过scripts编写命令,直接通过yarn 执行命令。 ``` // yarn dev 直接在浏览器启动 // 命令前加pre是执行命令前先加载的命令 "scripts": { "build":"yarn sass index.scss lx.css", "predev":"yarn build", "dev":"browser-sync ." } ``` ``` // 1.安装npm-run-all // 2.run-p 后面跟执行的命令 // 3.browser-sync . --files lx.css 监听文件变化 "scripts": { "build": "yarn sass index.scss lx.css --watch", // "dev": "browser-sync .", "dev": "browser-sync . --files lx.css", "start": "run-p build dev" } ``` 常见的自动化构建工具:Grunt Gulp FIS #### Grunt #### Gulp #### FIS ### webpack #### 语法提示 const {Configuration} = require("webpack") /** * @type {Configuration} */ #### loader的使用 例如编译css文件 module:{ rules:[ { test:/\.css$/, use:["style-loader","css-loader"] } ] } css-loader的作用是将css模块加载到js模块中。 style-loader的作用是使用css模块。 ##### 相关loader * file-loader * url-loader * babel-loader * style-loader * css-loader * sass-loader * postcss-loader * eslint-loader * vue-loader ##### 自定义loader------markdown-loader 对一种文件进行webpack编译的时候,最后执行的loader要返回js代码.source为文件内容 const {marked} = require("marked") module.exports = source=>{ const html = marked(source) const code = `module.exports = ${JSON.stringify(html)}` return code } #### plugin ##### 常用的plugin * clean-webpack-plugin * html-webpack-plugin ##### 自定义plugin------RemoveCommentsPlugin ``` // 去除注释 class RemoveCommentsPlugin{ apply(compiler){ compiler.hooks.emit.tap("RemoveCommentsPlugin",compilation=>{ for ( const name in compilation.assets){ if(name.endsWith(".js")){ const contents = compilation.assets[name].source() const noComments = contents.replace(/\/\*{2,}\/\s?/g,"") compilation.assets[name] = { source:()=>noComments, size:()=>noComments.length } } } }) } } module.exports = RemoveCommentsPlugin ``` #### devServer ``` devServer: { proxy: { "/api": { target: "https://gitee.com", pathRewrite: { '^/api': "" }, changeOrigin:true } } } ``` #### sourcemap 映射源代码和转化后代码的映射。 带module的模式是没有经历过loader加载的。 * eval模式 只能知道报错的文件路径,构建速度快。 * eval-source-map * cheap-eval-source-map * cheap-module-eval-source-map * cheap-source-map * inline-source-map * hidden-source-map * nosources-source-map 开发选择cheap-module-eval-source-map模式,可查看loader转化之前的源代码,只需要查看行代码报错,启动打包慢,通过webpack-dev-server监视模式下更新更快。 生产选择nosources-source-map,可以找到错误行,但查不到源代码。 sourcemap不是webpack特有功能,是webpack支持sourcemap。 #### 模块热更新(HMR) webpack4以上版本默认自动开启。 ``` 热替换 js模块代码更新改变会刷新页面。可通过以下api监视文件变化,还原更新前的数据。 if(module.hot){ module.hot.accept(url, () => { // hot为true此处报错会使页面自动刷新 // hotonly为true时会报错 console.log("文件改变了") }) } ``` #### tree-shaking sideEffects(副作用) ``` optimization:{ // 只打包导出外部被使用的方法 usedExports:true, // 压缩 minimize:true, // 合并模块 concatenateModules:true, sideEffects:[文件路径] }, ``` #### code spliting 和 动态导入 #### 环境区分使用webpackconfig npm库 webpack-merge合并公共配置。 #### 生产模式常用插件 * webpack.definePlugin * mini-css-extract-plugin * optimize-css-assets-webpack-plugin * terser-webpack-plugin ## Vue ## React ## Node ## 计算机网络 ### 计算机网络体系结构 计算机网络是一个将分散的、具有独立功能的计算机系统,通过通信设备与线路连接起来,由功能完善的软件实现资源共享和信息传递的系统。简而言之,计算机网络是一些互联的、自治的计算机系统的集合。 #### 计算机的组成 ##### 组成部分 从组成部分上看:一个完整的计算机网络主要由硬件、软件、协议三大部分组成,缺一不可。协议是计算机网络的核心。 1. 硬件 * 主机 * 通信链路(双绞线、光纤) * 交换设备(交换机、路由器) 2. 软件(主要在应用层) 3. 协议 * 语法:用来规定信息格式;数据及控制信息格式、编码及信号电平等 * 语义:控制信息每个部分的含义。发出何种控制信息,发出何种动作,发出何种响应。 * 规则:定义了何时进行通信,先讲什么,后讲什么,讲话的速度等。比如是采用同步传输还是异步传输。 ##### 工作方式 从工作方式上看:计算机网络(这里主要指Internet,即因特网)可分为边缘部分和核心部分。 ##### 功能组成 ### 物理层 ### 数据链路层 ### 网络层 ### 传输层 ### 应用层 ### 网络安全 ### 网络安全应用 ### 无线网络和移动网络 ## 掘金文章 ### JavaScript执行上下文 什么是执行上下文?我的理解,作用域。官方概念,代码被解析和执行时所在环境的抽象概念,JavaScript代码都是在执行上下文中运行 #### 执行上下文的类型 * 全局执行上下文 * 函数执行上下文 * eval执行上下文 #### 执行栈 顶端添加删除,后进先出(LIFO)。 用于储存代码执行期间创建的所有执行上下文。 js引擎首次读取脚本的时候,会创建一个全局执行上下文。每当一个函数发生调用,引擎会为该函数创建一个新的执行上下文,放在栈顶。 函数执行完后,其对应的执行上下文将会从执行栈中弹出。 #### 执行上下文是如何被创建的 两个阶段 1. 创建阶段 2. 执行阶段 ##### 创建阶段 1. 确定this的值。 2. 词法环境组件被创建。 3. 变量环境组件被创建。 ##### 词法环境 词法环境是一个包含标识符变量映射的结构。 在词法环境中,由环境记录和对外部环境的引用两部分组成。 环境记录是存储变量和函数声明的实际位置。 对外部环境的引用意味着它可以访问其外部词法环境。 词法环境有两种类型:全局环境和函数环境。 !!!!函数环境另外包括了一个arguments对象!!!! let const 的声明在词法环境中,创建的值为< uninitialized >,, var在创建的时候就会创建在变量环境中,并且为undefined,这就是变量的提升。 ### js的骚操作 #### 数组去重的方法 * 双层for循环 * indexOf去重 * set去重 #### 数组转化为对象 * for in 方法 key为index * 扩展运算符 {...arr} #### 转换为数字类型 * "100"*1 * +"100" * parseInt() Number() #### 转化为字符串类型 * toString() * value+"" #### 性能追踪 可以通过performace.now() 获取当前的时间计算时间差 #### 合并对象 * Object.assign() * 扩展运算符 #### 数组扁平化 * 递归 * reduce也是递归 * es6方法 arr.flat(x) x为深度 #### 求幂运算 * Math.pow() * 2 ** 10 = 1024 #### 浮点数转化为整数 * ~ >> << 等位运算符 * Math.floor() Math.round() Math.ceil() #### 获取数组最后一项 * 根据长度-1 * arr.slice(-1)[0] #### Object.create(null) var obj1 = {}; var obj2 = Object.create(null); 两者的区别,第一个有原型的属性 方法等 #### 拷贝数组 * slice(深) * 扩展运算符(深) * concat(深) #### Object.freeze() ## 设计模式 ### 发布订阅 ``` class EventEmitter { constructor() { this.subs = Object.create(null) } $on(eventType, handler) { if (this.subs[eventType] && this.subs[eventType].length) { this.subs[eventType].push(handler) } else { this.subs[eventType] = [handler] } } $emit(eventType, ...args) { this.subs[eventType] && this.subs[eventType]?.forEach(handler => { handler(...args) }) } $off(eventType, handler) { if (!this.subs[eventType]) return const findHandler = this.subs[eventType]?.findIndex(_handler => _handler = handler) findHandler !== null && findHandler !== undefined && this.subs[eventType].splice(findHandler, 1) } } ```