learningnotes_javascript_1
[toc]
Javascript
1 Javascript 基础
(注意:带有 * 号的强烈建议先不要看解法,按自己的思路码代码,码完后查看提示,有不会的可以先看课程所给的提示,还不会的直接一波 “Read-Search-Ask”)
1.1 注释
组合键 “Ctrl + /” 可以注释。
1 | // Hello World |
要正确注释,以确保自己能看得懂代码在干什么。
1.2 变量的定义与赋值
一般地,Javascript 使用 var 来定义变量。
1 | var variable1 |
要注意的地方:变量首字符不能是数字和空格 “ ”
定义完变量后就可以赋值了:
1 | variable1 = 5 |
一般来说,变量赋值更多的是一个变量的值赋给另外一个变量。
1 | var variable2 |
大多数程序猿定义变量和赋值直接一步到位:
1 | var variable3 = 9 |
字符串变量的定义同理:
1 | var string1 = "myName" |
在定义一个变量后,其值默认为 undefined
1 当一个整型变量与另外一个值为undefined
的整型变量进行运算时,其值为NaN
2 当一个字符串变量与另外一个值为undefined
的字符串变量进行字符串拼接时,其值为undefined
So 在赋值和运算之前,变量必须要赋好值。
在 Javascript 中,所有的变量名和函数名都是区分大小写的。
也就是说,VARIABLE
、Variable
和variable
是三个不同的变量!为了以后开发的代码能更清晰,强烈建议不要使用这一特性!
变量名的书写推荐:驼峰命名法,即第一个单词的首字符小写,后面单词的首字符大写。
就像这样:
1 | var myProject; |
使用 var 来定义变量有一个坏处,就是当这个变量同时赋予两个值时,先定义的值会被后定义的值所覆盖。
在 ES6 标准中,引入了一个新的定义变量的关键字,叫做 “let”,这个关键字,只允许代码块内定义同名变量一次。超过一次则会报错(在浏览器的控制台可以看到)
1 | var var1 = 5; |
1 | let var1 = 5; |
当然,let 定义的变量仍会被后面的赋值覆盖。
1 | let var1 = 9 |
1.3 常量的定义与赋值
在 ES6 中,除了 let 之外,还引入了一个新的关键字const
使用 const 定义的变量是一个定死的值,也就是常量.
一旦这个变量被定义成const
,那么重新赋值时会提示错误。
1 | const variable8 = 9 |
1.4 四则运算
1.4.1 基本运算
两个整型变量相加用 “+”
1 | const sum = 10 + 20 |
减、乘、除同理
1 | const sum = 20 - 10 |
1.4.2 递增与递减
++ 为自增,– 为自减
1 i++:i++
本身的值等于 i
,而 i
的值等于 i + 1
;
说得通俗点,就是先把原值代入 i
本身,再进行 i = i + 1
运算
1 | let i = 5; |
2 ++i:i++
和 i
的值都等于 i + 1
;
说得通俗点,就是先进行 i = i + 1
运算,再把结果代入 i
本身
1 | let i = 5; |
3 i–:i--
本身的值等于 i
,而 i
的值等于 i - 1
;
说得通俗点,就是先把原值代入 i
本身,再进行 i = i - 1
运算
1 | let i = 5; |
4 –i:i++
和 i
的值都等于 i - 1
;
说得通俗点,就是先进行 i = i - 1
运算,再把结果代入 i
本身
1 | let i = 5; |
1.4.3 小数
var, let, const 都可以定义一个浮点型变量 (float)
1 | let c = 6.16 |
小数对于上述四则运算也同样适用:
1 | const a = 1.5 + 6.23; |
1.4.4 求余 (商)
求余需要用到符号 “%”
1 | const a = 5 % 2; |
1.4.5 复合赋值
运算和赋值这两个步骤可以 “一步到位”
使用 “+=” 等就可以将运算和赋值写成一步,这种运算符叫做 “复合赋值符”
例子:
1 | let a = 5; |
1.5 转义符
在定义字符串变量时,难免在变量中引入引号。
我们要注意,不能在字符串变量中引入双引号“”,否则引号外面的内容没有被引用,会报错!(在 HTML 中这么写没什么,但是在 JS 中这么写,js 会不认,报错)
解决方法:1 将双引号改成单引号(双引号定义的字符串变量)或将单引号改成双引号(单引号定义的字符串变量)
2 使用转义符\
,就像这样:
1 | const myVar = "Hello \"World\"" |
除了\"
转义符,还有以下常用的转义符
1 | \' //单引号 |
1.6 字符串拼接
可以使用 “+” 号进行字符串与字符串的拼接,或字符串与变量的拼接.
1 | let num1 = 5 |
使用 “+=” 号也可以进行上述的拼接。
1 | let num = 111111 |
1.7 字符串长度
要想获取字符串长度,要使用.length
属性。(注意 length 前面还有一个 “.”)
1 | let num = 111111 |
1.8 方括号 []
使用 “[]” 来获取字符串中的第 X 个字符。
范围从 0 开始(绝大多数现代编程语言,都是从 0 开始计数的,这一点不同于人类)
1 | let mySentence = "Hello this is Javascript" |
要想获取最后一个字符,最简便的方法就是在字符串长度 - 1 (.length - 1
),就可以得到字符串中的最后一个字符。
1 | let mySentence = "Hello this is Javascript" |
获取最后第 X 个字符同理。
既然可以读取字符,那么是不是可以写字符呢?实际上不行,因为 Javascript 中的字符串的各个值是不能修改的。
要想修改 String 字符串,必须要重新赋值(var, let)
例如
1 | let mySentence = "Hello this is Javascript" |
1.9 数组
1.9.1 基本数组与嵌套数组
先创建一个基本的数组(以方括号作为首尾,每一个元素之间用逗号分隔,字符串元素外要用引号)
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45] |
然后在数组后面加逗号,再加入一个新数组,最后整体用 “[]” 框起来
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
上面的这个数组叫做嵌套数组(也叫多维数组)
1.9.2 数组元素的获取
那么怎么获取数组的值呢?和上一节方括号同理,使用方括号获取数组中的第 X 个元素。
例如:
1 | const myArray = [15,34,78,45,100,345,982,12] |
与字符串不同的是,数组中的各个元素是可以读和写的。
1 | const myArray = [15,34,78,45,100,345,982,12] |
再回过头来看之前的嵌套数组:
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
获取嵌套数组的值要用多个方括号(有多少层用多少方括号),就像这样:
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
1.9.3 对数组数据的修改
1.9.3.1 .push()
.push() -> 将参数添加到数组的末尾
(下面的代码有些复杂,需要耐心看,注释部分能理解多少就理解多少。)
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
1.9.3.2 .pop()
.pop() -> 将数组内的最后一个元素“弹出”(在数组内删除最后一个元素)。如果定义了变量,则被弹出的值就会赋给这个变量。
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
1.9.3.3 .shift()
.shift() -> 将数组内的第一个元素“移出” (在数组内删除第一个元素)。如果定义了变量,则被移出的值就会赋给这个变量。
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
1.9.3.4 .unshift()
.unshift() -> 将参数添加到数组的头部
(下面的代码有些复杂,需要耐心看,注释部分能理解多少就理解多少。)
1 | const myArray = [["c", "cplusplus", "javascript", "csharp",45],["python","go","JSP"]] |
1.10 函数
函数的基本结构
1 | function myFunction1() { |
试着写一个简单的函数~
1 | function myFunction1() { |
组合键 ”Shift + F10“ 运行…… 怎么没出结果呢?
在 Javascript 中,函数代码块不能直接运行,必须调用这个函数,函数才能从其代码块的第一行开始执行,一直到代码块执行完最后一行才算结束。
那么怎么调用呢?在下面输入 函数名() 就可以执行了~
1 | function myFunction1() { |
(注意函数被调用前后的变化)
1.10.1 函数的参数
在函数名后面的小括号内,可以定义几个值(变量),以逗号分隔,这些值叫做参数。如下
1 | function myFunction2(var1, var2) { |
在调用函数时,可以向函数传入这些参数,这就叫做传参。
这些 var1 var2这些还没有定义具体值的参数,就叫做形参(形式参数);调用函数时所传入的具体值,则为实参(实际参数)。
因为函数调用传入的参数绝大部分为具体的值,所以函数调用传的是实参。
例如
1 | function myFunction2(var1, var2) { |
(函数调用时,传递了参数 var1 = 4, var2 = 8,sum = var1 + var2 = 4 + 8 = 12,最后以日志的形式输出在控制台上.)
1.10.2 函数返回值
既然我们在函数调用时把参数传入到函数执行,那么我们也可以让函数通过 return
语句把数据从函数中传出来。
函数返回的参数与我们调用函数传入的参数一样,都是实参。
例如
1 | function myFunction3(var1, var2) { |
(注意,设置有返回值的函数,最好不要有过多无关、冗余变量)
如果函数没有设置 return
语句,函数会执行代码到其代码块最后一行,返回值为 undefined
.
(函数没设置参数,而调用函数带参数时,执行完后返回值也是 undefined
)
1 | let variable3 = 6; |
1.10.3 函数与作用域
在 Javascript 中,在函数代码块外定义的变量(使用let
、const
等关键字定义的)叫做全局变量,其拥有全局作用域,即在整个代码块内任何地方都可以被调用。
而在函数代码块内定义的变量(使用let
、const
等关键字定义的)叫做局部变量,其仅拥有函数代码块以内的作用域,在函数外调用会提示 “未定义” (not defined
)。
有个例外,当在函数代码块内定义的变量没有使用 let
、const
等关键字定义,将默认看成是全局变量(global
). 若一不小心在函数代码块以外重新定义了这个变量,则原来定义在函数代码块的变量的值将会被覆写甚至会失效!所以在函数代码块内定义的变量必须要使用诸如 let
、const
等关键字定义!
1 | let variable3 = 6; |
1 | let variable3 = 6; |
(把第八行的内容换到第二行的位置,输出都是 20)
另外,如果一个变量同时赋予了全局变量和局部变量,调用时局部变量会优先于全局变量:
1 | let variable3 = 6; |
1 | let variable3 = 6; |
(注意第 4 行,局部变量注释前后输出的变化。)
(*) 1.10.4 Javascript 中数组、函数与数据结构的运用:排队
排队:顾名思义,排队就是将元素依次由前往后排序,新进来的元素排到最后面,排到第一位的元素将会被移出。
试着利用数组有关的函数写一个简单的排队函数。这个函数应用数组和一个数字作为参数;执行时将这个数字添加到数组的最后,将数组的第一个元素移除并赋给一个变量(名称自定),最后返回这个变量的值。
测试要求
1 | nextInLine([], 5) 应该返回一个数字。 |
可以自己码,码完的或实在不会的再看答案()
查看提示 (点击查看)
const testArr1 = []
const testArr2 = [7]
const queuedArr = [3,5,8,13,21,34]
const queuedArr2 = [9,99,999,9999,99999]
function queueFunction(arr, item){
arr.push(item)
const removed = arr.shift()
return removed
}
const shiftedNum1 = queueFunction(testArr1, 6)
const shiftedNum2 = queueFunction(testArr2, 8)
const shiftedNum3 = queueFunction(queuedArr, 'new_item')
const shiftedNum4 = queueFunction(queuedArr2, 'new_item2')
console.log('testArr1 被排除的元素是' + shiftedNum1 + ' ,数组内容为 [' + testArr1 + ']')
console.log('testArr2 被排除的元素是' + shiftedNum2 + ' ,数组内容为 [' + testArr2 + ']')
console.log('queuedArr 被排除的元素是' + shiftedNum3 + ' ,数组内容为 [' + queuedArr + ']')
console.log('queuedArr2 被排除的元素是' + shiftedNum4 + ' ,数组内容为 [' + queuedArr2 + '], 数组的第 4 个元素为 ' + queuedArr2[3])
最后结果:(点击查看)
1.10.5 函数的条件
先简单叙述下另一种数据类型 “布尔值 boolean
”
布尔值比较简单,只有两个值:true
和 false
(注意:布尔值不带引号!)
1.10.5.1 if…else 语句
(从 C 语言开始就教这个了)
1 | function simpleIfFunc (value) { |
1.10.5.2 if…else if…else 语句
稍微高级一点的 if 语句
1 | function logicalIfElseif (num) { |
输出结果:
写 if…else if…else 语句时,要注意设计好条件的执行顺序,一般来说,建议按数字从小到大或从大到小的顺序来设计条件语句。
可以写多个 else if 条件语句。
FreeCodeCamp 上的例子:
1 | function testSize(num) { |
执行结果:
1.10.5.3 switch 语句
(以前学 C 语言的时候就比较熟悉,用 switch 语句设计在键盘按不同按键触发不同的功能)
最基本的 switch 语句
1 | function logicalSwitch(num){ |
一个基本带有 switch 语句功能的代码
1 | const switchArr = ['selected','selected2','unselected','unselected2','default'] |
输出结果
(知识点:switch -> if ; 参数 ->变量; case -> ===; break -> 结束执行)
可以给不同的输入值设置相同结果。
1 | const switchArr = ['selected','selected2','unselected','unselected2','default'] |
输出结果
引入 switch 语句之后,写 if…else if 这类的语句就变得更简单了,直接一个 switch 语句加上几个 case 的事。(代码来自 freeCodeCamp)
1 | function chainToSwitch(val) { |
1.10.5.4 return 语句
之前说到,return 语句可以返回值。
现在,return 语句还有一个作用,就是结束代码的执行。在 return 语句后面的代码均不会执行!
1 | function returnLogical () { |
(*) 1.10.5.5 经典案例:21 点卡牌游戏
(摘自 freeCodeCamp)
在赌场 21 点游戏中,玩家可以通过计算牌桌上已经发放的卡牌的高低值来让自己在游戏中保持优势。 这就叫 21 点算法。
牌桌上的大值的卡牌更多,对玩家有利。 根据下面的表格,每张卡牌都被分配了一个值。 如果卡牌的值大于 0,那么玩家应该追加赌注。 如果卡牌的值为 0 或负数,玩家应该追加少许赌注甚至不追加赌注。
计数 | 卡牌 |
---|---|
+1 | 2, 3, 4, 5, 6 |
0 | 7, 8, 9 |
-1 | 10, ‘J’, ‘Q’, ‘K’, ‘A’ |
要求:编写函数,实现 21 点算法。
根据参数 card
的值(见表格,可能是数字或者字符串)来递增或递减全局变量 count
。 然后函数返回一个由当前 count(计数)和 Bet
(当 count > 0 时)或 Hold
(当 count <= 0 时) 拼接的字符串。 注意 count(计数)和玩家的决定(Bet
或 Hold
)之间应该有空格。
当卡牌为 7、8、9 时,不要把 count
值重置为 0。 不要返回一个数组。输出结果中不要包含单引号或双引号。
代码示例
1 | let count = 0; |
测试要求:
1 | 卡片序列 2、3、4、5、6 应返回字符串 5 Bet |
码完代码之后就可以查看答案了~
查看解法 1,适用于 freeCodeCamp (点击查看)
let count = 0;
let result;
function cc(card) {
// 只修改这一行下面的代码
switch(card){
case 2:
case 3:
case 4:
case 5:
case 6:
count++;
break
case 10:
case 'J':
case 'Q':
case 'K':
case 'A':
count--;
break
}
if (count > 0) {
return(count + " Bet")
}
else {
return(count + " Hold")
}
// 只修改这一行上面的代码
}
查看解法 2,在 WebStorm 上测试有输出,freeCodeCamp 不通过 (点击查看)
let count = 0;
const testArr0 = [2,3,4,5,6]
const testArr1 = [7,8,9]
const testArr2 = [10,'J','Q','K','A']
const testArr3 = [3,7,'Q',8,'A']
const testArr4 = [2,'J',9,2,7]
const testArr5 = [2,2,10]
const testArr6 = [3,2,'A',10,'K']
function cc(arr) {
// 只修改这一行下面的代码
for (let i = 0; i < arr.length; i++) {
switch(arr[i]){
case 2:
case 3:
case 4:
case 5:
case 6:
count++;
break
case 10:
case 'J':
case 'Q':
case 'K':
case 'A':
count--;
break
}
}
if (count > 0) {
console.log(count + " Bet")
}
else {
console.log(count + " Hold")
}
count = 0;
// 只修改这一行上面的代码
}
cc(testArr0)
cc(testArr1)
cc(testArr2)
cc(testArr3)
cc(testArr4)
cc(testArr5)
cc(testArr6)
解法 2 最后输出结果:(点击查看)
(这个有一处坑,在 freeCodeCamp 测试时,卡牌不能用数组表示,测试的时候不认)
(解法 1 存在的问题:只输出第一组正确的结果,后面输出的结果一直会被上一个输出的结果影响;在函数内设置结果值重置,会导致所有的输出结果为重置值,很影响心态)
(解法 2 思路就比较清晰,嗯)
1.11 逻辑运算
1.11.1 等于运算符
1 | "=" -> 赋值 |
栗子
1 | function simpleIfFunc (value) { |
运行结果
(需要注意,不论相等还是全等,外面的引号(单 / 双)不影响判断结果;全等不仅要字符相等,数据类型也要相等)
不等号同理
1 | function simpleIfFunc (value) { |
1.11.2 (大 / 小)于(等于)运算符
大于、大于等于、小于、小于等于号(待比较的两个值的数据类型要求至少一个为数字,两个都为非数字的无法比较)
这里偷懒用 FreeCodeCamp 那边敲好的代码()
1 | function testGreaterThan(val) { |
1 | function testGreaterOrEqual(val) { |
1 | function testLessThan(val) { |
1 | function testLessOrEqual(val) { |
1.11.3 逻辑与(或)运算符
与 (&&
):只有两边同时为 true
,结果才为 true
;否则结果为 false
(也可以写成两个嵌套的 if 语句)
1 | function logicalAnd (num) { |
(第一个数字 6 同时满足大于 5 且小于 8 的要求,结果为 true;第二个数字 10 虽满足大于 5 的要求,但不满足小于 8 的要求,1 true 1 false, 结果为 false)
或(||) :只要一边及以上结果为 true
,结果就为 true
;两边结果为 false
,结果为 false
(也可以写成两个按顺序执行的 if 语句)
1 | function logicalOr (num) { |
(第一个数字 6 既不满足大于 8 也不满足小于 5这两个条件,所以结果就为 false;第二个数字 10 满足了大于 8 的条件,只有一边为 true,结果就为 true)
(*) 1.11.4 经典案例:高尔夫代码
(摘自 FreeCodeCamp)
在高尔夫游戏中,每个洞都有自己的标准杆数 par
,代表着把球打进洞所挥杆的平均次数 strokes
。 根据你把球打进洞所挥杆的次数 strokes
高于或低于 par
多少,有一个不同的昵称(代表打高尔夫球的水平)。
函数将会传送两个参数,par
和 strokes
。 根据下表返回正确的字符串。下表列出不同挥杆次数(从高到低)对应的字符串。
挥杆次数 | 返回字符串 |
---|---|
1 | “Hole-in-one!” |
<= par - 2 | “Eagle” |
par - 1 | “Birdie” |
par | “Par” |
par + 1 | “Bogey” |
par + 2 | “Double Bogey” |
>= par + 3 | “Go Home!” |
要求:编写函数,实现上表标准杆数与平均次数的正确对应,返回正确的字符串。其中par
和 strokes
必须是正整数。
这个比较简单,建议自己按照 freeCodeCamp 上所给的要求码代码~
1 | const names = ["Hole-in-one!", "Eagle", "Birdie", "Par", "Bogey", "Double Bogey", "Go Home!"]; |
测试要求
1 | golfScore(4, 1) 应该返回字符串 Hole-in-one! |
码完代码之后就可以查看答案了~
查看提示 (点击查看)
const names = ["Hole-in-one!", "Eagle", "Birdie", "Par", "Bogey", "Double Bogey", "Go Home!"];
function golfScore(par, strokes) {
// 只修改这一行下面的代码
if (strokes === 1) {
return names[0];
}
else if(strokes <= par - 2) {
return names[1];
}
else if (strokes === par - 1) {
return names[2];
}
else if (strokes === par) {
return names[3];
}
else if (strokes === par + 1) {
return names[4];
}
else if (strokes === par + 2) {
return names[5];
}
else {
return names[6];
}
// 只修改这一行上面的代码
}
const result0 = golfScore(4, 1)
const result1 = golfScore(4, 2)
const result2 = golfScore(5, 2)
const result3 = golfScore(4, 3)
const result4 = golfScore(4, 4)
const result5 = golfScore(1, 1)
const result6 = golfScore(5, 5)
const result7 = golfScore(4, 5)
const result8 = golfScore(4, 6)
const result9 = golfScore(4, 7)
const result10 = golfScore(5, 9)
console.log(result0)
console.log(result1)
console.log(result2)
console.log(result3)
console.log(result4)
console.log(result5)
console.log(result6)
console.log(result7)
console.log(result8)
console.log(result9)
console.log(result10)
最后输出结果:(点击查看)
1.12 Javascript 对象
Javascript 与 Java、C++ 一样都是面向对象的程序设计语言。
Javascript 对象的特性:不使用类 (class
)
对象 (object
) 与数组 (array
) 类似。但不同的是,数组是由索引(就是我们熟悉的数组后面带方括号)来获取和修改数据的,而对象是由属性 (properties
) 来访问和修改的。
随着面向对象程序设计语言的发展,对象比数组更为适合用来存储结构化数据,可以表示真实世界中的任何元素中的任何属性。
例如一台计算机
1 | let normalPC = { |
注意,对象当中的每一个属性之间要用逗号隔开,属性名可以不带引号;对象整体要用 = {} 表示,大括号后面可以不接分号(所有的 Javascript 语句都可以这么做)。
可以看着上面的例子,自己写一个带多个属性和值的对象。
写完对象,我们接下来获取对象当中的属性。
1.12.1 对象属性的访问
1.12.1.1 点号表示法
直接在对象后面接点号属性即可(Object.properties
)
1 | let normalPC = { |
1.12.1.2 方括号表示法
直接在对象后面接方括号属性即可(Object[properties]
)
注意,属性名带空格的一律使用方括号!!!
1 | let normalPC = { |
方括号表示法还有更高级的用法,就是通过变量来获取对象的属性值。
在对象外的任何地方定义一个变量,值为属性名,然后再通过之前方括号的方式,将变量写在方括号内,就可以通过变量上的值来获取对象的属性值了。
1 | let normalPC = { |
1.12.2 对象属性的更新
有人说,对象属性的更新不是很简单嘛,直接在对象里面修改就行了。
最好不要这么改,万一你把老板给的对象数据改了,小心被炒鱿鱼 ( x )
开个玩笑哈,回到正题,要想更新对象属性,直接在对象本体外面重新赋值即可,这个赋值无视对象前面所给的定义关键字(let、const)
1 | const normalPC = { |
添加对象的属性值也很简单,直接在对象外面新定义一个即可,定义可以使用上面所说的点号表示法和方括号表示法
例如
1 | const normalPC = { |
输出结果
属性添加之后,一般都排在对象的最后。
删除对象属性直接使用 delete
删除即可
1 | const normalPC = { |
可以看到 RAM 属性已经被删除了。
1.12.3 使用对象查找
由于对象可以用来存储属性 / 属性值(又称 键 / 值对),数据相对扁平,那么就可以根据对象当中所给的属性来查找你想要的值。
所以之前的代码可以这么改
1 | function logicalSwitchObjected (num){ |
(被注释的部分为源代码)
输出结果:
1.12.4 测试对象的属性
可以用对象当中的.hasOwnProperty(properties)
来测试对象是否存在此属性。
返回值为布尔值(即true
和false
)
1 | const normalPC = { |
执行结果:
可以看到,对象中有 RAM 这个属性,所以 hasOwnProperty(“RAM”) 方法返回为 true
;而对象中没有 alias 属性,所以 hasOwnProperty(“RAM”) 方法返回为 false
.
1.12.5 对象中的复杂结构
然而大多数用于生产环境的对象结构并没有这么简单,通常还会嵌套好几个如下的数据结构
1 | const normalPC = [ |
上述代码的特点:
1 | 1 这个数组包含了 2 个对象 |
可以看着上面的例子,自己写一个嵌套着数组的多个对象元素所组成的复杂数组。
1.12.5.1 复杂结构的对象(数组)的访问
对于复杂结构的对象的访问,还是上面的方法,点号和方括号任选,有多少层就用多少个点号(方括号),特别的是带空格的属性名一定要用方括号表示法!
(代码来自 freeCodeCamp)
1 | const myStorage = { |
输出结果:
对于复杂结构的数组的访问,与访问复杂结构的对象类似,只不过要多加一步:先使用方括号索引指明是哪一个元素(对象)
以这一节开头所给的复杂结构的数组为例:
1 | const normalPC = [ |
执行结果
(*) 1.12.5.2 复杂对象(数组)的更新与修改
(摘自 freeCodeCamp)
给定一个对象,用来表示部分音乐专辑收藏。 每张专辑都有几个属性和一个唯一的 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
对象做为测试参数对象。
测试要求:
1 | 执行 updateRecords(recordCollection, 5439, "artist", "ABBA") 后,artist 的值应该是字符串 ABBA。 |
代码示例
1 | // 设置 |
码完代码之后就可以查看答案了~
查看提示 (点击查看)
// 设置
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(prop) === 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")
updateRecords(recordCollection, 5439, "tracks", "Take a Chance on Me")
updateRecords(recordCollection, 2548, "artist", "")
updateRecords(recordCollection, 1245, "tracks", "Addicted to Love")
updateRecords(recordCollection, 2468, "tracks", "Free")
updateRecords(recordCollection, 2548, "tracks", "")
updateRecords(recordCollection, 1245, "albumTitle", "Riptide")
console.log(recordCollection)
最后输出结果:(点击查看)
1.13 循环
无论是 C 语言,还是到现在的 Go 语言,都可以循环执行相同的代码块。
1.13.1 while 循环
这个比较简单:当条件为 true
时则执行,条件为 false
时则不执行.
1 | function whileLoop (num) { |
用 while 循环有个不好的地方,在写 while 语句时会习惯忘记写 i++,而这么一写,直接导致代码无限循环执行! 后面的 for 循环的引入会解决这个问题,这里等后面会慢慢叙述。
1.13.2 for 循环
这个是开发过程中最常用的了。
1 | for (a; b; c) |
1 | function whileLoop (num) { |
输出结果和上图一样,就不展示了。
使用 for 循环还有个好处,就是一开始括号里面就写 i++ ,这样就可以避免 while 语句因忘记写 i++ 而导致的代码无限循环执行这种错误。
下面是关于 for 循环的几个高级用法。
1.13.2.1 用 for 循环遍历数组中的奇(偶)数
1 | // 偶数 |
输出结果
只需修改 for 循环当中的第一个初始化语句(奇数 1 偶数 0),就可以实现数组的奇偶数遍历。
反向遍历也比较简单,修改初始化语句 (与参数相关,参数为奇数则奇数 num 偶数 num - 1;反之亦然) 、条件语句 (正数的反向遍历一般设置条件为大于 0;负数条件相反) 以及最终表达式 (正数 – ,复数相反) 即可实现反向遍历
以正数为例
1 | // 奇数 |
输出结果与上图的数组排列顺序相反。
1.13.2.2 用 for 循环遍历已给数组
1 | const myArr = [8,7,5,3,2] |
输出结果
可以看到,代码执行了 4 次,结果换行输出
反向遍历方法同上节,这里不细说,看代码就行
1 | const myArr = [8,7,5,3,2] |
(摘自 freeCodeCamp)
声明并初始化一个变量 total
值为 0
。 使用 for
循环,使得 total
的值为 myArr
的数组中的每个元素的值的总和。
1 | const myArr = [2, 3, 4, 5, 6]; |
最后输出结果为:20
1.13.2.3 循环嵌套
如果有一个二维以上的数组,想要进行遍历,则可以使用循环嵌套,一般使用 for 嵌套里面的 for 循环。
1 | const myArr = [[7,13],[4,6],[20,34],[1,-6],[50,-89]] |
输出结果为换行输出每个数组的子元素。
(摘自 freeCodeCamp)
修改函数 multiplyAll
,获得 arr
内部数组的每个数字相乘的结果 product。
测试要求
1 | multiplyAll([[1], [2], [3]]) 应该返回 6 |
代码提示
1 | function multiplyAll(arr) { |
输出结果
1.13.3 do…while 循环
do…while 循环与 while 循环的转换
1 | do{ |
本质上就是 while 循环的改写形式。这里就不再叙述了,用的也少。
(*) 1.13.4 用递归来代替循环 (循环改写成递归)
函数可以在内部调用自己本身,如果没有跳出条件的话,会导致函数无限递归,recursion_here_we_go_again.jpg
还是直接看代码吧
1 | const myArr = [4,5,3,7,8] |
输出结果,两种方法输出的结果都是一样的
由上述递归部分代码,可知递归的简要过程:
在递归函数内,有一个跳出条件,当满足这个跳出条件时,函数返回特定值 1
并停止执行;若不满足这个跳出条件时,进入递归部分,函数会调用自己本身,参数的值为 arr
和 num - 1
(num 在调用时已经设定好具体值)。函数继续以上述方式调用自己本身,直到num = 0
时触发跳出条件,返回值 1,结束执行。
要注意的是,递归部分处的参数不能是一个不变值(非跳出条件里设定的具体值),否则同样会导致函数无限递归,recursion_here_we_go_again.jpg
现在根据要求,自己编写一个递归函数。(要求摘自 freeCodeCamp)
写一个递归函数,sum(arr, n)
,返回递归调用数组 arr
从前 n
个元素和。
测试要求
1 | sum([1], 0) 应该返回 0 。 |
代码示例
1 | function sum(arr, n) { |
码完代码之后就可以查看答案了~
查看提示 (点击查看)
let sum = 0;
// 传统循环
function sumOld(arr,num){
for (let i = 0; i < num; i++){
sum += arr[i]
}
console.log(sum)
sum = 0;
}
sumOld([1],0);
sumOld([2,3,4],1);
sumOld([2,3,4,5],3);
// 新式递归
function sumRecursion(arr,num) {
if (num <= 0){
return 0;
}
else {
return sumRecursion(arr, num - 1) + arr [num - 1]
}
}
console.log(sumRecursion([1],0))
console.log(sumRecursion([2,3,4],1))
console.log(sumRecursion([2,3,4,5],3))
最后输出结果:(点击查看)
(*) 1.13.5 经典案例:经典查找
(内容摘自 freeCodeCamp)
我们有一个对象数组,里面存储着通讯录。
lookUpProfile
函数已经写好了参数,需要 name
和属性 (prop
) 参数。
函数将会检查通讯录中是否存在一个 firstName
与传入的 name
相同的联系人。 如果存在,那么还需要检查对应的联系人中是否存在 prop
属性。
如果它们都存在,函数返回 prop 属性对应的值。
如果 name
不对应于任何联系人,然后返回字符串 No such contact
。
如果 prop
属性在匹配 name
的联系人里不存在,返回 No such property
。
测试要求:
1 | lookUpProfile("Kristian", "lastName") 应该返回字符串 Vos |
代码示例
1 | // 设置 |
码完代码之后就可以查看答案了~
查看提示 (点击查看)
// 设置
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 (name === contacts[i].firstName) {
if (contacts[i].hasOwnProperty(prop)) {
return contacts[i][prop]
}
else return "No such property"
}
}
return "No such contact"
// 只修改这一行上面的代码
}
// sample
// console.log(lookUpProfile("Akira", "likes"));
// Test
console.log(lookUpProfile("Kristian", "lastName"));
console.log(lookUpProfile("Sherlock", "likes"));
console.log(lookUpProfile("Harry", "likes"));
console.log(lookUpProfile("Bob", "number"));
console.log(lookUpProfile("Bob", "potato"));
console.log(lookUpProfile("Akira", "address"));
最后输出结果:(点击查看)
1.14 Math.random()
Math.random()
用来创建随机数。
注意 Math 一定要大写!!!注意 Math 一定要大写!!!注意 Math 一定要大写!!!
Math.random()
在默认情况下会生成一个由 0(含) 到 1(不含) 之间的随机小数,即 0 <= Math.random() < 1
.
这个方法可以直接返回值.
除了生成随机小数,还可以生成(正)整数。不过需要用到 Math.floor()
这个方法来 “四舍五入” (换一种说法,就是向下取整,遵循 “四舍五入” 的原则。)
方法:将 Math.random()
生成的随机数乘以想要的数字范围的最大值(比如说,我想生成 0~100(不含)的整数,那就在生成随机数的方法后面乘以 100),然后让 Math.floor()
把上面的那一串包裹起来即可。
就像这样:
之前我们生成的都是从 0 开始的随机数,那么可不可以从指定范围(不是指 0)生成随机数呢?、
当然可以!我们还需要用到 min
和 max
这两个变量。
看看下面的方法式:
1 | Math.floor(Math.random() * (max - min + 1)) + min |
试着去理解这个方法式在干什么,思考完就可以点开下面的提示了。
点击查看思路
1 括号里面,左边的 Math.random() 生成随机数 2 右边的 (max - min + 1) 计算差值 3 括号里面的左右两式相乘 4 大括号 Math.floor() 进行四舍五入 5 四舍五入后的整数在加上最小范围值,即可得到给定范围内的实际数值。
就像这样(生成在 20~99 范围内的随机数):
1.15 parseInt()
首先注意上面方法的写法!!!首先注意上面方法的写法!!!首先注意上面方法的写法!!!
这个方法的作用,就是把字符串转换成整型数值。
如果字符串第一个字符不能转为数字(大概率是字母),则返回为 NaN
这个方法也可以直接返回值.
1 | function parseIntEvent (str) { |
这个方法也可以进行不同进制转换为十进制,只需要在第一个(默认)参数后面再追加 2、4、6、8、16 这些数字作为第二个参数,就可以进行任意进制转换为十进制了。
1.16 三元运算符
三元运算符可以让 if...else
这类的语句直接给你干成一句。
形式为:a ? b : c
a 为条件,b 为条件结果为 true
时触发,c 为条件结果为 false
时触发。
使用三元运算符改写 if…else 语句前后:
1 | // if...else |
最终的输出结果都是 1
和大多数条件表达式一样,三元运算符也是可以嵌套的
1 | // 传统的条件形式 |
最终输出结果:
(*) 1.17 利用递归解决实际问题
终于来到最后一节啦!在 1.13.4 小节中,我们简单地把循环体改写成递归的形式。那么在这一节,我们要用递归来解决更多的实际问题,也算是 Javascript 基础课程的 BOSS 战了。这一节内容均摘自 freeCodeCamp.
BOSS 的第一层血: 使用递归创建一个倒计时
在上一个挑战,学习了怎样用递归来代替 for
循环。 现在来学习一个更复杂的函数,函数返回一个从 1
到传递给函数的指定数字的连续数字数组。
正如上一个挑战提到的,会有一个 base case。 base case 告诉递归函数什么时候不再需要调用其自身。 这是简单 情况,返回得到的值。 还有 recursive call,继续用不同的参数调用自身。 如果函数无误,一直执行直到 base case 为止。
比如,如果想写一个递归函数,返回一个数字 1
到 n
的连续数组。 这个函数需要接收一个参数 n
代表最终数字。 然后会持续的调用自身,传入一个比 n
更小的值一直到传入的值是 1
为止。 函数如下:
1 | function countup(n) { |
值 [1, 2, 3, 4, 5]
将显示在控制台中。
起初,这似乎是违反直觉的,因为 n
的值递减,但是最终数组中的值却递增。 之所以发生这种情况,是因为在递归调用返回之后,才调用 push。 在将 n
pushed 进数组时,countup(n - 1)
已经调用赋值成功并返回了 [1, 2, ..., n - 1]
。
题目:
**已经定义了一个函数 countdown
,函数有一个参数(n
)。 **
**函数应该基于参数 n
递归调用返回 n
到 1
的连续数字的数组。 **
**如果函数以小于 1 的参数调用,函数应该返回空数组。 **
**比如,用 n = 5
调用函数应该返回数组 [5, 4, 3, 2, 1]
。 **
函数必需使用递归函数调用自身,不能使用任何形式的循环。
测试要求:
1 | countdown(-1) 应该返回一个空数组。 |
代码示例:
1 | // 只修改这一行下面的代码 |
码完代码后,请到下一大节查看答案哦~
BOSS 的第二层血: 使用递归来创建一个数字序列
接着上一个挑战,有另外一个机会来用递归函数解决问题。
题目:
已经定义好了 rangeOfNumbers
函数,包含两个参数。
函数应该返回一个连续数字数组,startNum
参数开始 endNum
参数截止。
开始的数字小于或等于截止数字。
函数必需递归调用自身,不能使用任意形式的循环。
要考虑到 startNum
和 endNum
相同的情况。
测试要求:
1 | 函数应该返回一个数组。 |
代码示例:
1 | function rangeOfNumbers(startNum, endNum) { |
码完代码后,请到下一大节查看答案哦~
到这里, Javascript 的基础课程就到这里了,
下一大节:待定(x)