本指南中,我们将介绍:
.reduce() 函数是什么?
创建我们的 .reduce() 函数版本
计算电子商务购物车的商品总数和价格
为餐厅菜单创建一组独特的类别
从数组中删除重复的对象
用 .reduce() 替换 .filter().map()
按键分组对象(类似于 SQL GROUP BY 或 C# 的 .GroupBy())
乍一看似乎很混乱
如果您曾经为理解 .reduce() 函数而苦苦挣扎,那么您并不孤单。 要真正了解它的工作原理并自信地使用它,需要多花几次功夫。
大多数例子都使用一个简单的数字或字符串数组,但现实是我们通常使用很少涉及的复杂对象。
但在我们跳进去之前。
以下是如何从这些内容中获得更多信息。
打开您的代码编辑器并亲自尝试示例
修改它(更改值,向数组添加或从数组中删除元素等)
如果您发现自己迷失在其中一个示例中,请使用笔和纸或在您的代码编辑器上追踪变量的值。
让我们开始吧!
.reduce() 函数是什么?
它是一种对数组的每个元素执行回调函数的方法。
计算的返回值传递给下一个元素。
在第一次迭代中,没有“上一次计算的返回值”,如果传入,可以用一个初始值代替。
否则,数组的第一个元素被用作初始值,迭代从下一个元素开始。
简单的说,就是一个变量和一个循环。
变量保存新值
循环遍历数组,因此我们可以从每个元素中获取我们想要的内容
这个:
实现与此相同:
几乎每次需要使用变量和循环时,都可以用 reduce 函数替换它。
由于没有比实际尝试更好的学习方法,让我们构建我们的 .reduce() 方法版本,以便我们真正理解它。
构建我们自己的 .reduce()
让我们通过解决一个简单的问题来做到这一点:
我们有一个数字数组,我们想要所有数字的总和。
就像上面的变量和循环的例子一样。
我们需要三样东西:
数组
保存总值 total 的变量(通常称为累加器)
遍历数组的循环
假设你想把它变成一个函数,这样你就可以很容易地使用它,你会怎么做?
也许沿着这些路线:
我们将数组作为参数传递给我们的函数
我们最后返回总数。
这看起来不错,但是如果我们想要设置变量总计初始值的选项怎么办?
我们可以将选项添加为另一个参数并将总计设置为它的值。
像这样:
但是这个函数还有2个问题。
我们无法在循环内添加要执行的不同逻辑(它只会将当前元素添加到 sum 变量)
它仅适用于简单数组(其中每个项目都是值类型,而不是对象)
我会告诉你我的意思。
这次让我们创建一个对象数组
让我们调用传入对象数组的 myReducer 函数
它不起作用,因为我们要添加的值在对象内部而不是对象本身。
我们必须以 total += arr[i].number 的形式访问它
但是这个函数没有考虑到这一点。
但是,如果我们添加一个回调函数作为第二个参数,我们可以:
对数组的每个元素执行回调函数
创建我们需要的任何自定义逻辑
让我们修改我们的 myReducer 函数。
让我们用对象数组再次测试它。
首先,声明回调函数并调用传入它的 myReduce。
请注意,我们根据 total 的值添加了 if 和 else 条件,这是我们的 initialValue 选项。
如果未给出初始值
它不会在第一次迭代时执行回调
没有什么要补充的,initialValue 是未定义的。
只需将 total 设置为当前元素的值
如果给出初始值
它在所有迭代中执行回调
这几乎就是原始 Array.prototype.reduce() 所做的。
主要区别在于不需要将数组作为参数传递,因为原始 .reduce() 绑定到 Array.prototype,因此数组始终是调用 .reduce() 的数组。
计算电子商务购物车的总项目数和价格
假设你有一个像这样的数组:
您需要将此数组缩减为这样的对象 { totalItems: 0, totalPrice: 0 } 以便我们可以在结帐页面上显示正确的信息。
这是我们可以做到的:
为餐厅菜单创建一组独特的类别
给定这个数组:
我们需要将其缩减为一系列独特的类别,例如 ['Appetizer','Entree','Main']
这是我们可以做到的:
从数组中删除重复的对象
与上面的示例类似,但这次我们需要返回仅使用唯一对象过滤的相同对象数组。
这是具有重复对象的数组:
注意:你必须有一个类似于 id 的属性,它对每个对象都是唯一的
这就是我们如何创建另一个仅包含唯一元素的数组:
将 .filter().map() 替换为 .reduce()
过滤数组然后修改过滤后数组的元素是很常见的。
我们将使用上面菜单类别的相同数组
但是这次我们想得到一个数组,其中只有类别为“Entree”的项目的 itemName
我们可以使用 .filter() 数组,然后使用 .map() 来完成它
或者我们可以用 .reduce() 来做
使用 .reduce() 的优点是您只迭代数组一次。
按键分组对象(类似于 SQL GROUP BY 或 C# 的 .GroupBy())
这是我最喜欢的。 这是一个非常方便的函数,可能会在下一次更新时包含在 JavaScript 的数组方法中(目前处于第 3 阶段以供批准)
这个 groupBy 函数也可以用作解决更复杂问题的中间步骤。
我们将再次使用菜单类别数组:
这是我们的 groupBy 函数:
如果我们添加将密钥作为函数传递的选项,它会变得更加方便。
只需替换这一行:
有了这个:
这样我们就可以调用它并传递一个返回键的函数,而不是自己编写字符串,避免拼写错误。
结论
在这篇文章中,我们了解到:
什么是 .reduce() 函数
如何创建我们的 .reduce() 函数版本
计算电子商务购物车的总项目数和价格
为餐厅菜单创建一组独特的类别
从数组中删除重复的对象
将 .filter().map() 替换为 .reduce()
如何按键对对象进行分组(类似于 SQL GROUP BY 或 C# 的 .GroupBy())
谢谢阅读!
/下栽のke:quangneng.com/321/
JavaScript高级玩法的详细介绍
JavaScript是一种非常灵活且强大的脚本语言,广泛应用于网页开发、移动端开发、后端开发等多个领域。对于想要提升JavaScript编程能力的开发者来说,掌握一些高级玩法是非常必要的。以下是一些JavaScript的高级玩法:
JavaScript中的函数不仅能够执行特定任务,还具有许多高级特性。例如,函数可以接受参数,也可以返回值;函数可以嵌套在其他函数中,形成高阶函数;函数还可以作为一等公民存在,可以被赋值给变量,也可以作为函数的参数传递。
JavaScript中的所有对象都有一个内部属性[[Prototype]],这个属性指向它的原型对象。通过__proto__属性或者Object.getPrototypeOf()方法,可以访问一个对象的原型。JavaScript中的继承是通过原型链实现的,子类的原型可以通过__proto__属性或者constructor.prototype属性指向父类的实例。
JavaScript中的异步编程非常复杂,但非常重要。常见的异步编程方式有回调函数、Promise、async/await等。掌握这些异步编程方式,可以帮助开发者更好地处理复杂的异步逻辑。
设计模式是一套被广泛接受的面向对象的设计原则,用于解决特定问题的最佳实践。在JavaScript中,常用的设计模式有单例模式、工厂模式、观察者模式、装饰器模式等。掌握这些设计模式,可以帮助开发者写出更加优雅、高效的代码。
JavaScript程序的性能优化是一项非常重要的任务。通过合理的算法设计、代码组织、资源管理等方式,可以显著提高JavaScript程序的运行效率。
JavaScript的函数具有以下一些高级特性:
箭头函数:箭头函数是ES6引入的一种新的函数语法。它们更简洁,并且在处理多个返回值时更方便。箭头函数的语法如下:
(parameters) => { // code to be executed}
高阶函数:高阶函数是接受一个或多个函数作为参数,并返回一个新的函数的函数。它们在组合和抽象中非常有用。例如,map()、filter() 和 reduce() 等函数都是高阶函数的例子。
闭包:闭包是一种特殊的函数结构,它允许一个函数在其定义范围之外访问其父作用域的变量和函数。例如,你可以创建一个函数,该函数在其定义范围内定义一个局部变量,然后返回一个内部函数,该内部函数可以访问并修改这个局部变量。
函数表达式:除了函数声明外,JavaScript还支持函数表达式。函数表达式可以将函数赋值给变量,从而提供更大的灵活性。
函数柯里化:函数柯里化是一种特殊的技术,它将函数转化为一系列嵌套函数的形式。每个嵌套函数都只接受部分参数,并返回一个新的函数,等待接收剩余的参数。
函数的递归调用:函数可以调用自身,这种技术称为函数的递归调用。函数的递归调用在很多情况下都非常有用,例如计算斐波那契数列、遍历树形结构的数据等。
以上就是JavaScript的一些高级玩法,希望对你有所帮助。如果你想深入了解这些话题,可以阅读一些相关的书籍和教程,如《JavaScript高级教程》、《JavaScript权威指南》等。同时,多做一些实践项目,也是提升JavaScript编程能力的好方法。
习目标:了解JavaScript是如何与HTML结合来创建动态网页,网页中嵌入JavaScript的不同方式,JavaScript的内容类型及其与<script>的关系
<script>是由Netscape创造出来,后来加到HTML规范中的。
<script>有8个属性:
1、async:表示立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或者等待其他脚本加载。只对外部脚本文件有效。
2、charset:使用src属性指定代码字符集。这个属性很少用,因为大多数浏览器不在乎它的值。
3、crossorigin;配置资源请求的CORS(跨源资源共享)设置。默认情况下不使用CORS。crossorigin = “anonymous”配置文件请求不用设置凭据标志。crossorigin = ”use-credentials“设置凭据标志,意味着出站请求会包含凭据。
4、defer:表示脚本可以延迟到文档全部解析和显示后再执行。新版本中只能用于外部脚本。
5、integrity:允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource integrity),如果验证签名不匹配则脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。
6、language:此属性已被废止。
7、src:表示包含外部要执行的代码的外部文件。
8、type:代替language,表示代码块中脚本语言的内容类型(也称为MIME类型),按照惯例这个值始终都是”text/JavaScript“,尽管”text/JavaScript“和”text/ecmascript“都已经废弃。JavaScript文件的MIME类型通常是”application/x-javascript“,不过给type属性这个值的话可能会导致脚本被忽略。在非IE的浏览器中有效的值还有”application/JavaScript“和”application/ecmascript"。如果这个值是module,则代码会被当成是ES6模块,而且只有这时候代码中才能出现import和export关键字。
使用<script>的方式有内联和外嵌两种,只要把code写入<script>code</script>中就好,code中要是包含字符串“<script>”,只要加上转义字符“\”即可。
如果要外嵌JavaScript代码只要使用src属性来链接外部文件即可如:
<script src=“example.js”></script>
XHTML 文档中,可以忽略结束标签写成<script src=“example.js”/>即可,但是这在HTML中不能使用。
过去把JavaScript和CSS一起写在head中,但是这意味着必须下载所有code并解析和解释完成后才开始渲染页面,对于JavaScript很多的页面会导致页面渲染速度过慢,为解决这个问题,JavaScript一般写在body元素的页面内容的最后边,如下
<html>
<head></head>
<body>
message
<script>code<\script>
<\body>
</html>
在外联JavaScript时可以使用defer属性来推迟脚本的运行。可以写成:
<html>
<head>
<script defer src = "example.js">code<\script>
</head>
<body>
message
<\body>
</html>
async属性从脚本处理方式上与defer类似,但是不同的是标记async的脚本并不能保证脚本按照他们的出现顺序执行,比如:
<html>
<head>
<script sync src = "example1.js">code<\script>
<script sync src = "example2.js">code<\script>
</head>
<body>
message
<\body>
</html>
不能保证example1比example2先执行。
除了<script>以外还可以用其他方式加载脚本。因为JavaScript可以使用DOM API,所以通过向DOM中动态地加入script元素同样可以加载指定脚本。只要创建一个script元素并将其添加到DOM即可。
let script = document.createElement('script');
script.src = 'gibberish.js';
document.head.appendChild(script);
当然,在把 HTMLElement 元素添加到 DOM 且执行到这段代码之前不会发送请求。默认情况下,以这种方式创建的<script>元素是以异步方式加载的,相当于添加了 async 属性。不过这样做可能会有问题,因为所有浏览器都支持 createElement()方法,但不是所有浏览器都支持 async 属性。因此,如果要统一动态脚本的加载行为,可以明确将其设置为同步加载:
let script = document.createElement('script');
script.src = 'gibberish.js';
script.async = false;
document.head.appendChild(script);
以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响它们在资源获取队列中的优先级。根据应用程序的工作方式以及怎么使用,这种方式可能会严重影响性能。要想让预加载器知道这些动态请求文件的存在,可以在文档头部显式声明它们:
<link rel="preload" href="gibberish.js">
可扩展超文本标记语言(XHTML,Extensible HyperText Markup Language)是将 HTML 作为 XML的应用重新包装的结果。与 HTML 不同,在 XHTML 中使用 JavaScript 必须指定 type 属性且值为text/javascript,HTML 中则可以没有这个属性。XHTML 虽然已经退出历史舞台,但实践中偶尔可能也会遇到遗留代码,为此本节稍作介绍。在 XHTML 中编写代码的规则比 HTML 中严格,这会影响使用<script>元素嵌入 JavaScript 代码。下面的代码块虽然在 HTML 中有效,但在 XHML 中是无效的。
<script type="text/javascript">
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
</script>
在 HTML 中,解析<script>元素会应用特殊规则。XHTML 中则没有这些规则。这意味着 a < b语句中的小于号(<)会被解释成一个标签的开始,并且由于作为标签开始的小于号后面不能有空格,这会导致语法错误。避免 XHTML 中这种语法错误的方法有两种。第一种是把所有小于号(<)都替换成对应的 HTML实体形式(<)。结果代码就是这样的:
<script type="text/javascript">
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
</script>
这样代码就可以在 XHTML 页面中运行了。不过,缺点是会影响阅读。好在还有另一种方法。第二种方法是把所有代码都包含到一个 CDATA 块中。在 XHTML(及 XML)中,CDATA 块表示文档中可以包含任意文本的区块,其内容不作为标签来解析,因此可以在其中包含任意字符,包括小于号,并且不会引发语法错误。使用 CDATA 的格式如下:
<script type="text/javascript"><![CDATA[
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
]]></script>
在兼容 XHTML 的浏览器中,这样能解决问题。但在不支持 CDATA 块的非 XHTML 兼容浏览器中则不行。为此,CDATA 标记必须使用 JavaScript 注释来抵消:
<script type="text/javascript">
//<![CDATA[
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
//]]>
</script>
这种格式适用于所有现代浏览器。虽然有点黑科技的味道,但它可以通过 XHTML 验证,而且对XHTML 之前的浏览器也能优雅地降级。
自 1995 年 Netscape 2 发布以来,所有浏览器都将 JavaScript 作为默认的编程语言。type 属性使用一个 MIME 类型字符串来标识<script>的内容,但 MIME 类型并没有跨浏览器标准化。即使浏览器默认使用 JavaScript,在某些情况下某个无效或无法识别的 MIME 类型也可能导致浏览器跳过(不执行)相关代码。因此,除非你使用 XHTML 或<script>标签要求或包含非 JavaScript 代码,最佳做法是不指定 type 属性。在最初采用 script 元素时,它标志着开始走向与传统 HTML 解析不同的流程。对这个元素需要应用特殊的解析规则,而这在不支持 JavaScript 的浏览器(特别是 Mosaic)中会导致问题。不支持的浏览器会把<script>元素的内容输出到页面上,从而破坏页面的外观。Netscape 联合 Mosaic 拿出了一个解决方案,对不支持 JavaScript 的浏览器隐藏嵌入的 JavaScript 代码。最终方案是把脚本代码包含在一个 HTML 注释中,像这样:
<script><!--
function sayHi(){
console.log("Hi!");
}
//--></script>
使用这种格式,Mosaic 等浏览器就可以忽略<script>标签中的内容,而支持 JavaScript 的浏览器则必须识别这种模式,将其中的内容作为 JavaScript 来解析。虽然这种格式仍然可以被所有浏览器识别和解析,但已经不再必要,而且不应该再使用了。在XHTML 模式下,这种格式也会导致脚本被忽略,因为代码处于有效的 XML 注释当中。
虽然可以直接在 HTML 文件中嵌入 JavaScript 代码,但通常认为最佳实践是尽可能将 JavaScript 代码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下。
可维护性。JavaScript 代码如果分散到很多 HTML 页面,会导致维护困难。而用一个目录保存所有 JavaScript 文件,则更容易维护,这样开发者就可以独立于使用它们的 HTML 页面来编辑代码。
缓存。浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。
适应未来。通过把 JavaScript 放到外部文件中,就不必考虑用 XHTML 或前面提到的注释黑科技。包含外部 JavaScript 文件的语法在 HTML 和 XHTML 中是一样的。在配置浏览器请求外部文件时,要重点考虑的一点是它们会占用多少带宽。在 SPDY/HTTP2 中,预请求的消耗已显著降低,以轻量、独立 JavaScript 组件形式向客户端送达脚本更具优势。比如,第一个页面包含如下脚本:
<script src="mainA.js"></script>
<script src="component1.js"></script>
<script src="component2.js"></script>
<script src="component3.js"></script>
...
后续页面可能包含如下脚本:
<script src="mainB.js"></script>
<script src="component3.js"></script>
<script src="component4.js"></script>
<script src="component5.js"></script>
...
在初次请求时,如果浏览器支持 SPDY/HTTP2,就可以从同一个地方取得一批文件,并将它们逐个放到浏览器缓存中。从浏览器角度看,通过 SPDY/HTTP2 获取所有这些独立的资源与获取一个大JavaScript 文件的延迟差不多。在第二个页面请求时,由于你已经把应用程序切割成了轻量可缓存的文件,第二个页面也依赖的某些组件此时已经存在于浏览器缓存中了。当然,这里假设浏览器支持 SPDY/HTTP2,只有比较新的浏览器才满足。如果你还想支持那些比较老的浏览器,可能还是用一个大文件更合适。
IE5.5 发明了文档模式的概念,即可以使用 doctype 切换文档模式。最初的文档模式有两种:混杂模式(quirks mode)和标准模式(standards mode)。前者让 IE 像 IE5 一样(支持一些非标准的特性),后者让 IE 具有兼容标准的行为。虽然这两种模式的主要区别只体现在通过 CSS 渲染的内容方面,但对JavaScript 也有一些关联影响,或称为副作用。本书会经常提到这些副作用。
IE 初次支持文档模式切换以后,其他浏览器也跟着实现了。随着浏览器的普遍实现,又出现了第三种文档模式:准标准模式(almost standards mode)。这种模式下的浏览器支持很多标准的特性,但是没有标准规定得那么严格。主要区别在于如何对待图片元素周围的空白(在表格中使用图片时最明显)。
混杂模式在所有浏览器中都以省略文档开头的 doctype 声明作为开关。这种约定并不合理,因为混杂模式在不同浏览器中的差异非常大,不使用黑科技基本上就没有浏览器一致性可言。标准模式通过下列几种文档类型声明开启:
<!-- HTML 4.01 Strict -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<!-- XHTML 1.0 Strict -->
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- HTML5 -->
<!DOCTYPE html>
准标准模式通过过渡性文档类型(Transitional)和框架集文档类型(Frameset)来触发:
<!-- HTML 4.01 Transitional -->
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<!-- HTML 4.01 Frameset -->
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<!-- XHTML 1.0 Transitional -->
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- XHTML 1.0 Frameset -->
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
准标准模式与标准模式非常接近,很少需要区分。人们在说到“标准模式”时,可能指其中任何一个。而对文档模式的检测(本书后面会讨论)也不会区分它们。本书后面所说的标准模式,指的就是除混杂模式以外的模式。
针对早期浏览器不支持 JavaScript 的问题,需要一个页面优雅降级的处理方案。最终,<noscript>元素出现,被用于给不支持 JavaScript 的浏览器提供替代内容。虽然如今的浏览器已经 100%支持JavaScript,但对于禁用 JavaScript 的浏览器来说,这个元素仍然有它的用处。<noscript>元素可以包含任何可以出现在<body>中的 HTML 元素,<script>除外。在下列两种情况下,浏览器将显示包含在<noscript>中的内容:
浏览器不支持脚本;
浏览器对脚本的支持被关闭。任何一个条件被满足,包含在<noscript>中的内容就会被渲染。否则,浏览器不会渲染<noscript>中的内容。
下面是一个例子:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script defer="defer" src="example1.js"></script>
<script defer="defer" src="example2.js"></script>
</head>
<body>
<noscript>
<p>This page requires a JavaScript-enabled browser.</p>
</noscript>
</body>
</html>
这个例子是在脚本不可用时让浏览器显示一段话。如果浏览器支持脚本,则用户永远不会看到它。
JavaScript 是通过<script>元素插入到 HTML 页面中的。这个元素可用于把 JavaScript 代码嵌入到HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。本章的重点可以总结如下。
要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。
所有<script>元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的情况下,包含在<script>元素中的代码必须严格按次序解释。
对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。
可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。
通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。
*请认真填写需求信息,我们会在24小时内与您取得联系。