JavaScript函数式编程[入门]

简介: JavaScript函数式编程[入门]




⭐️函数式编程

函数式编程是另一种软件开发方法。在函数式编程中,代码被组织成较小的基本的函数,可以结合起来构建复杂的程序。

在这个课程中,你将学习函数式编程的核心概念,包括纯函数、如何避免突变、如何使用 .map() 和 .filter() 等方法编写更整洁的代码

⭐️学习函数式编程

函数式编程是一种方案简单、功能独立、对作用域外没有任何副作用的编程范式:INPUT -> PROCESS -> OUTPUT。

函数式编程:

1)功能独立——不依赖于程序的状态(比如可能发生变化的全局变量);

2)纯函数——同一个输入永远能得到同一个输出

3)有限的副作用——可以严格地限制函数外部对状态的更改

freeCodeCamp 的成员们爱喝茶。

在代码编辑器中,已经为你定义好了prepareTea和getTea函数。 调用 getTea 函数为团队准备 40 杯茶,并将它们存储在 tea4TeamFCC 变量里。

// 函数返回表示“一杯绿茶(green tea)”的字符串
const prepareTea = () => 'greenTea';
/*
有一个函数(代表茶的种类)和需要几杯茶,下面的函数返回一个数组,包含字符串(每个字符串表示一杯特别种类的茶)。
*/
const getTea = (numOfCups) => {
  const teaCups = [];
  for(let cups = 1; cups <= numOfCups; cups += 1) {
    const teaCup = prepareTea();
    teaCups.push(teaCup);
  }
  return teaCups;
};
// 只修改这一行下面的代码
const tea4TeamFCC = getTea(40);
// 只修改这一行上面的代码

了解函数式编程术语

FCC 团队需求有变更,现在想要两种茶:绿茶(green tea)和红茶(black tea)。 事实证明,用户需求变更是很常见的。

基于以上信息,我们需要重构上一节挑战中的 getTea 函数来处理多种茶的请求。 我们可以修改 getTea 接受一个函数作为参数,使它能够修改茶的类型。 这让 getTea 更灵活,也使需求变更时为程序员提供更多控制权。

首先,我们将介绍一些术语:

Callbacks 是被传递到另一个函数中调用的函数。 你应该已经在其他函数中看过这个写法,例如在 filter 中,回调函数告诉 JavaScript 以什么规则过滤数组。

函数就像其他正常值一样,可以赋值给变量、传递给另一个函数,或从其它函数返回这种函数叫做头等 first class 函数。 在 JavaScript 中,所有函数都是头等函数

将函数作为参数或将函数作为返回值返回的函数叫作高阶函数。

当函数被传递给另一个函数或从另一个函数返回时,那些传入或返回的函数可以叫作 lambda

准备 27 杯绿茶和 13 杯红茶,分别存入 tea4GreenTeamFCC 和 tea4BlackTeamFCC 变量。 请注意,getTea 函数已经变了,现在它接收一个函数作为第一个参数。

注意:数据(茶的数量)作为最后一个参数。 我们将在后面的课程中对此进行更多讨论。

// 函数返回表示“一杯绿茶(green tea)”的字符串
const prepareGreenTea = () => 'greenTea';
// 函数返回表示“一杯红茶(black tea)”的字符串
const prepareBlackTea = () => 'blackTea';
/*
有一个函数(代表茶的种类)和需要几杯茶,下面的函数返回一个数组,包含字符串(每个字符串表示一杯特别种类的茶)。
*/
const getTea = (prepareTea, numOfCups) => {
  const teaCups = [];
  for(let cups = 1; cups <= numOfCups; cups += 1) {
    const teaCup = prepareTea();
    teaCups.push(teaCup);
  }
  return teaCups;
};
// 只修改这一行下面的代码
const tea4GreenTeamFCC = getTea(prepareGreenTea,27);
const tea4BlackTeamFCC = getTea(prepareBlackTea,13);
// 只修改这一行上面的代码
console.log(
  tea4GreenTeamFCC,
  tea4BlackTeamFCC
);

了解使用命令式编程的危害

使用函数式编程是一个好的习惯。 它使你的代码易于管理,避免潜在的 bug。 但在开始之前,先看看命令式编程方法,以强调你可能有什么问题

在英语 (以及许多其他语言) 中,命令式时态用来发出指令。 同样,命令式编程是向计算机提供一套执行任务的声明

命令式编程常常改变程序状态,例如更新全局变量。 一个典型的例子是编写 for 循环,它为一个数组的索引提供了准确的迭代方向。

相反,函数式编程是声明式编程的一种形式。 通过调用方法或函数来告诉计算机要做什么

JavaScript 提供了许多处理常见任务的方法,所以你无需写出计算机应如何执行它们。 例如,你可以用 map 函数替代上面提到的 for 循环来处理数组迭代。 这有助于避免语义错误,如调试章节介绍的 “Off By One Errors”。

考虑这样的场景:你正在浏览器中浏览网页,并想操作你打开的标签。 下面我们来试试用面向对象的思路来描述这种情景。

窗口对象由选项卡组成,通常会打开多个窗口。 窗口对象中每个打开网站的标题都保存在一个数组中。 在对浏览器进行了如打开新标签、合并窗口、关闭标签之类的操作后,你需要输出所有打开的标签。 关掉的标签将从数组中删除,新打开的标签(为简单起见)则添加到数组的末尾

代码编辑器中显示了此功能的实现,其中包含 tabOpen(),tabClose(),和 join() 函数。 tabs 数组是窗口对象的一部分用于储存打开页面的名称。

在编辑器中运行代码。 它使用了有副作用的方法,导致输出错误。 存储在 finalTabs.tabs 中的打开标签的最终列表应该是 ['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium', 'new tab', 'Netflix', 'YouTube', 'Vine', 'GMail', 'Work mail', 'Docs', 'freeCodeCamp', 'new tab'],但输出会略有不同。

修改 Window.prototype.tabClose 使其删除正确的标签。

// tabs 是在窗口中打开的每个站点的 title 的数组
const Window = function(tabs) {
  this.tabs = tabs; // 我们记录对象内部的数组
};
// 当你将两个窗口合并为一个窗口时
Window.prototype.join = function(otherWindow) {
  this.tabs = this.tabs.concat(otherWindow.tabs);
  return this;
};
// 当你在最后打开一个选项卡时
Window.prototype.tabOpen = function(tab) {
  this.tabs.push('new tab'); // 我们现在打开一个新的选项卡
  return this;
};
// 当你关闭一个选项卡时
Window.prototype.tabClose = function(index) {
  // 只修改这一行下面的代码
  const tabsBeforeIndex = this.tabs.splice(0, index); // 点击之前获取 tabs
  const tabsAfterIndex = this.tabs.splice(1); // 点击之后获取 tabs
  this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); // 将它们合并起来
  // 只修改这一行上面的代码
  return this;
 };
// 我们创建三个浏览器窗口
const workWindow = new Window(['GMail', 'Inbox', 'Work mail', 'Docs', 'freeCodeCamp']); // 你的邮箱、Google Drive 和其他工作地点
const socialWindow = new Window(['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium']); // 社交网站
const videoWindow = new Window(['Netflix', 'YouTube', 'Vimeo', 'Vine']); // 娱乐网站
// 现在执行打开选项卡,关闭选项卡和其他操作
const finalTabs = socialWindow
  .tabOpen() // 打开一个新的选项卡,显示猫的图片
  .join(videoWindow.tabClose(2)) // 关闭视频窗口的第三个选项卡,并合并
  .join(workWindow.tabClose(1).tabOpen());
console.log(finalTabs.tabs);
const tabsBeforeIndex = this.tabs.slice(0, index); // get the tabs before the tab
 const tabsAfterIndex = this.tabs.slice(index + 1); // get the tabs after the tab

splice和slice区别

Splice ()应该始终谨慎使用,因为它修改正在处理的内容。有关文档以及拼接和切片之间的差异,请参阅:

1.splice()方法改变了原始的数组,它可以删除或者替换数组中的元素,并且能够在指定位置插入新的元素。这个方法会修改原始数组并返回被删除或被替换元素组成的一个新数组。

const months = ['Jan', 'March', 'April', 'June'];
//新增元素
let arr = months.splice(1, 0, 'Feb'); // 在索引为 1 的位置插入'Feb'
console.log(arr);//[]新增元素返回的是一个空数组
console.log(months); // ["Jan", "Feb", "March", "April", "June"]
//替换元素
let newArr = months.splice(4, 1, 'May'); // 替换索引为 4 的元素为'May'
console.log(newArr);//['June'] 返回被替换的元素组成的新数组
console.log(months); // ['Jan', 'Feb', 'March', 'April', 'May']
//删除元素
let val = months.splice(1, 1)//指定在下标为1的位置删除一个元素
console.log(val);//['Feb'] 返回被删除的元素组成的新数组
console.log(months);//['Jan', 'March', 'April', 'May']//改变后的原数组

因此,splice()和slice()两个方法在用途上是有较大差别的。splice()主要用于修改原始数组,而slice()则用于从原始数组中提取需要的元素。

使用函数式编程避免变化和副作用

如果你还没想通,上一个挑战的问题出在 tabClose() 函数里的 splice。 不幸的是,splice 修改了调用它的原始数组,所以第二次调用它时是基于修改后的数组,才给出了意料之外的结果

这是一个小例子,还有更广义的定义——在变量,数组或对象上调用一个函数,这个函数会改变对象中的变量或其他东西。

函数式编程的核心原则之一是不改变任何东西。 变化会导致错误。 如果一个函数不改变传入的参数、全局变量等数据,那么它造成问题的可能性就会小很多。

前面的例子没有任何复杂的操作,但是 splice 方法改变了原始数组,导致 bug 产生。

回想一下,在函数式编程中,改变或变更叫做 mutation,这种改变的结果叫做“副作用”(side effect)。 理想情况下,函数应该是不会产生任何副作用的 pure function。

让我们尝试掌握这个原则:不要改变代码中的任何变量或对象

填写 incrementer 函数的代码,使其返回值为全局变量 fixedValue 增加 1 。

// 全局变量
let fixedValue = 4;
function incrementer() {
  // 只修改这一行下面的代码
  return 1+fixedValue
  // 只修改这一行上面的代码
}

传递参数以避免函数中的外部依赖

上一个挑战是更接近函数式编程原则的挑战,但是仍然缺少一些东西。

虽然我们没有改变全局变量值,但在没有全局变量 fixedValue 的情况下,incrementer 函数将不起作用

函数式编程的另一个原则是:总是显式声明依赖关系。 如果函数依赖于一个变量或对象,那么将该变量或对象作为参数直接传递到函数中。

这样做会有很多好处。 其中一点是让函数更容易测试,因为你确切地知道参数是什么,并且这个参数也不依赖于程序中的任何其他内容

其次,这样做可以让你更加自信地更改,删除或添加新代码。 因为你很清楚哪些是可以改的,哪些是不可以改的,这样你就知道哪里可能会有潜在的陷阱。

最后,无论代码的哪一部分执行它,函数总是会为同一组输入生成相同的输出

更新 incrementer 函数,明确声明其依赖项。

编写 incrementer 函数,获取它的参数,然后将值增加 1。

// 全局变量
let fixedValue = 4;
// 只修改这一行下面的代码
function incrementer(fixedValue) {
  return fixedValue+1
  // 只修改这一行上面的代码
}

在函数中重构全局变量

目前为止,我们已经看到了函数式编程的两个原则:

不要更改变量或对象 - 创建新变量和对象,并在需要时从函数返回它们。 提示:使用类似 const newArr = arrVar 的东西,其中 arrVar 是一个数组,只会创建对现有变量的引用,而不是副本。 所以更改 newArr 中的值会同时更改 arrVar 中的值

声明函数参数 - 函数内的任何计算仅取决于参数,而不取决于任何全局对象或变量

给数字增加 1 不够有意思,但是我们可以在处理数组或更复杂的对象时应用这些原则。

重构代码,使全局数组 bookList 在函数内部不会被改变。 add 函数可以将指定的 bookName 增加到数组末尾并返回一个新的数组(列表)。 remove 函数可以从数组中移除指定 bookName。

注意: 两个函数都应该返回一个数组,任何新参数都应该在 bookName 参数之前添加。

// 全局变量
const bookList = ["The Hound of the Baskervilles", "On The Electrodynamics of Moving Bodies", "Philosophiæ Naturalis Principia Mathematica", "Disquisitiones Arithmeticae"];
// 修改这行下面的代码
function add(arr,bookName) {
  //let newarr=[...arr]
  return arr.slice().concat(bookName);
  // 修改这行上面的代码
}
// 修改这行下面的代码
function remove(arr,bookName) {
  const book_index = bookList.indexOf(bookName);
  if (book_index >= 0) {
    return arr.slice(0,book_index).concat(arr.slice(book_index+1));
    // 修改这行上面的代码
    }
}
// 全局变量
const bookList = ["The Hound of the Baskervilles", "On The Electrodynamics of Moving Bodies", "Philosophiæ Naturalis Principia Mathematica", "Disquisitiones Arithmeticae"];
// 修改这行下面的代码
function add(arr,bookName) {
  let newarr=[...arr]
  newarr.push(bookName);
  return newarr
  // 修改这行上面的代码
}
// 修改这行下面的代码
function remove(arr,bookName) {
  let newarr=[...arr]
  const book_index = bookList.indexOf(bookName);
  if (book_index >= 0) {
    newarr.splice(book_index,1)
    return newarr
    // 修改这行上面的代码
    }
}
// 全局变量
const bookList = ["The Hound of the Baskervilles", "On The Electrodynamics of Moving Bodies", "Philosophiæ Naturalis Principia Mathematica", "Disquisitiones Arithmeticae"];
// 修改这行下面的代码
function add(arr,bookName) {
  return [...arr,bookName]
  // 修改这行上面的代码
}
// 修改这行下面的代码
function remove(arr,bookName) {
  return arr.filter((book)=>book!=bookName)
}

使用 map 方法从数组中提取数据

目前为止,我们已经学会了使用纯函数来避免程序中的副作用。 此外,我们已经看到函数的值仅取决于其输入参数

仅仅是个开始。 顾名思义,函数式编程以函数理论为中心

能够将它们作为参数传递给其他函数,从另一个函数返回一个函数是有意义的。 函数在 JavaScript 中被视为 First Class Objects,它们可以像任何其他对象一样使用。 它们可以保存在变量中,存储在对象中,也可以作为函数参数传递

让我们从一些简单的数组函数开始,这些函数是数组对象原型上的方法。 在本练习中,我们来了解下数组的 map 方法(即 Array.prototype.map())

请记住,map方法是迭代数组中每一项的方式之一。 在对每个元素应用回调函数后,它会创建一个新数组(不改变原来的数组)。 它这样做时没有改变原始数组。

调用回调函数时,传入了三个参数。 第一个参数是当前正在处理的数组项。 第二个参数是当前数组项的索引值,第三个参数是在其上调用 map 方法的数组。

看下在 users 上使用 map 方法的例子,返回了一个新数组只包含了用户的名字。 为了简化,例子里只使用了回调函数的第一个参数。

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'camperCat', age: 10 }
];
const names = users.map(user => user.name);
console.log(names);

控制台将显示值 [ ‘John’, ‘Amy’, ‘camperCat’ ]。

watchList 数组保存了包含一些电影信息的对象。 在 watchList 上使用 map,将一个新的对象数组赋值给 ratings 变量。 新数组中的每个电影都只能有一个值为电影名称的 title 键,和一个值为 IMDB 评级的 rating 键。 目前编辑器中的代码是使用 for 循环实现,你应该使用 map 表达式替换循环功能。

// 全局变量
const watchList = [
  {
    "Title": "Inception",
    "Year": "2010",
    "Rated": "PG-13",
    "Released": "16 Jul 2010",
    "Runtime": "148 min",
    "Genre": "Action, Adventure, Crime",
    "Director": "Christopher Nolan",
    "Writer": "Christopher Nolan",
    "Actors": "Leonardo DiCaprio, Joseph Gordon-Levitt, Elliot Page, Tom Hardy",
    "Plot": "A thief, who steals corporate secrets through use of dream-sharing technology, is given the inverse task of planting an idea into the mind of a CEO.",
    "Language": "English, Japanese, French",
    "Country": "USA, UK",
    "Awards": "Won 4 Oscars. Another 143 wins & 198 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMjAxMzY3NjcxNF5BMl5BanBnXkFtZTcwNTI5OTM0Mw@@._V1_SX300.jpg",
    "Metascore": "74",
    "imdbRating": "8.8",
    "imdbVotes": "1,446,708",
    "imdbID": "tt1375666",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Interstellar",
    "Year": "2014",
    "Rated": "PG-13",
    "Released": "07 Nov 2014",
    "Runtime": "169 min",
    "Genre": "Adventure, Drama, Sci-Fi",
    "Director": "Christopher Nolan",
    "Writer": "Jonathan Nolan, Christopher Nolan",
    "Actors": "Ellen Burstyn, Matthew McConaughey, Mackenzie Foy, John Lithgow",
    "Plot": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
    "Language": "English",
    "Country": "USA, UK",
    "Awards": "Won 1 Oscar. Another 39 wins & 132 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMjIxNTU4MzY4MF5BMl5BanBnXkFtZTgwMzM4ODI3MjE@._V1_SX300.jpg",
    "Metascore": "74",
    "imdbRating": "8.6",
    "imdbVotes": "910,366",
    "imdbID": "tt0816692",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "The Dark Knight",
    "Year": "2008",
    "Rated": "PG-13",
    "Released": "18 Jul 2008",
    "Runtime": "152 min",
    "Genre": "Action, Adventure, Crime",
    "Director": "Christopher Nolan",
    "Writer": "Jonathan Nolan (screenplay), Christopher Nolan (screenplay), Christopher Nolan (story), David S. Goyer (story), Bob Kane (characters)",
    "Actors": "Christian Bale, Heath Ledger, Aaron Eckhart, Michael Caine",
    "Plot": "When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, the caped crusader must come to terms with one of the greatest psychological tests of his ability to fight injustice.",
    "Language": "English, Mandarin",
    "Country": "USA, UK",
    "Awards": "Won 2 Oscars. Another 146 wins & 142 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMTMxNTMwODM0NF5BMl5BanBnXkFtZTcwODAyMTk2Mw@@._V1_SX300.jpg",
    "Metascore": "82",
    "imdbRating": "9.0",
    "imdbVotes": "1,652,832",
    "imdbID": "tt0468569",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Batman Begins",
    "Year": "2005",
    "Rated": "PG-13",
    "Released": "15 Jun 2005",
    "Runtime": "140 min",
    "Genre": "Action, Adventure",
    "Director": "Christopher Nolan",
    "Writer": "Bob Kane (characters), David S. Goyer (story), Christopher Nolan (screenplay), David S. Goyer (screenplay)",
    "Actors": "Christian Bale, Michael Caine, Liam Neeson, Katie Holmes",
    "Plot": "After training with his mentor, Batman begins his fight to free crime-ridden Gotham City from the corruption that Scarecrow and the League of Shadows have cast upon it.",
    "Language": "English, Urdu, Mandarin",
    "Country": "USA, UK",
    "Awards": "Nominated for 1 Oscar. Another 15 wins & 66 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BNTM3OTc0MzM2OV5BMl5BanBnXkFtZTYwNzUwMTI3._V1_SX300.jpg",
    "Metascore": "70",
    "imdbRating": "8.3",
    "imdbVotes": "972,584",
    "imdbID": "tt0372784",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Avatar",
    "Year": "2009",
    "Rated": "PG-13",
    "Released": "18 Dec 2009",
    "Runtime": "162 min",
    "Genre": "Action, Adventure, Fantasy",
    "Director": "James Cameron",
    "Writer": "James Cameron",
    "Actors": "Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang",
    "Plot": "A paraplegic marine dispatched to the moon Pandora on a unique mission becomes torn between following his orders and protecting the world he feels is his home.",
    "Language": "English, Spanish",
    "Country": "USA, UK",
    "Awards": "Won 3 Oscars. Another 80 wins & 121 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMTYwOTEwNjAzMl5BMl5BanBnXkFtZTcwODc5MTUwMw@@._V1_SX300.jpg",
    "Metascore": "83",
    "imdbRating": "7.9",
    "imdbVotes": "876,575",
    "imdbID": "tt0499549",
    "Type": "movie",
    "Response": "True"
  }
];
// 只修改这一行下面的代码
//const ratings = [];
//for (let i = 0; i < watchList.length; i++) {
//  ratings.push({title: watchList[i]["Title"], rating: watchList[i]["imdbRating"]});
//}
const ratings = watchList.map(mov=>({
  title:mov.Title,
  rating:mov.imdbRating
  })
  )
// 只修改这一行上面的代码
console.log(JSON.stringify(ratings));

在原型上实现 map 方法

之前用到了 Array.prototype.map() 方法(即 map()),通过 map 返回一个与调用它的数组长度相同的数组只要它的回调函数不改变原始数组,它就不会改变原始数组。

换句话说,map 是一个纯函数,它的输出仅取决于输入的数组和作为参数传入的回调函数。 此外,它接收另一个函数作为它的参数

实现一个 map,加深对它的了解。 你可以用 for 循环或者 Array.prototype.forEach() 方法。

写一个和 Array.prototype.map() 一样的 Array.prototype.myMap()。 不能使用内置的 map 方法。 在 myMap 方法内,可以使用 this 访问 Array 实例。

Array.prototype.myMap = function(callback) {
  const newArray = [];
  // 只修改这一行下面的代码
  for(let i=0; i<this.length;i++){
    newArray.push(callback(this[i],i,this))
  }
  // 只修改这一行上面的代码
  return newArray;
};

使用 filter 方法从数组中提取数据

另一个有用的数组方法是 filter()(即 Array.prototype.filter())。

filter 在一个数组的每个元素上调用一个函数,并返回一个新的数组,其中只包含该函数返回一个真值的元素,也就是说,一个被传递给 Boolean() 构造函数后返回 true 的值。 换言之,它根据传递给它的函数过滤数组和 map 一样,filter 不会改变原始数组。

回调函数接收三个参数。 第一个参数是当前正在被处理的元素。 第二个参数是这个元素的索引第三个参数是在其上调用 filter 方法的数组。

看下在 users 上使用 filter 方法的例子,返回了一个包含了 30 岁以下的用户新数组。 为了简化,例子里只使用了回调函数的第一个参数。

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'camperCat', age: 10 }
];
const usersUnder30 = users.filter(user => user.age < 30);
console.log(usersUnder30);

控制台将显示值 [ { name: ‘Amy’, age: 20 }, { name: ‘camperCat’, age: 10 } ]

watchList 变量中包含一组存有多部电影信息对象。 结合 filter 和 map 返回一个 watchList 只包含 title 和 rating 属性的新数组。 新数组只包含 imdbRating 值大于或等于 8.0 的对象。 请注意,rating 值在对象中保存为字符串,你可能需要将它转换成数字来执行运算。

// 全局变量
const watchList = [
  {
    "Title": "Inception",
    "Year": "2010",
    "Rated": "PG-13",
    "Released": "16 Jul 2010",
    "Runtime": "148 min",
    "Genre": "Action, Adventure, Crime",
    "Director": "Christopher Nolan",
    "Writer": "Christopher Nolan",
    "Actors": "Leonardo DiCaprio, Joseph Gordon-Levitt, Elliot Page, Tom Hardy",
    "Plot": "A thief, who steals corporate secrets through use of dream-sharing technology, is given the inverse task of planting an idea into the mind of a CEO.",
    "Language": "English, Japanese, French",
    "Country": "USA, UK",
    "Awards": "Won 4 Oscars. Another 143 wins & 198 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMjAxMzY3NjcxNF5BMl5BanBnXkFtZTcwNTI5OTM0Mw@@._V1_SX300.jpg",
    "Metascore": "74",
    "imdbRating": "8.8",
    "imdbVotes": "1,446,708",
    "imdbID": "tt1375666",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Interstellar",
    "Year": "2014",
    "Rated": "PG-13",
    "Released": "07 Nov 2014",
    "Runtime": "169 min",
    "Genre": "Adventure, Drama, Sci-Fi",
    "Director": "Christopher Nolan",
    "Writer": "Jonathan Nolan, Christopher Nolan",
    "Actors": "Ellen Burstyn, Matthew McConaughey, Mackenzie Foy, John Lithgow",
    "Plot": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
    "Language": "English",
    "Country": "USA, UK",
    "Awards": "Won 1 Oscar. Another 39 wins & 132 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMjIxNTU4MzY4MF5BMl5BanBnXkFtZTgwMzM4ODI3MjE@._V1_SX300.jpg",
    "Metascore": "74",
    "imdbRating": "8.6",
    "imdbVotes": "910,366",
    "imdbID": "tt0816692",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "The Dark Knight",
    "Year": "2008",
    "Rated": "PG-13",
    "Released": "18 Jul 2008",
    "Runtime": "152 min",
    "Genre": "Action, Adventure, Crime",
    "Director": "Christopher Nolan",
    "Writer": "Jonathan Nolan (screenplay), Christopher Nolan (screenplay), Christopher Nolan (story), David S. Goyer (story), Bob Kane (characters)",
    "Actors": "Christian Bale, Heath Ledger, Aaron Eckhart, Michael Caine",
    "Plot": "When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, the caped crusader must come to terms with one of the greatest psychological tests of his ability to fight injustice.",
    "Language": "English, Mandarin",
    "Country": "USA, UK",
    "Awards": "Won 2 Oscars. Another 146 wins & 142 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMTMxNTMwODM0NF5BMl5BanBnXkFtZTcwODAyMTk2Mw@@._V1_SX300.jpg",
    "Metascore": "82",
    "imdbRating": "9.0",
    "imdbVotes": "1,652,832",
    "imdbID": "tt0468569",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Batman Begins",
    "Year": "2005",
    "Rated": "PG-13",
    "Released": "15 Jun 2005",
    "Runtime": "140 min",
    "Genre": "Action, Adventure",
    "Director": "Christopher Nolan",
    "Writer": "Bob Kane (characters), David S. Goyer (story), Christopher Nolan (screenplay), David S. Goyer (screenplay)",
    "Actors": "Christian Bale, Michael Caine, Liam Neeson, Katie Holmes",
    "Plot": "After training with his mentor, Batman begins his fight to free crime-ridden Gotham City from the corruption that Scarecrow and the League of Shadows have cast upon it.",
    "Language": "English, Urdu, Mandarin",
    "Country": "USA, UK",
    "Awards": "Nominated for 1 Oscar. Another 15 wins & 66 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BNTM3OTc0MzM2OV5BMl5BanBnXkFtZTYwNzUwMTI3._V1_SX300.jpg",
    "Metascore": "70",
    "imdbRating": "8.3",
    "imdbVotes": "972,584",
    "imdbID": "tt0372784",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Avatar",
    "Year": "2009",
    "Rated": "PG-13",
    "Released": "18 Dec 2009",
    "Runtime": "162 min",
    "Genre": "Action, Adventure, Fantasy",
    "Director": "James Cameron",
    "Writer": "James Cameron",
    "Actors": "Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang",
    "Plot": "A paraplegic marine dispatched to the moon Pandora on a unique mission becomes torn between following his orders and protecting the world he feels is his home.",
    "Language": "English, Spanish",
    "Country": "USA, UK",
    "Awards": "Won 3 Oscars. Another 80 wins & 121 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMTYwOTEwNjAzMl5BMl5BanBnXkFtZTcwODc5MTUwMw@@._V1_SX300.jpg",
    "Metascore": "83",
    "imdbRating": "7.9",
    "imdbVotes": "876,575",
    "imdbID": "tt0499549",
    "Type": "movie",
    "Response": "True"
  }
];
// 只修改这一行下面的代码
const filteredList = watchList
.filter(item=>item.imdbRating>=8.0)
.map(item=>({title:item.Title,rating:item.imdbRating}))
// 只修改这一行上面的代码
console.log(filteredList);
// 全局变量
const watchList = [
  {
    "Title": "Inception",
    "Year": "2010",
    "Rated": "PG-13",
    "Released": "16 Jul 2010",
    "Runtime": "148 min",
    "Genre": "Action, Adventure, Crime",
    "Director": "Christopher Nolan",
    "Writer": "Christopher Nolan",
    "Actors": "Leonardo DiCaprio, Joseph Gordon-Levitt, Elliot Page, Tom Hardy",
    "Plot": "A thief, who steals corporate secrets through use of dream-sharing technology, is given the inverse task of planting an idea into the mind of a CEO.",
    "Language": "English, Japanese, French",
    "Country": "USA, UK",
    "Awards": "Won 4 Oscars. Another 143 wins & 198 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMjAxMzY3NjcxNF5BMl5BanBnXkFtZTcwNTI5OTM0Mw@@._V1_SX300.jpg",
    "Metascore": "74",
    "imdbRating": "8.8",
    "imdbVotes": "1,446,708",
    "imdbID": "tt1375666",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Interstellar",
    "Year": "2014",
    "Rated": "PG-13",
    "Released": "07 Nov 2014",
    "Runtime": "169 min",
    "Genre": "Adventure, Drama, Sci-Fi",
    "Director": "Christopher Nolan",
    "Writer": "Jonathan Nolan, Christopher Nolan",
    "Actors": "Ellen Burstyn, Matthew McConaughey, Mackenzie Foy, John Lithgow",
    "Plot": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
    "Language": "English",
    "Country": "USA, UK",
    "Awards": "Won 1 Oscar. Another 39 wins & 132 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMjIxNTU4MzY4MF5BMl5BanBnXkFtZTgwMzM4ODI3MjE@._V1_SX300.jpg",
    "Metascore": "74",
    "imdbRating": "8.6",
    "imdbVotes": "910,366",
    "imdbID": "tt0816692",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "The Dark Knight",
    "Year": "2008",
    "Rated": "PG-13",
    "Released": "18 Jul 2008",
    "Runtime": "152 min",
    "Genre": "Action, Adventure, Crime",
    "Director": "Christopher Nolan",
    "Writer": "Jonathan Nolan (screenplay), Christopher Nolan (screenplay), Christopher Nolan (story), David S. Goyer (story), Bob Kane (characters)",
    "Actors": "Christian Bale, Heath Ledger, Aaron Eckhart, Michael Caine",
    "Plot": "When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, the caped crusader must come to terms with one of the greatest psychological tests of his ability to fight injustice.",
    "Language": "English, Mandarin",
    "Country": "USA, UK",
    "Awards": "Won 2 Oscars. Another 146 wins & 142 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMTMxNTMwODM0NF5BMl5BanBnXkFtZTcwODAyMTk2Mw@@._V1_SX300.jpg",
    "Metascore": "82",
    "imdbRating": "9.0",
    "imdbVotes": "1,652,832",
    "imdbID": "tt0468569",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Batman Begins",
    "Year": "2005",
    "Rated": "PG-13",
    "Released": "15 Jun 2005",
    "Runtime": "140 min",
    "Genre": "Action, Adventure",
    "Director": "Christopher Nolan",
    "Writer": "Bob Kane (characters), David S. Goyer (story), Christopher Nolan (screenplay), David S. Goyer (screenplay)",
    "Actors": "Christian Bale, Michael Caine, Liam Neeson, Katie Holmes",
    "Plot": "After training with his mentor, Batman begins his fight to free crime-ridden Gotham City from the corruption that Scarecrow and the League of Shadows have cast upon it.",
    "Language": "English, Urdu, Mandarin",
    "Country": "USA, UK",
    "Awards": "Nominated for 1 Oscar. Another 15 wins & 66 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BNTM3OTc0MzM2OV5BMl5BanBnXkFtZTYwNzUwMTI3._V1_SX300.jpg",
    "Metascore": "70",
    "imdbRating": "8.3",
    "imdbVotes": "972,584",
    "imdbID": "tt0372784",
    "Type": "movie",
    "Response": "True"
  },
  {
    "Title": "Avatar",
    "Year": "2009",
    "Rated": "PG-13",
    "Released": "18 Dec 2009",
    "Runtime": "162 min",
    "Genre": "Action, Adventure, Fantasy",
    "Director": "James Cameron",
    "Writer": "James Cameron",
    "Actors": "Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang",
    "Plot": "A paraplegic marine dispatched to the moon Pandora on a unique mission becomes torn between following his orders and protecting the world he feels is his home.",
    "Language": "English, Spanish",
    "Country": "USA, UK",
    "Awards": "Won 3 Oscars. Another 80 wins & 121 nominations.",
    "Poster": "http://ia.media-imdb.com/images/M/MV5BMTYwOTEwNjAzMl5BMl5BanBnXkFtZTcwODc5MTUwMw@@._V1_SX300.jpg",
    "Metascore": "83",
    "imdbRating": "7.9",
    "imdbVotes": "876,575",
    "imdbID": "tt0499549",
    "Type": "movie",
    "Response": "True"
  }
];
// 只修改这一行下面的代码
const filteredList = watchList
.map(item=>({title:item.Title,rating:item.imdbRating}))
.filter(item=>item.rating>=8.0)
// 只修改这一行上面的代码
console.log(filteredList);

在原型上实现 filter 方法

为了加深对 filter 的理解,可以自己实现一个。 可以用 for 循环或 Array.prototype.forEach()

编写一个和 Array.prototype.filter() 功能一样的 Array.prototype.myFilter() 方法。 不能使用内置的 filter 方法。 在 myFilter 方法内部,可以使用 this 访问 Array 实例。

Array.prototype.myFilter = function(callback) {
  const newArray = [];
  // 只修改这一行下面的代码
  for(let i=0; i<this.length;i++){
    if(callback(this[i],i,this)){
      newArray.push(this[i])
    }
  }
  // 只修改这一行上面的代码
  return newArray;
};

使用 slice 方法返回数组的一部分

slice 方法可以从已有数组中返回指定元素。 它接受两个参数,第一个规定从何处开始选取,第二个规定从何处结束选取(不包括该元素)。 如果没有传参,则默认为从数组的开头开始到结尾结束,这是复制整个数组的简单方式。 slice 返回一个新数组,不会修改原始数组。

举个例子:

const arr = ["Cat", "Dog", "Tiger", "Zebra"];
const newArray = arr.slice(1, 3);
newArray 值为 ["Dog", "Tiger"]

在 sliceArray 函数中使用 slice 方法,给出 beginSlice 和 endSlice 索引,返回 anim 数组的一部分。 这个函数应返回一个数组。

function sliceArray(anim, beginSlice, endSlice) {
  // 只修改这一行下面的代码
return anim.slice(beginSlice, endSlice)
  // 只修改这一行上面的代码
}
const inputAnim = ["Cat", "Dog", "Tiger", "Zebra", "Ant"];
sliceArray(inputAnim, 1, 3);

使用 slice 而不是 splice 从数组中移除元素

使用数组时经常遇到要删除一些元素并保留数组剩余部分的情况。 为此,JavaScript 提供了 splice 方法,它接收两个参数:从哪里开始删除项目的索引,和要删除的项目数。 如果没有提供第二个参数,默认情况下是移除一直到结尾的所有元素。 但 splice 方法会改变调用它的原始数组。 举个例子:

const cities = ["Chicago", "Delhi", "Islamabad", "London", "Berlin"];
cities.splice(3, 1);

在这里 splice 返回字符串 London 并从城市数组中删除它。 cities 将有值 [“Chicago”, “Delhi”, “Islamabad”, “Berlin”]。

正如我们在上一次挑战中看到的那样,slice 方法不会改变原始数组,而是返回一个可以保存到变量中的新数组。 回想一下,slice 方法接收两个参数,从开始索引开始选取到结束(不包括该元素),并在新数组中返回这些元素。 使用 slice 方法替代 splice 有助于避免数组变化产生的副作用

用 slice 代替 splice 重写 nonMutatingSplice 函数。 将 cities 数组长度限制为 3,并返回一个仅包含前 3 项的新数组。

不要改变提供给函数的原始数组。

function nonMutatingSplice(cities) {
  return cities.slice(0,3);
}

使用 concat 方法组合两个数组

Concatenation 意思是将元素连接到尾部。 同理,JavaScript 为字符串和数组提供了concat方法。 对数组来说,在一个数组上调用 concat 方法,然后提供另一个数组作为参数添加到第一个数组末尾。 它返回一个新数组,不会改变任何一个原始数组。 举个例子:

[1, 2, 3].concat([4, 5, 6]);

返回的数组将是 [1, 2, 3, 4, 5, 6]。

在 nonMutatingConcat 函数里使用 concat,将 attach 拼接到 original 尾部。 函数返回拼接后的数组。

function nonMutatingConcat(original, attach) {
  // 只修改这一行下面的代码
  return original.concat(attach)
  // 只修改这一行上面的代码
}
const first = [1, 2, 3];
const second = [4, 5];
nonMutatingConcat(first, second);

使用 concat 而不是 push 将元素添加到数组的末尾

函数式编程就是创建和使用具有不变性的函数。

上一个挑战介绍了 concat 方法,这是一种在不改变原始数组的前提下,将数组组合成一个新数组的方法。 将 concat 方法与 push 方法做比较push 将一个元素添加到调用它的数组的末尾,这样会改变该数组。 举个例子:

const arr = [1, 2, 3];
arr.push(4, 5, 6);

arr 的值被修改为 [1, 2, 3, 4, 5, 6],这不是函数编程方式。

concat 方法可以将新项目添加到数组末尾,而不附带改变数组

修改 nonMutatingPush 函数,用 concat 将 newItem 添加到 original 末尾,而不改变 original 或 newItem 数组。 该函数应返回一个数组。

function nonMutatingPush(original, newItem) {
  // 只修改这一行下面的代码
  return original.concat(newItem);
  // 只修改这一行上面的代码
}
const first = [1, 2, 3];
const second = [4, 5];
nonMutatingPush(first, second);

相关文章
|
4月前
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
1001 2
|
2月前
|
JavaScript 前端开发
【JavaScript】——JS基础入门常见操作(大量举例)
JS引入方式,JS基础语法,JS增删查改,JS函数,JS对象
|
3月前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
597 1
|
3月前
|
监控 前端开发 JavaScript
React 静态网站生成工具 Next.js 入门指南
【10月更文挑战第20天】Next.js 是一个基于 React 的服务器端渲染框架,由 Vercel 开发。本文从基础概念出发,逐步探讨 Next.js 的常见问题、易错点及解决方法,并通过具体代码示例进行说明,帮助开发者快速构建高性能的 Web 应用。
161 10
|
3月前
|
数据采集 存储 JavaScript
如何使用Puppeteer和Node.js爬取大学招生数据:入门指南
本文介绍了如何使用Puppeteer和Node.js爬取大学招生数据,并通过代理IP提升爬取的稳定性和效率。Puppeteer作为一个强大的Node.js库,能够模拟真实浏览器访问,支持JavaScript渲染,适合复杂的爬取任务。文章详细讲解了安装Puppeteer、配置代理IP、实现爬虫代码的步骤,并提供了代码示例。此外,还给出了注意事项和优化建议,帮助读者高效地抓取和分析招生数据。
152 0
如何使用Puppeteer和Node.js爬取大学招生数据:入门指南
|
4月前
|
存储 JavaScript 前端开发
前端开发:Vue.js入门与实战
【10月更文挑战第9天】前端开发:Vue.js入门与实战
|
4月前
|
自然语言处理 JavaScript 前端开发
JavaScript高级——ES6基础入门
JavaScript高级——ES6基础入门
57 1
|
4月前
|
机器学习/深度学习 自然语言处理 前端开发
前端大模型入门:Transformer.js 和 Xenova-引领浏览器端的机器学习变革
除了调用API接口使用Transformer技术,你是否想过在浏览器中运行大模型?Xenova团队推出的Transformer.js,基于JavaScript,让开发者能在浏览器中本地加载和执行预训练模型,无需依赖服务器。该库利用WebAssembly和WebGPU技术,大幅提升性能,尤其适合隐私保护、离线应用和低延迟交互场景。无论是NLP任务还是实时文本生成,Transformer.js都提供了强大支持,成为构建浏览器AI应用的核心工具。
913 1
|
4月前
|
Web App开发 JSON JavaScript
深入浅出:Node.js后端开发入门与实践
【10月更文挑战第4天】在这个数字信息爆炸的时代,了解如何构建一个高效、稳定的后端系统对于开发者来说至关重要。本文将引导你步入Node.js的世界,通过浅显易懂的语言和逐步深入的内容组织,让你不仅理解Node.js的基本概念,还能掌握如何使用它来构建一个简单的后端服务。从安装Node.js到实现一个“Hello World”程序,再到处理HTTP请求,文章将带你一步步走进Node.js的大门。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往后端开发新世界的大门。
|
4月前
|
前端开发 JavaScript 安全
JavaScript代码混淆入门
JavaScript代码混淆是Web应用安全防护的重要一环,通过一系列技术手段提高代码的防护能力。正确应用混淆策略不仅能有效阻止或延缓恶意攻击,还能在一定程度上保护开发者的核心技术和商业秘密。然而,需要注意的是,混淆并非绝对安全,应将其视为整体安全策略的一部分,结合其他防御措施共同构建坚固的安全防线。
99 0

热门文章

最新文章

  • 1
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    23
  • 2
    Node.js 中实现多任务下载的并发控制策略
    32
  • 3
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    25
  • 4
    【JavaScript】深入理解 let、var 和 const
    48
  • 5
    【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
    44
  • 6
    【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
    53
  • 7
    【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
    55
  • 8
    如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
    71
  • 9
    【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
    55
  • 10
    JavaWeb JavaScript ③ JS的流程控制和函数
    62