-
reduce方法定义reduce() 方法对数组中的每个元素执行一个传入的callback回调函数(升序执行,空值和已删除的除外),将其结果汇总为单个返回值。reduce方法语法arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])reduce方法参数callback自己传入的为数组中每个值 (如果没有传入 initialValue则第一个值除外)执行的函数,包含四个参数:(1)accumulator累计器累计回调的返回值; 它是上一次调用callback回调时返回的值,或初始化时initialValue的值(见于下方)。(2)currentValue数组中当前正在处理的元素。(3)index 可选数组中当前正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。(4)selfArray可选调用reduce()的源数组自身。initialValue可选作为第一次调用 callback函数时的第一个参数的值。 如果没有传入初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。reduce方法返回值函数累计处理的结果(可以是任意值,取决于自己需要什么)。reduce方法详细描述reduce为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:1、accumulator 累计器2、currentValue 当前值3、currentIndex 当前值的索引4、selfArray数组本身回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:如果调用reduce()时提供了initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。特别注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。如果调用reduce函数的数组为空且没有提供initialValue,会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。reduce方法使用示例参数累加 // 不传入initialValue参数 let arr1 = [1,3, , ,5] let newArr = arr1.reduce((acc, cur, index, selfArr) => { console.log({acc, cur, index}) return acc + cur }) console.log(newArr) // {acc: 1, cur: 3, index: 1} // {acc: 4, cur: 5, index: 4} // 9 // 传入initialValue参数 let newArr1 = arr1.reduce((acc, cur, index) => { console.log({acc, cur, index}) return acc + cur }, 0) console.log(newArr1) // {acc: 0, cur: 1, index: 0} // {acc: 1, cur: 3, index: 1} // {acc: 4, cur: 5, index: 4} // 9上述代码的最终结果都是返回数组中每个元素相加的和,区别就是第一组代码没有传入initialValue 参数,第二组传入了initialValue 参数。根据打印结果可以看出,第一组没有传入initialValue参数的代码从arr1数组中索引为1的元素开始执行callback函数,一共执行了两次,并且acc初始值为数组的第一个元素。第二组传入initialValue 参数的代码,从arr1原数组中索引值为0的元素开始执行并且acc初始值为传入的initialValue的值。两组代码都没有为arr1原数组中的空值执行callback函数。数组对象结构的参数累加 let arr2 = [ { num: 1, }, { num: 5, }, { num: 7, } ] const newArr2 = arr2.reduce((acc, cur, index) => { return acc + cur.num }, 0) console.log(newArr2) // 13上述代码是循环原数组arr2中每一项,并将每一项里面num的值进行相加,并且返回最后相加的结果。数组去重 let arr3 = [1,2,1,1,3,5,2] const newArr3 = arr3.reduce((acc, cur) => { if (!acc.includes(cur)) acc.push(cur) return acc }, []) console.log(newArr3) // [1, 2, 3, 5]上述代码 执行时初始参数传入一个空数组,然后开始从原数组arr3的第一个元素开始执行callback函数,每次都会利用数组的includes方法判断当前元素是否已经存在传入的数组里面,最后返回传入的数组赋值给acc参数。最后的结果就是已经去重好的新数组newArr3。对象分类 let arr4 = [ { name: '小红', age: 21 }, { name: '小绿', age: 20 }, { name: '小蓝', age: 20 }, { name: '大白', age: 19 } ]; const newArr4 = arr4.reduce((acc, cur, index) => { if (!acc[cur.age]) acc[cur.age] = [] acc[cur.age].push(cur) return acc }, {}) console.log(newArr4) // { // 19: [{ name: '大白', age: 19 }], // 20: [{name: "小绿", age: 20}, {name: "小蓝", age: 20}], // 21: [{name: "小红", age: 21}] // }上述代码将数据列表根据age进行分类,同样age的数据合并组成key:value形式展示出来。计算数组中元素出现的次数 let arr5 = [1,2,3,1,2,1] const newArr5 = arr5.reduce((acc, cur) => { acc[cur]? acc[cur] ++: acc[cur] = 1 return acc }, {}) console.log(newArr5) // {1: 3, 2: 2, 3: 1}上述代码利用对象中key – value数据形式来计算得出数组中每个元素出现的次数。最后在来看下空数组和数组只有一个参数时的值: // 数组为空,传入initialValue初始值时和只有一个参数没有传入初始值时 console.log([].reduce((acc, cur) => { console.log(acc, cur) return cur }, 0)) // 0 console.log([1].reduce((acc, cur) => { console.log(acc, cur) return 10 })) // 1下面根据上述描述和使用来模拟实现我们自己的reduce方法。步骤思路1、 将方法添加到Array的原型上2、 传入回调函数形参和初始值形参3、 判断initialValue 初始值是否传入4、 判断原数组的元素是不是空值,来调用callback函数5、 将每次执行的callback函数的返回值赋值给accumulator6、 返回accumulator实现代码 Array.prototype.myReduce = function(callback, initialValue) { let accumulator,initIndex, length = this.length // 判断initialValue初始参数有没有传入 if (initialValue || initialValue === 0) { // 空数组时直接返回传入的初始参数 if (length === 0) return initialValue initIndex = 0 accumulator = initialValue } else { // 空数组时报错 if (length === 0) throw new TypeError('xxxxxxxxx') // 原数组只有一个元素时,返回此元素 if (length === 1) return this[0] initIndex = 1 accumulator = this[0] } for (let index = initIndex; index < length; index++) { // 通过hasOwnProperty方法剔除空值,然后将传入的callback函数的值赋值给accumulator累计参数 if(this.hasOwnProperty(index)) accumulator = callback(accumulator, this[index], index, this) } return accumulator }测试验证 Array.prototype.myReduce = function(callback, initialValue) { let accumulator,initIndex, length = this.length // 判断initialValue初始参数有没有传入 if (initialValue || initialValue === 0) { // 空数组时直接返回传入的初始参数 if (length === 0) return initialValue initIndex = 0 accumulator = initialValue } else { // 空数组时报错 if (length === 0) throw new TypeError('xxxxxxxxx') // 原数组只有一个元素时,返回此元素 if (length === 1) return this[0] initIndex = 1 accumulator = this[0] } for (let index = initIndex; index < length; index++) { // 通过hasOwnProperty方法剔除空值,然后将传入的callback函数的值赋值给accumulator累计参数 if(this.hasOwnProperty(index)) accumulator = callback(accumulator, this[index], index, this) } return accumulator } // 不传入initialValue参数 let arr1 = [1,3, , ,5] let newArr = arr1.myReduce((acc, cur, index, selfArr) => { console.log({acc, cur, index}) return acc + cur }) console.log(newArr) // {acc: 1, cur: 3, index: 1} // {acc: 4, cur: 5, index: 4} // 9 // 传入initialValue参数 let newArr1 = arr1.myReduce((acc, cur, index) => { console.log({acc, cur, index}) return acc + cur }, 0) console.log(newArr1) // {acc: 0, cur: 1, index: 0} // {acc: 1, cur: 3, index: 1} // {acc: 4, cur: 5, index: 4} // 9 let arr2 = [ { num: 1, }, { num: 5, }, { num: 7, } ] const newArr2 = arr2.myReduce((acc, cur, index) => { return acc + cur.num }, 0) console.log(newArr2) // 13 let arr3 = [1,2,1,1,3,5,2] const newArr3 = arr3.myReduce((acc, cur) => { if (!acc.includes(cur)) acc.push(cur) return acc }, []) console.log(newArr3) // [1, 2, 3, 5] let arr4 = [ { name: '小红', age: 21 }, { name: '小绿', age: 20 }, { name: '小蓝', age: 20 }, { name: '大白', age: 19 } ]; const newArr4 = arr4.myReduce((acc, cur, index) => { if (!acc[cur.age]) acc[cur.age] = [] acc[cur.age].push(cur) return acc }, {}) console.log(newArr4) // { // 19: [{ name: '大白', age: 19 }], // 20: [{name: "小绿", age: 20}, {name: "小蓝", age: 20}], // 21: [{name: "小红", age: 21}] // } let arr5 = [1,2,3,1,2,1] const newArr5 = arr5.myReduce((acc, cur) => { acc[cur]? acc[cur] ++: acc[cur] = 1 return acc }, {}) console.log(newArr5) // {1: 3, 2: 2, 3: 1} // 数组为空,传入initialValue初始值时和只有一个参数没有传入初始值时 console.log([].myReduce((acc, cur) => { console.log(acc, cur) return cur }, 0)) // 0 console.log([1].myReduce((acc, cur) => { console.log(acc, cur) return 10 })) // 1使用自己模拟实现的方法打印结果和原方法一致。作者:绿芽链接:https://www.jianshu.com/p/a31f43462007
-
经常看见下面这样的定义: typedef struct { int a; double b; }emp_i, *pemp_i; //typedef 了两个新的数据类型(结构体),其中一个是指针方式的名字 int main(void) { char i = 'y'; emp_i a1; //emp_i 声明的a1是一个实体,声明了就已经有存储空间了 pemp_i b1 = &a1; //pemp_i 声明的b1是一个指针(但这里不用加*号,因为pemp_i已经被指定为指针),它可以指向一个struct a 的实体。 a1.a++; //a1的元素访问方式 实体方式 b1->a++; //b1的访问方式 指针方式 } 也就是说,用结构体名字去定义声明的变量是一个真正的变量,他在内存中分配有自己的存储空间;而是用指针去形式定义的变量是一个指针,使用的时候给他赋予一个结构体变量的地址。 访问方式不一样:结构体变量直接访问使用实体方式,用点;结构体变量使用指针方式,如上面例子所示。 ———————————————— 版权声明:本文为CSDN博主「微电子学与固体电子学-俞驰」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/appleyuchi/article/details/126641509
-
1、UserAgent的简介 User Agent即用户代理,简称为UA,是一种特殊的字符串头,会使得服务器能够识别客户使用的操作系统、版本、CPU 类型、浏览器版本、浏览器渲染引擎、浏览器语言、浏览器插件等等,是不感觉很厉害的样子,通过这个串头,我们就可以进行获得很多的信息,从而进行相关业务的处理。 在server抓包的时候,会经常碰到直接使用wget或者curl被服务器拒绝的状况,这时候只需要通过增加一个user-agent串头进行模拟就可以通过了。2、查看User Agent的方法2.1 通过js获取 在浏览器地址栏中输入以下代码navigator.userAgent 或者window.navigator.userAgent 进行获取相关信息,获取后的信息会是这个样子的:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/xxx (KHTML, like Gecko) Chrome/xxx Safari/xxx'2.2 通过Chrome开发者模式获取 打开chrome开发者模式(快捷键是F12),或者在网页上右键选择检查(快捷键是Ctrl+Shift+I),在Network栏中找到需要查询的请求,在Headers的最后可以看到User-Agent的相关信息了。这种方法可以通过模拟机型,在选择模拟的设备机型后,找到相应的请求就可以找到这类机型所对应的User-Agent信息了。3、User-Agent的应用3.1 User-Agent在js中的应用 其中判断是否是微信环境,通常我们会通过window.navigator.userAgent获取User-Agent串头后进行转小写,然后通过正则匹配/MicroMessenger/i是否为'micromessenger'来判断是否是微信内部浏览器从而判断是否是微信环境,这里如果不加window的话Android机可能会判断不了是否是微信,所以还是加上比较保险。function isWX() { var ua = window.navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == 'micromessenger') { return true; } else { return false; }} 3.2 User-Agent在java中的应用 通常我们会通过reques请求中获取header中的user-Agent元素,然后进行转小写,在获取到信息之后,我们就可以通过这个User-Agent串头信息来进行环境判断了,其中包含micromessenger则为微信内部浏览器标识,即微信内部环境,在微信内部环境又包括小程序和企微,我们通过判断User-Agent串头信息是否包含wxwork元素来判断是否为企微环境,通过判断User-Agent串头信息是否包含miniprogram来判断是否为小程序环境下。String userAgent = request.getHeader("user-agent").toLowerCase();if (userAgent.indexOf("micromessenger") < 0) {// 非微信 return true;} else{ if (userAgent.indexOf("wxwork") > 0) { // 企业微信客户端 return true; }else if(userAgent.indexOf("miniprogram") > 0){//小程序下 return true; }}以上就是User-Agent的相关应用和总结。
-
1. 摘要Webpack是一种前端资源构建工具,一个静态模块打包器。在Webpack看来,前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理,当Webpack处理应用程序时,它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源。Webpack打包流程图如图1-1所示。图1-1 Webpack打包流程图2. Webpack五个核心概念2.1 Entry入口(Entry)指示Webpack以哪个文件作为入口起点分析构建内部依赖图并进行打包。2.2 Output输出(Output)指示Webpack打包后的资源bundles输出到哪里去,以及如何命名。2.3 LoaderLoader让Webpack能够去处理那些非JavaScript语言的文件,Webpack本身只能理解JavaScript。2.4 Plugins插件(Plugins)可以用于执行范围更广的任务,插件的范围包括从打包和压缩,一直到重新定义环境中的变量等。2.5 Mode模式(Mode)指示Webpack使用相应模式的配置。分为development和production两种模式,下面分别进行简述。development: 开发模式,能让代码本地运行的环境,会将process.env.NODE_ENV的值设为development,同时启用NamedChunksPlugin和NamedModulesPlugin插件;production: 生产模式,能让代码优化运行的环境,会将process.env.NODE_ENV的值设为production,同时启用FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin、OccurrenceOrderPlugin、SideEffectsFlagPlugin和UglifyJsPlugin插件。3. Wbepack配置3.1 webpack.config.js文件webpack.config.js是webpack的配置文件,用来指示webpack工作,运行webpack指令时,会加载里面的配置,所有构建工具都是基于nodejs平台运行的,默认采用commonjs模块化。webpack.config.js基础配置如图3-1所示。图3-1 webpack.config.js基础配置3.2 devServer配置开发服务器(devServer)用来实现自动化(自动编译、自动打开浏览器、自动刷新浏览器),只会在内存中编译打包,不会有任何文件输出,本地安装webpack-dev-server后,通过npx webpack-dev-server命令启动devServer,核心代码如图3-2所示。图3-2 devServer配置核心代码3.3 打包html/样式/图片/其它资源打包不同的资源会使用不同的loader和插件,打包html/样式/图片/其它资源的流程如下所述。3.3.1 打包html资源1.下载html-webpack-plugin插件;2.引入html-webpack-plugin插件;3.使用html-webpack-plugin插件,并进行相应配置。3.3.2 打包样式资源不同的样式文件需要配置不同的loader1.下载loader;2.配置loader,css样式文件使用css-loader和style-loader,less文件使用less-loader、css-loader和style-loader。其中css-loader的作用是将css文件变成commonjs模块加载到js文件中,style-loader的作用是创建style标签,将js中的样式资源插入进去,添加到head中生效。3.3.3 打包图片资源1.下载url-loader,file-loader2.配置loader3.3.4 打包其它资源1.下载file-loader2. 配置loader,配置该loader作用于不为html/css/less/js的其他文件3.4 提取css成单独文件/css兼容性处理/压缩css3.4.1 提取css成单独文件样式文件打包后会默认和js文件一起输出,可以通过插件将打包后的css文件单独输出,流程如下所述。1.下载mini-css-extract-plugin插件2.引用该插件3.配置3.4.2 css兼容性处理1.下载postcss-loader和postcss-preset-env2.在package.json中browsetslist属性中分别对开发环境和生产环境进行兼容性配置,设置支持样式的浏览器版本3.通过postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式。3.4.3 压缩css1.下载optimize-css-assets-webpack-plugin插件2.引用该插件3.使用该插件3.5 js语法检查eslint/js兼容性处理/js压缩3.5.1 js语法检查eslint1.下载eslint-loader和eslint2.在package.json中的eslintConfig中进行配置3.配置eslint-loader,其中只需检测js文件并要排除第三方库,只检测自己写的源代码,同时可在options配置中设置fix:true,自动修复eslint的错误。3.5.2 js兼容性处理 1.下载babel-loader、@babel/core、@babel/preset-env,通过@babel/preset-env做基本的js兼容性处理,然后通过corejs做前面无法实现的兼容性处理,并实现按需加载 2. 配置loaderjs兼容性处理核心代码如图3-3所示图3-3 js兼容性处理核心代码3.5.3 js压缩mode设置为production生产环境时会自动压缩js代码。4. webpack性能优化可以从开发环境和生产环境分别对webpack进行性能优化。其中开发环境主要考虑从打包构建速度和代码调试两个方面进行优化,生产环境主要考虑从打包构建速度和代码运行性能这两个方面进行优化。下面简单介绍下开发环境上通过HMR提升构建速度。4.1 HMR HMR(热模块替换),作用是一个模块发生变化后,只会更新打包这一个模块而不是所有模块,通过在devServer中设置hot:true属性启动HMR功能。其中对于样式文件,可以使用HMR功能,因为style-loader内部实现了;对于js文件,默认不能使用HMR功能,解决方法:修改入口文件js代码,添加支持HMR功能的代码,另外HMR只能处理非入口js文件的其他文件,对入口文件并不能生效,因为一旦入口文件更新,入口文件引入的其他文件一定会被重新加载;对于html文件,默认不能使用HMR功能,同时会导致html文件不能热更新,解决方法:修改entry入口文件,将html文件引入,只能解决html文件不能热更新的问题。js文件支持HMR功能的核心代码如图4-1所示。图4-1 js文件支持HMR功能核心代码4.2 HMR效果在入口index.js文件中引入print.js文件,运行npx webpack-devserver后,页面如图4-2所示。4-2 初始页面修改print.js文件后,只会重新加载print.js文件,而不会重新加载index.js文件,HMR效果如图4-3所示。4-3 HMR效果图
-
## 一、 场景和问题 树结构,多级嵌套(不确定几级),使用递归方式实现,数据量上万情况下,页面加载很慢。 数据结构:十个父级节点,每个父节点有10000个子节点,父子是嵌套关系 #### 代码:  #### 页面加载:  此时页面渲染需要13.5s ## 二、问题解决 #### 2.1 树数据结构扁平化  从performance工具定位问题:调用栈createChildren被疯狂调用,在vue源码中,createChildren做的是创建Vue实例这件事。创建Vue实例包括依赖收集、响应式监听、数据事件绑定、编译模板等等。 如果能将数据结构扁平化,就只会有一个tree组件,createChildren只执行进行一次。 扁平化数据还有个好处就是: + 减少栈的读取,递归的本质是栈的读取,栈的读取是由解析器做的,扁平化可以减少V8引擎的开销。 + 扁平化数据可以减少dom 的数量。  扁平化后优化到6.5s,我们觉得6.5s还是远远不够 #### 2.2 虚拟长列表 我们可以用懒加载解决海量数据一次性加载的问题,但是最终dom数量还是很大,容易造成页面卡顿。 所以我们使用虚拟长列表解决这个问题:  通过虚拟长列表,我们就控制住了DOM数量。 #### 2.3 其他优化思考 滚动事件加防抖,但要保证滚动的平滑度,使我们需要去权衡的。样式直接在数据中输出,不通过计算:树结构缩进padding距离不通过层级计算,而是在数据中直接给出样式,避免每个节点都计算一次样式的性能问题。
上滑加载中
推荐直播
-
华为云码道Skill实战与极速交付,智能开发全链路实战2026/07/22 周三 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;姜浩-华为云HCDG核心组成员
直播深度解读华为云码道6月产品新特性,从Skill市场安装专家技能,带你零距离体验从需求,开发,审查,重构全链路闭环的开发过程。从零构建并交付一个完整项目,让您体验从代码提交到服务上线的“极速”之旅。
回顾中 -
聚开发者之力,创具身新未来2026/07/23 周四 15:00-17:00
张豪杰/程文/王军/刘新春/黄钦开 /张晓天
本次华为云具身智能开发平台CloudRobo培训面向具身智能开发者,带您全流程体验机器人本体R2C小时级接入、环境重建与轨迹生成仿真数据生产、PB级数据管理、数据评测、模型训推、强化学习和Benchmark一键评测等功能,并体验业界主流具身模型应用。
回顾中
热门标签