# 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