JS FreeCodeCamp

1 给代码添加注释
单行注释://
多行注释:/* content */

2 声明变量
八种数据类型:undefined、null、boolean、string、symbol、number、bigint、object
变量声明:var ourName;

3 使用赋值运算符存储值
var myVar;//变量声明
myVar = 5;//变量赋值

4 将一个变量的值赋给另一个
var a;
a = 7;
var b ;
b = a;

5 使用赋值运算符初始化变量
赋值运算符: =
var myVar = 9;

6 声明字符串变量
字符串文字或字符串是用 单引号 或 双引号 括起来的一系列零个或多个字符。
var myFirstName = "Jerry";
var myLastName = "Li";

7 理解未初始化的变量
当 JavaScript 中的变量被声明的时候,程序内部会给它一个初始值 undefined。
当你对一个值为 undefined 的变量进行运算操作的时候,算出来的结果将会是 NaN,
NaN的意思是 "Not a Number"。
用 undefined 变量连接一个字符串,你将得到一个 undefined 的 字符串。

8 了解变量名区分大小写
在 JavaScript 中所有的变量和函数名都是大小写敏感的。 要区别对待大写字母和小写字母。
使用驼峰命名法(camelCase)来书写一个 Javascript 变量。
在驼峰命名法(camelCase)中,变量名的第一个单词的首写字母小写,后面的单词的第一个字母大写

9 探索 var 和 let 关键字之间的差异
使用 var 关键字声明变量的最大问题之一是你可以轻松覆盖变量声明:
let 不像 var,当你使用 let 时,同名的变量只能声明一次。同名的变量声明两次会报错。

10 使用 const 关键字声明只读变量
使用 const 声明的变量是只读的。 它们是一个常量值,一个变量被赋值为 const,它就不能被重新赋值:
const FAV_PET = "Cats";
FAV_PET = "Dogs";
由于重新分配 FAV_PET 的值,控制台将显示错误。

 

11 加法运算
Number 是 JavaScript 中的一种数据类型,用来表示数值。
JavaScript 中,我们通过符号 + 来进行加法运算。
const sum = 10 + 10;

12 减法运算
JavaScript 中使用 - 来做减法运算。
const difference = 45 - 33;

13 乘法运算
JavaScript 使用 * 符号表示两数相乘。
const product = 8 * 10;

14 除法运算
JavaScript 中使用 / 符号做除法运算。
const quotient = 66 / 33;

15 数字递增
使用 ++,对变量进行自增或者 +1 运算。
i++;
等效于:
i = i + 1;

16 数字递减
使用自减符号 --,对一个变量执行自减或者 -1 运算。
i--;
等效于:
i = i - 1;

17 创建一个小数
我们也可以把小数存储到变量中。 小数有时候也被称作浮点数或者 floats。
const myDecimal = 5.7;

18 两个小数相乘
把两个小数相乘,并得到它们乘积。
const product = 2.0 * 2.5;

19 两个小数相除
一个小数除以另一个小数。
const quotient = 4.4 / 2.0;

20 求余运算
求余运算符 % 返回两个数相除得到的余数。
示例
5 % 2 = 1 因为
Math.floor(5 / 2) = 2 (商)
2 * 2 = 4
5 - 4 = 1 (余数)
用法
在数学中,判断一个数是奇数还是偶数,只需要判断这个数除以 2 得到的余数是 0 还是 1。
17 % 2 = 1(17 是奇数)
48 % 2 = 0(48 是偶数)

21 复合赋值之 +=
赋值时 JavaScript 会先计算等号右边的内容,myVar = myVar + 5;
这是最常见的运算赋值语句,即先运算、再赋值。
还有一类操作符是一步到位,既做运算也赋值的。
其中一种就是 += 运算符。
let myVar = 1;
myVar += 5;
console.log(myVar);
控制台将会显示 6。

22 复合赋值之 -=
与 += 操作符类似,-= 操作符用来对一个变量进行减法赋值操作。
myVar = myVar - 5;
将从 myVar 中减去 5。 等价于:
myVar -= 5;

23 复合赋值之 *=
*= 操作符是让变量与一个数相乘并赋值。
myVar = myVar * 5;
将 myVar 乘以 5。 等价于:
myVar *= 5;

24 复合赋值之 /=
/= 操作符是让变量与另一个数相除并赋值。
myVar = myVar / 5;
将 myVar 除以 5。 等价于:
myVar /= 5;

  

25 转义字符串中的引号
在 JavaScript 中,可以通过在引号前面使用反斜杠(\)来转义引号。
const sampleStr = "Alan said, \"Peter is learning JavaScript\".";

26 用单引号引用字符串
JavaScript 中的字符串可以使用开始和结束都是同类型的单引号或双引号表示。
与其他一些编程语言不同的是,单引号和双引号的功能在 JavaScript 中是相同的。
字符串在开头和结尾都有相同的引号,如果在中间使用了相同的引号,字符串会提前中止并抛出错误。

const goodStr = 'Jake asks Finn, "Hey, let\'s go on an adventure?"';
const badStr = 'Finn responds, "Let's go!"';
在这里 badStr 会产生一个错误。

在上面的 goodStr 中,通过使用反斜杠 \ 转义字符可以安全地使用两种引号。

const myStr = '<a href="http://www.example.com" target="_blank">Link</a>';

27 转义字符
代码 输出
\' 单引号
\" 双引号
\\ 反斜杠
\n 换行符
\r 回车符
\t 制表符
\b 退格
\f 换页符

使用转义序列把下面三行文本赋值给一个变量 myStr。

FirstLine
\SecondLine
ThirdLine

const myStr = "FirstLine\n\t\\SecondLine\nThirdLine";

28 用加号运算符连接字符串
在 JavaScript 中,当 + 操作符被用于一个 String 类型的值的时候,它被称作拼接操作符。
拼接操作不会在两个字符串之间添加空格。需要自己在字符串里面添加。
const myStr = "This is the start." + " " + "This is the end.";

29 用 += 运算符连接字符串
可以使用 += 运算符来拼接字符串到现有字符串变量的结尾。
例如:

let ourStr = "I come first. ";
ourStr += "I come second.";
ourStr 的值为字符串 I come first. I come second.

30 用变量构造字符串
通过使用连接运算符(+),你可以插入一个或多个变量来组成一个字符串。
例如:
const ourName = "freeCodeCamp";
const ourStr = "Hello, our name is " + ourName + ", how are you?";

31 将变量追加到字符串
可以使用加且赋值(+=)运算符将字符串追加到字符串的末尾。

示例:
const anAdjective = "awesome!";
let ourStr = "freeCodeCamp is ";
ourStr += anAdjective;

32 查找字符串的长度
可以通过在字符串变量或字符串后面写上 .length 来获得 String 的长度。
console.log("Alan Peter".length);
字符串 10 将会出现在控制台中。

33 使用方括号查找字符串中的第一个字符
方括号表示法(Bracket notation)是一种在字符串中的特定 index(索引)处获取字符的方法。
示例:
const firstName = "Charles";
const firstLetter = firstName[0];
firstLetter 值为字符串 C 。

34 了解字符串的不变性
在 JavaScript 中,字符串(String)的值是不可变的(immutable),这意味着一旦字符串被创建就不能被改变。
只是字符串字面量 string literal 的各个字符不能被改变。 改变 myStr 的唯一方法是重新给它赋一个值,例如:
let myStr = "Bob";
myStr = "Job";

35 使用方括号查找字符串中的第 N 个字符
可以使用方括号( bracket notation)来获得一个字符串中的其他位置的字符。
程序是从 0 开始计数,所以获取第一个字符实际上是第零个字符串。

36 使用方括号查找字符串中的最后一个字符
要获取字符串的最后一个字符,可以用字符串的长度减 1 的索引值。
示例:
const firstName = "Ada";
const lastLetter = firstName[firstName.length - 1];

37 使用方括号查找字符串中的倒数第 N 个字符
可以使用 firstName[firstName.length - 3] 获取 const firstName = "Augusta" 字符串的倒数第三个字母的值

38 填词造句
使用字符串连接运算符 + 来拼接字符串变量:myNoun、myAdjective、myVerb 和 myAdverb 来构建一个新字符串。
const myNoun = "dog";
const myAdjective = "big";
const myVerb = "ran";
const myAdverb = "quickly";

// 只修改这一行下面的代码
const wordBlanks = "My " + myNoun + " is " + myAdjective + " and " + myVerb + " " + myAdverb + "."; // 修改这一行

39 使用 JavaScript 数组将多个值存储在一个变量中
以左方括号开始定义一个数组,以右方括号结束,里面每个元素之间用逗号隔开,例如:
const sandwich = ["peanut butter", "jelly", "bread"];

40 将一个数组嵌套在另一个数组中
可以在其他数组中嵌套数组,如:
const teams = [["Bulls", 23], ["White Sox", 45]];
这也叫做多维数组(multi-dimensional array)。

41 通过索引访问数组中的数据
可以使用索引(indexes)来访问数组中的数据。数组索引与字符串一样使用方括号来表示,索引一样是从 0 开始(zero-based)的
const myArray = [50, 60, 70];
const myData = myArray[0];

42 通过索引修改数组中的数据
与字符串不同,数组的条目是 可变的 并且可以自由更改,即使数组是用 const 声明的
const myArray = [18, 64, 99];
myArray[0] = 45;

43 使用索引访问多维数组
可以把多维数组看作成是数组中的数组。 使用方括号表示法访问数组时,第一个方括号访问的是数组的最外层(第一层),第二个方括号访问的是数组的第二层,以此类推。

例如:

const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[[10, 11, 12], 13, 14]
];

arr[3];
arr[3][0];
arr[3][0][1];
arr[3] 为 [[10, 11, 12], 13, 14],arr[3][0] 为 [10, 11, 12],并且 arr[3][0][1] 为 11。

44 使用 push() 操作数组
一个将数据添加到数组末尾的简单方法是 push() 函数。
.push() 接受一个或多个参数(parameters),并把它压入到数组的末尾。
const myArray = [["John", 23], ["cat", 2]];
myArray.push(["dog", 3]);

  

45  使用 pop() 操作数组
改变数组中数据的另一种方法是用 .pop() 函数。
.pop() 函数用来弹出一个数组末尾的值。 我们可以把这个弹出的值赋给一个变量存储起来。 换句话说就是 .pop() 函数移除数组末尾的元素并返回这个元素。
数组中任何类型的元素(数值,字符串,甚至是数组)都可以被弹出来 。
const threeArr = [1, 4, 6];
const oneDown = threeArr.pop();
console.log(oneDown);
console.log(threeArr);
第一个 console.log 将显示值 6,第二个将显示值 [1, 4]。

46  使用 shift() 操作数组
想要移出第一个元素要怎么办呢?这时候我们就需要 .shift() 了。
它的工作原理就像 .pop(),但它移除的是第一个元素,而不是最后一个。
示例:
const ourArray = ["Stimpson", "J", ["cat"]];
const removedFromOurArray = ourArray.shift();
removedFromOurArray 值为 Stimpson,ourArray 值为 ["J", ["cat"]]

47  unshift(移入)一个元素到数组的头部。

unshift() 在数组的头部添加元素。
示例:
const ourArray = ["Stimpson", "J", "cat"];
ourArray.shift();
ourArray.unshift("Happy");
在 shift、ourArray 后值为 ["J", "cat"]。 在 unshift、ourArray 后值为 ["Happy", "J", "cat"]。

48  购物清单
创建一个名叫 myList 的购物清单。 清单的数据格式就是多维数组。
每个子数组中的第一个元素应该是购买的物品名称。 第二个元素应该是物品的数量,类似于:
const myList = [['apple', 10], ['peach', 8], ['banana', 50], ['orange', 30], ['dog', 2]];

49  用函数编写可重用代码
在 JavaScript 中,我们可以把代码的重复部分抽取出来,放到一个函数中。
通过函数名加上后面的小括号来调用(invoke)这个函数
举个例子:
function reusableFunction() {
  console.log('Hi World');
}
reusableFunction();

50  将值传递给带有参数的函数
形参:函数中的占位符称为形参。
实参:调用函数时的传递的实际值称为实参。
这是带有两个形参的函数,param1 和 param2:
function testFun(param1, param2) {
  console.log(param1, param2);
}
testFun(“Hello”,“World”);

51  使用 return 给函数返回值
我们可以通过函数的参数(arguments)把值传入函数, 也可以使用 return 语句把数据从一个函数中传出来。
示例
function plusThree(num) {
  return num + 3;
}
const answer = plusThree(5);
answer 的值为 8。

52  全局作用域和函数
在 JavaScript 中,作用域涉及到变量的作用范围。 
在函数外定义的变量具有 全局 作用域。 这意味着,具有全局作用域的变量可以在代码的任何地方被调用。
未使用 let 或 const 关键字声明的变量会在 global 范围内自动创建。 当在代码其他地方无意间定义了一个变量,刚好变量名与全局变量相同,这时会产生意想不到的后果。 你应该总是用 let 或 const 声明你的变量。

  

53  局部作用域和函数
在一个函数内声明的变量,以及该函数的参数都具有局部(local)作用域。 这意味着它们只在该函数内可见。
这是在函数 myTest 内声明局部变量 loc 的例子:
function myTest() {
  const loc = "foo";
  console.log(loc);
}
myTest();
console.log(loc);
myTest() 函数调用将在控制台中显示字符串 foo。 console.log(loc) 行(在 myTest 函数之外)将抛出错误,因为 loc 未在函数之外定义。

54  函数中的全局作用域和局部作用域
一个程序中有可能具有相同名称的局部变量 和全局变量。 在这种情况下,局部变量将会优先于全局变量。
下面为例:
const someVar = "Hat";
function myFun() {
  const someVar = "Head";
  return someVar;
}
函数 myFun 将会返回字符串 Head,因为局部变量的优先级更高。

55  函数也可以返回 undefined
函数一般用 return 语句来返回值,但这不是必须的。 在函数没有 return 语句的情况下,当你调用它时,该函数会执行内部代码,返回的值是 undefined。
示例
let sum = 0;
function addSum(num) {
  sum = sum + num;
}
addSum(3);
addSum 是一个没有 return 语句的函数。 该函数将更改全局变量 sum,函数的返回值为 undefined。

56  使用返回值赋值
如果你还记得我们在使用赋值运算符存储值中的讨论的话,等号右侧的所有操作都会在赋值之前完成。 这意味着我们可以获取函数的返回值,并将其赋值给一个变量。
假设我们有一个预先定义的函数 sum ,它将两个数相加,然后:
ourSum = sum(5, 12);
将调用 sum 函数,该函数返回 17 的值并将其分配给 ourSum 变量。

57  排队
在计算机科学中队列(queue)是一个抽象的数据结构(Data Structure),队列中的条目都是有秩序的。 新的条目会被加到队列的末尾,旧的条目会从队列的头部被移出。
写一个函数 nextInLine,用一个数组(arr)和一个数字(item)作为参数。
把数字添加到数组的结尾,然后移出数组的第一个元素。
最后 nextInLine 函数应该返回被删除的元素。
function nextInLine(arr, item) {
  // 只修改这一行下面的代码
arr.push(item);
item = arr.shift();
  return item;
  // 只修改这一行上面的代码
}
// 设置
const testArr = [1, 2, 3, 4, 5];
// 显示代码
console.log("Before: " + JSON.stringify(testArr));
console.log(nextInLine(testArr, 6));
console.log("After: " + JSON.stringify(testArr));
  
58  理解布尔值
另一种数据类型是布尔(Boolean)。 布尔值只能是两个值中的一个:true 或者 false。 它非常像电路开关,true 是 “开”,false 是 “关”。 这两种状态是互斥的。
注意: 布尔值是不带引号的。 字符串 "true" 和 "false" 不是布尔值,在 JavaScript 中也没有特殊含义。

59  用 if 语句来表达条件逻辑
if 语句用于在代码中做出决定。 关键字 if 告诉 JavaScript 在小括号中的条件为真的情况下去执行定义在大括号里面的代码。 这种条件被称为 Boolean 条件,因为他们只可能是 true(真)或 false(假)。
当条件的计算结果为 true,程序执行大括号内的语句。 当布尔条件的计算结果为 false,大括号内的代码将不会执行。

60  相等运算符
在 JavaScript 中,有很多 相互比较的操作。 所有这些操作符都返回一个 true 或 false 值。
最基本的运算符是相等运算符:==。 相等运算符比较两个值,如果它们是相等,返回 true,如果它们不相等,返回 false。 值得注意的是相等运算符不同于赋值运算符(=),赋值运算符是把等号右边的值赋给左边的变量。

61  严格相等运算符
严格相等运算符(===)是相对相等操作符(==)的另一种比较操作符。 与相等操作符转换数据两类型不同,严格相等运算符不会做类型转换。
如果比较的值类型不同,那么在严格相等运算符比较下它们是不相等的,会返回 false 。

62  比较不同值
如果要比较的值不是同一类型,相等运算符会先执行数据类型转换,然后比较值。 而严格相等运算符只比较值,不会进行数据类型转换。

63  不等运算符
不相等运算符(!=)与相等运算符是相反的。 这意味着不相等并返回 false 的地方,用相等运算符会返回 true,反之亦然。 与相等运算符类似,不相等运算符在比较的时候也会转换值的数据类型。
示例
1 !=  2    // true
1 != "1"   // false
1 != '1'   // false
1 != true  // false
0 != false // false

64  严格不等运算符
严格不相等运算符(!==)与全等运算符是相反的。 这意味着严格不相等并返回 false 的地方,用严格相等运算符会返回 true,反之亦然。 严格不相等运算符不会转换值的数据类型。
示例
3 !==  3  // false
3 !== '3' // true
4 !==  3  // true

  

 

 

80  从函数返回布尔值
你应该还记得相等运算符这道挑战题。 在那里我们提到,所有比较操作符都会返回 boolean:要么是 true 要么是 false。
有时人们通过 if/else 语句来做比较,像这样。
function isEqual(a, b) {
  if (a === b) {
    return true;
  } else {
    return false;
  }
}
但有更好的方式来达到相同的效果。 既然 === 返回 true 或 false 我们可以直接返回比较结果:
function isEqual(a, b) {
  return a === b;
}

81  函数执行到 return 语句就结束
当代码执行到 return 语句时,函数返回一个结果就结束运行了,return 后面的语句不会执行。
示例
function myFun() {
  console.log("Hello");
  return "World";
  console.log("byebye")
}
myFun();
以上将在控制台中显示字符串 Hello 并返回字符串 World。 字符串 byebye 将永远不会在控制台中显示,因为函数在 return 语句处就退出了。

82 21点游戏

83  创建 JavaScript 对象
对象和 arrays 类似,区别在于数组使用索引来访问和修改数据,而对象中的数据是通过 properties 访问的。
对象非常适合用来存储结构化数据,可以表示真实世界中的物体,比如一只猫。
这里是一个猫对象的样本:
const cat = {
  "name": "Whiskers",
  "legs": 4,
  "tails": 1,
  "enemies": ["Water", "Dogs"]
};
在此示例中,所有属性都存储为字符串,例如 name、legs 和 tails。 然而,你也可以使用数字作为属性。 你甚至可以省略单字字符串属性中的引号,如下所示:
const anotherObject = {
  make: "Ford",
  5: "five",
  "model": "focus"
};
然而,如果你的对象有非字符串属性的话,JavaScript 会自动将它们转为字符串。

84 通过点号表示法访问对象属性
和访问数组类似,访问对象属性有两种方式:点号表示法(.)和方括号表示法([])。

85  使用方括号表示法访问对象属性
访问对象属性的第二种方式是方括号表示法([])。 如果你想访问的属性名中包含空格,就必须使用方括号表示法来获取它的属性值。
当然,如果属性名不包含空格,也可以使用方括号表示法。

86  通过变量访问对象属性
对对象上使用方括号表示法,还可以访问对象上作为变量值存储的属性。 当你需要遍历对象的所有属性,或者根据一个变量的值查找对应的属性值时,这种写法尤其适用。
以下是一个使用变量来访问属性的例子:
const dogs = {
  Fido: "Mutt",
  Hunter: "Doberman",
  Snoopie: "Beagle"
};
const myDog = "Hunter";
const myBreed = dogs[myDog];
console.log(myBreed);
字符串 Doberman 将会出现在控制台中。
使用这一概念的另一种情况是:属性的名字是在程序运行期间动态收集得到的。如下所示:
const someObj = {
  propName: "John"
};
function propPrefix(str) {
  const s = "prop";
  return s + str;
}
const someProp = propPrefix("Name");
console.log(someObj[someProp]);
someProp 的值将为字符串 propName,并且字符串 John 将会出现在控制台中。
注意,当使用变量名访问属性时,我们没有使用引号包裹它,因为我们正在使用的是变量的值,而不是变量的名字。

87  更新对象属性
在你创建了 JavaScript 对象后,你可以随时更新它的属性,就像更新任何其他变量那样。 你可以使用点或中括号操作符来更新。
举个例子,让我们看看 ourDog:
const ourDog = {
  "name": "Camper",
  "legs": 4,
  "tails": 1,
  "friends": ["everything!"]
};
既然他是一个特别愉快的狗,让我们将他的名字更改为字符串 Happy Camper。 这有两种方式来更新对象的 name 属性: ourDog.name = "Happy Camper"; 或 ourDog["name"] = "Happy Camper";。更新后,ourDog.name 的值就不再是 Camper,而是 Happy Camper。

88  给 JavaScript 对象添加新属性
你也可以像更改属性一样给 JavaScript 对象添加属性。
这里展示了如何给 ourDog 添加一个属性 bark:
ourDog.bark = "bow-wow";
或者
ourDog["bark"] = "bow-wow";
现在,当我们执行 ourDog.bark 时,就能得到他的叫声,bow-wow。
例如:
const ourDog = {
  "name": "Camper",
  "legs": 4,
  "tails": 1,
  "friends": ["everything!"]
};
ourDog.bark = "bow-wow";

89  删除对象的属性
我们同样可以删除对象的属性,例如:
delete ourDog.bark;
可以使用点操作符或者中括号操作符。

90  使用对象进行查找
对象和字典一样,可以用来存储键/值对。 如果数据是扁平的,你可以用对象来查找你想要的值,而不是链式使用 switch 或 if/else 语句。 当你知道你的输入数据在某个范围时,这种查找方式极为有效。
这是简单的反向字母表:
const alpha = {
  1:"Z",
  2:"Y",
  3:"X",
  4:"W",
  24:"C",
  25:"B",
  26:"A"
};
alpha[2];
alpha[24];
const value = 2;
alpha[value];
alpha[2] 是字符串 Y,alpha[24] 是字符串 C,alpha[value] 是字符串 Y。

  

91  测试对象的属性
有时检查一个对象属性是否存在是非常有用的。 我们可以用对象的 .hasOwnProperty(propname) 方法来检查对象是否有指定的属性。 .hasOwnProperty() 找到该属性时返回 true,找不到该属性时返回 false。
示例
const myObj = {
  top: "hat",
  bottom: "pants"
};
myObj.hasOwnProperty("top");
myObj.hasOwnProperty("middle");
第一个 hasOwnProperty 返回 true,第二个返回 false。

92  操作复杂对象
有时你可能希望将数据存储在一个灵活的数据结构(Data Structure)中。 JavaScript 对象是一种灵活的数据结构。 它可以储存字符串(strings)、数字(numbers)、布尔值(booleans)、数组(arrays)、函数(functions)和对象(objects)以及这些值的任意组合。
这是一个复杂数据结构的示例:
const ourMusic = [
  {
    "artist": "Daft Punk",
    "title": "Homework",
    "release_year": 1997,
    "formats": [ 
      "CD", 
      "Cassette", 
      "LP"
    ],
    "gold": true
  }
];
这是一个包含一个对象的数组。 该对象有关于专辑的各种元数据(metadata)。 它也有一个嵌套的 formats 数组。 可以将专辑添加到顶级数组来增加更多的专辑记录。 对象将数据以一种键 - 值对的形式保存。 在上面的示例中,"artist": "Daft Punk" 有一个键为 artist 值为 Daft Punk 的属性。 JavaScript Object Notation 简称 JSON 是可以用于存储数据的数据交换格式。
{
  "artist": "Daft Punk",
  "title": "Homework",
  "release_year": 1997,
  "formats": [ 
    "CD",
    "Cassette",
    "LP"
  ],
  "gold": true
}
**提示:**数组中有多个 JSON 对象的时候,对象与对象之间要用逗号隔开。

93  访问嵌套对象
我们可以通过连续使用点号表示法和方括号表示法来访问对象的嵌套属性。
这是一个嵌套对象:
const ourStorage = {
  "desk": {
    "drawer": "stapler"
  },
  "cabinet": {
    "top drawer": { 
      "folder1": "a file",
      "folder2": "secrets"
    },
    "bottom drawer": "soda"
  }
};
ourStorage.cabinet["top drawer"].folder2;    注意:【】内的字符串要加引号“”
ourStorage.desk.drawer;
ourStorage.cabinet["top drawer"].folder2 将会是字符串 secrets,并且 ourStorage.desk.drawer 将会是字符串 stapler。

94  访问嵌套数组
在之前的挑战中,我们学习了在对象中嵌套对象和数组。 与访问嵌套对象类似,数组的方括号可以用来对嵌套数组进行链式访问。
下面是访问嵌套数组的例子:
const ourPets = [
  {
    animalType: "cat",
    names: [
      "Meowzer",
      "Fluffy",
      "Kit-Cat"
    ]
  },
  {
    animalType: "dog",
    names: [
      "Spot",
      "Bowser",
      "Frankie"
    ]
  }
];
ourPets[0].names[1];
ourPets[1].names[0];
ourPets[0].names[1] 应该是字符串 Fluffy, 并且 ourPets[1].names[0] 应该是字符串 Spot。

95 记录集合
给定一个对象,用来表示部分音乐专辑收藏。 每张专辑都有几个属性和一个唯一的 id 号作为键值。 并非所有专辑都有完整的信息。

以 updateRecords 函数开始,这个函数需要一个对象 records,包含一个音乐专辑集合,一个 id,一个 prop(如 artist 或 tracks),和一个 value。 使用下面的规则完成函数来修改传递给函数的对象。

函数必须始终返回整个音乐专辑集合对象。
如果 prop 不是 tracks 并且 value 不是一个空字符串, 将专辑的 prop 更新或设置为 value。
如果 prop 是 tracks 但专辑没有 tracks 属性,则应创建空数组并为其添加 value。
如果 prop 是 tracks 并且 value 不是一个空字符串,将 value 添加到专辑现有 tracks 数组的末尾。
如果 value 是空字符串,从专辑里删除指定的 prop。
注意: 用 recordCollection 对象做为测试参数对象。
const recordCollection = {
  2548: {
    albumTitle: 'Slippery When Wet',
    artist: 'Bon Jovi',
    tracks: ['Let It Rock', 'You Give Love a Bad Name']
  },
  2468: {
    albumTitle: '1999',
    artist: 'Prince',
    tracks: ['1999', 'Little Red Corvette']
  },
 1245: {
    artist: 'Robert Palmer',
    tracks: []
  },
  5439: {
    albumTitle: 'ABBA Gold'
  }
};
function updateRecords(records, id, prop, value) {
  if (prop !== "tracks" && value !== "") {
    records[id][prop] = value;
  } else if (prop === "tracks" && records[id].hasOwnProperty("tracks") === false) {
    records[id][prop] = [value];
  } else if(prop === "tracks" && value !=="") {
    records[id][prop].push(value);
  } else if(value === "") {
    delete records[id][prop];
  }
  return records; 
}
updateRecords(recordCollection, 5439, 'artist', 'ABBA');

96  while 循环
你可以使用循环多次执行相同的代码。
我们将学习的第一种类型的循环称为 while 循环,当 while 指定的条件为真,循环才会执行,反之不执行。
const ourArray = [];
let i = 0;
while (i < 5) {
  ourArray.push(i);
  i++;
}
在上面的代码里,while 循环执行 5 次把 0 到 4 的数字添加到 ourArray 数组里。

97  for 循环
你可以使用循环多次执行相同的代码。
JavaScript 中最常见的循环就是 for,它可以循环指定次数。
for 循环中的可选三个表达式用分号隔开:
for (a; b; c),其中a为初始化语句,b为条件语句,c 是最终的表达式。
初始化语句只会在执行循环开始之前执行一次。 它通常用于定义和设置你的循环变量。
循环条件语句会在每一轮循环的开始前执行,只要条件判断为 true 就会继续执行循环。 当条件为 false 的时候,循环将停止执行。 这意味着,如果条件在一开始就为 false,这个循环将不会执行。
终止循环表达式在每次循环迭代结束, 在下一个条件检查之前时执行,通常用来递增或递减循环计数。
在下面的例子中,先初始化 i = 0,条件 i < 5 为 true 时,进入循环。 每次循环后 i 的值增加 1,然后执行终止循环条件表达式 i++。
const ourArray = [];
for (let i = 0; i < 5; i++) {
  ourArray.push(i);
}
ourArray 现在的值为 [0, 1, 2, 3, 4]。

98  使用 For 循环遍历数组的奇数
对于循环,一次不必递增一个。 通过更改我们的 final-expression,我们可以用偶数来计数。
初始化 i = 0,当 i < 10 的时候继续循环。 i += 2 让 i 每次循环之后增加 2。
const ourArray = [];
for (let i = 0; i < 10; i += 2) {
  ourArray.push(i);
}
ourArray 现在将包含 [0, 2, 4, 6, 8]。 改变计数器(initialization) ,这样我们可以用奇数来递增。

99 使用 For 循环反向遍历数组
只要我们定义好合适的条件,for 循环也可以反向遍历。
为了让每次递减 2,我们需要改变 initialization、condition 和 final-expression。
设置 i = 10,并且当 i > 0 的时候才继续循环。 我们使用 i -= 2 来让 i 每次循环递减 2。
const ourArray = [];
for (let i = 10; i > 0; i -= 2) {
  ourArray.push(i);
}
ourArray 现在将包含 [10, 8, 6, 4, 2]。 让我们改变初始值和最后的表达式,这样我们就可以按照奇数从后往前两两倒着数。

100  使用 For 循环遍历数组
JavaScript 中的一个常见任务是遍历数组的内容。 一种方法是使用 for 循环。 下面的代码将输出数组 arr 的每个元素到控制台:
const arr = [10, 9, 8, 7, 6];
for (let i = 0; i < arr.length; i++) {
   console.log(arr[i]);
}
记住数组的索引从零开始的,这意味着数组的最后一个元素的下标是:length - 1(数组的长度 -1)。 我们这个循环的条件是 i < arr.length,当 i 的值为 length 的时候循环就停止了。 在这个例子中,最后一个循环是 i === 4,也就是说,当 i 的值等于 arr.length - 1 时,结果输出 6。 然后 i 增加到 5,循环会终止,因为 i < arr.length 是 false。

101  循环嵌套
如果你有一个二维数组,可以使用相同的逻辑,先遍历外面的数组,再遍历里面的子数组。 下面是一个例子:
const arr = [
  [1, 2], [3, 4], [5, 6]
];
for (let i = 0; i < arr.length; i++) {
  for (let j = 0; j < arr[i].length; j++) {
    console.log(arr[i][j]);
  }
}
这里一次输出了 arr 中的每个子元素。 提示,对于内部循环,我们可以通过 arr[i] 的 .length 来获得子数组的长度,因为 arr[i] 本身就是一个数组。

102  do...while 循环
下一种循环叫作 do...while 循环。 它被称为 do...while 循环,是因为不论什么情况,它都会首先 do(运行)循环里的第一部分代码,然后 while(当)规定的条件被评估为 true(真)的时候,它会继续运行循环。
const ourArray = [];
let i = 0;
do {
  ourArray.push(i);
  i++;
} while (i < 5);
上面的示例行为类似于其他类型的循环,由此产生的数组将看起来像 [0, 1, 2, 3, 4]。 然而,do...while 不同于其他循环的地方,是第一次循环检查失败时的行为。 让我们看看代码中的区别:这里是一个常规的 while 循环,只要 i < 5,就会在循环中运行代码:
const ourArray = []; 
let i = 5;
while (i < 5) {
  ourArray.push(i);
  i++;
}
这个例子中,定义了一个空数组 ourArray 以及一个值为 5 的 i 。 当执行 while 循环时,因为 i 不小于 5,所以循环条件为 false,循环内的代码将不会执行。 ourArray 最终没有添加任何内容,因此示例中的所有代码执行完时,ourArray 仍然是[]。 现在,看一下 do...while 循环。
const ourArray = []; 
let i = 5;
do {
  ourArray.push(i);
  i++;
} while (i < 5);
在这里,和使用 while 循环一样,将 i 的值初始化为 5。 执行下一行时,没有执行循环检查,直接执行花括号内的代码。 数组会添加一个元素,并在进行条件检查之前递增 i。 然后,在条件检查时因为 i 等于 6 不符合条件 i < 5,所以退出循环。 最终 ourArray 的值是 [5]。 本质上,do...while 循环确保循环内的代码至少运行一次。 让我们通过 do...while 循环将值添加到数组中。

103 使用递归代替循环
递归是函数调用自身的操作。 为了便于理解,有如下任务:计算数组内元素前 n 的元素乘积。 使用 for 循环, 可以这样做:

  function multiply(arr, n) {
    let product = 1;
    for (let i = 0; i < n; i++) {
      product *= arr[i];
    }
    return product;
  }
下面是递归写法,注意代码里的 multiply(arr, n) == multiply(arr, n - 1) * arr[n - 1]。 这意味着可以重写 multiply 以调用自身而无需依赖循环。

  function multiply(arr, n) {
    if (n <= 0) {
      return 1;
    } else {
      return multiply(arr, n - 1) * arr[n - 1];
    }
  }
递归版本的 multiply 详述如下。 在 base case 里,也就是 n <= 0 时,返回 1。 在 n 比 0 大的情况里,函数会调用自身,参数 n 的值为 n - 1。 函数以相同的方式持续调用 multiply,直到 n <= 0 为止。 所以,所有函数都可以返回,原始的 multiply 返回结果。
注意: 递归函数在没有函数调用时(在这个例子是,是当 n <= 0 时)必需有一个跳出结构,否则永远不会执行完毕。

104 资料查找
我们有一个对象数组,里面存储着通讯录。
lookUpProfile 函数已经写好了参数,需要 name 和属性 (prop) 参数。
函数将会检查通讯录中是否存在一个 firstName 与传入的 name 相同的联系人。 如果存在,那么还需要检查对应的联系人中是否存在 prop 属性。
如果它们都存在,函数返回 prop 属性对应的值。
如果 name 不对应于任何联系人,然后返回字符串 No such contact。
如果 prop 属性在匹配 name 的联系人里不存在,返回 No such property。
// 设置
const contacts = [
  {
    firstName: "Akira",
    lastName: "Laine",
    number: "0543236543",
    likes: ["Pizza", "Coding", "Brownie Points"],
  },
  {
    firstName: "Harry",
    lastName: "Potter",
    number: "0994372684",
    likes: ["Hogwarts", "Magic", "Hagrid"],
  },
  {
    firstName: "Sherlock",
    lastName: "Holmes",
    number: "0487345643",
    likes: ["Intriguing Cases", "Violin"],
  },
  {
    firstName: "Kristian",
    lastName: "Vos",
    number: "unknown",
    likes: ["JavaScript", "Gaming", "Foxes"],
  },
];
function lookUpProfile(name, prop) {
  // 只修改这一行下面的代码
  for (let i = 0; i < contacts.length; i++) {
    if (contacts[i]["firstName"] === name) {
      if(contacts[i].hasOwnProperty(prop)) {
        return contacts[i][prop];
      } else {
      return "No such property";
    }
    } 
  }
  return "No such contact";
  // 只修改这一行上面的代码
}
lookUpProfile("Akira", "likes");

105  使用 JavaScript 生成随机分数
随机数非常适合用来创建随机行为。
在 JavaScript 中,可以用 Math.random() 生成一个在0(包括 0)到 1(不包括 1)之间的随机小数。 因此 Math.random() 可能返回 0,但绝不会返回 1。
提示:使用赋值运算符存储值这一节讲过,所有函数调用将在 return 执行之前结束,因此我们可以 return(返回)Math.random() 函数的值。

106 使用 JavaScript 生成随机整数
生成随机小数很棒,但随机数更有用的地方在于生成随机整数。
  1.用 Math.random() 生成一个随机小数。
  2.把这个随机小数乘以 20。
  3.用 Math.floor() 向下取整,获得它最近的整数。
记住 Math.random() 永远不会返回 1。同时因为我们是在向下取整,所以最终我们获得的结果不可能有 20。 这确保了我们获得了一个在 0 到 19 之间的整数。
把操作连缀起来,代码类似于下面:
Math.floor(Math.random() * 20);
我们先调用 Math.random(),把它的结果乘以 20,然后把上一步的结果传给 Math.floor(),最终通过向下取整获得最近的整数。

107  生成某个范围内的随机整数
我们之前生成的随机数是在 0 到某个数之间,现在我们要生成的随机数是在两个指定的数之间。
我们需要定义一个最小值 min 和一个最大值 max。
下面是我们将要使用的方法, 仔细看看并尝试理解这行代码到底在干嘛:
Math.floor(Math.random() * (max - min + 1)) + min
返回一个在 min(包括 min)和 max(包括 max)之间的随机整数。

108 使用 parseInt 函数
parseInt() 函数解析一个字符串返回一个整数。 下面是一个示例:
const a = parseInt("007");
上述函数将字符串 007 转换为整数 7。 如果字符串中的第一个字符不能转换为数字,则返回 NaN。

109 使用 parseInt 函数并传入一个基数
parseInt() 函数解析一个字符串并返回一个整数。 它还可以传入第二个参数,指定了字符串中数字的基数。 基数可以是 2 到 36 之间的整数。
函数调用如下所示:
parseInt(string, radix);
这是一个示例:
const a = parseInt("11", 2);
变量 radix 表示 11 是在二进制系统中。 这个示例将字符串 11 转换为整数 3。

110 使用三元运算符
条件运算符( conditional operator,)(也称为三元运算符( ternary operator))的就像写成一行的 if-else 表达式
语法是:a ? b : c, where a 是条件,当条件返回 true 的时候运行代码 b,当条件返回 false 的时候运行代码 c。
以下函数使用 if/else 语句来检查条件:
function findGreater(a, b) {
  if(a > b) {
    return "a is greater";
  }
  else {
    return "b is greater or equal";
  }
}
这可以使用三元运算符重写:
function findGreater(a, b) {
  return a > b ? "a is greater" : "b is greater or equal";
}

111 使用多个三元运算符
在之前的挑战中,你使用了一个条件运算符。 你也可以将多个运算符串联在一起以检查多种条件。
下面的函数使用 if,else if 和 else 语句来检查多个条件:
function findGreaterOrEqual(a, b) {
  if (a === b) {
    return "a and b are equal";
  }
  else if (a > b) {
    return "a is greater";
  }
  else {
    return "b is greater";
  }
}
以上函数可以使用多个三元运算符重写:

function findGreaterOrEqual(a, b) {
  return (a === b) ? "a and b are equal" 
    : (a > b) ? "a is greater" 
    : "b is greater";
}
如上文所示,对多个三元运算符进行每个条件都是单独一行的格式化被认为是最佳做法。 使用多个三元运算符而没有适当的缩进可能会使您的代码难以理解。 例如:
function findGreaterOrEqual(a, b) {
  return (a === b) ? "a and b are equal" : (a > b) ? "a is greater" : "b is greater";
}

112 使用递归创建一个倒计时
在上一个挑战,学习了怎样用递归来代替 for 循环。 现在来学习一个更复杂的函数,函数返回一个从 1 到传递给函数的指定数字的连续数字数组。

正如上一个挑战提到的,会有一个 base case。 base case 告诉递归函数什么时候不再需要调用其自身。 这是简单 情况,返回得到的值。 还有 recursive call,继续用不同的参数调用自身。 如果函数无误,一直执行直到 base case 为止。

比如,如果想写一个递归函数,返回一个数字 1 到 n 的连续数组。 这个函数需要接收一个参数 n 代表最终数字。 然后会持续的调用自身,传入一个比 n 更小的值一直到传入的值是 1 为止。 函数如下:

function countup(n) {
  if (n < 1) {
    return [];
  } else {
    const countArray = countup(n - 1);
    countArray.push(n);
    return countArray;
  }
}
console.log(countup(5));
值 [1, 2, 3, 4, 5] 将显示在控制台中。

起初,这似乎是违反直觉的,因为 n 的值递减,但是最终数组中的值却递增。 之所以发生这种情况,是因为在递归调用返回之后,才调用 push。 在将 n pushed 进数组时,countup(n - 1) 已经调用赋值成功并返回了 [1, 2, ..., n - 1]。

113 使用递归来创建一个数字序列
接着上一个挑战,有另外一个机会来用递归函数解决问题。
已经定义好了 rangeOfNumbers 函数,包含两个参数。 函数应该返回一个连续数字数组,startNum 参数开始 endNum 参数截止。 开始的数字小于或等于截止数字。 函数必需递归调用自身,不能使用任意形式的循环。 要考虑到 startNum 和 endNum 相同的情况。
function rangeOfNumbers(startNum, endNum) {
  if (endNum - startNum === 0) {
    return [startNum];
  } else {
    var numbers = rangeOfNumbers(startNum, endNum - 1);
    numbers.push(endNum);
    return numbers;
  }
}

  

posted on 2022-04-10 23:19  PerfectData  阅读(149)  评论(0编辑  收藏  举报

导航