数据类型转换 (强制类型转换 和 隐式类型转换)
强制类型转换的方法 String() Number() Boolean()
// String() 把其他类型转化为字符串类型
// Number() 把其他类型转化为数字类型
// Boolean() 把其他类型转化为布尔类型
String() 把其他类型转化为字符串类型 (将数据包裹到字符串中即可)
// console.log(String("hello")); // "hello"
// console.log(String(100)); // "100"
// console.log(String(true)); // "true"
// console.log(String(false)); // false
// console.log(String(null)); // "null"
// console.log(String(undefined)); //"undefined"
Number() 把其他类型转化为数字类型
// (1) 纯数字类型的字符串 => 直接转数字
// 非纯数字类型的字符串 => NaN
// (2) true转数字类型 1 false转数字类型0
// (2) null转数字类型0 undefined转数字类型NaN
// var result = Number("100"); // 先存储转化后的结果
// console.log(result); // 打印结果
// console.log(1 / 0); // Infinity (正无穷)
console.log(Number("100"));
console.log(Number("hello")); // NaN (not a number => 表示非数字(结果不是一个数字))
console.log(Number("100px")); // NaN (not a number => 表示非数字(结果不是一个数字))
console.log(Number(10));
console.log(Number(true));
console.log(Number(false));
console.log(Number(null));
console.log(Number(undefined));
Boolean() 把其他类型转化为布尔类型
"" 0 NaN false null undefined 转布尔值为false,其他的均为true
// console.log(Boolean("100")); // true
// console.log(Boolean("hello")); // true
// console.log(Boolean(" ")); // true
// console.log(Boolean("")); // false
console.log(Boolean(100)); // true
console.log(Boolean(1)); // true
console.log(Boolean(Infinity)); // true
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
隐式类型转换
算术运算符的隐式类型转换
// (1) 算术运算符 常用于做数字运算
// (2) 当其他类型参与算术运算时,会发生隐式类型转换
// a.字符串遇到 "+" 会拼接形成新的字符串 (字符串拼接)
// b.字符串遇到 "- * / %" 会先隐式转化为数字, 在参与运算
// 其中:纯数字类型的字符串 => 直接转数字
// 非纯数字类型的字符串 => NaN (NaN 和 任何数运算得到的结果均为NaN)
// c. 布尔值遇到算术运算符,会先隐式转化为数字, 在参与运算 => true转数字类型 1 false转数字类型0
// d. null undefined 遇到算术运算符,会先隐式转化为数字, 在参与运算 => null转数字类型0 undefined转数字类型NaN
小结: 除字符串遇到"+"会拼接形成新的字符串外,其他数据均会先会先隐式转化为数字, 在参与运算
关系运算符的隐式类型转换 (> >= <
// (1) 字符串,布尔值和数字比较时,会先隐式转化为数字,在参数比较
// (2) 字符串 和 字符串 比较时, 依次比较字符对应的ASCII码(unicode码) => 第一个字符不同就比较第一个,第一个相同就比第二个
// (3) null 和 undefined 遇到 > >= <
// null 和 undefined 遇到 == != 时, 不会转化而是直接比较(没有可比性)
// (4) null 和 undefined 在数值上是相同的(官方规定)
// (5) NaN和其他数据没有可比性, 和任何值比较都是false => NaN != NaN
++ --的隐式类型转换
//其他数据类型遇到++和--也会先隐式转化为数字类型,再自增或自减
程序的三大流程控制
// 顺序结构: 代码自上而下 依次执行
// 选择结构: 根据不同的情况,执行对应代码
// 循环结构: 重复的做一件事
// 表达式 => 由运算符和操作数组成的式子
// 运算符: 算术 关系 逻辑 赋值
// 操作数: 变量和常量(常规数据(也叫字面量/直接量) -> 基本数据,无需声明可以直接使用)
// 字面量 直接量
// 100
// "hello"
// true
var a = 10;
a + 5 ; // 算术表达式
a > 5 ; // 关系表达式(条件表达式->单)
a > 5 && a < 20; // 逻辑表达式(条件表达式->多)
a
5
选择结构: if
单分支: 爱走不走,不走拉倒
if(表达式){
执行语句;
}
首先求解表达式, 当括号内的表达式结果成立(为true时),则执行大括号内的语句,否则不做任何操作,继续执行后续的代码。
括号里的表达式一般是关系表达式或者逻辑表达式(如果表达式的结果不是布尔值,会隐式转化为布尔值,在判断)
双分支: 二选一,必须选一个
if(表达式){
执行语句1;
}else{
执行语句2;
}
首先求解表达式,括号内的表达式结果成立(为true时),则执行if之后大括号内的语句1, 否则执行else后大括号中的语句2;
多分支: 比如填报志愿
if(如果清华北大录取我?){
去清华北大;
}else if(武汉科技大学录取我?){
去武汉科技大学;
}else if(武汉工程大学录取我?){
去武汉工程大学;
}else if(如果江西软件大学录取我?){
去江西软件大学;
}else{
家里蹲大学;
}
if(表达式1){
执行语句1;
}else if(表达式2){
执行语句2;
}else if(表达式3){
执行语句3;
}else{
执行语句4;
}
首先求解表达式1, 如果表达式1成立,则执行语句1,语句1执行完毕,跳出当前选择结构,代码执行向后执行
如果表达式不成立, 求解表达式2,依此类推,全都不成立就走else后大括号中的语句;
从上往下,满足哪个条件就执行其相对应的语句,都不满足时,执行最后的else的语句,只能进入其中之一。
多分支至少是三分支 if...else if...else
选择结构的嵌套: 在满足特定条件的情况下,进行二次判断
断点调试:
// 目的: 让代码执行到某个位置后停止, 后续手动操作代码一步一步向后执行
// 如何使用断点:
// (1) 哪里不会点哪里? => 打断点(debugger)
// (2) 打开页面 => 开发者工具 => source(资源) => 找到断点所在位置
// (3) 触发断点 => 让页面中的代码重新执行 (因为页面加载时,代码默认执行完毕)
// a. 如果断点所在的代码在页面加载时执行 => 刷新页面
// b. 如果断点在事件中, => 触发事件(点击事件)
// (4) 如何让代码继续向后执行
// F9 F10 F11 => 代码继续向后执行一步 (fn + F9)
// F8 恢复脚本执行 => 代码继续向后自动执行 (结束断点或跳到下一个断点所在位置)
// (5) 代码调试完毕 => 删除/注释debugger
选择结构 switch => 多分支
switch(表达式){
case 常量1: 执行语句1; break;
case 常量2: 执行语句2; break;
case 常量3: 执行语句3; break;
case 常量4: 执行语句4; break;
default:
执行语句5;
break;
}
switch中的表达式一般是一个变量, 在case中列举出所有变量可能出现的情况, 执行对应的语句,语句执行完毕跳出当前选择结构,
执行流程: switch中的表达式(变量),依次和case中的常量(字面量/直接量)做全等(===)比较,如果
结果为true,则执行对应的语句,语句执行完毕跳出当前选择结构, 否则继续向后比较,以此类推,全都不满足就执行default之后的语句
break的作用是跳出当前的选择结构, 如果不添加break,会进行执行后续分支中的执行语句,而不进行判断(switch穿透/case穿透)
小结:
if 一般常用于做条件判断 (if中的表达式一般是一个条件表达式)
switch 一般常用于做数值判断 (switch中接收一个变量,列举改变了所有可能出现的情况)
三元运算符 / 三目运算符 (条件运算符) => 简单的双分支
// 语法:
// 表达式1 ? 表达式2 : 表达式3;
// 首先求解表达式1, 如果表达式1成立,则求解表达式2, 否则求解表达式3;
// 常用于: 判断赋值
循环结构 => 循:沿着 环: 圆 / 环 循环: 周而复始,循环往复
// 循环就是重复做一件事
// 在满足特定条件的情况下,重复的做一件事, 直到条件不满足为止
// 常见循环
// while
// do...while
// for
循环的三要素
// (1) 初始值 (初始值 => 循环开始的位置)
// (2) 循环条件 (满足条件就循环,否则就跳出循环)
// (3) 自增/自减 (计数)
while循环
// while(表达式){
// 执行语句;
// }
// 首先求解表达式,如果表达式成立,则执行对应的循环语句(while对应的{}内的语句), 本次循环执行完毕, 再次求解表达式, 依次类推知道条件不满足位置
while循环流程图:
do...while循环 => 不论条件成立与否, 先执行一次,再判断 (冲动型)
缺点: 即便条件不成立,也会执行一次
do{
执行语句;
}while(表达式);
while和do...while的区别:
while循环 => 先判断条件是否成立,条件成立再执行 (谨慎型)
do...while循环 => 不论条件成立与否, 先执行一次,再判断 (冲动型)
while和do...while选择:
一般情况下更倾向于使用while,
当while处理循环逻辑比较别扭时(先取值,在判断),使用do...while
while循环 => 先判断条件是否成立,条件成立再执行 (谨慎型)
while(表达式){
执行语句;
}
for循环
while循环 => 先判断条件是否成立,条件成立再执行 (谨慎型) while(表达式){ 执行语句; } for循环 for(初始值;循环条件;自增/自减){ 执行语句; }
for(初始值;循环条件;自增/自减){
执行语句;
}
while循环 和 for循环
while循环 每次循环执行完毕后继续求解表达式
for循环 每次循环执行完毕后,会先自增/自减,然后在求解表达式 (好处:将循环三要素整合到一起)
while循环和for循环选择:
一般情况下,已知循环次数(循环三要素都知道),用for循环
如果不知道循环次数, 可以选中while循环
while do...while for 之 常见死循环
while(true){
}
while(true);
do{
}while(true);
for(;;){
}
for(;;);
continue 循环关键词(仅能在循环中使用)
// (1) 跳过本次循环进入下一次循环
// (2) 位于continue之后的代码不执行(直接跳过本次,进入下一次)
break 循环的关键词
// (1) 在switch中使用时表示跳出当前的选择结构, 在循环中使用表示跳出当前循环结构,循环会被终止
// (2) 位于break关键词之后的语句 也不执行
// (3) 如果存在循环嵌套, 一个break语句只向外跳一层(使用break的该层循环)。
判断一个数是不是素数。(除了1和它本身以外不再有其他的除数整除。)
素数: 除了1和它本身以外不再有其他的除数除。 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
// 7 => 1 2 3 4 5 6 7 => 除了1和它本身以外 2 3 4 5 6 => 素数
// 9 => 1 2 3 4 5 6 7 8 9 => 除了1和它本身以外 2 3 4 5 6 7 8 => 3(合数=> 非素数)
函数封装概念:
函数就是把完成特定功能的一段代码抽象出来,使之成为程序中的一个独立实体,起个名字(函数名)。可以在同一个程序或其他程序中多次重复使用(通过函数名调用)。
函数的定义语法:
function 函数名(形参1,形参2,……){ // 函数可以没有形参
执行语句;
[return 返回值;] //函数可以没有返回值(默认返回undefined)
}
函数名();
函数封装的步骤:
1. 要对封装的代码/功能足够熟悉(先完成特定功能的代码)
2. 将完成特定功能的代码抽离出来, 放到函数中,并起一个函数名
3. 把可变参数提取为形式参数(可以理解为也是一个变量) => 形式参数是函数内和函数外交流的唯一入口 (可以接收函数调用时传入的实际值)
4. 确定函数的返回值 => 函数执行完毕之后返回的结果
5. 如何调用函数?
函数名()
关于形参(形式参数)和实参(实际参数)
形参(形式参数): 形式参数是函数内和函数外交流的唯一入口 => 可以理解成是一个变量(存储数据),用于接收函数调用时传入的实际值
实参(实际参数): 实际参数函数调用时实际传入的值
形参和实参一一对应
关于函数的返回值
(1) 返回值表示函数执行完毕之后返回的结果 => 可以由用户自行决定(根据需求)
(2) 如果不设置返回值, 函数默认返回undefined (此处缺少一个值)
(3) 返回值表示函数执行完毕之后返回的结果(函数的唯一出口) => 一个函数只能设置一个返回值, 表示函数执行完毕 => 位于return之后的语句不执行
关于函数调用:
(1) 对应函数名调用函数,如果函数不调用,函数中的代码不执行
(2) 每次调用函数都会执行函数中的上下文 (从函数function对应的{}中的第一行 一直执行到最后一行)
函数的创建方式
(1) 命名函数 (声明式创建) => 函数创建过程中定义函数名,
注意: 可以直接通过函数名获取函数, 函数不会被调用,输出的是函数封装的内容 (可以理解函数名类似变量名, 可以对应函数名查找该函数)
函数()
function sum(a,b){
return a + b;
}
console.log(sum);
var result = sum(1,2);
console.log(result);
(2) 匿名函数 => 函数在创建时没有添加函数名, 无法通过函数名调用该函数
匿名函数没有办法直接单独收声明
function(a,b){ // 语法有问题 => 需要函数名
return a + b;
}
(2.1) 赋值式 可以将匿名函数存储到变量 / 其他数据类型当中
var sum = function(a,b){
return a + b;
}
console.log(sum);
var result = sum(10,20);
console.log(result);
将匿名函数存储到数组
// 将匿名函数存储到数组
var arr = [1,2,function(a,b){
return a + b;
}];
console.log(arr[2])
var result = arr[2](100,200);
console.log(result);
将匿名函数作为属性值存储到对象
// 将匿名函数作为属性值存储到对象
var zhang = {
name:"张三",
age:18,
sum:function(a,b){
return a + b;
}
}
console.log(zhang.sum);
var result = zhang.sum(100,200);
console.log(result);
绑定点击事件 => 元素被点击后执行对应的函数
绑定点击事件 => 元素被点击后执行对应的函数
btn.onclick = function(){
}
(2.2) 自执行函数(自调用函数) => 匿名函数创建完毕之后立即调用, 此时匿名函数只能执行一次
(2) 自执行函数(自调用函数) => 匿名函数创建完毕之后立即调用, 此时匿名函数只能执行一次
var result = (function(a,b){
console.log("函数执行了",a,b);
return a + b;
})(10,20);
console.log(result);
3. 通过官方给定构造函数创建(了解) =>newFunction("a","b","return a + b");
注意: new Function执行过程中 最后一个实际参数是执行语句, 除最后参数以外的所有参数都是形式参数
通过构造函数创建对象的方式 => 实例化
被创建的对象也叫实例化对象
函数也是一个特殊的实例化对象
var sum = new Function("a","b","return a + b");
console.log(sum);
局部变量
(1) 在函数内通过var关键词声明的变量叫做局部变量
(2) 形参也是一个局部变量
特征
(1) 局部变量仅在函数内生效,对函数外没有任何影响
(2) 函数中的局部变量和方法仅在函数内有效, 当函数执行完毕,对应的局部变量和方法都会被释放, 下次函数调用时会重新生成
全局变量
(1) 在函数外(脚本中)外通过var关键词声明的变量叫做全局变量
(2) 不通过关键词直接声明的变量,也是全局变量, 缺点: 没有变量提升
作用域: 代码生效的区域
局部作用域(函数作用域) => 变量和方法仅在函数内有效
全局作用域: 自当前script标签开始的任何位置均可访问
局部变量和全局变量混用
对于函数而言: (1) 访问自己作用域内声明的变量 (2) 可以访问全局变量 => 先后顺序
函数内访问变量和方法的顺序
(1) 自己有先找自己的 (访问自己作用域内声明的变量)
(2) 自己没有,向外跳一层找父作用域(包裹函数的作用域 => 函数作用域/全局作用域)
(3) 依此类推, 全都找不到,就找全局作用域, 全局作用域也找不到=> 变量/方法还未定义
arguments 参数集合
(1) 函数的内置参数(所有的函数都有)
(2) 用于存储函数本次调用时,所有实际参数的集合
arguments特征(同数组)
(1) 有length属性,表长度(本次函数调用时,实际参数的个数)
(2) 可以通过下标取值和赋值 (下标的最大值 = arguments.length - 1)
如果取值取不到 默认返回undefined
(3) 可以循环遍历
作用: 参数不定项 ()
数组 => 一组数,一般放置一组相同类型的数(不同类型也可以)
如何创建数组?
(1) 字面量(直接量)创建 => 不经声明可以直接使用
var arr = [1,2,3];
console.log(arr);
var brr = []; // 空数组
console.log(brr);
(2) 构造函数创建 => Array官方提供专门创建数组的方法
(2) 构造函数创建 => Array官方提供专门创建数组的方法
var arr = new Array(1,2,3);
console.log(arr);
数组的特征:
(1) 有length属性,表长度(数组中元素的个数 元素/成员:组成数组的每项数据)
(2) 可以通过下标取值和赋值 (下标的最大值 = 数组.length - 1)
如果取值取一个不存在的下标 默认返回undefined
如对一个不存在的下标赋值, 会新增该数组到数组中,如果存在空余下标,会用,分隔预留位置
(3) 可以循环遍历
var list = [1,4,7,2,5,8,3,6,9];
console.log(list);
下标取值和赋值
console.log(list[3]);
list[3] = 22;
console.log(list);
console.log(list[100]);
list[100] = 100; // [1,4,7,2,5,8,3,6,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,....100];
console.log(list);
循环遍历:
for(var i=0;i<list.length;i++){
var item = list[i];
console.log(item);
}
数组的递增赋值
var list = [];
for (var i = 1; i <= 100; i++) {
if (i % 3 == 0 || i % 7 == 0) {
// 怎么存?
// list[0] = xxx;
// list[1] = xxx;
// list[2] = xxx;
// 数组的递增赋值
// 初始数组长度 len: 0
// list[list.length] = 3; // list[0] = 3; len:更新1
// list[list.length] = 6; // list[1] = 6; len:更新2
// (1) 数组中新增元素后, lenth也会自动更新
// (2) 下标的最大值 = 数组.length - 1 => 数组.length = 下标的最大值 + 1 => 在数组中所有元素的最后方新增元素
list[list.length] = i;
}
}
console.log(list);
JS代码从加载到执行经过了哪些流程?
1. 语法解析
=> 发生在代码执行之前,通篇排查脚本中是否存在语法错误(SyntaxError),如果有错,脚本将不会执行
2. 预编译(预解析)
=> 发生在代码执行之前,代码执行之前的准备工作
(1) 脚本执行之前的预编译 (全局作用域)
a. 变量提升 => 把(全局)变量声明提升到当前作用域最前面(当前脚本的最前面)
b. 确定函数体(函数体提升 => 把(命名)函数的整个函数声明提前)
(2) 函数执行之前的预编译 ()
a. 变量提升 => 把(局部)变量声明提升到当前作用域最前面(当前函数作用域的最前面)
b. 确定函数体(函数体提升 => 把(命名)函数的整个函数声明提前)
c. 把实际参数赋值给形式参数 => 形参和实参一一对应
3. 解释执行 => 代码自上而下依次执行
// 注意: 不通过关键词直接声明的变量,也是全局变量, 缺点: 没有变量提升
函数嵌套
// 嵌套调用(在函数内调用另一个函数)
// 嵌套封装(在函数内封装另一个函数)
// 注意:
// (1) 外层函数在执行过程中,调用内层函数
// (2) 外层函数需要等待内层函数执行完毕后,才会继续向后执行
// (3) 外层函数和内层函数都可以设置自己的返回值, 内层函数的返回值不会直接传递给外层