一、分支语句
- if分支语句
if分支语句有三种:单分支、双分支、多分支。// 单分支语句 if (条件) { // 括号中‘条件’的值都会转为布尔值, // 数字中除了'0'都为真,字符串中除了空字符串''都为真 '条件'为'真(true)'执行的代码 } // 双分支语句 if (条件) { 满足'条件'执行 } else { 不满足'条件'执行 } // 多分支语句 if (条件一) { 满足'条件一'执行 } else if (条件二) { 不满足'条件一'但满足'条件二'执行 } else if (条件三) { 不满足'条件一和二',但满足'条件三'执行 } else if (条件n) { 前面的'条件n-1'都不满足,满足'条件n'执行 } else { 前面'n个条件'都不满足执行 }
- 三元运算符
是比“if双分支”更简单的写法,常用来取值
语法:条件 ? 满足条件执行 : 不满足条件执行
let a = 3; let b = 0; b = a < 5 ? a : 5; // 此时b的值等于a的值为3
- switch语句
switch...case...
语句一般用于等值判断,不适合于区间判断;
switch...case...
语句一般需要配合break
关键字使用,没有break会造成case穿透。switch (数据) { // 找到与'数据'全等(===)的case值,并执行相应的代码,然后遇到break跳出switch语句,若没有遇到全等的case则会找到最后执行default中的代码 // 若在找到了全等的case值,但是代码执行过程中没有遇到break关键字,则代码执行完之后会继续往下执行,直到遇到break或switch语句的结尾(case穿透) case 值1: 满足值1执行的代码 break case 值2: 满足值2执行的代码 break ... case 值n: 满足值n执行的代码 break default: 不符合上面所有值的默认执行代码 break }
- if和switch
- 共同点
- 都能实现多分支选择,实现多选一;
- 大部分情况下可以互换。
- 区别
switch...case...
语句通常处理case
为比较确定值的情况,而if...else...
语句更加灵活,通常用于范围判断;switch
语句进行判断后直接执行到程序的语句,效率更高,而if
语句有几种判断条件就得判断多少次;switch
判断时一定是全等(===
),且需要注意配合break
避免case穿透。
- 当分支比较少时,
if...else
语句执行效率更高;分支较多时,switch
语句执行效率更高,且结构更清晰。
- 共同点
二、循环语句
循环本质就是以某个变量为起始值,然后不断产生变化量,慢慢靠近终止条件的过程。
循环三要素:变量起始值,变量变化量,终止条件(没有终止条件循环会一直执行,造成死循环)
- while循环
在满足条件期间,重复执行某些代码,直到不再满足条件。while (条件) { 要重复执行的代码(循环体) } // 重复输出10个javascript // 起始值为1 let i = 1; // 终止条件,当'i'满足'i <= 10'时,执行循环体,当'i > 10'时(终止条件)结束循环。 while (i <= 10) { document.write('Javascript'); i++; // 变量变化量,每执行一次'i'就自增一 }
- for循环
在for循环中,一般情况下把声明起始值、循环条件、变化值写到一起,是最常用的循环方式。for (变量起始值; 循环条件; 变量变化量) { 循环体 } // for循环的执行顺序是:1.变量起始值 --> 2.循环条件判断 --> 3.满足执行循环体(不满足退出循环不再执行下一步) --> 4.变量变化 --> 重复步骤2、3、4 // 重复输出10个javascript for (let i = 1; i<= 10; i++) { // 当'i > 10'时退出循环 document.write('Javascript'); } // 循环嵌套打印乘法表,形式为 i * j = m, 为 for(let i = 1; i <= 9; i++) { // 行的值 for (let j = 1; j <= i; j++) { // 列的值 document.write(j + 'x' + i + '=' + j * i + ' '); } document.write('<br>'); }
- 循环退出
break
:退出循环,不再执行循环;continue
:结束本次循环,继续执行下次循环。
let i = 1, j = 1;
// 使用while打印 10 以内的奇数
while (i <= 10) {
if (i % 2 === 0) { // 如果能被2整除就执行
i++; // 变量变化
continue; // 跳出本次循环
}
console.log(i);
i++;
} // 最后依次打印‘1,3,5,7,9’
// 使用for打印 10 以内的奇数
for (j; j <= 10; j++) {
if (j % 2 === 0) { // 如果能被2整除就执行
continue; // 跳出本次循环
}
console.log(j);
} // 最后依次打印‘1,3,5,7,9’
while (true) {
let m = parseInt(prompt('请输入一个比10大的3的倍数'));
if (m > 10 && m % 3 === 0) {
break; // 输入正确结束整个循环,不再弹出输入框
}
}
for (let n = 1; n<= 10; n++) {
if (n === 7) {
break; // 当遇到7的时候结束循环
}
console.log(n)
} // 最后依次打印‘1,2,3,4,5, 6’
三、数组
数组(Array)是一种可以按顺序保存数据的数据类型(属于对象中的一种)。是有序的数据集合,可以使用一个变量管理多个数据,非常方便。
- 数组的声明
- 使用字面量声明:
let 数组名 = [数据1, 数据2, ..., 数据n]
- 使用
new Array
构造函数声明:let 数组名 = new Array(数据1, 数据2, ..., 数据n)
- 使用字面量声明:
- 数组使用
- 数组中的每个数据都叫做数组元素;
- 数组可以存储任意类型的数据;
- 数组是有序的,每一个数据都有一个编号,也叫数组的下标或是索引号,从
0
开始,使用时按照数组名[下标]
; - 数组中数据的个数也就是数组的长度,可通过属性
length
获得。
- 操作数组
- 修改某个元素:通过数组的下标给指定元素重新赋值
- 新增元素:
数组.push()
将一个或多个元素添加到数组的末尾,并返回该数组的新长度;数组.unshift()
将一个或多个元素添加到数组的开头,并返回该数组的新长度。
- 删除元素
数组.pop()
删除末尾元素,并返回该元素的值;数组.shift()
删除开头元素,并返回该数组的新长度;数组.splice(start, delCount)
删除指定位置元素,参数start
表示下标为start
的开始删除(起始位置),delCount
表示需要删除的元素个数。
// 修改元素 let arr = ['苹果', '菠萝', '梨']; console.log(arr[0]); // 输出:苹果 arr[0] = '香蕉'; // 修改第一个元素 console.log(arr[0]); // 输出:香蕉 let arrA = ['red'] // 末尾新增元素,数组.push()方法 arrA.push('blue'); arrA.push('yellow', 'green'); console.log(arrA); // 输出:['red', 'blue', 'yellow', 'green'] // 开头新增元素,数组.push()方法 arrA.unshift('蓝'); arrA.unshift('黄', '绿'); console.log(arrA); // 输出:['黄', '绿', '蓝', 'red', 'blue', 'yellow', 'green'] // 删除末尾元素,数组.pop()方法 arrA.pop(); console.log(arrA); // 输出:['黄', '绿', '蓝', 'red', 'blue', 'yellow'] // 删除开头元素,数组.shift()方法 arrA.shift(); console.log(arrA); // 输出:['绿', '蓝', 'red', 'blue', 'yellow'] // 删除指定位置元素,数组.splice()方法 arrA.splice(2, 1); // 从下标为2的元素开始删除1个元素 console.log(arrA); // 输出:['绿', '蓝', 'blue', 'yellow']
- 遍历数组
用for循环遍历数组let arr = [1, 2, 3, 4, '五', '六'] for (let i = 0; i < arr.length; i++) { console.log(i); } // 求数组[1, 2, 1, 5, 8, 9, 4, 6]中的最大值 let arrA = [1, 2, 1, 5, 8, 9, 4, 6]; let max = arrA[0]; // 假设数组中最大的是数组中的第一个 for (let i = 1; i < arrA.length; i++) { // 遍历数组 if (max < arrA[i]) { max = arrA[i]; // 如果数组元素比max大则更新max值 } } console.log(`最大值是:${max}`); // 冒泡排序:是一种简单的算法。一次比较两个相邻元素,得到的结果就按从小到大(从大到小)排列,直到最后两个数比较完成,称为一轮排序。 // 一个长度为5的数组,需要经过四轮排序才能得到一个全部按从小到大(从大到小)排列的数组结果。 // 对于数组arrA进行冒泡排序 for (let i = 0; i < arrA.length - 1; i++) { // 外层循环控制比较轮数,一共比较'arrA.length-1'轮 for (let j = 0; j < arrA.length - 1 - i; j++) { // 内层循环控制每一轮比较次数,每一轮比较'arrA.length-1-i'次(在一轮排序中,一次比较会将最大(小)的数放在末尾,所以下一轮比较次数将会少一)。 if (arrA[j] > arrA[j + 1]) { // 如果前一个元素大于后一个元素,则交换位置 let temp = arrA[j]; arrA[j] = arrA[j + 1]; arrA[j + 1] = temp; } } } console.log(arrA); // 输出:[1, 1, 2, 4, 5, 6, 8, 9]
四、函数
函数(function)是被设置及为执行特定任务的代码块。函数可以把具有相同或相似逻辑的代码包裹起来,通过函数调用执行这些被包裹起来的代码逻辑,这样有利于精简代码方便复用。比如alert()、prompt()、console.log()
都是一些封装好的js函数。
- 函数基本使用
函数需要先使用function
关键字声明,函数名之后需要小括号()
,函数体需要用大括号{}
包裹起来。在使用函数时,通过函数名()
调用,可以一次声明多次调用。
函数命名规范- 和变量命名基本一致;
- 尽量小驼峰式命名法(如:
getUser()
); - 前缀应该为动词;
- 常用动词:
can、has、is、get、set、load
。
// 函数声明 function 函数名() { 函数体 } // 函数调用,函数不调用自己不执行 函数名()
- 函数的参数
将需要传入的参数写在声明时函数名后面的括号中,需要几个参数写几个,参数之间用逗号隔开,function 函数名(参数1, 参数2, ..., 参数n)
。调用函数时将实际的值写在调用时的括号中,函数名(参数值1, 参数值2, ..., 参数值n)
。- 在声明时写在括号里的参数称为形参(形式上的参数);
- 在调用函数时写在括号里的称为实参(实际上的参数);
- 形参可以理解为是函数内声明的变量,实参是给这个变量赋值;
- 开发者尽量保存形参和实参个数一致;
alert('警告')、parseInt('5')
本质上都是函数调用的传参;- 当用户不传入实参时,也可以在函数声明时给形参设置默认值
function 函数名(参数 = 值)
。
// 函数声明 function 函数名(参数列表) { 函数体(在函数体中使用参数列表中的参数) } function getSquare(num) { document.write(num * num); } function getSum(num1, num2) { document.write(num1 + num2); } function getSum1(num1 = 0, num2 = 0) { document.write(num1 + num2) } // 调用 getSum(1, 2); // 输出:3 getSum(); // 输出:NaN getSum1(); // 输出:0
- 函数返回值
在有些情况下,需要在特定的情况使用函数处理值并得到一个结果,这种时候需要函数返回一个值。比如parseInt('5')
的返回值是数字5。- 将需要返回的数据使用
return
关键字返回,return 数据
。 - 在函数中,执行了
return
则会立即结束当前函数,后面的代码不再执行,所以返回的数据不要换行写。 - 对于有返回值的某些函数,可以使用变量存放返回值。
- 函数可以没有
return
返回值,这种情况默认返回值为undefined
。
function getSum(num1, num2) { let sum = num1 + num2; } function getSum1(num1 = 0, num2 = 0) { return } // 调用 console.log(getSum(1, 2)); // 输出:undefined console.log(getSum1()); // 输出:undefined
- 将需要返回的数据使用
- 作用域
通常来说,一段程序代码中所用到的变量并不总是有效/可用的,而限定这个变量的可用性的代码范围就是这个变量的作用域。
作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少变量冲突。- 全局作用域:作用于所有代码执行的环境(整个script标签内部或独立的js文件),在此作用域下声明的变量叫做全局变量。
- 局部作用域(函数作用域):作用于函数内的代码环境,在此作用域下声明的变量叫做局部变量。
- 全局变量在全局(代码的任何位置)下都可以使用;全局作用域中无法访问到局部作用域中的变量。
- 如果在函数内部不声明变量,直接赋值,也被看做全局变量,但强烈不推荐。
- 函数的形参可以看作是局部变量。
- 局部变量只能在函数内部使用,在局部作用域中可以访问到全局变量。
- 在访问变量时按就近原则,从离得最近的一层作用域往上找。
- 匿名函数
函数可分为具名函数(function 函数名() {}
)和匿名函数(function () {}
)。
匿名函数无法直接使用,使用方式为:函数表达式,立即执行函数。- 函数表达式:将匿名函数赋值给一个变量,通过变量名进行调用,称为函数表达式。
- 立即执行函数:避免全局变量的污染,形式
(函数整体)(形参)
。
// 函数表达式 let fn = function () { 函数体 } fn(); // 调用函数,必须先声明后调用(具名函数可以在任意地方调用) // 立即执行函数 // (立即执行函数中第一个小括号内也可以写具名函数) (function () { let num = 10 })(); // 多个立即执行函数之间要用分号隔开 (function () { let num = 20 })();
- 函数注意点
- 两个函数名相同的函数,后面的会覆盖前面的;
- 在JS中,实参的个数和形参可以不一样,按顺序依次给形参传值。多余的实参不参与运算,多余的形参自动填充为
undefined
。 - 函数的结束用
return
,break
无法结束函数,可以结束循环和switch。
五、逻辑中断
逻辑中断就是逻辑运算符中的短路,只存在于&&
和||
中,当满足一定条件时,右边的代码不执行。
&&
的左边为false
就短路,即右边的不再执行;||
的左边为true
就短路,即右边的不再执行;- 无论是
&&
还是||
,运算结果都是最后被执行的表达式,如console.log(2 && 1)
输出1
,console.log(2 || 1)
输出2
。
六、转换为布尔型
- 显式转换
Boolean(需转换的值)
:空字符串('')、0、undefined、null、false、NaN
转换为布尔型后都为false
,其余为true
。
- 隐式转换
- 有字符串的加法,
'' + 1
的结果是'1'
; - 减法(只能用于数字),会使空字符串
''
转换为0
; null
经过数字转换后变为0
;undefined
经过数字转换后变为NaN
。
- 有字符串的加法,
七、对象
对象(object)也是Javascript中的数据类型。可以理解为使一种无序的数据集合,可详细地描述某个事物。对象中的值有属性和方法两种,以键值对的形式(属性名: 属性值
、方法名: 函数
)存在,多个键值对中间用逗号隔开。
- 对象的声明
// 方法一(常用) let 对象名 = {}; let 对象名 = { 属性名: 方法, 方法名: 函数 }; // 方法二 let 对象名 = new Object();
- 对象的属性
数据描述性的信息称为属性,如人的姓名、身高、年龄等,一般是名词性的。- 属性都是成对出现的,包括属性名和属性值,中间用英文冒号(
:
)隔开,多个属性之间用英文逗号隔开(,
)。 - 属性就是依附在对象上的变量(外面是变量,对象内是属性)。
- 属性名可以使用引号
""、''
,一般情况下可省略,除非遇到特殊符号如空格、中横线等。
- 属性都是成对出现的,包括属性名和属性值,中间用英文冒号(
- 对象的操作
- 访问对象:通过
对象名.属性名
或对象名['属性名']
访问对象; - 修改对象属性值:通过
对象名.属性名 = 新值
即可修改; - 新增对象属性值:通过
对象名.新属性名 = 值
即可新增; - 删除属性:通过
delete 对象名.属性名
即可删除
// 声明一个对象 let user = { username: '张三', age: '18' } console.log(user); // 输出:{username: '张三', age: '18'} // 访问对象 // “对象名.属性名”的属性名不加引号 // “对象名['属性名']”中的属性名必须加引号,单双引号都可以 console.log(user.username) // 输出:张三 console.log(user['age']) // 输出:18 // 修改属性,访问属性并重新赋值 user.name = '李四'; console.log(user) // 输出:{username: '李四', age: '18'} // 新增属性,和修改一样,只要被赋值的属性名在原来的对象中不存在,则会新增一个新属性 user.gender = '男'; console.log(user) // 输出:{username: '李四', age: '18', gender: '男'} // 删除属性 delete user.age; console.log(user); // 输出:{username: '李四', gender: '男'}
- 访问对象:通过
- 对象的方法
数据行为性的信息称为方法,一般是动词性的,其本质是函数。- 对象中的方法由方法名和函数构成,中间用英文冒号(
:
)隔开。 - 方法是依附在对象中的函数。
- 方法名可以使用引号
""、''
,一般情况下可省略,除非遇到特殊符号如空格、中横线等。
let obj = { uname: '张三', printSum: function (num1, num2) { console.log(num1 + num2) } } // 调用对象的方法:对象名.方法名() obj.printSum(20, 30); // 输出:50
- 对象中的方法由方法名和函数构成,中间用英文冒号(
- 遍历对象
对象没有像数组一样的length
属性,它的长度是无法确定的,对象里面是无序的键值对,没有像数组一样的下标,无法像数组那样使用for
循环遍历。- 遍历对象使用
for (变量名 in 对象)
。这种方法也可以遍历数组,但不推荐。 - 在
for (变量名 in 对象)
中的变量在循环过程中依次代表对象的属性名。 - 由于是使用变量代表属性名,所以必须使用
对象名[变量名]
获得对象的属性值。
let user = { username: '张三', age: '18', gender: '男', } // 遍历对象 for (let key in user) { console.log(key) // 依次输出:username、age、gender // 由于使用key是变量,所以要用方括号访问属性 console.log(user[key]) // 依次输出:'张三'、'18'、'男' console.log(user.key) // 依次输出:undefined、undefined、undefined; }
- 遍历对象使用
- 内置对象
JavaScript内部提供的对象,包含各种属性和方法给开发者调用。如document.write(),console.log()
常用内置对象 —— 数学对象(Math),拥有一些数学常数属性和数学函数方法。// Math中的一些属性 console.log(Math.PI); // 圆周率,3.141592653589793 console.log(Math.E); // 欧拉常数,也是自然对数的底数,2.718281828459045 // Math中的一些方法 console.log(Math.random()); // 生成一个 0 到 1 之间的随机数(包括 0 不包括 1) console.log(Math.ceil(1.1)); // 向上取整,2 console.log(Math.floor(1.9)); // 向下取整,1 console.log(Math.round(1.5)); // 四舍五入,2 console.log(Math.abs(-1)); // 绝对值,1 console.log(Math.max(1, 2, 3)); // 最大值,3 console.log(Math.min(1, 2, 3)); // 最小值,1 console.log(Math.pow(2, 3)); // 幂运算,8 console.log(Math.sqrt(4)); // 开平方,2 // 使用 Math.random() 生成一定范围内的随机数 // 生成 m 到 n 之间的随机数 Math.floor(Math.random() * (n - m + 1) + m); // 生成一个 1 到 10 之间的随机数 console.log(Math.floor(Math.random() * 10) + 1); // 生成一个 5 到 10 之间的随机数 console.log(Math.floor(Math.random() * 6) + 5);
八、基本数据类型和引用数据类型
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。
- 值类型:简单/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型。如:
string、number、boolean、undefined、null
。 - 引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型。通过
new
关键字创建的对象(系统对象、自定义对象)都是引用数据类型,如Object、Array、Date
等。
- 堆栈空间分配区别
- 栈:由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。简单数据类型存放到栈里面。
- 堆:存储复杂数据类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。引用数据类型存放到堆里面。
- 复杂类型的地址分配
- 引用类型(复杂数据类型):通过
new
关键字创建的对象(系统对象、自定义对象)都是引用数据类型,如Object、Array、Date
等。 - 引用数据变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中。
// 简单数据类型,直接存放值
let num1 = 10;
let num2 = num1; // 将num1的值赋值给了num2,此时num1和num2的值是相同的
num2 = 20; // num1和num2的值各自存放在一个独立的空间,修改时互不影响。
console.log(num1, num2); // 输出:10 20
// 复杂数据类型,存放地址
let obj1 = {age: 18};
let obj2 = obj1; // 将obj1的地址赋值给了obj2,所以此时obj1和obj2存的地址是一样的
obj2.age = 20;
// 由于obj1和obj2存的地址是一样,所以指向的堆中的实例对象是同一个
// 当通过obj2对实例对象进行修改后,obj1再次指向对象实例时是同一个经过修改的结果
console.log(obj1.age, obj2.age) // 输出:20 20