🔧 JavaScript
一、引言
Javascript三大部分ECMAScript语法标准- 由 ECMA 制定的一种脚本语言的标准化规范
- DOM
- 通过⽂档对象模型(dom)操作网页
- BOM
- 通过浏览器对象模型(bom)操作浏览器

- 我们写的代码需要翻译成机器能够识别的机器码(010101)
- 解释型语言
- 定义:翻译一行执行一行,不生成编译文件
- 优点:可移植性较好,只要有解释环境,可在不同的操作系统上运行
- 缺点 :运行需要解释环境,运行起来比编译的要慢
- 例子:Javascript、php、python
- 编译型语言
- 定义:通篇翻译,生成编译文件之后再执行
- 优点:运行速度快,代码效率高,编译后的程序不可修改,保密性较好
- 缺点 :代码需要经过编译方可运行,可移植性差,只能在兼容的操作系统上运行
- 例子:c、c++
- 解释型语言
单线程( js 引擎是单线程执行的)
同一时间只能做一件事情
为什么js是单线程?
多线程可以提高开发效率,为什么设计成单线程?
- 主要是由 js 的用途决定的
- 与用户互动
- 操作DOM
- 主要是由 js 的用途决定的
假如 js 是多线程那么现在有两个事件,同时操作 dom,
一个事件编辑 dom,另一个事件删除 dom,那么此时会产生矛盾指令
二、Js 引入与注释
如何引入
- 内部引入
html<script>xxx</script>- 外部引入( js 文件)
html<script src='xxx'></script>注意:推荐 将位置放在 body 标签的里面最下方 ,防止阻塞内容的渲染
使用 w3c 的标准结构、样式、行为相分离推荐外部引入
注释
js单行://js多行:/* */结束符号
js; // 写完一个语句后加分号,避免解析时报错
三、变量声明
- 变量声明
用var方式定义变量 (es6语法:let、const)
// 声明和赋值的结合
var name = 'irai';
// 声明和赋值分开
var name ;
name = 'irai'
// 使用 , 同时声明多个变量
var a = 2, b = 3;
console.log(a,b);命名规则
- JS 中的变量是弱类型可以保存所有类型的数据,即 变量没有类型 而值有类型
jsvar ir = 'iraionly.cn; console.log(typeof ir); ir = 18; console.log(typeof ir); 注意:js语言中不能将关键字用来做变量名,比如 true、if、while、class 等。 var class='iraionly.cn'
四、基础数据类型
4.1 概念
基础类型
- 原始类型(基础类型)数据保存在 ⾥
js基础类型: Number String Boolean Undefined Null Symbol
复杂类型
复杂类型(引用类型)数据保存在 ⾥通常内存占据空间计较大,
所以设计存放在堆内存,在
js引用类型: 对象:{} 数组:[] 函数:(){}
4.2 Number
- 声明:
//字面量声明
var num1 = 1;
console.log(typeof num1); //number
//数字对象方式声明
var num2 = new Number(2);
console.log(num2+3); //5Number方法:- 整数判断
jsconsole.log(Number.isInteger(1.3)); //false- 指定返回的小数位数 ( 四舍五入 )
jsconsole.log((12.345).toFixed(2)); //12.35
NaN (
not a number) 表示非数字值jsisNaN() //用来判断一个值是不是数字 // 如果该值转换为number等于 NaN,则此函数返回 true 注意:null, "" 两个值会被判断为数字,所以使用前应该屏蔽
浮点精度
- js 语言在浮点数计算时会有精度误差问题
jsvar num = 0.1 + 0.2 console.log(num)// 结果:0.30000000000000004- 解决方式 (不要求高精度)
jsvar num = 0.1 + 0.2 console.log(num.toFixed(1))// 结果:0.3
4.3 String
- 声明:
//字面量声明
var irai = 'iraionly'; //可以使用单引号或者双引号
console.log(irai);
//字符串对象方式声明
var irai = new String('iraionly');- 【常用】字符串方法 :
//连接运算符
var address = "iraionly.cn"
name = "irai的博客"
console.log(name + "网址为" + address); //irai的博客网址为iraionly.cn
//获取长度
console.log("iraionly.cn".length); //11
//大小写转换
console.log("irai".toUpperCase()); //IRAI
console.log("IRAI".toLocaleLowerCase()); //irai
//移除两端空白
var s = " \t irai \t\n "
console.log(s.length); //11
console.log(s.trim().length); //4
//获取指定下标单字符
console.log("iraionly.cn".charAt(3)); //3
console.log("iraionly.cn"[3]); //3
//截取字符串(切片)
var n = "iraionly.cn".slice(1,5);
console.log(n); //raio
//slice(start,end) 包含start不包含end(左闭右开)
//查找字符串
console.log("iraionly.cn".indexOf("c")); //9
console.log("iraionly.cn".indexOf("i",2)); //从第2个字符开始搜索(包含第二个)
console.log("iraionly.cn".indexOf("z")); //-1(不存在返回-1)
//替换字符串
var name = "iraionly";
var change_name = name.replace("irai","js")
console.log(change_name); //jsonly
//字符分割 split
var date = "2018-06-23"
console.log(date.split("-")); //(3) ['2018', '06', '23']
//(应用:格式转换)
//将 "2018-06-23" 时间格式转换为 "2018/06/23"
// 序列合并为字符串 join
var data_arr = date.split("-")
console.log(data_arr.join("/")); // 2018/06/234.4 Boolean
声明 :
js//字面量声明 var irai = true;隐式转换
js//几乎所有的类型都可以隐式转换为 Boolean 类型 true false String 非空字符串 空字符串 Number 非0的数值 0/NaN Array 数组不参与比较时 参与比较的数组 Object ✅ undefined ✅ null ✅ NaN ✅当其他类型与 Boolean 类型对比时,会将其他类型先转换为数值类型再对比
显式转换
jsvar irai = ''; console.log(!!irai); //false num = 0; console.log(!!num); //false b = null; console.log(!!b); //false
4.5 Undefined、Null、Symbol
Undefined/Null: Null 和 Undefined 基本是同义的jsundefined == null //true //null是一个表示"无"的对象,转为数值时为0; //undefined是一个表示"无"的原始值,转为数值时为NaN。 Number(null) //0 Number(undefined) // NaN 5 + undefined // NaN(任何数值和undefined运算都是NaN)
Undefinedjs// 变量被声明了,但没有赋值时,就等于undefined var i; // i undefined // 对象没有赋值的属性,该属性的值为undefined var o = new Object(); // o.p undefined // 函数没有返回值时,默认返回undefined var x = f(); //x undefinedNulljsnull表示"没有对象",即该处不应该有值。 /* 用法: (1) 作为函数的参数,表示该函数的参数不是对象 (2) 作为对象原型链的终点 Object.getPrototypeOf(Object.prototype) // null */
Symbol(es6的语法)js// 在一个对象中需要增加一个相同的属性名 var o = { aa: '1' }; var aa = Symbol('aa'); o[aa] = '2'; console.log(o); //{aa: '1', Symbol(aa): '2'}
五、JS 对象
5.1 JS对象形式
除了原始值(基本类型),其他(函数、数组)都是对象
txt基础数据类型: Number、String、Boolean、Undefined、Null、Symbol
字面量声明一个对象:
jsvar car = { color: 'red', //键值对 height: 1.8, start: function () { console.log('‘启动'); }, stop: function () { console.log('熄火'); }, suv: true, wheel:[1,2,3,4] }; console.log(car); //{color: 'red', height: 1.8, suv: true, start: ƒ, stop: ƒ, …}- 在对象⾥的函数叫⽅法 (methods)
- 对象里包含属性与值,属性的值可以是任意类型的
5.2 JS 对象常用操作
增/改/查:- 通过
.属性的方式 或者["属性"]的方式
js// 键值对 // 键名就是属性名, 值就是属性值 var person = { name: '张三', age: 29, job: 'teacher', eat: function() {} } person.name = "李四"; //改 person["sex"] = 0; //增 console.log(person); //{name: '李四', age: 29, job: 'teacher', sex: 0, eat: ƒ} console.log(person.job); //teacher //查- 通过
删:- 通过
delete运算符
jsvar person = { name: '张三', age: 29, job: 'teacher', eat: function() {} } delete person.job //删 console.log(person); //{name: '张三', age: 29, eat: ƒ}- 通过
5.3 this
- 通过
this我们能够访问对象自身属性和方法
var person = {
name: 'zhangsan',
sayHello: function() {
console.log('Hello, I am ' + this.name) //this调自身属性
},
speak:function(){
this.sayHello() //this调自身方法
console.log('I am speaking')
}
}5.4 创建对象
对象字面量
{}- 声明⼀个对象,赋值给⼀个变量
jsvar person = { name: '张三', age: 29, job: 'teacher', eat: function() {} }
构造函数
Object⾃定义构造函数 (大驼峰)this指向的是对象的本身- 使⽤
new实例化⼀个对象,就像工厂⼀样
jsvar a = new Object(); a.aa = '1'; console.log(a); //{aa: '1'} function Factory(color, height, suv) { this.color = color; this.height = height; this.suv = suv; } var car1 = new Factory('red', 1.8, true); console.log(car1); //Factory {color: 'red', height: 1.8, suv: true}
5.5 构造函数参数
- 固定参数应用场景
- 明确知道对象属性
- 位置要严格对应
function Car(name, price, size) {
this.name = name
this.price = price
this.size = size
}- 对象类型的参数
- 维护方便
- 使用方便
//构造函数
function Car(obj) {
this.name = obj.name
this.price = obj.price
this.size = obj.size
}
//创建对象
var car = new Car({name:"xiaomi",price:"30w",size:"big"})
console.log(car); //Car {name: 'xiaomi', price: '30w', size: 'big'}5.6 【面试】new 作用
没有 new ,直接调⽤构造函数
- 构造函数内部的 this 指向的是 window
jsfunction Student(obj) { this.name = obj.name; this.score = obj.score; this.grade = obj.grade; console.log(this); } //创建对象(没有new) var stu1 = Student({ name: 'Jack', score: 88, grade: 3, }); console.log(stu1); //Window {window: Window, self: Window, document: document, name: 'Jack', location: Location, …} //undefined
- new
- 创建了新空对象
- 将构造函数的作用域赋值给新对象 (this指向新对象)
- 执行构造函数代码 (为这个新对象添加属性)
- 返回新对象
function Student(obj) {
// this={}; //这个{}就是创建对象时创建的,然后隐式调用了这句,this就指向了新对象
this.name = obj.name;
this.score = obj.score;
this.grade = obj.grade;
// return this; //把创建好的对象,构建好属性然后return给外面var出来接收对象的变量
}
var a = new Student({ name: '1', score: '2', grade: '3' });
console.log(a); //Student {name: '1', score: '2', grade: '3'}5.7 【面试】深拷贝与浅拷贝
浅拷贝
- 定义:对于引用类型而言,指两个引用类型指向同一个地址,改变一个,另一个也会随之改变
js//浅拷贝 var person1 = { age: "18" }; var person2 = person1; person1.age = 20; //只修改了person1 console.log(person1, person2); //{age: 20} //{age: 20}
深拷贝
- 定义:对于引用类型而言,复制后引用类型指向一个新的内存地址,两个对象改变互不影响
js//深拷贝 var person3 = { age: "18" }; var person4 = JSON.parse(JSON.stringify(person3)); person3.age = 20; console.log(person3, person4); //{age: 20} //{age: '18'} //注意:数组的concat、slice是一层对象的深拷贝,如果对象的子属性是引用类型的话,就是浅拷贝 var a = [1, 2, 3]; var b = [4, 5]; var ab = a.concat(b); a = [2, 3]; console.log(a, b); //(2) [2, 3] (2) [4, 5] console.log(ab); // (5) [1, 2, 3, 4, 5]
5.8 【重点格式】JSON
JSON(JavaScript Object Notation, JS 对象简谱)是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会的一个子集,
采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON
成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地
提升网络传输效率 ,
- 说明
JSON 的语法 :
var obj="{'属性名':'属性值','属性名':{'属性名':'属性值'},'属性名':['值1','值1','值3']}"JSON 字符串一般用于传递数据 , 所以字符串中的函数就显得没有意义 , 在此不做研究
通过
JSON.parse()方法可以通过
JSON.stringify()方法可以
- 示例:( JSON --> 对象)
/*
定义一个 JSON 串 : 注意因为 JSON 格式里面使用双引号,
定义的时候避免矛盾,外面是使用单引号
*/
var personStr ='{"name":"张小明","age":20,"girlFriend":{"name":"铁铃","age":23},"foods":["苹果","香蕉","橘子","葡萄"],"pets":[{"petName":"大黄","petType":"dog"},{"petName":"小花","petType":"cat"}]}'
console.log(personStr) //(太长,略,本质就是一个字符串)
console.log(typeof personStr) // string
/* 将一个 JSON 串转换为对象 */
var person =JSON.parse(personStr);
console.log(person)
//{name: '张小明', age: 20, girlFriend: {…}, foods: Array(4), pets: Array(2)}
console.log(typeof person) //object
/* 获取对象属性值 */
console.log(person.name) // 张小明
console.log(person.age) // 20
console.log(person.girlFriend.name) // 铁铃
console.log(person.foods[1]) // 香蕉
console.log(person.pets[1].petName) // 小花
console.log(person.pets[1].petType) // cat- 示例:( 对象 --> JSON )
/* 定义一个对象 */
var person={
'name':'张小明',
'age':20,
'girlFriend':{
'name':'铁铃',
'age':23
},
'foods':['苹果','香蕉','橘子','葡萄'],
'pets':[
{
'petName':'大黄',
'petType':'dog'
},
{
'petName':'小花',
'petType':'cat'
}
]
}
/* 获取对象属性值 */
console.log(person.name)
console.log(person.age)
console.log(person.girlFriend.name)
console.log(person.foods[1])
console.log(person.pets[1].petName)
console.log(person.pets[1].petType)
/* 将对象转换成JSON字符串 */
var personStr =JSON.stringify(person)
console.log(personStr)
console.log(typeof personStr) // string- 前后端传递数据

六、数组
定义
数组是⼀种列表对象,它的原型中提供了遍历和修改元素的相关操作,
JavaScript 数组的⻓度和元素类型都是⾮固定的
通俗解释:把数据⼀股脑的放在⼀起就是⼀个数组
6.1 声明与读写
声明:
js// 字面量形式 var list=[1,2,true,'str'] // 构造函数声明 var list2=new Array(1,2,true,'str')
访问数组的元素:
js//通过索引(下标) var list=[1,2,3] console.log(list[0]) //通过length获取数组⻓度 var list=[1,2,3] console.log(list.length)
6.2 数组基本操作
//数组基本操作
var lst = [1,2,true,'irai']
//增 push unshift
// push 数组后面插入一条元素
lst.push(555)
console.log(lst); //(5) [1, 2, true, 'irai', 555]
// unshift 数组第一位插入一条元素
lst.unshift(999)
console.log(lst); //(6) [999, 1, 2, true, 'irai', 555]
var lst = [1,2,true,'irai']
//删 pop shift
//pop 删除数组最后一位元素
res = lst.pop() //返回值是删除的元素
console.log(lst,res); //(3) [1, 2, true] 'irai'
//shift 删除数组第一位元素
res = lst.shift() //返回值是删除的元素
console.log(lst,res); //(2) [2, true] 1
//改 索引
//查 索引6.3 数组进阶操作
splice: 这是一个非常强大的方法,熟悉之后可以做 插入操作、删除操作、替换操作...- 删除操作、插入操作、替换操作 ...
- 函数有返回值,返回的是被删除的元素
- 这个⽅法在原来的数组上直接做修改
- 清空的数组的作用
- 格式:
//lst.splice(start,deleteCount,...args)
// 第⼀个参数(start)是控制从第几位(包含)开始删除或者替换(得看第三个参数有没有值)
// 第⼆个参数(deleteCount)控制删除的数量
// 第三个参数及以后(...args)在start位置开始插入,可用逗号隔开示例:
js//1.删除下标为1的元素 var lst = [1,2,true,'irai'] lst.splice(1,1) //解释: 从下标为1开始删除,删除数量为1 console.log(lst); //(3) [1, true, 'irai'] //2.把下标为2的true替换为true false true var lst = [1,2,true,'irai'] //解释: 从下标为2开始删除,删除数量为1(true),下标为2开始插入true,false,true lst.splice(2,1,true,false,true) console.log(lst); //(6) [1, 2, true, false, true, 'irai'] //3.在下标为2的位置,也就是2和true之间插入999 var lst = [1,2,true,'irai'] lst.splice(2,0,999) //解释:从下标为2开始删除,删除数量为0,下标为2开始插入999 console.log(lst); //(5) [1, 2, 999, true, 'irai'] //4.清空数组 var lst = [1,2,true,'irai'] lst.splice(0) console.log(lst); //[]
join- 将数组类型的数据转换成字符串
- 和
toString的区别可以自定义元素之间用自定义符号隔开
- 格式:
//lst.join(seperator=",")- 示例:
//join
var lst = [1,2,true,'irai']
//不指定参数与 toString 效果一样 (默认逗号分隔)
console.log(lst.join()); //1,2,true,irai
console.log(lst.toString()); //1,2,true,irai
//指定separator(分隔符)
console.log(lst.join("-")); //1-2-true-iraiconcat- 用于连接两个或多个数组
- 不会更改现有数组,而是返回一个新数组(深拷贝),其中包含已连接数组的值
格式:
//array1.concat(array2, array3, ..., arrayX)- 示例:
//concat
var arr1 = [1,2]
var arr2 = [true,56]
var arr3 = ["irai","only"]
//两个数组连接
console.log(arr1.concat(arr2)); //(4) [1, 2, true, 56]
//多个数组连接
console.log(arr1.concat(arr2,arr3)); //(6) [1, 2, true, 56, 'irai', 'only']
//了解:使用加号运算符实现的效果是把两个数组 toString 后进行字符串连接
console.log(arr1+arr2); // 1,2true,56
console.log(arr1.toString() + arr2.toString()); //1,2true,56七、运算符与流程控制
7.1 赋值、算术、自增和自减运算符
- 赋值运算符
var url = 'iraionly.cn';- 算术运算符
* //剩法
/ //除法
+ //加法
/*
Number 类型 : 数值相加
String 类型 : 字符串连接
Array 类型 : 把数组进行 toString 后然后字符串连接
*/
- //减法
% //余数自增和自减运算符
- 前置操作 : 前置操作会在表达式前先执行
js//单独使用无所谓前置后置 var num = 1; ++num console.log(num); //2 --num console.log(num); //1 //与表达式配合使用 var num = 2; var f = 30 + ++num; //前置使用先做运算再代入表达式(30+(2+1)=33) console.log(f); //33- 后置操作 : 后置操作会在表达式最后执行
js//单独使用无所谓前置后置 var num = 1; num++ console.log(num); //2 var num = 2; var f = 30 + num++; //后置使用会先代入表达式运算再进行自增(30+2=32)-->2+1=3 console.log(f,num); //32 3
7.2 比较、逻辑运算符
- 比较运算符
| 运算符 | 说明 |
|---|---|
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于等于 |
== | 强制类型转换后比较 |
=== | 不进行类型转换比较 |
逻辑运算符
逻辑与 / 短路与
&&当运算符左边表达式为
false,立马返回当前值;当左边表达式为
true时,立马返回右边值
逻辑或 / 短路或
||- 当运算符两边有一个为
true时,则返回为true的值,否则为false
- 当运算符两边有一个为
- 逻辑非
!- 这是一个单目运算符,前面两个都是双目运算符
- 单目格式
!表达式 - 作用:对表达式的布尔值取反
优先级:
&&优先级高于||js//&& 的优先级高所以结果是 true console.log(true || false && false); // true //用 () 来提高优先级 console.log((true || false) && false); // false
7.3 调试技巧
html 和 css 调试技巧
给 a 元素设置了不同状态下的样式

快速定位元素
- 英文:

- 中文:

浏览器
f12调试 :console.log()debugger

7.3 if、if-else、三元表达式
if
//当条件为真时执行表达式代码块
if (){ //表达式或者变量=》布尔值
//条件为 true 时执行
}
//如果只有一条代码块,可以不用写 {} (为了代码可读性,不推荐省略{})
if ()
console.log(xx)
//条件为 true 时执行if-else
if (){
//条件为 true 时执行
}else{
//条件为 false 时执行
}
//if...else if...else语句
if (){
//条件1为 true 时执行
}else if(){
//条件2为 true 时执行
}else{
//条件都不满足时执行
}三元表达式
- 语法
js条件 ? 表达式1 : 表达式2 // 条件为true时,执⾏表达式1,否则执⾏表达式2- 使用场景 : 简单的条件判断(非真即假)
7.4 swich 语句
swich- 语法
jsswitch(表达式) { case n: 代码块 break; case n: 代码块 break; default: 默认代码块 } var weather = '下雨'; switch (weather) { case '晴朗': console.log('打篮球'); break; case '下雨': console.log('收衣服'); break; case '下雪': console.log('堆雪人'); break; default: console.log('学习'); break; }注意要在每一个case(default 除外)添加
break防止 case 穿透
- 和
if的选择- 有 3,4 种以上条件的时候,就可以考虑使用
switch
- 有 3,4 种以上条件的时候,就可以考虑使用
- 注意
- 内部严格按照
===的规则
- 内部严格按照
7.5 循环语句
for循环- 语法
jsfor (语句 1; 语句 2; 语句 3) { 要重复的代码块 } // 语句1 声明⼀个变量 // 语句2 指定循环跳出条件 // 语句3 控制变量的变化- 流程
- 语句 1 在循环(代码块)开始之前执行
- 语句 2 定义结束循环(代码块)的⼀个条件
- 语句 3 会在循环(代码块)运行完成后执行
- 示例:计算数字 1 - 100 的和
js// 计算数字 1 - 100 的和 var s = 0; //记录加和结果的变量 for(var i = 1;i<=100;i++){ s += i } console.log(s); //5050while循环- 语法
jsvar i=0 //循环变量 while (条件表达式或者变量 { 代码块 i++ //改变循环变量 }- 示例:计算数字 1 - 100 的和
js// 计算数字 1 - 100 的和 var i = 1,s = 0; while(i<=100){ s += i; i++; //改变循环变量 } console.log(s); //5050do-while循环- 语法
jsvar i=0 //循环变量 do { 代码块 i++ //改变循环变量 } while (条件);与
while不同就在,代码执行到这,先会至少执行一次代码体,才去检查 while 条件成不成立- 示例:(演示至少执行一次)
jsvar i = 0; do{ console.log("代码块执行~~~"); //代码块执行~~~ }while(i > 0) //i初始化为0,显然不大于0,但是也至少执行了代码块一次注意
- 别忘了改变条件中的变量,不然就写了个死循环
do-while循环能保证至少循环一次for循环语句更加紧凑
八、函数
8.1 概念
- 语法
function name(参数 1, 参数 2, 参数 3) {
//要执⾏的代码
}- 示例
var a=3,b=2;
function sum(a,b) {
return a * 10 + b
}
console.log(sum(a,b)); //32- 注意:
return作用:- 返回值
- 中断函数
- 只能写在函数体里面
8.2 形参和实参
- 形参:声明函数的时候定义的参数
function sum(a,b){ // a、b是形参
// 要执行的代码
}- 实参:调⽤函数的时候传的⼀个参数
sum(1,2) // 1、2就是实参- 特点
- 形参和实参是一一对应的
- 数量可以不对应 ( 但建议一致 )
- 参数的类型不确定 ( JS 是弱类型语言)
- 函数可以设置默认参数
- 实参可以是字面量也可以是变量
8.3 声明函数
刚才我们定义函数,是为函数指定了一个名字。 那我们也可以不为函数指定名字,
那这一类的函数,我们称之为 匿名函数 。那接下来,就来介绍一下匿名函数的定义和调用。
匿名函数 : 是指一种没有名称的函数,由于它们没有名称,
因此无法直接通过函数名来调用,而是通过变量或表达式来调用。
匿名函数定义可以通过两种方式:函数表达式 和 箭头函数。
- 函数表达式:
var add = function (a,b){
return a + b;
}- 箭头函数
var add = (a,b) => {
return a + b;
}上述匿名函数声明好了之后,是将这个函数赋值给了
add变量。那我们就可以直接通过
add函数直接调用而箭头函数这种形式,在现在的前端开发中用的会更多一些。
- 注意:声明函数过程中,函数里的语句是不会执⾏
8.4 作用域及作用域链
作用域 (Scope)
全局作用域(Global Scope)
- 挂载到
window对象上的
- 挂载到
函数作用域(Function Scope)
- 在函数内部声明的变量只能在该函数内部被访问
块级作用域(Block Scope)
块级作用域是指变量只在代码块( 如
if语句、for循环等 )内部有效。在ES6(ECMAScript 2015)之前,JavaScript 没有块级作用域的概念,
只有 函数作用域 和 全局作用域 。
ES6引入了
let和const关键字,使得变量具有块级作用域。
作用域链(Scope Chain)
作用域链是指当访问一个变量时,JavaScript 引擎会按照一定的顺序查找该变量的作用域。
查找顺序是从当前作用域开始,逐级向上查找,直到找到该变量或到达全局作用域。
换言之:内层函数是可以访问外层函数声明的⼀个变量,反之,则不可以
且遵循
就近原则, 逐级向上查找 , 同名的先用就近的
8.5 arguments 参数
arguments- 是用来取参的
- 传入的实参都能在函数体⾥通过
arguments类数组取到 - 具有数组的⼀些特点
- 通过索引取参数
- 有长度
- 示例
function irai(a,b,c,d,e){
for(var i = 0;i < arguments.length;i++){
console.log(arguments[i]); //可以取到一个个传入的实参
/*output:
4
5
6
7
8
*/
}
}
irai(4,5,6,7,8)8.6 【了解】编程思想-递归
- 什么是递归
- 函数自己调用自己
- 注意:要有终止递归的条件,不然就变成死循环了
示例:
LC. 70. 爬楼梯假设你正在爬楼梯。需要
n阶你才能到达楼顶。每次你可以爬
1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?示例 1:
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
- 示例代码 (递归):
jsvar climbStairs = function(n) { //爬到第 k 阶台阶的方法数可以由以下两种方式: //1. 从第 k - 1 阶台阶 爬 1 个台阶 //2. 从第 k - 2 阶台阶 爬 2 个台阶 //即 递推公式 : f(n) = f(n-1) + f(n-2) // 会超时的递归代码 function f(n){ //递归终止条件 if(n <= 2){ //爬到第一阶的方法数1种 //爬到第二阶的方法数2种 return n; } //n > 2 时递归 return f(n-1) + f(n-2) } return f(n) };
在
LeetCode上运行可以通过case,但是提交的话就会 超时

超时的原因是其实递归是一种比较暴力的算法,它会重复计算很多已经计算过的工作
为了不超时可以把递归改进为 - -
记忆化搜索(了解)
- 示例代码(记忆化搜索)(了解即可):
var climbStairs = function(n) {
//爬到第 k 阶台阶的方法数可以由以下两种方式:
//1. 从第 k - 1 阶台阶 爬 1 个台阶
//2. 从第 k - 2 阶台阶 爬 2 个台阶
//即 递推公式 : f(n) = f(n-1) + f(n-2)
memo = Array(n + 1).fill(0); //memo数组记录递归函数的入参和返回值
function f(n){
//递归终止条件
if(n <= 2){
//爬到第一阶的方法数1种
//爬到第二阶的方法数2种
return n;
}
//n > 2 时递归
if (memo[n]) { // 之前计算过
return memo[n];
}
res = f(n - 1) + f(n - 2);
memo[n] = res // 记忆化
return res
}
return f(n)
};记忆化搜索就可以通过测试了:
8.7 立即执行函数
立即执行函数(immediately invoked function expression , IIFE )
- 括号的作用
- 帮助我们调用函数
- 特点
- 匿名函数
- 自动执行
- 执行完成以后销毁
- 写法
js(function(形参1,形参2,...) { //函数体 })(实参1,实参2,...);- 注意
- !!!!! 立即执行函数前面的语句必须加分号,否则会解析错误
- 可以传参数
- 括号的作用
九 、【面试】 闭包
9.1 引子
- 《 关于一个计数器引发的思考 》
// 初始化计数器
var count = 0;
// 递增计数器的函数
function add() {
count += 1;
}
// 调用三次 add()
add();
add();
add();
console.log(count); // 3
// 目前还没发现有什么异常~
// 当突然有个外部影响
count = 8
console.log(count); // 8最后发生这种改变不是我们希望的 ...
我们希望的是 ” 只有调用 add 的时候才改变计数器的值,其他方式没法改变 “
思考:那可以把 count 的声明放在函数内部吗??这样就只有 add 可以使用了啊??
结果是不行的,因为声明在内部就变成了 函数作用域形式的变量 了
当这个函数执行完成的时候,函数会被销毁,同时这个计数器变量也会被销毁 ...
9.2 什么是闭包?
- 闭包
- 闭包是指有权访问另⼀个函数作用域中的变量的函数
- 闭包的副作用
- 产生内存泄漏
- ⽐如说我本来要销毁函数的数据,被强行保存下来了,保存在内存当中
- 产生内存泄漏
- 通过闭包能实现什么
- 实现外界访问函数体内部的变量
- 示例:( 使用闭包解决
9.1的疑惑 )
// 递增计数器的函数
function add() {
var count = 0; // 闭包中的变量
return {
// 返回一个对象,包含对count的引用和修改它的函数
getCount: function() {
return count;
},
increment: function() {
count += 1;
}
};
}
var num = add() // 需要一个变量接收 return 回来的对象
//获取count的值
console.log(num.getCount()); // 0
//调用三次自增1
num.increment()
num.increment()
num.increment()
//获取count的值
console.log(num.getCount()); //3
//外部意外使用 ?? (使用不了...)
// console.log(count); //Uncaught ReferenceError: count is not defined十、【面试】原型&原型链
10.1 原型(prototype)
原型是
function对象的一个属性,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象
- 示例 :
//1.字面量创建对象 (这些对象会继承 Object.prototype)
var obj = {name:"小明"}
console.log(obj);
//2.构造函数
function Car(brand,color){
this.brand = brand
this.color = color
}
var car = new Car("xiaomi","black");
console.log(car);
//3.普通函数
function add(){
var a = 1;
}
console.log(add.prototype);- 运行结果:

- 示例:
function Person(){
}
function Car(brand,color){
this.brand = brand
this.color = color
}
Person.prototype.lastName='irai'
var person1 = new Person()
var car = new Car("xiaomi","black");
console.log(Person.prototype.lastName); //irai
console.log(Person.lastName); //undefined
console.log(person1.lastName); //irai
//注意: 虽然都继承于原型,但互不干扰
console.log(Car.prototype.lastName); //undefined
console.log(car.lastName); //undefined- 原型的作用
- 给我们构造函数实例化出来的对象设置公共的属性或者方法使用的
方法写在哪
- 方法写在原型上
- 写在构造函数里的方法和属性会重新克隆⼀次,会导致占用内存较高
- 需要配置的属性是写在构造函数上
- 方法写在原型上
只有构造函数才能对原型上的属性进行改动
10.2 原型链 (prototype chain)
- 函数才有
prototype属性 , 对象有__proto__ || [[prototype]]属性
- 原型链
- js ⾥万物皆对象,所以⼀直访问
__proto__属性就会产生⼀条链条 - 链条的尽头是
null - 当 js 引擎查找对象的属性时,会先判断对象本身是否存在该属性
- 不存在的属性就会沿着原型链往上找
- js ⾥万物皆对象,所以⼀直访问

function Car() {}
var car = new Car()
console.log(car.__proto__); //{}
console.log(car.__proto__.__proto__); //{__defineGetter__: ƒ, __defineSetter__: ƒ, …}
console.log(car.__proto__.__proto__.__proto__); //null原型链解决的主要是继承问题
每个对象拥有一个原型对象,通过
proto指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,
最终指向 null (Object.proptotype. __ proto __ 指向的是 null ) 。
这种关系被称为原型链 ( prototype chain ),通过原型链一个对象可以拥有定义在其他对象中的属性和方法
10.3 类型检测
typeof- 用于判断基础数据的类型,无法区分对象与数组
jsvar a = 1; console.log(typeof a); //number var b = "1"; console.log(typeof b); //string var irai; console.log(typeof xd); //undefined function fun() {} console.log(typeof fun); //function var c = [1, 2, 3]; console.log(typeof c); //object var d = { name: "irai" }; console.log(typeof d); //object
instanceof用于判断复杂数据的类型,可以区分对象与数组
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,可以理解为
jsvar ir = []; var irai = {}; console.log(ir instanceof Array); //true console.log(irai instanceof Array); //false var a = [1, 2, 3]; console.log(a instanceof Array); //true var b = { name: "iraionly.cn" }; console.log(b instanceof Object); //true function Fun() {} var irai = new Fun(); console.log(irai instanceof Fun); //true
十一、DOM 编程
11.1 概述
简单来说 : DOM ( Document Object Model 文档对象模型 ) 编程就是
使用 document 对象的 API 完成对网页 HTML 文档进行动态修改 ,
以实现网页数据和样式动态变化效果的编程.
document 对象代表整个 html 文档,可用来访问页面中的所有元素,
是最复杂的一个dom对象,可以说是学习好 dom 编程的关键所在。
根据 HTML 代码结构特点 , document 对象本身是一种树形结构的文档对象。

- 上述代码生成的树如下:

DOM 编程其实就是用 window 对象的 document 属性的相关 API 完成对页面元素的控制的编程
( BOM 编程 将在后续 十二章 讲到 ....)

dom树中节点的类型node节点 , 所有结点的父类型element元素节点 , node 的子类型之一,代表一个完整标签attribute属性节点 , node 的子类型之一,代表元素的属性text文本节点 , node 的子类型之一,代表双标签中间的文本
11.2 DOM节点的常见操作
DOM 编程的核心思想:将网页的内容当做对象来处理,
标签的所有属性在该对象上都可以找到,并且修改这个对象的属性,就会自动映射到标签身上。
11.2.1 查找节点
在早期的 JS 中,我们也可以通过如下方法获取DOM元素(了解)(👎 不推荐使用)
| 方法 | 描述 |
|---|---|
| document.getElementById(id) | 根据 id 属性值获取,返回单个 Element 对象。 |
| document.getElementsByTagName(name) | 根据标签名称获取,返回 Element 对象数组。 |
| document.getElementsByClassName(name) | 根据 class 属性值获取,返回 Element 对象数组。 |
| document.getElementsByName(name) | 根据 name 属性值获取,返回 Element 对象数组 |
现在常用 CSS 选择器来获取 DOM 元素 (👍 推荐使用)
| 方法 | 描述 |
|---|---|
| document.querySelector('CSS选择器') | 根据CSS选择器来获取DOM元素,获取到匹配到的第一个元素 |
| document.querySelectorAll('CSS选择器') | 根据CSS选择器来获取DOM元素,获取匹配到的所有元素 |
- 注意:获取到的所有元素,会封装到一个 NodeList 节点集合中,
是一个伪数组(有长度、有索引的数组,但没有 push 、pop 等数组方法)
11.2.1+ 子节点&父节点&兄弟节点
在
11.2.1根据选择器获取到某个节点后,可以根据这个节点再去选择 子节点、父节点、兄弟节点 ...可以理解成 : 前面是
直接获取, 这里是间接获取
- 子节点
| 功能 | API | 返回值 |
|---|---|---|
| 查找子标签 | element.children | 返回子标签数组 |
| 查找第一个子标签 | element.firstElementChild | 标签对象 |
| 查找最后一个子标签 | element.lastElementChild | 标签对象 |
- 父节点
| 功能 | API | 返回值 |
|---|---|---|
| 查找指定元素节点的父标签 | element.parentElement | 标签对象 |
- 兄弟节点
| 功能 | API | 返回值 |
|---|---|---|
| 查找前一个兄弟标签 | node.previousElementSibling | 标签对象 |
| 查找后一个兄弟标签 | node.nextElementSibling | 标签对象 |
11.2.2 【插叙】事件的绑定
为什么叫插叙呢?
因为在学完怎么用 DOM 编程 获取到节点后,
我们会经常使用这个节点做一些事件的动态绑定。
但同时事件的绑定也可以是静态的(直接写在 html 标签的属性部分里)
所以就很难界定这个属不属于 父标题
DOM常见操作 ~ ~
11.2.2.1 什么是事件
HTML 事件可以是浏览器行为,也可以是用户行为。
当这些一些行为发生时,可以自动触发对应的 JS 函数的运行,
我们称之为事件发生. JS 的事件驱动指的就是行为触发代码运行的这种特点
鼠标事件
| 属性 | 描述 |
|---|---|
| onclick | 当用户点击某个对象时调用的事件句柄。 |
| oncontextmenu | 在用户点击鼠标右键打开上下文菜单时触发 |
| ondblclick | 当用户双击某个对象时调用的事件句柄。 |
| onmousedown | 鼠标按钮被按下。 |
| onmouseenter | 当鼠标指针移动到元素上时触发。 |
| onmouseleave | 当鼠标指针移出元素时触发 |
| onmousemove | 鼠标被移动。 |
| onmouseover | 鼠标移到某元素之上。 |
| onmouseout | 鼠标从某元素移开。 |
| onmouseup | 鼠标按键被松开。 |
键盘事件
| 属性 | 描述 |
|---|---|
| onkeydown | 某个键盘按键被按下。 |
| onkeypress | 某个键盘按键被按下并松开。 |
| onkeyup | 某个键盘按键被松开。 |
表单事件
| 属性 | 描述 |
|---|---|
| onblur | 元素失去焦点时触发 |
| onchange | 该事件在表单元素的内容改变时触发 ( <input>, <keygen>, <select>, 和 <textarea>) |
| onfocus | 元素获取焦点时触发 |
| onfocusin | 元素即将获取焦点时触发 |
| onfocusout | 元素即将失去焦点时触发 |
| oninput | 元素获取用户输入时触发 |
| onreset | 表单重置时触发 |
| onsearch | 用户向搜索域输入文本时触发 ( <input="search">) |
| onselect | 用户选取文本时触发 ( <input> 和 <textarea>) |
| onsubmit | 表单提交时触发 |
11.2.2.2 事件绑定
通过属性绑定(静态绑定)
- 示例代码:
<body>
<input type="text"
onkeydown="testDown1(),testDown2()"
onfocus="testFocus()"
onblur="testBlur()"
onchange="testChange(this)"
onmouseover="testMouseOver()"
onmouseleave="testMouseLeave()"
onmousemove="testMouseMove()"
/>
<script>
function testDown1(){
console.log("down1")
}
function testDown2(){
console.log("down2")
}
function testFocus(){
console.log("获得焦点")
}
function testBlur(){
console.log("失去焦点")
}
function testChange(input){
console.log("内容改变")
console.log(input.value);
}
function testMouseOver(){
console.log("鼠标悬停")
}
function testMouseLeave(){
console.log("鼠标离开")
}
function testMouseMove(){
console.log("鼠标移动")
}
</script>
</body>- 说明
- 通过事件属性绑定函数,在行为发生时会自动执行函数
- 一个事件可以同时绑定多个函数
- 一个元素可以同时绑定多个事件
- 方法中可以传入
this对象,代表当前元素
通过 DOM 编程绑定 (动态绑定)
- 页面加载完毕事件 -
window.onload
在进行 动态绑定的 时候我们需要用到
window.onload,它是一个事件处理程序,当整个网页(包括所有资源,如图片、样式表、脚本等)加载完成后,
该事件会被触发。它通常用于确保在文档和所有资源完全加载后执行某些操作
为什么需要这个?
想一下,在动态绑定中有一个关键步骤就是
而会不会有一种可能, DOM 对象都还没加载出来,就去执行获取的话,这就会导致获取不到
所以
window.onload核心在于 :
- 示例代码:
<body>
<input id="in1" type="text" />
<script>
// 页面加载完毕事件,浏览器加载完整个文档行为
window.onload=function(){
var in1 = document.querySelector("#in1");
// 通过DOM编程绑定事件
in1.onchange=testChange
}
function testChange(){
console.log("内容改变")
console.log(event.target.value);
}
</script>
</body>说明:
event:是一个全局对象,表示当前正在处理的事件。它包含了事件的各种信息,如事件类型、触发事件的元素等。
event.target:表示触发事件的 DOM 元素。无论事件是如何冒泡或捕获的,event.target 总是指向实际触发事件的元素。
event.target.value:表示触发事件的 DOM 元素的当前值。对于表单元素( 如
<input>、<textarea>、select等 ),value属性返回该元素的当前值。
11.2.2.3 事件监听
在前面的两个方式(静态绑定 & 动态绑定)本质上是一样的
都是 来进行事件绑定
- 对于某个事件来说,怎么做出反应
- 通过元素的事件属性
- 启用事件监听器
- 什么是事件监听器
addEventListener给 DOM 对象添加事件处理程序removeEventListener删除给DOM对象的事件处理程序
- 什么是事件监听器
- (添加事件监听)语法
element.addEventListener(eventType, eventHandler, useCapture);eventType:字符串,表示要监听的事件类型,如"click"、"change"、"load"等。eventHandler:事件处理函数,当事件发生时,该函数会被调用。useCapture:布尔值,表示事件监听器是在捕获阶段还是冒泡阶段触发。默认值为
false( 冒泡阶段 )。如果设置为true,则在捕获阶段触发。
- (删除事件监听)语法
element.removeEventListener(eventType, eventHandler, useCapture);eventType:字符串,表示要移除的事件类型。eventHandler:要移除的事件处理函数。必须与添加时的函数完全相同。useCapture:布尔值,表示事件监听器是在捕获阶段还是冒泡阶段触发。必须与添加时的值一致。
- 改变元素属性值 和 添加事件监听 的区别
- 改变元素属性值 会被覆盖
addEventListener可以同时注册多个,根据注册顺序,先后执行
- 示例代码:
<body>
<button id="myButton">点击我</button>
<script>
// 获取按钮元素
var button = document.querySelector("#myButton");
// 定义事件处理函数
function handleClick(event) {
console.log("按钮被点击了");
console.log("事件类型:", event.type);
console.log("触发事件的元素:", event.target);
}
// 添加事件监听器
button.addEventListener("click", handleClick);
// 移除事件监听器(可选)
// button.removeEventListener("click", handleClick);
</script>
</body>- 注意:
- 在定义事件处理函数时,是需要传入参数
event的 event对象提供了关于事件的详细信息,如事件类型、触发事件的元素、事件发生的时间等- 常见的属性和方法包括:
event.type:事件类型,如"click"、"change"等。event.target:触发事件的元素。event.currentTarget:绑定事件监听器的元素。event.preventDefault():阻止事件的默认行为。event.stopPropagation():阻止事件的进一步传播(冒泡或捕获)。
- 在定义事件处理函数时,是需要传入参数
11.2.2.4 事件流机制
- 事件传播的两种机制
- 冒泡
- 捕获
- 图解事件捕获和事件冒泡

- 示例代码:
<body>
<ul id="outer">
我是一个列表
<li id="inner">第一个列表项</li>
</ul>
<script>
var outer = document.querySelector("#outer");
var inner = document.querySelector("#inner");
// 捕获阶段(useCapture 设置为 true,则在捕获阶段触发)
outer.addEventListener("click", function(event) {
console.log("捕获阶段 - 外层 ul 被点击");
}, true);
// 冒泡阶段
inner.addEventListener("click", function(event) {
console.log("冒泡阶段 - 内层 li 被点击");
});
// 捕获阶段(useCapture 设置为 true,则在捕获阶段触发)
inner.addEventListener("click", function(event) {
console.log("捕获阶段 - 内层 li 被点击");
}, true);
</script>
</body>事件代理
思考:父级那么多子元素,怎么区分事件本应该是哪个子元素的
事件代理其实就是,比如一个
ul有很多个li那么其实事件监听只需要设置给
ul, 在点击 任意一个li都会冒泡上去ul那么就可以使用
event.target来区分出触发这个事件的实际元素是谁
- 示例代码:
<body>
<ul id="father">
<li id="son">第1个</li>
<li id="son">第2个</li>
<li id="son">第3个</li>
<li id="son">第4个</li>
<li id="son">第5个</li>
</ul>
<script>
document.querySelector("#father").addEventListener(
"click",
function(event){
console.log(event.target.textContent + "被点击了");
}
)
</script>
</body>event.stopPropagation():阻止事件的进一步传播(冒泡或捕获)。
11.2.3 改变元素内容
| 方法 | 描述 |
|---|---|
| element.innerText | 获取或改变标签体的文本内容 |
| element.innerHTML | 获取或改变元素的 inner HTML |
| element.attribute = new value | 改变 HTML 元素的属性值 |
| element.setAttribute(attribute, value) | 改变 HTML 元素的属性值 |
| element.style.property = new style | 改变 HTML 元素的样式 |
通过示例说明如何使用:
- 初始代码:
html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DOM-操作</title> <style> * { margin: 0; padding: 0; } .outer { margin: 50px auto; } div { width: 100px; height: 100px; } #title1 { background-color: red; } .title2 { background-color: blue; } .title3 { background-color: green; } </style> </head> <body> <div class="outer"> <div id="title1">111</div> <div class="title2">222</div> <div class="title3"><h1>333</h1></div> </div> </body> </html>- 初始效果:

- 现通过 获取到 dom 元素修改对应对象的属性
html<script> //1. 获取第三个div var title3 = document.querySelector(".title3") //1.1 分别获取 innerText 和 innerHTML console.log(title3.innerText); // 333 console.log(title3.innerHTML); // <h1>333</h1> //1.2 通过 innerHTML 修改为 <em><s>5555</s></em> title3.innerHTML = "<em><s>5555</s></em>" </script>修改效果:

html<script> //1. 获取第三个div var title3 = document.querySelector(".title3") //1.1 分别获取 innerText 和 innerHTML console.log(title3.innerText); // 333 console.log(title3.innerHTML); // <h1>333</h1> //1.2 通过 innerHTML 修改为 <em><s>5555</s></em> title3.innerHTML = "<em><s>5555</s></em>" //2. 获取第一个div var title1 = document.querySelector("#title1") //改变样式 title1.style.width = "150px" title1.style.height = "150px" //关键点: 像 background-color 这种样式名,需要去掉 - , 改成大驼峰(- 后面单词首字母大写) title1.style.backgroundColor = "pink" </script>- 修改效果:

11.2.4 添加和删除元素
| 方法 | 描述 |
|---|---|
| document.createElement(“标签名”) | 创建元素节点并返回,但不会自动添加到文档中 |
| 元素.remove() | 删除当前元素 |
| 父元素.appendChild(ele) | 将 ele 添加到 element 所有子节点后面 |
| 父元素.insertBefore(newEle,targetEle) | 将 newEle 插入到 targetEle 前面 |
| 父元素.replaceChild(newEle, oldEle) | 用新的节点替换原有的某个旧子节点 |
| document.write(text) | 可写入 HTML |
- 示例代码:
<body>
<ul id="city">
<li id="bj">北京</li>
<li id="sh">上海</li>
<li id="sz">深圳</li>
<li id="gz">广州</li>
</ul>
<hr>
<!-- 目标1 在城市列表的最后添加一个子标签 <li id="cs">长沙</li> -->
<button onclick="addCs()">增加长沙</button>
<!-- 目标2 在城市列表的深圳前添加一个子标签 <li id="cs">长沙</li> -->
<button onclick="addCsBeforeSz()">在深圳前插入长沙</button>
<!-- 目标3 将城市列表的深圳替换为 <li id="cs">长沙</li> -->
<button onclick="replaceSz()">替换深圳</button>
<!-- 目标4 将城市列表删除深圳 -->
<button onclick="removeSz()">删除深圳</button>
<!-- 目标5 清空城市列表 -->
<button onclick="clearCity()">清空</button>
<script>
function addCs(){
// 创建一个新的元素
// 创建元素
var csli =document.createElement("li") // <li></li>
// 设置子元素的属性和文本 <li id="cs">长沙</li>
csli.id="cs"
csli.innerText="长沙"
// 将子元素放入父元素中
var cityul =document.getElementById("city")
// 在父元素中追加子元素
cityul.appendChild(csli)
}
function addCsBeforeSz(){
// 创建一个新的元素
// 创建元素
var csli =document.createElement("li") // <li></li>
// 设置子元素的属性和文本 <li id="cs">长沙</li>
csli.id="cs"
csli.innerText="长沙"
// 将子元素放入父元素中
var cityul =document.getElementById("city")
// 在父元素中追加子元素
//cityul.insertBefore(新元素,参照元素)
var szli =document.getElementById("sz")
cityul.insertBefore(csli,szli)
}
function replaceSz(){
// 创建一个新的元素
// 创建元素
var csli =document.createElement("li") // <li></li>
// 设置子元素的属性和文本 <li id="cs">长沙</li>
csli.id="cs"
csli.innerText="长沙"
// 将子元素放入父元素中
var cityul =document.getElementById("city")
// 在父元素中追加子元素
//cityul.replaceChild(新元素,被替换的元素)
var szli =document.getElementById("sz")
cityul.replaceChild(csli,szli)
}
function removeSz(){
var szli =document.getElementById("sz")
// 哪个元素调用了remove该元素就会从 dom 树中移除
szli.remove()
}
function clearCity(){
var cityul =document.getElementById("city")
var fc =cityul.firstChild
while(fc != null ){
fc.remove()
fc =cityul.firstChild
}
// 或者 cityul.innerHTML=""
}
</script>
</body>十二、BOM 编程
12.1 概述
BOM 是
Browser Object Model的简写,即 浏览器对象模型。BOM 由一系列对象组成,是访问、控制、修改浏览器的属性和方法
( 通过window对象及属性的一系列方法 控制浏览器行为的一种编程 )
BOM 没有统一的标准 ( 每种客户端都可以自定标准 )。
BOM 编程是将浏览器窗口的各个组成部分抽象成各个对象,通过各个对象的API操作组件行为的一种编程
BOM 编程的对象结构如下
window顶级对象,代表整个浏览器窗口location对象 window对象的属性之一,代表浏览器的地址栏history对象 window对象的属性之一,代表浏览器的访问历史screen对象 window对象的属性之一,代表屏幕navigator对象 window对象的属性之一,代表浏览器软件本身document对象 window对象的属性之一,代表浏览器窗口目前解析的 html 文档console对象 window对象的属性之一,代表浏览器开发者工具的控制台localStorage对象 window对象的属性之一,代表浏览器的本地数据持久化存储sessionStorage对象 window对象的属性之一,代表浏览器的本地数据会话级存储

12.2 BOM 控制浏览器行为
window.alert中的window可省略 , 类似同理 ...
三种弹窗方式
- 警告框
jsalert('hello')- 确认框
jsvar isConfirm = confirm('请确认') console.log('下⼀步', isConfirm) // 有返回值的- 提示框
jsvar isPrompt = prompt('请输⼊姓名') console.log(isPrompt) // 是 null 或者 用户输入的值
- 页面跳转
<body>
<input type="button" value="跳转到百度" onclick="goBaidu()"/> <br>
<script>
function goBaidu(){
var flag =confirm("即将跳转到百度,本页信息即将丢失,确定吗?")
if(flag){
// 通过BOM编程地址栏url切换
window.location.href="https://www.baidu.com"
}
}
</script>
</body>12.3 持久级存储和会话级存储
会话级数据 : 内存型数据,是浏览器在内存上临时存储的数据,浏览器关闭后,数据失去,
通过
window的sessionStorge属性实现,使用sessionStorage.getItem()获取
持久级数据 : 磁盘型数据,是浏览器在磁盘上持久存储的数据,浏览器关闭后,数据仍在,
通过
window的localStorge实现,使用localStorage.getItem()获取
可以用于将来存储一些服务端响应回来的数据 , 比如:
token令牌,或者一些其他功能数据,根据数据的业务范围我们可以选择数据存储的
会话/持久级别
- 示例代码:
<body>
<button onclick="saveItem()">存储数据</button>
<button onclick="removeItem()">删除数据</button>
<button onclick="readItem()">读取数据</button>
<script>
function saveItem(){
// 让浏览器存储一些会话级数据
window.sessionStorage.setItem("sessionMsg","sessionValue")
// 让浏览器存储一些持久级数据
window.localStorage.setItem("localMsg","localValue")
console.log("haha")
}
function removeItem(){
// 删除数据
sessionStorage.removeItem("sessionMsg")
localStorage.removeItem("localMsg")
}
function readItem(){
console.log("read")
// 读取数据
console.log("session:"+sessionStorage.getItem("sessionMsg"))
console.log("local:"+localStorage.getItem("localMsg"))
}
</script>
</body>- 在 F12开发者工具 的 应用程序栏 , 可以查看数据的状态

12.4 定时器
延迟执行
jssetTimeout(function(){}, 毫秒)停止
jsclearTimeout(timer) // 参数必须是由 setTimeout() 返回的timer
定时执行
jssetInterval(function(){}, 毫秒)停止
jsclearInterval(timer) // 参数必须是由 setInterval() 返回的timer
- 示例代码:
<body>
<button onclick="stopTimer()">停止输出实时时间</button>
<button onclick="startTimer()">开始输出实时时间</button>
<script>
// 1. 延迟执行
//三秒(3000毫秒)后控制台打印 555
setTimeout(function(){
console.log(555);
},3000)
// 2 . 定时执行
//定时器ID
var timer_id = null;
// 开启定时器的函数
function startTimer() {
if (timer_id) {
alert('定时器已经开启!');
return;
}
timer_id = setInterval(() => {
var d = new Date()
console.log(d.toLocaleDateString() + "\t" + d.toLocaleTimeString());
}, 1000); // 每隔1秒执行一次
}
// 关闭定时器的函数
function stopTimer() {
if (!timer_id) {
alert('定时器尚未开启!');
return;
}
clearInterval(timer_id); // 清除定时器
timerId = null; // 重置定时器ID
console.log("已经关闭定时器!");
}
</script>
</body>- 补充:定时器是一个异步任务,不会影响主线程任务的执行
<script>
console.log(111);
console.log(222);
console.log(333);
setTimeout(function(){
console.log(444);
},3000)//是个异步任务,会挂去任务队列,先会输出下面555,不会阻塞主线程
console.log(555);
/*output:
111
222
333
555
444
*/
</script>十三、正则表达式
13.1 简介
正则表达式是描述字符模式的对象。
正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具。
- 语法
var patt=new RegExp(pattern,modifiers);
//或者更简单的方式:
var patt=/pattern/modifiers;- 修饰符(modifiers)
| 修饰符 | 描述 |
|---|---|
| i | 执行对大小写不敏感的匹配。 |
| g | 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。 |
| m | 执行多行匹配。 |
- 方括号(中括号)
| 表达式 | 描述 |
|---|---|
| [abc] | 查找方括号之间的任何字符。 |
| [^abc] | 查找任何不在方括号之间的字符。 |
| [0-9] | 查找任何从 0 至 9 的数字。 |
| [a-z] | 查找任何从小写 a 到小写 z 的字符。 |
| [A-Z] | 查找任何从大写 A 到大写 Z 的字符。 |
| [A-z] | 查找任何从大写 A 到小写 z 的字符。 |
| [adgk] | 查找给定集合内的任何字符。 |
| [^adgk] | 查找给定集合外的任何字符。 |
| (red|blue|green) | 查找任何指定的选项。 |
- 元字符
| 元字符 | 描述 |
|---|---|
| . | 查找单个字符,除了换行和行结束符。 |
| \w | 查找数字、字母及下划线。 |
| \W | 查找非单词字符。 |
| \d | 查找数字。 |
| \D | 查找非数字字符。 |
| \s | 查找空白字符。 |
| \S | 查找非空白字符。 |
| \b | 匹配单词边界。 |
| \B | 匹配非单词边界。 |
| \0 | 查找 NULL 字符。 |
| \n | 查找换行符。 |
| \f | 查找换页符。 |
| \r | 查找回车符。 |
| \t | 查找制表符。 |
| \v | 查找垂直制表符。 |
| \xxx | 查找以八进制数 xxx 规定的字符。 |
| \xdd | 查找以十六进制数 dd 规定的字符。 |
| \uxxxx | 查找以十六进制数 xxxx 规定的 Unicode 字符。 |
- 量词
| 量词 | 描述 |
|---|---|
| n+ | 匹配任何包含至少一个 n 的字符串。例如,/a+/ 匹配 "candy" 中的 "a","caaaaaaandy" 中所有的 "a"。 |
| n* | 匹配任何包含零个或多个 n 的字符串。例如,/bo*/ 匹配 "A ghost booooed" 中的 "boooo","A bird warbled" 中的 "b",但是不匹配 "A goat grunted"。 |
| n? | 匹配任何包含零个或一个 n 的字符串。例如,/e?le?/ 匹配 "angel" 中的 "el","angle" 中的 "le"。 |
| n{X} | 匹配包含 X 个 n 的序列的字符串。例如,/a{2}/ 不匹配 "candy," 中的 "a",但是匹配 "caandy," 中的两个 "a",且匹配 "caaandy." 中的前两个 "a"。 |
| n{X,} | X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。例如,/a{2,}/ 不匹配 "candy" 中的 "a",但是匹配 "caandy" 和 "caaaaaaandy." 中所有的 "a"。 |
| n{X,Y} | X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。例如,/a{1,3}/ 不匹配 "cndy",匹配 "candy," 中的 "a","caandy," 中的两个 "a",匹配 "caaaaaaandy" 中的前面三个 "a"。注意,当匹配 "caaaaaaandy" 时,即使原始字符串拥有更多的 "a",匹配项也是 "aaa"。 |
| n$ | 匹配任何结尾为 n 的字符串。 |
| ^n | 匹配任何开头为 n 的字符串。 |
| ?=n | 匹配任何其后紧接指定字符串 n 的字符串。 |
| ?!n | 匹配任何其后没有紧接指定字符串 n 的字符串。 |
RegExp对象方法
| 方法 | 描述 |
|---|---|
| compile | 在 1.5 版本中已废弃。 编译正则表达式。 |
| exec | 检索字符串中指定的值。返回找到的值,并确定其位置。 |
| test | 检索字符串中指定的值。返回 true 或 false。 |
| toString | 返回正则表达式的字符串。 |
- 支持正则的 String 的方法
| 方法 | 描述 |
|---|---|
| search | 检索与正则表达式相匹配的值。 |
| match | 找到一个或多个正则表达式的匹配。 |
| replace | 替换与正则表达式匹配的子串。 |
| split | 把字符串分割为字符串数组。 |
13.2 正则体验
13.2.1 验证
注意:这里是用 正则表达式对象 来调用方法
// 创建一个最简单的正则表达式对象
var reg = /o/;
// 创建一个字符串对象作为目标字符串
var str = 'Hello World!';
// 调用正则表达式对象的test()方法验证目标字符串是否满足我们指定的这个模式,返回结果true
console.log("/o/.test('Hello World!')="+reg.test(str));
// /o/.test('Hello World!')=true13.2.2 (全局)匹配
// 创建一个正则表达式对象
// 修饰符 g 表示 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
var reg = /o/g;
// 创建一个字符串对象作为目标字符串
var str = 'Hello World!';
// 在目标字符串中查找匹配的字符,返回匹配结果组成的数组
var resultArr = str.match(reg);
console.log("resultArr.length="+ resultArr.length); //resultArr.length=2
console.log(resultArr); // (2) ['o', 'o']13.2.3 替换
注意:这里是使用 字符串对象 来调用方法
// 创建一个正则表达式对象
var reg = /o/g;
// 创建一个字符串对象作为目标字符串
var str = 'Hello World!';
var newStr = str.replace(reg,'@');
console.log("str.replace(reg)="+newStr); // str.replace(reg)=Hell@ W@rld!
// 原字符串并没有变化,只是返回了一个新字符串
console.log("str="+str); // str=Hello World!13.2.4 忽略大小写
//目标字符串
var targetStr = 'Hello WORLD!';
//使用了忽略大小写的正则表达式
// i(ignore): 执行对大小写不敏感的匹配。
var reg = /o/gi;
//获取全部匹配
var resultArr = targetStr.match(reg);
//数组长度为2
console.log("resultArr.length="+resultArr.length);
//遍历数组,得到'o'和'O'
for(var i = 0; i < resultArr.length; i++){
console.log("resultArr["+i+"]="+resultArr[i]);
}11.2.5 元字符使用
var str01 = 'I love Java';
var str02 = 'Java love me';
// 匹配以Java开头
var reg = /^Java/g;
console.log('reg.test(str01)='+reg.test(str01)); // false
console.log('reg.test(str02)='+reg.test(str02)); // truevar str01 = 'I love Java';
var str02 = 'Java love me';
// 匹配以Java结尾
var reg = /Java$/g;
console.log('reg.test(str01)='+reg.test(str01)); // true
console.log('reg.test(str02)='+reg.test(str02)); // false11.2.6 字符集合的使用
//n位数字的正则
var targetStr="123456789";
var reg=/^[0-9]{0,}$/;
//或者 : var reg=/^\d*$/;
var b = reg.test(targetStr);//true//数字+字母+下划线,6-16位
var targetStr="HelloWorld";
var reg=/^[a-z0-9A-Z_]{6,16}$/;
var b = reg.test(targetStr);//true11.2.7 常用正则
| 需求 | 正则表达式 |
|---|---|
| 用户名 | /^[a-zA-Z ][a-zA-Z-0-9]{5,9}$/ |
| 密码 | /^[a-zA-Z0-9 _-@#& *]{6,12}$/ |
| 前后空格 | /^\s+\s+$/g |
| 电子邮箱 | /^[a-zA-Z0-9 _.-]+@([a-zA-Z0-9-]+[.]{1})+[a-zA-Z]+$/ |
十四、Ajax & Axios
14.1 概述
Ajax: 全称Asynchronous JavaScript And XML,异步的 JavaScript 和 XML 。XML:(英语:Extensible Markup Language)可扩展标记语言,
本质是一种数据格式,可以用来存储复杂的数据结构。
Ajax作用有如下 2 点 :与服务器进行数据交换:通过
Ajax可以给服务器发送请求,并获取服务器响应的数据。异步交互:可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分网页 的技术,
如:搜索联想、用户名是否可用的校验等等。
详细解释一下这两点作用:
与服务器进行数据交互
如下图所示前端资源被浏览器解析,但是前端页面上缺少数据,前端可以通过 Ajax 技术,
向后台服务器发起请求,后台服务器接受到前端的请求,从数据库中获取前端需要的资源,
然后响应给前端,前端在通过我们学习的
vue技术,可以将数据展示到页面上,这样用户就能看到完整的页面了。此处可以对比
JavaSE中的网络编程技术来理解

异步交互 : 可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分网页 的技术
如下图所示,当我们再百度搜索 java 时,下面的联想数据是通过 Ajax 请求从后台服务器得到的,
在整个过程中,我们的 Ajax 请求不会导致整个百度页面的重新加载,
并且只针对搜索栏这局部模块的数据进行了数据的更新,
不会对整个页面的其他地方进行数据的更新,这样就大大提升了页面的加载速度,用户体验高。

补充:同步异步是什么??
针对于上述 Ajax 的局部刷新功能是因为 Ajax 请求是异步的,与之对应的有同步请求
- 同步请求如下图所示:

浏览器页面在发送请求给服务器,在服务器处理请求的过程中,浏览器页面不能做其他的操作。
只能等到服务器响应结束后才能,浏览器页面才能继续做其他的操作。
- 异步请求如下图所示:

浏览器页面发送请求给服务器,在服务器处理请求的过程中,浏览器页面还可以做其他的操作
14.2 Axios
使用原生的 Ajax 请求的代码编写起来还是比较繁琐的,
所以接下来我们学习一门更加简单的发送 Ajax 请求的技术Axios 。
Axios是对原生的 AJAX 进行封装,简化书写。
Axios官网是:https://www.axios-http.cn

14.2.1 入门程序
Axios 的使用比较简单,主要分为 2 步 :
- 1). 引入Axios文件(如果网络不通畅,可以使用离线的已经下载好的 js 文件,下面网盘链接已提供):
html<script src="https://unpkg.com/axios/dist/axios.min.js"></script>txt通过网盘分享的文件:axios.js 链接: https://pan.baidu.com/s/1ELaOyyT_jF6RShFMa2eXdQ?pwd=y2h8 提取码: y2h8- 2). 点击按钮时,使用 Axios 发送请求
html<body> <button id="getData">发起 GET 请求</button> <button id="postData">发起 POST 请求</button> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> //GET请求 document.querySelector('#getData').onclick = function() { axios({ url:'https://mock.apifox.cn/m1/3083103-0-default/emps/list', method:'get' }).then(function(res) { console.log(res.data); }).catch(function(err) { console.log(err); }) } //POST请求 document.querySelector('#postData').onclick = function() { axios({ url:'https://mock.apifox.cn/m1/3083103-0-default/emps/update', method:'post', data:"id=1" //POST请求体 }).then(function(res) { console.log(res.data); }).catch(function(err) { console.log(err); }) } </script> </body>
14.2.2 请求方式别名
- 为了方便起见,
Axios已经为所有支持的请求方法提供了别名 - 格式 :
axios.请求方式(url[,data[,config]])
- 所以在上述的入门案例中,我们可以将
get请求代码改写成如下:
//GET请求
document.querySelector('#getData').onclick = function() {
axios.get("https://mock.apifox.cn/m1/3083103-0-default/emps/list")
.then((result) => {
console.log(result.data.data);
}).catch((err) => {
console.log(err);
});
}post请求改写成如下:
//POST请求
document.querySelector('#postData').onclick = function() {
axios.post("https://mock.apifox.cn/m1/3083103-0-default/emps/update","id=1")
.then((result) => {
console.log(result.data);
}).catch((err) => {
console.log(err);
});
}补充 : 在使用 axios 时,在 axios 之后,输入
thenc然后Tab键会自动生成成功及失败回调函数结构 。

