往期文章
目录
1 JS书写位置
1.1 script标签
script标签作用
使用<script>标签将 JavaScript程序插入到 HTML 文档的任何位置。
<script>属性
<script type=…>:通常情况下,我们会将<script>标签的type属性设置为"text/javascript",表示该脚本内容是JavaScript代码。该属性如今变得可选了。如果省略了type属性,浏览器会默认将其解析为 JavaScript 代码
<script src="...">:src属性用于指定外部 JavaScript 文件的 URL 地址,从而将该文件引入到当前的 HTML 页面中。这样做的作用是可以将 JavaScript 代码与 HTML 内容分离,使代码更加模块化和可维护。
1.2 三种书写位置
1.2.1 行内JS
可以将单行或少量 JS 代码写在HTML标签的事件属性中( 以 on 开头的属性)
例子
注意单双引号的使用:在HTML中我们推荐使用双引号, JS 中我们推荐使用单引号
onsubmit事件属性:form表单事件,当用户点击提交按钮时触发
<!-- onsubmit属性 -->
<form onsubmit="alert('Hello, World!');">
<input type="text" name="username">
<input type="submit" value="提交">
</form>
onclick事件属性:当用户点击时触发
<!-- onclick属性 -->
<input type="button" value="点我试试" onclick="alert('Hello World')" />
onload事件属性:在对象已加载时触发
<img src="image.jpg" onload="alert('Hello World'">
1.2.2 内嵌JS
将多行JS代码写到 <script> 标签中,例如:
<script>
alert('Hello World~!');
</script>
1.2.3 外部JS
利于HTML页面代码结构化,把大段 JS代码独立到 HTML 页面之外, 既美观,也方便文件级别的复用(适合于JS 代码量比较大的情况)
例子
同一文件夹下有两个文件
my.js内容如下:
alert('Hello World~!')
在js.html引入my.js,如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 引入my.js -->
<script src="my.js"></script>
</head>
<body>
</body>
</html>
1.3 script标签的位置
1.3.1 页面的加载、解析、渲染(简单理解)
一个页面(HTML文档)的加载顺序是从上到下顺序加载的,在这过程中进行解析和渲染
页面加载
页面加载是指从服务器下载网页资源的过程。过程如下:
- 浏览器接收到用户输入的网址,并向服务器发送请求。
- 服务器接收到请求后,将网页的 HTML、CSS、JavaScript 等资源发送回浏览器。
- 浏览器开始下载这些资源。
页面解析
- 浏览器接收到 HTML 文件后,开始对其进行解析。
- 根据 HTML 的结构构建 DOM(文档对象模型)树,表示网页的结构。
- 当遇到外部资源的引用(如 CSS 和 JavaScript),会暂停 HTML 的解析,转而下载这些资源。
页面渲染
- 浏览器解析到 CSS 资源时,会开始构建 CSSOM(CSS 对象模型)树,表示网页的样式信息(页面解析)。
- 浏览器将 DOM 树和 CSSOM 树合并,生成渲染树(Render Tree)。 渲染树包含了所有需要显示的内容和样式信息。 浏览器根据渲染树开始布局(Layout),确定每个元素在页面中的位置和大小。 最后,浏览器根据布局信息对渲染树进行绘制(Paint),将页面内容显示在屏幕上。 (页面渲染)
- 简单理解:页面内容可视化过程
1.3.2 script标签的位置问题
head标签内部
<!DOCTYPE html>
<html lang="en">
<head>
<script>
var myElement = document.getElementById("helloTag");
// 输出结果为空
console.log(myElement);
</script>
</head>
<body>
<div id="helloTag">
<p>Hello World</p>
</div>
</body>
</html>
页面解析时遇到JS,会暂停页面的解析,要在JS解析完毕并执行后才能继续解析页面,因此console.log(myElement)的结果为null。因此,把script标签放在head标签里,可能会出现问题
当JS脚本通常不会直接操作页面中的 DOM 元素(例如引入外部JS库)时,才可放在head标签
body标签内部
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div id="helloTag">
<p>Hello World</p>
</div>
<script>
var myElement = document.getElementById("helloTag");
console.log(myElement);
</script>
</body>
</html>
将script标签放在body尾部,浏览器会先解析完DOM,再下载并执行JS脚本,此时console.log(myElement)能正常输出,但是对于那些高度依赖于js的网页,效率会很慢(因为自上而下的顺序加载)
当JS脚本需要等待 DOM 的加载完成才能执行时(例如操作DOM元素),需要放在body标签末尾,以保证能够正确操作已加载的 DOM 元素
1.3.3 async defer
async
格式如下:
<script src="my.js" async></script>
多个async脚本的加载顺序是随机的, 一旦加载完async脚本,解析渲染就会中断,执行完成后才会继续解析渲染
当JS脚本并不关心页面中的DOM元素,并且也不会产生其他脚本需要的数据,则考虑用async
defer
格式如下:
<script src="my.js" defer></script>
多个defer脚本的加载顺序从上到下, 不会阻止页面解析渲染,等到页面解析完成后再按顺序执行脚本
当JS脚本需要等待 DOM 的加载完成才能执行时(例如操作DOM元素),或者被其他脚本文件依赖,则考虑defer
图解普通script async defer
如下所示,有两个JS脚本
<!DOCTYPE html>
<html lang="en">
<head>
<script src="my01.js" defer></script>
<script src="my02.js" defer></script>
</head>
<body>
</body>
</html>
如图三种颜色,代表不同含义
普通script
文档解析过程中,遇到JS脚本就会停止解析,转而下载JS脚本
my01.js和my02.js可以同时加载,但是my01.js在my02.js的前面,因此必须my01.js执行完后才能执行my02.js
defer
文档解析时,遇到defer脚本,会进行下载,但是并不会阻止文档的渲染,当页面解析&渲染完毕后按顺序执行
async
async脚本异步加载,加载完成后立即执行并中断解析渲染文档,脚本执行完成后,继续解析渲染文档。
由于是异步加载,这里只给其中一种情况
1.3.4 总结
问题1:srcipt标签放head标签还是放body标签内部
- 当JS脚本通常不会直接操作页面中的 DOM 元素(例如引入外部JS库)时,放在head标签内部
- 当JS脚本需要等待 DOM 的加载完成才能执行时(例如操作DOM元素),需要放在body标签末尾,以保证能够正确操作已加载的 DOM 元素
- 感觉放body比较稳定
问题2:async和derfer的应用场景
- 当JS脚本并不关心页面中的DOM元素,并且也不会产生其他脚本需要的数据,则考虑用async
- 当JS脚本需要等待 DOM 的加载完成才能执行时(例如操作DOM元素),或者被其他脚本文件依赖,则考虑defer
- defer会比较稳定
2 JavaScript语法
2.1 标识符
所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则组合起来的一或多个字符:
- 第一个字符必须是字母、下划线(_)或美元符号($)
- 其它字符可以是字母、下划线、美元符号或数字
标识符中的字母也可以包含扩展的 ASCII 或 Unicode 字母字符,并不推荐
对于多个英文单词,ECMAScript标识符采用驼峰大小写格式——除首个单词外,后面单词的首字母大写
例子
myCar,doSomethingImportant都是标识符
2.2 区分大小写
JavaScript 严格区分大小写,例如 Test 和 test 是两个不同的标识符。
为了避免输入混乱和语法错误,建议采用小写字符编写代码,以下特殊情况下可以使用大写形式:
- 如果标识符由多个单词组成,考虑使用骆驼命名法——除首个单词外,后面单词的首字母大写。例如:typeOf
- 构造函数的首字母建议大写。构造函数不同于普通函数。(后面会学到)
2.3 严格模式
ECMAScript 5 引入了严格模式(strict mode)的概念,在该模式下,ECMAScript 3 的一些不正确行为将得到处理,也对某些不安全的操作抛出错误。
- 在整个脚本中启用严格模式方法:在顶部添加代码:“ust strict”
- 指定函数在严格模式下执行方法:function doSomething(){"use strict"; },即在函数内部的顶部加上代码 “ust strict”
2.4 关键字和保留字
关键字
关键字是指被JavaScript语言保留并用于特定目的的单词。这些关键字在代码中具有特定的含义,不能被用作标识符或变量名,
ECMAscript的全部关键字(带*号代表第五版新增的关键字):
保留字
保留字是指目前未被使用,但在将来可能被JavaScript语言保留作为关键字使用的单词。开发者应当避免使用保留字作为标识符或变量名,以免日后这些单词被JavaScript引入为新的关键字而导致代码出现错误。
ECMA-262 第3版定义的全部保留字:
第五版进行了修改,如下:
- 在非严格模式下,仅规定 class、const、enums、export、extends、import、super 为保留字,其它第三版保留字可以自由使用;
- 在严格模式下,严格限制 implements、interface、let、package、private、protected、public、static、yield、eval(非保留字)、arguments(非保留字)的使用。
2.5 分号问题
JS会将换行符当成一个隐式的分号,因此当存在换行符时(即你换行时),绝大多数情况下可以省略分号。如下例子:
alert('Hello World~!')
alert('Hello World~!')
但是有特殊情况,例如:
s = a + b
(x + y).doSomething()
结果会被解析成如下所示:这里的b是一个函数了
s = a + b(x + y).doSomething()
还有很多其它例子,这里不一一列出了
综上所述:如果不想因为分号产生错误,最好还是加上分号
2.6 注释
单行注释
快捷键:ctrl+/
// 注释用来提高代码的可读性
alert('Hello World~!');
alert('Hello World~!');
多行注释
默认快捷键:alt+shift+a
// 注释用来提高代码的可读性
alert('Hello World~!');
alert('Hello World~!');
/*
这是多行注释
你好
*/
如果使用vscode,修改快捷键为ctrl+shift+/,步骤如下:
1、打开vscode,点击左上角的"文件"选项。
2、选择下方选项列表中的"首选项"。
3、点击其中的"键盘快捷方式"选项。
4、进入新界面后,选择搜索alt+shift+a
5、点击右键选择更改键绑定
6、进入新界面,直接按ctrl+shift+/(不要加号),再按Enter键,修改成功
3 JavaScript 输入输出语句
方法 | 说明 | 归属 |
alert(msg) | 浏览器弹出警示框 | 浏览器 |
console.log(msg) | 浏览器控制台打印输出信息 | 浏览器 |
prompt(info) | 浏览器弹出输入框 | 浏览器 |
alert(msg)
alert("hello world");
console.log(msg)
console.log("hello world");
prompt(info)
let result = prompt(message, default);
参数说明:
- message(可选):要显示给用户的文本消息,通常是一条提示信息。
- default(可选):用户输入框中的默认值。
返回值:
- 如果用户点击了确定按钮并提供了输入内容,则返回该内容(字符串)。
- 如果用户点击了取消按钮或者直接关闭了对话框,则返回 null。
let age = prompt('How old are you?', 20);
alert(`You are ${age} years old!`);
按确定后,如图所示:
4 变量
4.1 变量的基础知识
变量的概念
变量是用于存储数据的命名存储器。它们被用来存储各种类型的数据,如数字、字符串、对象、函数等
变量的命名规则
变量的命名规则和标识符的命名规则一致
- 注意:做项目的时候,变量名应该要有意义,简单明了
4.2 变量的定义
定义变量需要使用 var 操作符(var是一个关键字),后跟变量名(即标识符)如下所示:
var 标识符;
- 注意:变量没有赋值,初始值为:undefined
例子如下:
var message;//定义变量
var message_1,message_2,message_3,message_4;//同时定义多个变量
4.3 变量的赋值
方式一
var message;//定义变量
message = 10;
方式二
var message = 10;//定义变量并赋值
var message_1 = 1, message_2 = 2;//定义多个变量并赋值
注意一个细节问题:可以在修改变量值的同时修改值的类型,但不推荐
var message = "hi";//定义变量并赋值
message = 10;//不推荐
4.4 其它关键字
4.4.1 let关键字
let关键字用于声明块级作用域的变量,使用let关键字声明变量,其作用域限定在最近的块(一对花括号)内
例子1
if (true) {
let x = 10; // 块级作用域内的变量
console.log(x); // 10
}
console.log(x); // x is not defined
效果为:
if (true) {
var x = 10;
console.log(x); // 10
}
console.log(x); // 10
效果为:
4.4.2 const关键字
const关键字和let关键字一样,不同点是:其值在声明后不能被修改。例子
const PI = 3.14159; // 常量
5 数据类型
5.1 typeof操作符
typeof:检测给定变量的数据类型
例子
var message = "string";
console.log(typeof message);//string
console.log(typeof(message));//string
5.2 Undefined类型
Undefined 类型只有一个值,即 undefined。在使用 var 声明变量但未对其初始化时,这时变量的值就是 undefined
var message;
console.log(message); // undefined
使用 typeof 操作符查看未赋值的变量类型时,它们的类型是 undefined,并且未声明变量的类型也是 undefined
var message;
console.log(typeof message); // undefined
console.log(typeof age); // undefined
5.3 Null类型
Null 类型只有一个值,即 null。从逻辑角度看,null 值表示一个空对象指针,因此typeof操作符检测 null 值会返回 “object”
var message = null;
console.log(typeof message); // object
5.4 Boolean类型
Boolean(布尔)类型只有两个值,即 true 或者 false ,ture 和 false 是区分大小写的。
为变量赋布尔类型值的例子
var message = true;
var message_1 = 3 > 1; //通过表达式赋值,表达式为真,则布尔值为true
转型函数Boolean()
var message = "Hello";
var message_1 = Boolean(message);// 结果为true
转换规则如下:
n/a代表不存在
如果遇到流控制语句(如 if 语句),则自动转换为布尔值(遵循上述规则),例如
var message = "Hello";
if(message){//判断为true
console.log("true");
}
5.5 Number类型
5.5.1 介绍
Number类型用 IEEE754格式 表示整数和浮点数(也称双精度数值)
5.5.2 整数
十进制
var intNum = 10; // 整数
八进制
八进制第一位必须是0
var intNum1 = 070; // 八进制56
var intNum2 = 079; // 八进制没有9,无效八进制,视为十进制,为79
var intNum3 = 08; // 八进制没有8,无效八进制,视为十进制,为8
十六进制
十六进制的前两位必须是0x,后跟十六进制数字( 0-9 以及 A-F ),字母 A-F 可小写
var intNum1 = 0xA; // 十进制10
5.5.3 浮点数值
浮点数值必须有小数点且小数点后必须至少有一位数字
var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = .1; // 结果为0.1,但不推荐这么写
保存浮点数值所需内存空间比保存整数值所需内存空间大,因此 ECMAScript 会在合适时候将浮点数值转换为整数值
var floatNum1 = 1.; //解析为1
var floatNum2 = 10.0; //整数,解析为10
"e" 表示法是一种用于表示科学计数法的语法格式。它允许使用指数来表示非常大或非常小的数值。"e" 表示法的语法格式如下:
number e exponent
- 其中,
number
是一个数值,exponent
是一个整数指数。 - 表示number × 10的 exponent 次方
var floatNum1 = 3.125e7; // 等于3125000
var floatNum2 = 3e-7; // 等于0.0000003
浮点数进行算术运算时精度远远不如整数,例如 0.1 + 0.2 的结果不是 0.3,而是0.3000...,会存在一点误差,但是 0.05 + 0.25 的结果就是0.3,这是因为IEEE754使用二进制表示浮点数,0.1+0.2 存在舍入误差,而 0.05+0.25 则恰好没有舍入误差。因此无法测试特定的浮点数值(后续会在计算机组成学习IEEE754)
例子
不能这样测试特定的浮点数值
if(a + b == 0.3)
5.5.4 数值范围和三个特殊值
最大和最小值
- 最大值: Number.MAX_VALUE,这个值为: 1.7976931348623157e+308
- 最小值: Number.MIN_VALUE,这个值为: 5e-324
alert(Number.MAX_VALUE); // 1.7976931348623157e+308
alert(Number.MIN_VALUE); // 5e-324
三个特殊值
- Infinity ,代表无穷大,大于任何数值
- -Infinity ,代表无穷小,小于任何数值
- NaN , Not a number,代表一个非数值
alert(Infinity); // Infinity
alert(-Infinity); // -Infinity
alert(NaN); // NaN
5.5.5 IsNaN 函数
语法格式
isNaN(value)
- 判断value是否 “不是数值”,如果不是数值或者无法转换为数字,返回true,否则返回false
5.5.6 数值转换
1 Number函数
语法格式:
Number(value)
- value是要转换为数字的值,可以是任何数据类型。
转换规则如下:
例子
2 parseInt函数(常用)
语法格式:
parseInt(string, radix)
string
是要解析的字符串,radix
是一个可选参数,表示要解析的数字的基数(进制),默认为 10。- praselnt以
radix
进制解析数字,返回结果是十进制数字
对字符串解析规则:
- 遇到空字符串,返回NaN
- 遇到非空字符串,忽略前面的空格。如果第一个非空格字符不是数字字符或者正负号,返回NaN;如果是,则解析到非数字字符为止或解析完整个字符串
- 如果字符串以 0x 开头且后面跟数字字符,则当作十六进制,如果字符串以 0 开头且后面跟数字字符,则当作八进制(对于八进制,ECMAScript 5 已不再支持,建议由第二个参数写明)
- 简单理解:从第一个有效的非空格字符(数字字符或者正负号)开始,解析出有效的数字
var num1 = parseInt("1123blue"); // 1123
var num2 = parseInt(""); // NAN
var num3 = parseInt("22.5"); // 22
var num4 = parseInt("0xA"); // 十进制10
var num5 = parseInt("070"); // 十进制56,不推荐写法(八进制)
var num6 = parseInt('10') // 10
var num7 = parseInt('10',2) // 2
var num8 = parseInt('10',16) // 16
var num9 = parseInt('-10',8) // -8,推荐写法(八进制)
3 parseFloat函数
语法格式:
parseFloat(string)
- string是要转换为浮点数的字符串
- 只解析十进制数字
对字符串解析规则:
- 简单理解:从第一个有效的非空格字符(数字字符或者正负号)开始,解析出有效的数字,对于没有小数点的有效数,解析为整数
5.6 String类型
5.6.1 String的基本使用
字符串类型是一段以 '' 或 “” 包裹起来的文本,JS推荐使用单引号。例如
var str1 = "hello";
var str2 = 'world';//推荐
JS 可以用单引号嵌套双引号 , 或者用双引号嵌套单引号 (外双内单,外单内双)
var strMsg = '我是"高帅富"程序猿'; // 可以用''包含""
var strMsg2 = "我是'高帅富'程序猿"; // 也可以用"" 包含''
// 常见错误
var badQuotes = 'What on earth?"; // 报错, 不能 单双引号搭配
5.6.2 字符串转义符
字符串中有特殊的字符字面量,称之为转义符,如下所示
例子
想在单引号包裹起来的文本这种再用单引号,则需要转移符
var str = '\'hello\''; //结果为:'hello'
5.6.3 字符串拼接
多个字符串之间可以使用 + 进行拼接,其拼接方式为 字符串 + 任何类型 = 拼接之后的新字符串
//1.1 字符串 "相加"
alert('hello' + ' ' + 'world'); // hello world
//1.2 数值字符串 "相加"
alert('100' + '100'); // 100100
//1.3 数值字符串 + 数值
alert('11' + 12); // 1112
var age = 18;
console.log('pink老师' + age); // pink老师18
console.log('pink老师' + age + '岁啦'); // pink老师18岁啦
5.6.4 转换为字符串
toString函数
numObj.toString([radix])
numObj
表示需要转换为字符串的对象(数值,布尔值,对象和字符串值)。radix
是一个可选参数,用于指定转换时使用的进制数。如果省略该参数,则默认使用十进制。- 返回一个副本,不改变原来的
var num = 42;
console.log(num.toString()); // 输出字符串 "42"
console.log(num.toString(2)); // 输出二进制字符串 "101010"
console.log(num.toString(16)); // 输出十六进制字符串 "2a"
var bool = true;
console.log(bool.toString()); // 输出字符串 "true"
String函数
转换规则如下:
例子:
5.7 Object 类型
Object类型(对象)是一组由键、值组成的无序集合,对象类型的键都是字符串类型的,值则可以是任意数据类型,通过 对象.键 的方式获取值
例子
const person = {
name: 'John',
age: 30,
gender: 'male'
};// 对象
console.log(person.name); //John
6 操作符
6.1 算术运算符
var x = 5,y = 2;
console.log(x + y); // 输出:7
console.log(x - y); // 输出:3
console.log(x * y); // 输出:10
console.log(x / y); // 输出:2.5
console.log(x % y); // 输出:1
6.2 递增递减运算符
运算符 | 名称 | 结果 |
++x | 递增运算符 | 将x加1,返回x的值 |
x++ | 递增运算符 | 返回x的值,将x加1 |
--x | 递减运算符 | 将x减1,返回x的值 |
x-- | 递减运算符 | 返回x的值,将x减1 |
var x = 1;
console.log(++x); // 输出:2
console.log(x); // 输出:2
x = 1;
console.log(x++); // 输出:1
console.log(x); // 输出:2
x = 1;
console.log(--x); // 输出:0
console.log(x); // 输出:0
x = 1;
console.log(x--); // 输出:1
console.log(x); // 输出:0
6.3 比较运算符
比较运算符( 关系运算符) 是两个数据进行比较时所使用的运算符, 比较运算后, 会返回一个布尔值( true / false)作为比较运算的结果。
var x = 1;
var y = 2;
var z = "1";
console.log(x == z); // 输出: true
console.log(x === z); // 输出: false
console.log(x != y); // 输出: true
console.log(x !== z); // 输出: true
console.log(x < y); // 输出: true
console.log(x > y); // 输出: false
console.log(x <= y); // 输出: true
console.log(x >= y); // 输出: false
6.4 逻辑运算符
逻辑运算符是用来进行布尔值运算的运算符, 其返回值也是布尔值。 后面开发中经常用于多个条件的判断
逻辑与
如图所示
逻辑与存在短路运算:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值,语法格式如下
表达式1 && 表达式2
- 如果第一个表达式的值为真,则返回表达式2
- 如果第一个表达式的值为假,则返回表达式1
console.log( 123 && 456 ); // 456
console.log( 0 && 456 ); // 0
console.log( 123 && 456&& 789 ); // 789
console.log(1 > 3 && 2 > 1); // false
逻辑或
如图所示
逻辑或也存在短路运算:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值,语法格式如下
表达式1 || 表达式2
- 如果第一个表达式的值为真,则返回表达式1
- 如果第一个表达式的值为假,则返回表达式2
console.log( 123 || 456 ); // 123
console.log( 0 || 456 ); // 456
console.log( 123 || 456 || 789 ); // 123
console.log(2 > 1 || 1 > 3); // true
逻辑非( !)
也叫作取反符, 用来取一个布尔值相反的值,如 true 的相反值是 false
var isOk = !true;
console.log(isOk); // false
6.5 赋值运算符
概念: 用来把数据赋值给变量的运算符。
var age = 10;
age += 5; // 相当于 age = age + 5;
age -= 5; // 相当于 age = age - 5;
age *= 10; // 相当于 age = age * 10;/= %=同理
6.7 运算符优先级
一元运算符的概念
只能操作一个值的运算符
优先级
7 流程控制
7.1 流程控制介绍
流程控制就是来控制我们的代码按照什么结构顺序来执行,流程控制主要有三种结构,分别是顺序结构、 分支结构和循环结构, 这三种结构代表三种代码执行的顺序。
7.2 分支流程控制
7.2.1 if语句
if 语句
语法结构
// 条件成立执行代码, 否则什么也不做
if (条件表达式) {
// 条件成立执行的代码语句
}
执行流程
例子
var usrAge = prompt('请输入您的年龄: ');
if(usrAge >= 18){
alert('您的年龄合法,欢迎来天际网吧享受学习的乐趣! ');
}
if else语句
语法结构
// 条件成立 执行 if 里面代码,否则执行else 里面的代码
if (条件表达式) {
// [如果] 条件成立执行的代码
} else {
// [否则] 执行的代码
}
执行流程
例子
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
alert("这个年份是闰年");
} else { // 剩下的是平年
alert("这个年份是平年");
}
if else if 语句
语法结构
// 适合于检查多重条件。
if (条件表达式1) {
语句1;
} else if (条件表达式2) {
语句2;
} else if (条件表达式3) {
语句3;
....
} else {
// 上述条件都不成立执行此处代码
}
执行流程
例子
var score = prompt('请您输入分数:');
if (score >= 90) {
alert('宝贝,你是我的骄傲');
} else if (score >= 80) {
alert('宝贝,你已经很出色了');
} else if (score >= 70) {
alert('你要继续加油喽');
} else if (score >= 60) {
alert('孩子,你很危险');
} else {
alert('熊孩子,我不想和你说话,我只想用鞭子和你说话');
7.2.2 三元表达式
三元表达式能做一些简单的条件选择。 有三元运算符组成的式子称为三元表达式,其语法结构如下:
表达式1 ? 表达式2 : 表达式3;
- 如果表达式1为 true ,则返回表达式2的值, 如果表达式1为 false,则返回表达式3的值
- 简单理解: 就类似于 if else (双分支) 的简写
例子
var time = prompt('请您输入一个 0 ~ 59 之间的一个数字');
// 三元表达式 表达式 ? 表达式1 :表达式2
var result = time < 10 ? '0' + time : time; // 把返回值赋值给一个变量
alert(result);
7.2.3 switch语句
语法结构
- witch 表达式的值会与结构中的 case 的值做比较,如果存在匹配全等(===) , 则与该 case 关联的代码块会被执行,并在遇到 break 时停止,整个 switch 语句代码,执行结束
- 如果所有的 case 的值都和表达式的值不匹配, 则执行 default 里的代码
例子
var fruit = prompt('请您输入查询的水果:');
switch (fruit) {
case '苹果':
alert('苹果的价格是 3.5/斤');
break;
case '榴莲':
alert('榴莲的价格是 35/斤');
break;
default:
alert('没有此水果');
}
7.2.4 if与switch比较
① 一般情况下,它们两个语句可以相互替换
② switch...case 语句通常处理 case为比较确定值的情况, 而 if…else…语句更加灵活,常用于范围判断(大于、等于某个范围)
③ switch 语句进行条件判断后直接执行到程序的条件语句,效率更高。 而if…else 语句有几种条件,就得判断多少次。
④ 当分支比较少时, if… else语句的执行效率比 switch语句高。
⑤ 当分支比较多时, switch语句的执行效率比较高, 而且结构更清晰
7.3 循环流程控制
7.3.1 for循环
语法结构
for(初始化变量; 条件表达式; 操作表达式 ){
//循环体
}
- 初始化变量, 初始化操作在整个 for 循环只会执行一次。
- 执行条件表达式,如果为true,则执行循环体语句,否则退出循环,循环结束。
- 执行操作表达式,此时第一轮结束。
- 第二轮开始,直接去执行条件表达式(不再初始化变量),如果为 true ,则去执行循环体语句,否则退出循环。
- 继续执行操作表达式,第二轮结束。
- 后续跟第二轮一致,直至条件表达式为假, 结束整个 for 循环。
例子
// 基本写法
for(var i = 1; i <= 10; i++){
console.log('媳妇我错了~');
}
// 用户输入次数
var num = prompt('请输入次数:');
for ( var i = 1 ; i <= num; i++) {
console.log('媳妇我错了~');
}
7.3.2 双重for循环
语法结构
for (外循环的初始; 外循环的条件; 外循环的操作表达式) {
for (内循环的初始; 内循环的条件; 内循环的操作表达式) {
需执行的代码;
}
}
- 外层循环执行一次,内层循环要执行全部次数
例子
打印n行n列星星
var row = prompt('请输入您打印几行星星:');
var col = prompt('请输入您打印几列星星:');
var str = '';
for (var i = 1; i <= row; i++) {
for (j = 1; j <= col; j++) {
str += '☆';
}
str += '\n';//打满一行星星就换行
}
console.log(str);
7.3.3 while循环
语法结构
while (条件表达式) {
// 循环体代码
}
- 使用 while 循环时一定要注意,它必须要有退出条件,否则会成为死循环
例子
var i = 1;
while (i <= 10) {
console.log(i);
i++;
}
7.4 do while 循环
语法结构
do {
// 循环体代码,条件表达式为 true 时重复执行循环体代码
} while(条件表达式);
- 先执行一次循环体代码
- 再执行条件表达式, 如果结果为 true, 则继续执行循环体代码, 如果为 false, 则退出循环, 继续执行后面 代码
注意: 先再执行循环体,再判断,我们会发现 do…while 循环语句至少会执行一次循环体代码(和while循环不同的地方)
例子
var i = 1;
do {
console.log(i);
i++;
} while (i <= 5);
7.5 continue break
continue 关键字
continue 关键字用于立即跳出本次循环, 继续下一次循环(本次循环体中 continue 之后的代码就会少执行一次)
for (var i = 1; i <= 5; i++) {
if (i == 3) {
console.log('这个包子有虫子,扔掉');
continue; // 跳出本次循环, 跳出的是第3次循环
}
console.log('我正在吃第' + i + '个包子呢');
}
break 关键字
break 关键字用于立即跳出整个循环(循环结束)
for (var i = 1; i <= 5; i++) {
if (i == 3) {
break; // 直接退出整个for 循环, 跳到整个for下面的语句
}
console.log('我正在吃第' + i + '个包子呢');
}
8 数组(基础)
8.1 数组概念
数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式
数组元素的类型
数组中可以存放任意类型的数据,例如字符串,数字,布尔值等。
var arrStus = ['小白',12,true,28.9];
8.2 创建数组
new 创建数组
var 数组名 = new Array() ;
var arr = new Array(); // 创建一个新的空数组
学完对象后会细说
数组字面量创建数组(常用)
//1. 使用数组字面量方式创建空的数组
var 数组名 = [];
//2. 使用数组字面量方式创建带初始值的数组
var 数组名 = ['小白','小黑','大黄','瑞奇'];
8.3 获取数组元素
索引 (下标) : 用来访问数组元素的序号(数组下标从 0 开始) 。
可以通过“数组名[索引]” 的形式来获取数组中的元素
// 定义数组
var arrStus = [1,2,3];
// 获取数组中的第2个元素
alert(arrStus[1]);
8.4 遍历数组和数组长度
数组的长度
使用“数组名.length”可以访问数组元素的数量(数组长度)
var arrStus = [1,2,3];
alert(arrStus.length); // 3
遍历数组
遍历: 就是把数组中的每个元素从头到尾都访问一次
var arr = ['red','green', 'blue'];
for(var i = 0; i < arr.length; i++){
console.log(arrStus[i]);
}
8.5 数组新增元素
修改数组索引新增数组元素(常用)
var arr = ['red', 'green', 'blue', 'pink'];
arr[4] = 'hotpink';
console.log(arr)
修改 length 长度新增数组元素
var arr = ['red', 'green', 'blue', 'pink'];
arr.length = 7;
console.log(arr);
console.log(arr[4]);
console.log(arr[5]);
console.log(arr[6]);
其中索引号是 4, 5, 6 的空间没有给值,就是声明变量未给值, 默认值就是 undefined。
9 函数(基础)
9.1 函数概念
函数: 封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
9.2 声明函数和调用函数
声明函数
// 声明函数
function 函数名() {
//函数体代码
}
例子
function sayHello(name){
console.log("Hello " + name);
}
调用函数
// 调用函数
函数名(); // 通过调用函数名来执行函数体代码
- 注意: 声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码。
function sayHello(name){
console.log("Hello " + name);
}
sayHello("John");
9.3 函数的参数
9.3.1 形参和实参
- 在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参
- 在调用该函数时,同样也需要传递相应的参数,这些参数被称为实参
// 带参数的函数声明
function 函数名(形参1, 形参2 , 形参3...) { // 可以定义任意多的参数,用逗号分隔
// 函数体
}
// 带参数的函数调用
函数名(实参1, 实参2, 实参3...);
例子
function getSum(num1, num2) {
//num1,num2是形参
console.log(num1 + num2);
}
//1,3;6,5是实参
getSum(1, 3); // 4
getSum(6, 5); // 11
9.3.2 形参和实参个数不匹配问题
例子
function sum(num1, num2) {
console.log(num1 + num2);
}
sum(100, 200); // 形参和实参个数相等, 输出正确结果
sum(100, 400, 500, 700); // 实参个数多于形参,只取到形参的个数
sum(200); // 实参个数少于形参,多的形参定义为undefined,结果为NaN
9.4 函数返回值
语法格式
// 声明函数
function 函数名() {
...
return 需要返回的值;
}
// 调用函数
函数名(); // 此时调用函数就可以得到函数体内return 后面的值
- 注意:return 只能返回一个值。如果用逗号隔开多个值,以最后一个为准。
- 如果有return 则返回 return 后面的值
- 如果没有return 则返回 undefined
例子1
function getArray() {
// 创建一个包含一些元素的数组
var array = [1, 2, 3, 4, 5];
// 返回数组
return array;
}
// 调用函数并接收返回值
var result = getArray();
例子2
function add(num1, num2){
//函数体
return num1, num2;
}
var resNum = add(21,6); // 调用函数, 传入两个实参,并通过 resNum 接收函数返回值
alert(resNum); // 6
9.5 arguments的使用
当我们不确定有多少个参数传递的时候, 可以用 arguments 来获取。arguments 中存储了传递的所有实参
arguments展示形式是一个伪数组,因此可以进行遍历。 伪数组具有以下特点:
- 具有 length 属性
- 按索引方式储存数据
- 不具有数组的 push , pop 等方法
例子
function maxValue() {
var max = arguments[0];
for (var i = 0; i < arguments.length; i++) {
if (max < arguments[i]) {
max = arguments[i];
}
}
return max;
}
console.log(maxValue(2, 4, 5, 9));
console.log(maxValue(12, 4, 9));
9.6 匿名函数
// 这是函数表达式写法, 匿名函数后面跟分号结束
var fn = function(){...};
// 调用的方式, 函数调用必须写到函数体下面
fn();
- 因为函数没有名字, 所以也被称为匿名函数
- 这个fn 里面存储的是一个函数
- 函数表达式方式原理跟声明变量方式是一致的
- 函数调用的代码必须写到函数体后面
10 作用域
10.1 作用于概念
一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
JavaScript(ES6前) 中的作用域有两种:
- 全局作用域:作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件。
- 局部作用域(函数作用域):作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系, 所以也称为函数作用域。
JS 没有块级作用域
块作用域由 { } 包括,但JS没有块级作用域(在ES6之前)
if(true){
var num = 123;
console.log(123); //123
}
console.log(123); //123
10.2 变量作用域
全局变量
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
- 全局变量在代码的任何位置都可以使用
- 在全局作用域下 var 声明的变量 是全局变量
- 特殊情况下,在函数内不使用 var 声明的变量也是全局变量(不建议使用)
在任何一个地方都可以使用, 只有在浏览器关闭时才会被销毁,因此比较占内存
局部变量
在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
- 局部变量只能在该函数内部使用
- 在函数内部 var 声明的变量是局部变量
- 函数的形参实际上就是局部变量
只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后, 就会被销毁,因此更节省内存空间
10.3 作用域链
- 只要是代码,就至少有一个作用域
- 写在函数内部的局部作用域
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
- 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问, 就称作作用域链
例子
function f1() {//外部函数
var num = 123;
function f2() {//内部函数
console.log( num );
}
f2();
}
var num = 456;
f1();
最外层的0级链:f1 和 num=456
1级链:num=123 和 f2
2级链:console.log(num);
console.log(num)从本级依次往上找,二级链没有,因此到一级链,找到 num=123 ,输出
11 预解析
11.1 基本概念
JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。 JavaScript 解析器在运行 JavaScript 代码的时候分为两步: 预解析和代码执行。
- 预解析: 在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明或者定义。
- 代码执行: 从上到下执行JS语句。
预解析只会发生在通过 var 定义的变量和 function 上。
变量提升
变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。
例子1:结果为undefined
console.log(message);
var message = "hi";//定义变量并赋值
例子2:结果为hi
var message = "hi";//定义变量并赋值
console.log(message);
函数提升
函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
fn();// 输出打印
function fn() {
console.log('打印');
}
注意匿名函数的坑
fn();//此时fn还是undefined,因为fn是变量
var fn = function() {
console.log('想不到吧');
}
11.2 分析方法(用来做题)
例题
num结果是多少
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
第一步:写出等价代码
把带有 var 和 function 声明的变量在内存中进行提前声明或者定义(当前作用域)
//把带有 var 和 function 声明的变量在内存中进行提前声明或者定义(当前作用域)
var num
function fun() {
var num
console.log(num);
num = 20;
}
num = 10;
fun();
第二步:分析作用域链
根据作用域链原则,fun()里的 var num 和 console.log(num) 在同一链内,因此输出结果为:undefined
12 对象(基础)
12.1 对象的概念
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的。
- 属性:事物的特征, 在对象中用属性来表示(常用名词)
- 方法:事物的行为, 在对象中用方法来表示(常用动词)
12.2 创建对象的三种方式
字面量创建对象
对象字面量: 就是花括号 { } 里面包含了表达这个具体事物(对象)的属性和方法。{ } 里面采取键值对的形式表示
- 键:相当于属性名
- 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型,函数类型等)
var star = {
name : 'pink',
age : 18,
sex : '男',
sayHi : function(){
alert('大家好啊~');
}
};
new Object创建对象
使用的格式: 对象.属性 = 值;(属性自定义)
var andy = new Obect();
andy.name = 'pink';
andy.age = 18;
andy.sex = '男';
andy.sayHi = function(){
alert('大家好啊~');
}
构造函数
构造函数 : 主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。
在 js 中,使用构造函数要时要注意以下问题:
- 构造函数用于创建某一类对象,其首字母要大写
- 构造函数要和 new 一起使用才有意义
- 函数内的属性和方法前面需要添加 this ,表示当前对象的属性和方法。
- 当我们创建对象的时候, 必须用 new 来调用构造函数
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayHi = function() {
alert('我的名字叫: ' + this.name + ', 年龄: ' + this.age + ', 性别: ' + this.sex);
}
}
var bigbai = new Person('大白', 100, '男');
var smallbai = new Person('小白', 21, '男');
console.log(bigbai.name);
console.log(smallbai.name);
12.3 访问修改删除对象属性
访问对象属性
要访问或获取属性的值,可使用 对象名.属性名 或者 对象名["属性名"] 的形式,例子如下
- 注意:如果属性名中包含空格或者特殊字符
var person = {
name: "Alice",
age: 25,
"favorite color": "blue"//不能使用对象名.属性名
};
console.log(person.name); // 输出 "Alice"
console.log(person["age"]); // 输出 25
console.log(person["favorite color"]); // 输出 "blue"
修改对象属性
使用 对象名.属性名 或者 对象名["属性名"] 的形式修改属性值
var person = {
name: "Alice",
age: 25,
};
person.name = "Bob";
console.log(person.name); // 输出 "Bob"
person["age"] = 30;
console.log(person["age"]); // 输出 30
删除对象属性
使用 对象名.属性名 或者 对象名["属性名"] 的形式删除属性值
var person = {
name: "Alice",
age: 25,
};
// 使用对象名.属性名 的方式删除属性
delete person.age;
console.log(person.age); // 输出 undefined
// 使用对象名["属性名"] 的方式删除属性
delete person["name"];
console.log(person.name); // 输出 undefined
12.4 遍历对象
for (var k in obj) {
console.log(k); // 这里的 k 是属性名
console.log(obj[k]); // 这里的 obj[k] 是属性值
}