1、初识JavaScript
1.1、JavaScript是什么?
- HTML(骨架,元素,结构)
- CSS(表现,效果,状态)
- JS(响应行为,交互,复杂操作)
JavaScript 是一种运行在客户端(浏览器)的编程语言,它是一种基于原型、解释型、弱类型的脚本语言
- Javascript: 基于原型,对象(万物皆对象),原型可以指定,方法可灵活使用
- 弱类型/动态类型: 变量声明不需要规定类型,并且可以相互转换
- 解释型: 不需要在运行前编译,在运行程序的时候才翻译,不生成其他可执行文件,专门的解释器负责在每个语句执行的时候解释程序代码。这样解释型语言每执行一次就要翻译一次,效率比较低
1.2、JavaScript在客户端的三大组成
- ECMAScript,规定了JS基础语法核心知识,比如:变量、分支语句、循环语句、对象等等
- ES是JS的语法标准 JS是ES的具体实现
- DOM(Document Object Model)文档对象模型,操作文档,比如对页面元素进行移动、大小、添加删除等操作
- BOM(Browser Object Model)浏览器对象模型,操作浏览器,比如页面弹窗,检测窗口宽度、存储数据到浏览器等等
1.3、JavaScript作用
- 网页特效 (监听用户的一些行为让网页作出对应的反馈)
- 表单验证 (针对表单数据的合法性进行判断)
- 数据交互 (获取后台的数据, 渲染到前端)
- 服务端编程 (node.js)
权威网站:MDN
2、变量与数据类型
2.1、引入JavaScript
与CSS引入方式基本一致!
行内引入
<button onclick="alert('hello,js')">点我</button>
内部(内联)引入
<script>
// script标签内,编写JS代码
alert("hello,world!");
</script>
外部引入
<!-- 注:script必须成对出现 -->
<script src="./js/hello.js"></script>
注:此种方式引用,script标签中间无需写代码,否则会被忽略!
2.2、输入输出语句
输出和输入也可理解为人和计算机的交互,用户通过键盘、鼠标等向计算机输入信息,计算机处理后再展示结果给用户,这便是一次输入和输出的过程
输出语法
<script>
document.write('要显示的内容');
</script>
作用:向body内输出内容
注:如果输出的内容写的是标签,也会被解析成网页元素
<script>
alert('要显示的内容');
</script>
作用:页面弹出警告对话框
<script>
console.log('控制台打印');
</script>
作用:控制台输出语法,供程序员调试使用!
输入语法
<script>
let age = prompt('请输入你的芳龄:', '');
</script>
作用:显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字
2.3、基本语法
变量
变量是计算机中用来存储数据的“容器”,它可以让计算机变得有记忆,通俗的理解变量就是使用【某个符号】来代表【某个具体的数值】(数据)
声明(定义)变量有两部分构成:声明关键字、变量名(标识)
<script>
// let 变量名
// 声明(定义)变量有两部分构成:声明关键字、变量名(标识)
// let 即关键字,所谓关键字是系统提供的专门用来声明(定义)变量的词语
// name 即变量的名称,也叫标识符
let name
</script>
赋值
声明(定义)变量相当于创造了一个空的“容器”,通过赋值向这个容器中添加数据
<script>
// 赋值,将 小白 这个数据存入了 name 这个“容器”中
name = '小白'
// 这样 name 的值就成了 小白
document.write(name)
// 也可以声明和赋值同时进行
let str = 'hello world!'
alert(str)
</script>
更新变量
变量赋值后,还可以简单的给它一个不同的值来更新它
<script>
let balance = 1000
// 修改变量
balance = 1000 - 500
console.log(balance)
</script>
声明多个变量
语法:多个变量中间用逗号隔开
<script>
let uname = '小昭', upwd = '666666', uemail = 'xiaozhao@qq.com'
</script>
变量本质
变量本质:是程序在内存中开辟的一块用来存放数据的小空间
内存:计算机中存储数据的地方,相当于一个空间
变量命名规则与规范
规则:必须遵守,不遵守报错 (法律层面)
规范:建议,不遵守不会报错,但不符合业内通识 (道德层面)
规则:
- 不能用关键字
- 关键字:有特殊含义的字符,JavaScript 内置的一些英语词汇。例如:let、var、if、for等
- 只能用下划线、字母、数字、$组成,且数字不能开头(字下美元其后数)
- JavaScript严格区分大小写,如 Age 和 age 是不同的变量
规范:
- 见名知意
- 遵守驼峰命名法
- 第一个单词首字母小写,后面每个单词首字母大写。例:userName
<script>
// 声明变量
let a;
// 赋值
a = 55;
// 声明变量并赋值
let num = 30; // ES6(局部变量)
var text = 66; // ES5(全局变量)
console.log(num); // 在控制台打印变量
// 修改变量
text = 100;
alert(text);
// 一次声明多个变量
let a = 10, b = 20, c = 30;
console.log(a + "\t" + b + "\t" + c); // 10 20 30 (会自动转换为字符类型)
console.log(a, b, c); // 10 20 30
</script>
let 和 var 区别:
在较旧的JavaScript,使用关键字 var 来声明变量 ,而不是 let。 var 现在开发中一般不再使用它,只是我们可能再老版程序中看到它。 let 为了解决 var 的一些问题
var 声明:
- 可以先使用,再声明 (不合理)
- var 声明过的变量可以重复声明(不合理)
- 比如变量提升、全局变量、没有块级作用域等等
常量
概念:使用 const 声明的变量称为“常量”
使用场景:当某个变量永远不会改变的时候,就可以使用 const 来声明,而不是let
<script>
// 声明常量
const PI = 3.14;
console.log(PI);
</script>
注:常量不允许重新赋值,如果试图修改常量值会报错,而且声明的时候必须赋值(初始化)
2.4、数据类型
计算机世界中的万事万物都是数据!
计算机程序可以处理大量的数据,为了方便数据的管理,将数据分成了不同的类型
概念:数据类型可以更加充分和高效的利用内存,也更加方便程序员的使用数据
JS分为两大数据类型
基本数据类型:number 数值型、string 字符串、boolean 布尔类型、undefined 未定义、null 空引用
引用(复杂)数据类型:array 数组、object 对象、function 函数
number 数值
js中不区分小数和整数,里面的正数、负数、小数、整数等统称为数值类型!
123 // 整数
123.1 // 小数123.1
1.123e3 // 科学计数法
- 99 // 负数
NaN // not a number (NaN === NaN // false)
Infinity // 正无穷大
- Infinity // 负无穷大
注:JS 是弱数据类型,变量到底属于那种类型,只有赋值之后,我们才能确认
而Java是强数据类型,例如 int num = 3;此时的num变量存放的数据必须是整数!
数值可以有很多操作,比如,加法 + 、减法 -、乘法 * 、除法 / 等等,所以经常和算术运算符一起做运算!
- %:取模(取余数)开发中经常用来判断某个数字是否被整除
<script>
console.log(5 + 5) // 10
console.log(5 - 5) // 0
console.log(5 * 5) // 25
console.log(5 / 5) // 1
console.log(5 % 5) // 0
console.log((1 + 5) * 6) // 36
console.log(1 + 5 * 6) // 31
console.log(2 + 25 / 5 * 6) // 32
</script>
运算规则:遵循先乘除后加减,有括号先算括号里面的
NaN
NaN 代表一个计算错误。它是一个不正确的或者一个未定义的数学操作所得到的结果
<script>
console.log('小张' - 6); // NaN
console.log(NaN + 6); // NaN
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true
</script>
注:
- NaN 是粘性的。任何对 NaN 的操作都会返回 NaN
- NaN===NaN,NaN不等于任何值,包括它本身
- 只能通过isNaN(NaN)来判断这个数是否是NaN
isNaN():用于检查其参数是否是非数字(字符串)
string 字符串
通过单引号( ''
) 、双引号( ""
)或反引号包裹的数据都叫字符串,单引号和双引号没有本质上的区别,推荐使用单引号
<script>
let a = 'hello world'; // 使用单引号
let b = "hello world"; // 使用双引号
let c = `hello world`; // 使用反引号(模板字符串)
let str = ''; // 该种情况叫做 空字符串(空串)
console.log(a, b, c,str);
console.log('他,会"魔法"吗?'); // 外单里双
console.log("他,会'魔法'吗?"); // 外双里单
console.log('他,会\'魔法\'吗?'); // 使用 \ 转义
console.log("他,会\"魔法\"吗?"); // 使用 \ 转义
</script>
注:
- 无论单引号或是双引号必须成对使用
- 单引号/双引号可以互相嵌套,但是不以自已嵌套自已(口诀:外双里单,或者外单里双)
- 必要时可以使用转义符
\
,输出单引号或双引号
字符串拼接
场景: + 运算符 可以实现字符串的拼接
口诀:数字相加,字符相连,单/双引号里面写++,++里面写变量
<script>
// 字符串拼接
let surname = 'zhao'
console.log(6 + 6); // 12
console.log('Mr' + surname); // Mrzhao
console.log('number' + ' one'); // number one
console.log('number' + 1); // number1
</script>
模板字符串(ES6)
使用场景
- 拼接字符串和变量,在没有它之前,要拼接变量比较麻烦
<script>
let id = 1001
let uname = '小张'
console.log('https://www.hua.com/?id=' + id + '&uname=' + uname);
</script>
使用模板字符串, 可以让我们拼接字符串更简便!
语法:
- `` (反引号)
- 内容拼接变量时,用
${}
包住变量
<script>
let id = 1001
let uname = '小张'
console.log(`https://www.hua.com/?id=${id}&uname=${uname}`);
</script>
boolean 布尔型
表示肯定或否定,在计算机中对应的是布尔类型数据,它有两个固定的值 true
和 false
,表示肯定的数据用 true
,表示否定的数据用 false
<script>
let isLove = true;
console.log('你爱我吗?' + isLove); // 你爱我吗?true
console.log(100 > 110); // false
</script>
undefined 未定义
未定义是比较特殊的类型,只有一个值 undefined
在什么情况出现未定义类型?
只声明变量,不赋值的情况下,变量的默认值为 undefined,一般很少【直接】为某个变量赋值为 undefined
<script>
/*
1.声明变量未赋值就是undefined
2.undefined === undefined // true
3.千万不要从undefined // 身上读取任何属性,否则会报错
4. 对象中访问不存在的属性,返回undefined
*/
let a; // 声明变量但未赋值
console.log(a); // undefined
</script>
应用场景:
我们平时开发中经常声明一个变量,等待传送过来的数据。如果我们不知道这个数据是否传递过来,此时我们可以通过检测这个变量是不是undefined,就可以判断用户是否有数据传递过来!
注:undefined做任何操作都是返回NaN,除了拼接字符串!
null 空引用
JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值
<script>
let obj = null
console.log(obj); // null
// typeof() 判断括号里面的是什么数据类型
console.log(typeof(null)); // obj
</script>
null 和 undefined 区别:
- undefined 表示没有赋值
- null 表示赋值了,但是内容为空
<script>
console.log(undefined + 66); // NaN
console.log(null + 66); // 66
</script>
应用场景:将来有个变量存放的是一个对象,但是对象还未创建好,这个时候可以先给个null
注:null经过数字转换之后会变为0
数组
数组 (Array):将一组数据存储在单个变量名下的优雅方式
let arr = [66,77,88,'world',null,true]; // arr[2] 88
- 注:数组可以存储任意类型的数据(js特有)
对象
对象是大括号{},数组是中括号[]
每个属性之间使用逗号隔开,最后一个不需要添加
let person = {
name:'张三',
age:18,
tags:['Java','C++','WEB']
}
取对象的值:person.属性
typeof 检测数据类型
通过 typeof 关键字检测数据类型
typeof 运算符可以返回被检测的数据类型。它支持两种语法形式:
- 作为运算符: typeof x (常用写法)
- 函数形式: typeof(x)
<script>
let num = 18
let str = '小赵'
let flag = true
let a
let obj = null
console.log(typeof num); // number
console.log(typeof str); // string
console.log(typeof flag); // boolean
console.log(typeof a); // undefined
console.log(typeof obj); // object
console.log(typeof null); // object
console.log(typeof NaN); // number
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof function(){}); // function
</script>
2.5、数据类型的转换
为什么需要数据类型转换?
从用户得到的数据都是字符串,而字符串和数字相加会变成相连
隐式转换
某些运算符被执行时,系统内部会自动将数据类型进行转换,这种转换称为隐式转换
<script>
let a = 100; // 数值
let b = '10'; // 字符串
// 结果:10010
// 原因是将数值 a 转换成了字符串,相当于 '100'
// 然后 + 将两个字符串拼接到了一起
console.log(a + b);
// 结果:90
// 原因是将字符串 b 转换成了数值,相当于 10
// 然后数值 100 减去 数值 10
console.log(a - b);
</script>
经验:
- +号作为正号解析可以转换成数字型
- 任何数据和字符串相加结果都是字符串
- 除了+以外的算术运算符,比如:- * / 等都会把数据转成数字类型
缺点:转换类型不明确,靠经验才能总结!
显示转换
在编写程序时,过度依靠系统内部的隐式转换是不严谨的,因为隐式转换规律并不清晰,大多是靠经验总结的规律。为了避免因隐式转换带来的问题,通常根据逻辑需要对数据进行显示转换
转换为数值型
-
Number(数据)
- 转成数值类型
- 如果字符串内容里有非数字,通过
Number
显示转换成数值类型,当转换失败时结果为NaN
(Not a Number)即不是一个数字 - NaN也是number类型的数据,代表非数字
-
parse系列
-
parseInt(数据)
- 只保留整数
-
parseFloat(数据)
- 可以保留小数
-
<script>
console.log(Number('123')); // 123
console.log(Number('a123')); // NaN
console.log(Number('123a')); // NaN
console.log(parseInt('20.96px')) // 20
console.log(parseInt('20.55px')) // 20
console.log(parseInt('20ad.8px')) // 20
console.log(parseInt('ad20.8px')) // NaN
console.log(parseFloat('20.96px')) // 20.96
console.log(parseFloat('20.55px')) // 20.55
console.log(parseFloat('20.ab8px')) // 20
console.log(parseFloat('2ac0.ab8px')) // 2
console.log(parseFloat('ac20.ab8px')) // NaN
</script>
转换为字符串
- String(数据)
- 变量.toString(进制)
<script>
let num = '66'; // 字符串
let str = 'hello'; // 字符串
// 显式将字符串 66 转换成数值 66
num = Number(num);
// 检测转换后的类型
console.log(typeof num); // number
console.log(num);
// 并不是所有的值都可以被转成数值类型,将 hello 转成数值是不现实的
// 当无法转换成数值时,得到的结果为 NaN (Not a Number)
console.log(parseInt(str)); // NaN
let sum = 99; // 数值
// 显式将数值 99 转换成字符串 99
sum = String(sum);
// 检测转换后的类型
console.log(typeof (sum)); // string
console.log(sum);
</script>
3、流程控制
3.1、运算符
赋值运算符
何为赋值运算符?对变量进行赋值的运算符
- 将等号右边的值赋予给左边,要求左边必须是一个变量
- 其他赋值运算符:
+= -= *= /= %=
<script>
let num = 20
num += 10 // num = num + 10
console.log(num); // 30
</script>
一元运算符
众多的 JavaScript 的运算符可以根据所需表达式的个数,分为一元运算符、二元运算符、三元运算符
二元运算符:需要两个操作数
例:let num = 20 + 20
一元运算符: 只需要一个操作数
例: 正负号
自增:
- 符号:++
- 作用:让变量的值 +1
自减:
- 符号:–
- 作用:让变量的值 -1
应用场景:通常用于计数来使用!
前置自增
<script>
// 前置自增
let num = 5;
++num
console.log(num); // 6
</script>
后置自增
<script>
// 后置自增
let num = 5;
num++
console.log(num); // 6
</script>
每执行1次,当前变量数值加1,其作用相当于 num += 1
两者区别:
- 前置自增和后置自增单独使用没有任何区别!但两者参与运算就有区别了
- 前置自增:先自加再使用(++在前执行时先+1)
- 后置自增:先使用再自加(++在后执行过后+1)
<script>
let i = 6;
// i先自加1,变为7之后,在和2相加
console.log(++i + 2); // 9
let n = 6;
// n先和2相加,运算输出完毕后,n在自加1
console.log(n++ + 2); // 8
let s = 6;
// 6 ---> 7 ++s --->8+8
console.log(s++ + ++s + s); // 6 + 8 + 8 = 22
</script>
算数运算符
+ - * / %
比较运算符
应用场景:比较两个数据大小、是否相等!
比较运算符:> < >= <= != == === !==
比较运算符返回的结果为boolean类型,即只会得到 true 或 false
<script>
console.log(10 > 5); // true
console.log(10 < 5); // false
console.log(10 >= 10); // true
console.log(10 <= 5); // false
console.log(10 != 5); // true
// 比较运算符有隐式转换,会把 字符串'66'数值 转为 66
console.log(66 == '66'); // true 只判断两者值是否相等
console.log(66 === '66'); // false // 两者值相等,但数据类型不一样
</script>
=、==、===三者区别:
- = 单等是赋值
- == 是判断,(类型不一样,值一样,也会判断为true)
- === 是全等(类型一样,值一样,结果才为true)
经验:在开发中判断是否相等,通常会使用===
在js中尽量避免使用小数比较,存在精度问题!
逻辑运算符
- && 两个都为真,结果为真,只要一个为假,结果为假
- || 一个为真,结果为真
- ! 真即假,假即真
<script>
// 逻辑与 一假即假
console.log(true && true); // true
console.log(5 > 6 && 5 < 6); // false
console.log(3 < 10 && 10 > 3); // true
// 逻辑或 一真即真
console.log(false || true); // true
console.log(false || false); // false
// 逻辑非 真即假 假即真
console.log(!true); // false
console.log(!false); // true
</script>
逻辑运算符里的短路
短路:只存在于 && 和 || 中,当满足一定条件会让右边代码不执行
符号 | 短路条件 |
---|---|
&& | 左边为false就短路 |
|| | 左边为true就短路 |
原因:通过左边能得到整个式子的结果,因此没必要再判断右边
<script>
let num = 6
// 由于逻辑与 一假则假 后面的表达式不在执行 所以num 并没有 +1
console.log(false && num++);
console.log(num); // 6
// 由于逻辑或 一真则真 后面的表达式不在执行 所以num 还是没有 +1
console.log(true || num++)
console.log(num); // 6
console.log(22 && 66); // 都是真,则返回最后一个真值 66
console.log(22 || 66); // 输出第一个真值 22
</script>
运算符优先级
3.2、表达式和语句
何为表达式?表达式是可以被求值的代码,JavaScript 引擎会将其计算出一个结果
何为语句?语句是一段可以执行的代码,比如: prompt() 可以弹出一个输入框,还有 if语句 for 循环语句等等
两者区别:
- 因为表达式会计算出一个值,所以需要将它赋值给左边的变量
- 而语句不一定有值,所以比如alert() for和break等语句就不能被用于赋值
3.3、程序三大流程控制语句
顺序结构
是三大流程控制语句中最简单的结构:代码从上到下依次执行
选择结构
If分支语句
选择结构用于判断给定的条件,根据判断的结果来控制程序的流程
if语句有三种使用:单分支、双分支、多分支
单分支:
<script>
let score = 90;
if (score > 80) {
alert("不错");
}
// 除了 0 其他数字都为真
if (-12) {
console.log('条件成立');
}
// 除了 '' 其他字符串都为真
if ('a') {
console.log('条件成立');
}
</script>
双分支:二选一
<script>
let score = 80;
if (score > 80) {
alert("不错");
} else {
alert('放学别走');
}
</script>
多分支:多选一
<script>
let score = prompt('请输入成绩')
if (score >= 90) {
console.log('优秀');
} else if (score >= 70) {
console.log('良好');
} else if (score >= 60) {
console.log('及格');
} else {
console.log('不及格');
}
</script>
多分支:
- 括号内的条件为true时,进入大括号里执行代码
- 小括号内的结果若不是布尔类型时,会发生隐式转换转为布尔类型
- 如果大括号只有一个语句,大括号可以省略,但是,不提倡这么做!
三元运算符
应用场景:简化if双分支,一般用来赋值
具体语法:条件 ? 满足条件执行代码 : 不满足条件执行代码
<script>
// 用户输入2个数 利用三元运算符输出最大值
let num1 = +prompt('请输入第一个数:')
let num2 = +prompt('请输入第二个数:')
// 100 1000
alert(num1 > num2 ? num1 : num2)
</script>
switch 语句
<script>
// 需求:用户输入2个数字,然后输入 + - * / 任何一个,可以计算结果
let num1 = +prompt('请输入第一个数:')
let num2 = +prompt('请输入第二个数:')
let operator = prompt('请输入操作符:')
switch (operator) {
case '+':
alert(`${num1} + ${num2}= ${num1 + num2}`)
break;
case '-':
alert(`${num1} - ${num2}= ${num1 - num2}`)
break;
case '*':
alert(`${num1} * ${num2}= ${num1 * num2}`)
break;
case '/':
alert(`${num1} / ${num2}= ${num1 / num2}`)
break;
default:
alert('请输入正确的操作符!')
}
</script>
注:
- 若没有全等
===
的则执行default里的代码 - switch case一般需要配合break关键字使用,如果没有break会造成case穿透
- switch case语句一般用于等值判断,不适合于区间判断
- if分支既可以等值判断也可以区间判断,但它更适合区间判断!
- 当分支较少时,if…else语句执行效率高
- 当分支较多时,switch语句执行效率高,且结构更清晰
循环结构
循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构
while
<script>
// 小括号 循环条件
let i = 1 // 初始变量
while (i <= 10) {
console.log('好好学习,天天向上') // 循环语句
i++ // 迭代变量
}
// 计算从1~100的偶数和
let n = 1
let sum = 0 // 记录偶数和
while (n <= 100) {
if (n % 2 === 0) {
sum += n
}
n++
}
console.log(`1~100偶数和为:${sum}`)
</script>
- while大括号里代码执行完毕后不会跳出,而是继续回到小括号里判断条件是否满足,若满足又执行大括号里的代码,然后再回到小括号判断条件,直到括号内条件不满足,即跳出
- while循环适合在循环次数不确定的情况下使用!
循环三要素:
- 变量起始值
- 终止条件(没有终止条件,循环会一直执行,造成死循环)
- 迭代变量(用自增或者自减)
任何循环结构都需要上述三要素,如果没有,可能会出现死循环!
循环退出
- break:退出循环
- continue:结束本次循环,继续下次循环
两者区别:
- continue 跳出本次循环,继续下一次循环,一般用于排除或者跳过某一个选项的时候,可以使用continue(一旦遇到continue,直接奔向i++)
- break 退出整个循环,一般用于结果已经得到,后续的循环不需要的时候可以使用(一旦遇到break,直接跳出循环体)
do-while循环
<script>
let i = 1; // 初始变量
do {
console.log('好好学习,天天向上'); // 循环语句
i++; // 迭代变量
} while (i <= 10); // 循环条件
let n = 1;
let sum = 0; // 记录偶数和
do {
if (n % 2 === 0) {
sum += n;
}
n++;
} while (n <= 100);
console.log(`1~100偶数和为:${sum}`);
</script>
while与do-while区别:
- while先判断,后执行,有可能一次也不会执行
- do-while先执行,后判断,至少执行一次
for循环
<script>
// i = 1 初始变量
// i <= 循环条件
// i++ 迭代变量
for (let i = 1; i <= 10; i++) {
console.log('好好学习,天天向上') // 循环语句
}
let sum = 0 // 记录偶数和
for (let n = 1; n <= 100; n++) {
if (n % 2 === 0) {
sum += n
}
}
console.log(`1~100偶数和为:${sum}`)
</script>
优点:把声明起始值、循环条件、变化值写到一起,让人一目了然,它是最常使用的循环形式
小技巧:
- while(true) 来构造“无限”循环,需要使用break退出循环
- for(;😉 也可以来构造“无限”循环,同样需要使用break退出循环
while与for的应用场景:
- for循环一般用在循环次数确定的情况下使用!
- while循环一般用在循环次数不太确定的情况下使用!
for循环嵌套
所谓循环嵌套顾名思义就是一个循环里再套一个循环,一般用在for循环里
<script>
let row = +prompt('请输入行:')
let col = +prompt('请输入列:')
for (let x = 1; x <= row; x++) {
for (let y = 1; y <= col; y++) {
document.write('☆')
}
document.write('<br>') // 换行
}
// 九九乘法表
for (let i = 1; i <= 9; i++) {
for (let j = 1; j <= i; j++) {
document.write(`<span>${j}*${i}=${i * j}</span>`)
}
document.write('<br>') // 换行
}
</script>
- 外层循环循环一次,内层循环循环一遍(从头到尾)
断点调试
作用:学习时可以帮助更好的理解代码运行,工作时可以更快找到bug,主要用来调试程序用的
浏览器打开调试界面
- 按F12打开开发者工具
- 点到
sources
一栏 - 选择代码文件
断点:在某句代码上加的标记就叫断点,当程序执行到这句有标记的代码时会暂停下来
4、数组
4.1、何为数组
数组:(Array)是一种可以按顺序保存数据的数据类型
为什么需要数组?
变量一次只能存储单个值,而数组可以一次存储多个值,如果需要保存一个班里所有同学的姓名,这个时候就可以用到数组
<script>
// 方式一:new Array 构造函数声明
let array = new Array(10, 20, 30, 40, 50, 60)
console.log(array);
// 方式二:定义数组(常用)
let clazz = ['Amy', 'Jack', 'Tom', 'Jerry', 'DaMing', '...']
// 遍历数组中的元素
for (let i = 0; i < clazz.length; i++) {
console.log(clazz[i]);
}
console.log(clazz[10]); // undefined
// 需求:求数组元素的和以及平均值
let sum = 0 // 求和
let array = [2, 4, 6, 7, 8]
for (let i = 0; i < array.length; i++) {
sum += array[i]
}
let avg = sum / array.length // 求平均值
console.log(`数组元素中的和是:${sum}`); // 27
console.log(`数组元素中的平均值是:${avg}`); // 5.4
// 求数组 [1,6,8,12,22,96,100,110,366] 中的最大值及最小值 打擂台
let array = [6, 8, 5, 22, 96, 12, 100, 110, 366]
let max = array[0] // 默认数组中的第一个数为最大值
let min = array[0] // 默认数组中的第一个数为最小值
// 因为默认第一个值为最大/小值,所以数组第一个元素就不需要在比较了
for (let i = 1; i < array.length; i++) {
// 如果 max 小于 数组中的元素 那么此时说明 max 已经不是最大了,需要把数组的元素赋值给 max
if (max < array[i]) {
max = array[i]
}
// 如果 min 比 数组中的元素大,那么此时说明数组中的元素比我小 需要把数组中的元素给 min(把小的给我)
if (min > array[i]) {
min = array[i]
}
}
console.log(`数组中最大值是:${max}`);
console.log(`数组中最小值是:${min}`);
</script>
- 数组是按顺序保存,所以每个数据都有自己的编号,数据的编号也叫索引或下标
- 通过下标取数据
- 取出来数据是什么类型的,就根据这种类型特点来访问即可
术语:
- 元素:数组中保存的每个数据都叫数组元素
- 下标:数组中数据的编号
- 长度:数组中数据的个数,通过数组的length属性获得
注:取数组下标,如果越界了,就会出现undefined
4.2、操作数组
数组本质是数据集合,操作数据无非就是增、删、改、查
新增
push
数组.push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度(压入尾部)
<script>
let fruits = ['苹果', '橘子', '葡萄']
// 添加到元素末尾
fruits.push('香蕉', '白桃')
console.log(fruits);
// 需求:将数组 [25,36,99,86,72,95,82,66,33,100] 中大于等于 80 的元素选出来,放入新数组
let arr = [25, 36, 99, 86, 72, 95, 82, 66, 33, 100]
// 声明空数组
let newArr = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] > 80) {
// 满足条件 追加给新数组
newArr.push(arr[i])
}
}
// 输出新数组
console.log(newArr);
</script>
unshift
arr.unshift(新增的内容) 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(压入头部)
<script>
let fruits = ['苹果', '橘子', '葡萄']
// 数组开头追加
let arrSize = fruits.unshift('火龙果', '橙子')
console.log(fruits)
console.log(arrSize) // 5
</script>
删除
pop
数组. pop() 方法从数组中删除最后一个元素,并返回该元素的值(弹出尾部)
<script>
let fruits = ['苹果', '橘子', '葡萄']
// 从尾部移除
fruits.pop()
console.log(fruits); // 苹果 橘子
</script>
shift
数组. shift() 方法从数组中删除第一个元素,并返回该元素的值(弹出头部)
<script>
let fruits = ['苹果', '橘子', '葡萄'];
// 数组开头移除
console.log(fruits.shift()); // 苹果
console.log(fruits); // 橘子 葡萄
</script>
splice
数组. splice() 方法删除指定元素
具体语法arr.splice(start,deleter)
- start 起始位置: 从那个位置开始删(索引号从0开始)
- deleteCount:表示要移除的数组元素的个数,可选值。 如果省略则默认从指定的起始位置删除到最后
<script>
let arr = [10, 20, 30, 40, 50, 60, 70, 80]
// 从数组第三个元素删 删除 2 个元素
arr.splice(2, 2)
console.log(arr) // 10 20 50 60 70 80
// 从数组中第二个元素删 一直删到最后
arr.splice(1)
console.log(arr) // 10
</script>
修改
<script>
let fruits = ['苹果', '橘子', '葡萄']
console.log(fruits);
fruits[0] = '火龙果' // 修改
console.log(fruits);
for (let i = 0; i < fruits.length; i++) {
fruits[i] = fruits[i] + '我的最爱!' // 批量修改
}
console.log(fruits);
</script>
- 所谓修改就是给数组重新赋值,通过
数组[下标] = 新值
的方式修改数组元素中的值
查询
- 所谓查询就是通过
数组[下标]
的方式来访问数组元素,上面已经写过
排序
数组. sort() 方法可以对数组的元素进行排序,并返回数组
<script>
let arr = [65, 55, 1, 9, 100, 88, 35]
// 排序 升序排列
arr.sort(function (a, b) {
return a - b
})
// 降序排列
arr.sort(function (a, b) {
return b - a
})
console.log(arr)
</script>
翻转
数组. reverse() 方法可以将数组中元素的位置颠倒,并返回该数组
<script>
let letters = ['A', 'B', 'C']
letters.reverse()
console.log(letters); // C B A
</script>
冒泡排序
<script>
let arr = [65, 95, 100, 33, 28, 69]
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
// 开始交互位置 前提条件 第一个数大于第二个数
if (arr[j] > arr[j + 1]) {
// 交互两个变量
let temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
console.log(arr);
</script>
- 冒泡排序(Bubble Sort)是一种简单的排序算法
- 它重复地走访过要排序的数列,依次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行,直到没有再需要交换,也就是说该数列已经排序完成
- 这个算法的名字由来是因为越小的元素会经过交换慢慢“浮”到数列的顶端
5、函数
4.1、何为函数
函数:函数是执行特定任务的代码块,对内部的代码块进行封装
目的:实现代码复用,提高开发效率
4.2、函数定义及使用
函数的声明及调用语法:
<script>
// 声明函数
function 函数名(){
// 函数体
}
// 调用函数,函数体内的代码逻辑会被执行
函数名()
function sayHello() {
console.log('say Hello');
}
// 调用
sayHello()
// 计算两个数的和
function getSum() {
let a = 20
let b = 60
console.log(a + b);
}
// 调用
getSum()
</script>
注:声明(定义)的函数必须调用才会真正被执行,使用 () 调用函数,且函数可以调用多次!
4.3、函数传参
为什么需要函数传参?如果没有函数传参,那么函数局限性非常大
- 如果函数完成功能需要调用者传入数据,那么就需要用到有参数的函数
- 提高程序的灵活性(锦上添花)
函数传参语法:
<script>
// 声明函数 形参
function 函数名(参数列表){
// 函数体
}
// 调用
函数名(实参)
// 求 任意数 累加和
function getSum(begin, end) {
let sum = 0
for (let i = begin; i <= end; i++) {
sum += i
}
console.log(`${begin}到${end}之和为:${sum}`);
}
getSum(1, 100) // 1~100累加值
getSum(200, 210) // 200~210累加值
</script>
- 形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数)
- 实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)
参数默认值
形参: 可以看做变量,但是如果一个变量不给值,默认是什么?undefined
如果用户不传递参数,可以给形参默认值,使程序更为严谨!
<script>
function getSum(a = 0, b = 0) {
console.log(a + b);
}
getSum(6, 6) // 12
getSum() // 0
</script>
4.4、函数返回值
为什么需要函数返回值?函数执行完特定任务之后,需要把结果给调用者,而不是内部处理
函数返回值语法:
<script>
function 函数名(形参列表){
// 函数体
// 返回值
return '数据'
}
// 调用
let 结果 = 函数名(实参列表)
// 求 两数 最大值
function getMax(m = 0, n = 0) {
return m > n ? m : n
}
let res = getMax(500, 299)
console.log(res) // 500
// 求数组中的最大值并返回这个最大值
function getArrMax(arr = []) {
let max = arr[0]
for (let i = 1; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i]
}
}
return max
}
let max = getArrMax([99, 888, 77, 666, 15])
console.log(max); // 888
</script>
- 当调用某个函数,这个函数会返回一个结果出来,这就是有返回值的函数
- 更加提高程序的灵活性(如虎添翼)
注:
- 在函数体中使用 return 关键字能将内部的执行结果交给函数外部使用
- return 后面代码不会再被执行,会立即结束当前函数,所以请不要将需要返回的数据写在 return “后面”!
- return函数可以没有 return,这种情况函数默认返回值为 undefined
4.5、作用域
全局作用域
全局有效:作用于所有代码执行的环境(整个 script 标签内部) 或者一个独立的 js 文件
局部作用域
局部有效:作用于函数内的代码环境,就是 局部作用域。 因为跟函数有关系, 所以也称为函数作用域
作用:作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突
在JavaScript中,根据作用域的不同,变量可以分为:
全局变量
函数外部let 的变量,全局变量在任何区域都可以访问和修改
局部变量
函数内部let的变量,局部变量只能在当前函数内部访问和修改
函数访问原则:在能够访问到的情况下先局部, 局部没有在找全局(就近原则)
注:
- 函数内部的形参也可以看做是局部变量
4.6、匿名函数
函数分为具名函数和匿名函数
所谓匿名函数就是没有名字的函数
<script>
// 具名函数
function 函数名() {
// 函数体
}
// 匿名函数
function(){
}
</script>
注:匿名函数无法直接使用!
函数表达式
将匿名函数赋值给一个变量,并且通过变量名称进行调用,我们将这个称为函数表达式
<script>
// 函数表达式
let res = function (x = 0, y = 0) {
return x + y
}
console.log(res);
console.log(res(5, 5)); // 10
</script>
具名函数与函数表达式区别:
- 具名函数的调用可以写到任何位置
- 函数表达式,必须先声明表达式,后调用
立即执行函数
立即执行函数无需调用,立即执行,其实本质已经调用了
应用场景:避免全局变量之间的污染
具体语法:
<script>
(function () { }());
(function () { })();
// 方式一
(function () {
let num = 10
console.log(num) // 10
}());
// 方式二
(function () {
let num = 20
console.log(num) // 20
})();
(function (x = 1, y = 1) {
console.log(x * y);
})(6, 6) // 36
// 立即执行,不需要调用
</script>
注: 多个立即执行函数必须要用;
隔开,否则会报错!
6、对象
6.1、何为对象?
对象(object):JavaScript里的一种数据类型
可以理解为是一种无序的数据集合
用来描述某个事物,例如描述一个人
- 人有姓名、年龄、性别等信息、还有吃饭睡觉打代码等功能
- 如果用多个变量保存则比较散,用对象比较统一
6.2、对象定义及使用
定义
对象声明具体语法:
<script>
let 对象名 = {}
let 对象名 = new Object()
// 声明了一个用户对象
let user = {}
</script>
对象有属性和方法组成
- 属性:信息或叫特征(名词)。 比如 用户姓名、年龄、邮箱等
- 方法:功能或叫行为(动词)。 比如 唱歌、跳舞、购物…
<script>
let 对象名 = {
属性名:属性值,
属性名:属性值,
方法名:函数
...
}
let user = {
name: '小白',
age: 18,
email: 'xiaobai@qq.com',
sing: function () {
console.log('I can sing!');
}
}
</script>
注:
- 属性都是成对出现的,包括属性名和值,它们之间使用英文
:
分隔 - 多个属性之间使用英文
,
分隔,最后一个属性或方法可以省略,
使用
对象本质是无序的数据集合,操作数据无非就是增、删、改、查
查询
声明对象,并添加了若干属性后,可以使用.
获得对象中属性对应的值,称之为属性访问
具体语法:对象名.属性
| 对象名['属性名']
<script>
let user = {
name: '小白',
age: 18,
email: 'xiaobai@qq.com',
sing: function (name) {
console.log(`${name} is sing!`);
}
}
console.log(user.name); // 小白
console.log(user.age); // 18
// 对象名.方法(形参列表)
user.sing('Mr zhao') // Mr zhao is sing!
</script>
对于多词属性或者 - 等属性,点操作就不能用了
解决方案: 对象[‘属性’] 方式, 单引号和双引号均可
<script>
let user = {
'user-name': '小白',
age: 18,
email: 'xiaobai@qq.com',
sing: function () {
console.log('I can sing!');
}
}
console.log(user.user - name); // NaN
console.log(user['user-name']); // 小白
// 查询对象中是否有该属性
console.log(user.hasOwnProperty('age')); // true
console.log(user.hasOwnProperty('address')); // false
</script>
修改
具体语法:对象名.属性 = 新值
<script>
let user = {
name: '小白',
age: 18,
email: 'xiaobai@qq.com',
sing: function () {
console.log('I can sing!');
}
}
user.name = '小黑'
console.log(user.name); // 小黑
</script>
增加
具体语法:对象名.新属性 = 新值
<script>
let user = {
name: '小白',
age: 18,
email: 'xiaobai@qq.com',
sing: function () {
console.log('I can sing!');
}
}
user.hobbies = ['basketball', 'code', 'gril']
console.log(user.hobbies); // 'basketball', 'code', 'gril'
</script>
注:对象中如果没有该属性为新增,否则则为修改!
删除
具体语法:delete 对象名.新属性
<script>
let user = {
name: '小白',
age: 18,
email: 'xiaobai@qq.com',
sing: function () {
console.log('I can sing!');
}
}
delete user.email
console.log(user.email); // undefined
</script>
6.3、遍历对象
<script>
let goods = {
'g-name': 'Vue.js设计与实现(图灵出品)',
price: 89.8,
weight: '0.95kg',
info: '基于Vue.js3深入解析Vue.js设计细节,本书提供源代码下载'
}
for (const key in goods) {
console.log(key); // 属性名 'g-name' 'price' 'weight' 'info'
console.log(goods[key]);
}
</script>
- for in语法中的 key 是一个变量,在循环的过程中依次代表对象的属性名
- 由于 key 是变量,所以必须使用 [ ] 语法解析
- key 是获得对象的属性名, 对象名[key] 是获得属性值
对象数组
<script>
let users = [
{ name: '小张', age: 18, gender: '男', address: '河南郑州' },
{ name: '小白', age: 19, gender: '女', address: '河南洛阳' },
{ name: '小黑', age: 17, gender: '男', address: '湖北武汉' },
{ name: '小昭', age: 18, gender: '女', address: '北京海淀' }
]
for (let i = 0; i < users.length; i++) {
console.log(i); // 下标
console.log(users[i]); // 每一个对象
console.log(users[i].age); // 每个对象的年龄
}
</script>
6.4、内置对象
所谓内置对象,就是JavaScript内部提供的对象,包含各种属性和方法供开发者调用
document.write() console.log() ...
数学内置对象
Math对象是JavaScript提供的一个“数学”对象
作用:提供了一系列做数学运算的方法
Math对象包含的方法有:
- ceil():向上取整
- floor():向下取整
- round():四舍五入
- max():求最大值
- min():求最小值
- abs:绝对值
- random:生成0-1之间的随机数(包含0不包括1
左闭右开
)
<script>
console.log(Math.ceil(25.01)); // 26
console.log(Math.floor(25.99)); // 25
console.log(Math.round(25.49)); // 25
console.log(Math.max(100, 999, 66, 88, 22, 11)); // 999
console.log(Math.min(100, 999, 66, 88, 22, 11)); // 11
console.log(Math.abs(-6)); // 6
</script>
随机数函数
如何生成0-10的随机数呢?
Math.floor(Math.random() * (10 + 1))
<script>
// 0~10之间随机数
let random = Math.floor(Math.random() * 11)
console.log(random)
// 随机显示数组中的名字
let arr = ['小白', '小张', '小赵', '夏琳', '小昭', '晓明', '小黑']
let ran = Math.floor(Math.random() * arr.length)
document.write(arr[ran])
</script>
如何生成N-M之间的随机数?
Math.floor(Math.random() * (M - N + 1)) + N
<script>
// 生成6位随机数
let ran = Math.floor(Math.random() * (999999 - 100000 + 1) + 100000)
console.log(ran);
</script>
基本数据类型和引用数据类型
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型
值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
- number、string、boolean、undefined、null
引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
- 通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等
堆栈空间分配区别:
- 栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈
- 简单数据类型存放到栈里面
- 堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收
- 引用数据类型存放到堆里面