Skip to content

🔧 JavaScript


一、引言


  • Javascript 三大部分
    • ECMAScript 语法标准
      • 由 ECMA 制定的一种脚本语言的标准化规范
    • DOM
      • 通过⽂档对象模型(dom)操作网页
    • BOM
      • 通过浏览器对象模型(bom)操作浏览器
微信截图_20250522003539
  • 我们写的代码需要翻译成机器能够识别的机器码(010101)
    • 解释型语言
      • 定义:翻译一行执行一行,不生成编译文件
      • 优点:可移植性较好,只要有解释环境,可在不同的操作系统上运行
      • 缺点 :运行需要解释环境,运行起来比编译的要慢
      • 例子:Javascript、php、python
    • 编译型语言
      • 定义:通篇翻译,生成编译文件之后再执行
      • 优点:运行速度快,代码效率高,编译后的程序不可修改,保密性较好
      • 缺点 :代码需要经过编译方可运行,可移植性差,只能在兼容的操作系统上运行
      • 例子:c、c++

  • 单线程( js 引擎是单线程执行的)

    • 同一时间只能做一件事情

    • 为什么js是单线程?

      多线程可以提高开发效率,为什么设计成单线程?

      • 主要是由 js 的用途决定的
        • 与用户互动
        • 操作DOM

    假如 js 是多线程那么现在有两个事件,同时操作 dom,

    一个事件编辑 dom,另一个事件删除 dom,那么此时会产生矛盾指令


二、Js 引入与注释


  • 如何引入

    • 内部引入
    html
    <script>xxx</script>
    • 外部引入( js 文件)
    html
    <script src='xxx'></script>
  • 注意:推荐 将位置放在 body 标签的里面最下方 ,防止阻塞内容的渲染


  • 使用 w3c 的标准结构、样式、行为相分离推荐外部引入

  • 注释

    js
    单行://
    js
    多行:/* */
  • 结束符号

    js
    ;  // 写完一个语句后加分号,避免解析时报错

三、变量声明


  • 变量声明
js
用var方式定义变量 (es6语法:let、const)

// 声明和赋值的结合
var name = 'irai'; 

// 声明和赋值分开
var name ;
name = 'irai'

// 使用 , 同时声明多个变量
var a = 2, b = 3;
console.log(a,b);

  • 命名规则

    • JS 中的变量是弱类型可以保存所有类型的数据,即 变量没有类型 而值有类型
    js
    var ir = 'iraionly.cn;  
    console.log(typeof ir);
    
    ir = 18;
    console.log(typeof ir);
    
    注意:js语言中不能将关键字用来做变量名,比如 trueifwhileclass 等。
    var class='iraionly.cn'

四、基础数据类型


4.1 概念


  • 基础类型

    • 原始类型(基础类型)数据保存在
    js
    基础类型:
        Number
        String
        Boolean
        Undefined
        Null
        Symbol

  • 复杂类型

    • 复杂类型(引用类型)数据保存在 ⾥通常内存占据空间计较大,

      所以设计存放在堆内存,在

    js
    引用类型:
        对象:{}
        数组:[]
        函数:(){}
    微信截图_20250526110619

4.2 Number


  • 声明:
js
//字面量声明
var num1 = 1;
console.log(typeof num1); //number  

//数字对象方式声明
var num2 = new Number(2);
console.log(num2+3); //5

  • Number 方法:

    • 整数判断
    js
    console.log(Number.isInteger(1.3)); //false
    • 指定返回的小数位数 ( 四舍五入 )
    js
    console.log((12.345).toFixed(2)); //12.35

  • NaN ( not a number ) 表示非数字值

    js
    isNaN() //用来判断一个值是不是数字
    // 如果该值转换为number等于 NaN,则此函数返回 true
    
    注意:null, "" 两个值会被判断为数字,所以使用前应该屏蔽

  • 浮点精度

    • js 语言在浮点数计算时会有精度误差问题
    js
    var num = 0.1 + 0.2
    console.log(num)// 结果:0.30000000000000004
    • 解决方式 (不要求高精度)
    js
    var num = 0.1 + 0.2
    console.log(num.toFixed(1))// 结果:0.3

4.3 String


  • 声明:
js
//字面量声明
var irai = 'iraionly'; //可以使用单引号或者双引号
console.log(irai);

//字符串对象方式声明
var irai = new String('iraionly');

  • 【常用】字符串方法 :
js
//连接运算符
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/23

4.4 Boolean


  • 声明 :

    js
    //字面量声明
    var irai = true;

  • 隐式转换

    js
    //几乎所有的类型都可以隐式转换为 Boolean 类型 
    
                true            false
    String     非空字符串        空字符串
    Number     非0的数值         0/NaN
    Array      数组不参与比较时   参与比较的数组
    Object     ✅  
    undefined
    null
    NaN

    当其他类型与 Boolean 类型对比时,会将其他类型先转换为数值类型再对比


  • 显式转换

    js
    var 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 基本是同义的

    js
    undefined == null //true
    
    //null是一个表示"无"的对象,转为数值时为0;
    //undefined是一个表示"无"的原始值,转为数值时为NaN。
    
    Number(null) //0
    Number(undefined) // NaN
    5 + undefined // NaN(任何数值和undefined运算都是NaN)

  • Undefined

    js
    // 变量被声明了,但没有赋值时,就等于undefined
    var i;   // i undefined
    
    // 对象没有赋值的属性,该属性的值为undefined
    var  o = new Object();  // o.p undefined
    
    // 函数没有返回值时,默认返回undefined
    var x = f(); //x  undefined

  • Null

    js
    null表示"没有对象",即该处不应该有值。
    
    /*
    	用法:
        (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

  • 字面量声明一个对象:

    js
    var 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 运算符
    js
    var person = {
     name: '张三',
     age: 29,
     job: 'teacher',
     eat: function() {}
    }
    
    delete person.job	//删
    console.log(person);  //{name: '张三', age: 29, eat: ƒ}

5.3 this


  • 通过 this 我们能够访问对象自身属性和方法
js
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 创建对象


  • 对象字面量 {}

    • 声明⼀个对象,赋值给⼀个变量
    js
    var person = {
     name: '张三',
     age: 29,
     job: 'teacher',
     eat: function() {}
    }

  • 构造函数

    • Object ⾃定义构造函数 (大驼峰)
      • this 指向的是对象的本身
      • 使⽤ new 实例化⼀个对象,就像工厂⼀样
    js
    var 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 构造函数参数


  • 固定参数应用场景
    • 明确知道对象属性
    • 位置要严格对应
js
function Car(name, price, size) {
     this.name = name
     this.price = price
     this.size = size
}

  • 对象类型的参数
    • 维护方便
    • 使用方便
js
//构造函数
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
    js
    function 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指向新对象)
    • 执行构造函数代码 (为这个新对象添加属性)
    • 返回新对象
js
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}
    微信截图_20250527112357

  • 深拷贝

    • 定义:对于引用类型而言,复制后引用类型指向一个新的内存地址,两个对象改变互不影响
    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]
    微信截图_20250527112412

5.8 【重点格式】JSON


JSONJavaScript Object Notation , JS 对象简谱)是一种轻量级的数据交换格式。

它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会的一个子集,

采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON

成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地

提升网络传输效率 ,


  • 说明
    • JSON 的语法 :

      var obj="{'属性名':'属性值','属性名':{'属性名':'属性值'},'属性名':['值1','值1','值3']}"

    • JSON 字符串一般用于传递数据 , 所以字符串中的函数就显得没有意义 , 在此不做研究

    • 通过JSON.parse() 方法可以

    • 通过 JSON.stringify() 方法可以


  • 示例:( JSON --> 对象)
js
/*
定义一个 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 )
js
/* 定义一个对象 */
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

  • 前后端传递数据
1681292306466

六、数组


  • 定义

    • 数组是⼀种列表对象,它的原型中提供了遍历和修改元素的相关操作,

      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 数组基本操作


js
//数组基本操作
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 : 这是一个非常强大的方法,熟悉之后可以做 插入操作、删除操作、替换操作...
    • 删除操作、插入操作、替换操作 ...
    • 函数有返回值,返回的是被删除的元素
    • 这个⽅法在原来的数组上直接做修改
    • 清空的数组的作用
  • 格式:
js
//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 的区别可以自定义元素之间用自定义符号隔开
  • 格式:
js
//lst.join(seperator=",")
  • 示例:
js
//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-irai

  • concat

    • 用于连接两个或多个数组
    • 不会更改现有数组,而是返回一个新数组(深拷贝),其中包含已连接数组的值
  • 格式:

js
//array1.concat(array2, array3, ..., arrayX)
  • 示例:
js
//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 赋值、算术、自增和自减运算符


  • 赋值运算符
js
var url = 'iraionly.cn';

  • 算术运算符
js
*   //剩法
  
/   //除法

+   //加法
/*
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 元素设置了不同状态下的样式

      image-20211115175709695

    • 快速定位元素

      • 英文:
      image-20211115175748703
      • 中文:
      Snipaste_2025-05-27_18-24-10

  • 浏览器 f12 调试 :

    • console.log()
    • debugger

    微信截图_20250527182616


7.3 if、if-else、三元表达式


  • if
js
//当条件为真时执行表达式代码块
if (){  //表达式或者变量=》布尔值
  //条件为 true 时执行
}

//如果只有一条代码块,可以不用写 {}  (为了代码可读性,不推荐省略{})
if ()
  console.log(xx)
//条件为 true 时执行

  • if-else
js
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

    • 语法
    js
    switch(表达式) { 
    	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
  • 注意
    • 内部严格按照 === 的规则

7.5 循环语句


  • for 循环


    • 语法
    js
    for (语句 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);  //5050

  • while 循环


    • 语法
    js
    var i=0 //循环变量
    while (条件表达式或者变量 {
     代码块
     i++ //改变循环变量
    }

    • 示例:计算数字 1 - 100 的和
    js
    // 计算数字 1 - 100 的和 
    var i = 1,s = 0;
    while(i<=100){
        s += i;
        i++; //改变循环变量
    }
    console.log(s); //5050

  • do-while 循环


    • 语法
    js
    var i=0 //循环变量
    do {
     代码块
     i++ //改变循环变量
    }
    while (条件);

    while 不同就在,代码执行到这,先会至少执行一次代码体,才去检查 while 条件成不成立


    • 示例:(演示至少执行一次)
    js
    var i = 0;
    do{
        console.log("代码块执行~~~");  //代码块执行~~~
    }while(i > 0) //i初始化为0,显然不大于0,但是也至少执行了代码块一次

  • 注意

    • 别忘了改变条件中的变量,不然就写了个死循环
    • do-while 循环能保证至少循环一次
    • for 循环语句更加紧凑

八、函数


8.1 概念


  • 语法
js
function name(参数 1, 参数 2, 参数 3) {
 //要执⾏的代码
}

  • 示例
js
var a=3,b=2;
function sum(a,b) {
    return a * 10 + b
}
console.log(sum(a,b)); //32
  • 注意:return 作用:
    • 返回值
    • 中断函数
    • 只能写在函数体里面

8.2 形参和实参


  • 形参:声明函数的时候定义的参数
js
function sum(a,b){  // a、b是形参
  // 要执行的代码
}

  • 实参:调⽤函数的时候传的⼀个参数
js
sum(1,2)  // 1、2就是实参

  • 特点
    • 形参和实参是一一对应的
    • 数量可以不对应 ( 但建议一致 )
    • 参数的类型不确定 ( JS 是弱类型语言)
    • 函数可以设置默认参数
    • 实参可以是字面量也可以是变量

8.3 声明函数

刚才我们定义函数,是为函数指定了一个名字。 那我们也可以不为函数指定名字,

那这一类的函数,我们称之为 匿名函数 。那接下来,就来介绍一下匿名函数的定义和调用。

匿名函数 : 是指一种没有名称的函数,由于它们没有名称,

因此无法直接通过函数名来调用,而是通过变量或表达式来调用。

匿名函数定义可以通过两种方式:函数表达式 和 箭头函数。


  • 函数表达式:
js
var add = function (a,b){
    return a + b;
}

  • 箭头函数
js
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引入了letconst 关键字,使得变量具有块级作用域。


  • 作用域链(Scope Chain)

    • 作用域链是指当访问一个变量时,JavaScript 引擎会按照一定的顺序查找该变量的作用域。

      查找顺序是从当前作用域开始,逐级向上查找,直到找到该变量或到达全局作用域。

    • 换言之:内层函数是可以访问外层函数声明的⼀个变量,反之,则不可以

      且遵循 就近原则 , 逐级向上查找 , 同名的先用就近的


8.5 arguments 参数


  • arguments
    • 是用来取参的
    • 传入的实参都能在函数体⾥通过 arguments 类数组取到
    • 具有数组的⼀些特点
      • 通过索引取参数
      • 有长度

  • 示例
js
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 阶你才能到达楼顶。

    每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?


    • 示例 1:

      输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。

      1. 1 阶 + 1 阶
      2. 2 阶

    • 示例 2:

      输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。

      1. 1 阶 + 1 阶 + 1 阶
      2. 1 阶 + 2 阶
      3. 2 阶 + 1 阶

    • 示例代码 (递归):
    js
    var 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 ,但是提交的话就会 超时

微信截图_20250528143706

超时的原因是其实递归是一种比较暴力的算法,它会重复计算很多已经计算过的工作

为了不超时可以把递归改进为 - - 记忆化搜索 (了解)


  • 示例代码(记忆化搜索)(了解即可):
js
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)
};

记忆化搜索就可以通过测试了:

微信截图_20250528144253

8.7 立即执行函数


  • 立即执行函数(immediately invoked function expression , IIFE )

    • 括号的作用
      • 帮助我们调用函数
    • 特点
      • 匿名函数
      • 自动执行
      • 执行完成以后销毁
    • 写法
    js
    (function(形参1,形参2,...) {
        //函数体
    })(实参1,实参2,...);
    • 注意
      • !!!!! 立即执行函数前面的语句必须加分号,否则会解析错误
      • 可以传参数

九 、【面试】 闭包


9.1 引子


  • 《 关于一个计数器引发的思考 》
js
// 初始化计数器
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 的疑惑 )
js
// 递增计数器的函数
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 对象的一个属性,它定义了构造函数制造出的对象的公共祖先,

    通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象


  • 示例 :
js
//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);
  • 运行结果:
微信截图_20250529173719
  • 示例:
js
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 引擎查找对象的属性时,会先判断对象本身是否存在该属性
    • 不存在的属性就会沿着原型链往上找

image-20211115151307787
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

    • 用于判断基础数据的类型,无法区分对象与数组
    js
    var 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 属性

      是否出现在某个实例对象的原型链上,可以理解为

    js
    var 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 对象本身是一种树形结构的文档对象。

1681269953136
  • 上述代码生成的树如下:
1681269970254
  • DOM 编程其实就是用 window 对象的 document 属性的相关 API 完成对页面元素的控制的编程

    ( BOM 编程 将在后续 十二章 讲到 ....)

1681270260741
  • 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 事件绑定

通过属性绑定(静态绑定)

  • 示例代码:
html
<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 核心在于 :


  • 示例代码:
html
<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对象的事件处理程序

  • (添加事件监听)语法
js
element.addEventListener(eventType, eventHandler, useCapture);
  • eventType :字符串,表示要监听的事件类型,如 "click""change""load" 等。

  • eventHandler :事件处理函数,当事件发生时,该函数会被调用。

  • useCapture :布尔值,表示事件监听器是在捕获阶段还是冒泡阶段触发。

    默认值为 false( 冒泡阶段 )。如果设置为 true,则在捕获阶段触发。


  • (删除事件监听)语法
js
element.removeEventListener(eventType, eventHandler, useCapture);
  • eventType:字符串,表示要移除的事件类型。

  • eventHandler:要移除的事件处理函数。必须与添加时的函数完全相同。

  • useCapture:布尔值,表示事件监听器是在捕获阶段还是冒泡阶段触发。必须与添加时的值一致。


  • 改变元素属性值 和 添加事件监听 的区别
    • 改变元素属性值 会被覆盖
    • addEventListener 可以同时注册多个,根据注册顺序,先后执行

  • 示例代码:
html
<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 事件流机制

  • 事件传播的两种机制
    • 冒泡
    • 捕获

  • 图解事件捕获和事件冒泡
image-20211115165055089
  • 示例代码:
html
<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 来区分出触发这个事件的实际元素是谁


  • 示例代码:
html
<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>
    • 初始效果:
    微信截图_20250529183716
    • 现通过 获取到 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>
    • 修改效果:

      微信截图_20250529190305
    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>
    • 修改效果:
    微信截图_20250529191649
11.2.4 添加和删除元素

方法描述
document.createElement(“标签名”)创建元素节点并返回,但不会自动添加到文档中
元素.remove()删除当前元素
父元素.appendChild(ele)将 ele 添加到 element 所有子节点后面
父元素.insertBefore(newEle,targetEle)将 newEle 插入到 targetEle 前面
父元素.replaceChild(newEle, oldEle)用新的节点替换原有的某个旧子节点
document.write(text)可写入 HTML

  • 示例代码:
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对象的属性之一,代表浏览器的本地数据会话级存储

1681267483366

12.2 BOM 控制浏览器行为


window.alert 中的 window 可省略 , 类似同理 ...


  • 三种弹窗方式


    • 警告框
    js
    alert('hello')

    • 确认框
    js
    var isConfirm = confirm('请确认')
    console.log('下⼀步', isConfirm)
    // 有返回值的

    • 提示框
    js
    var isPrompt = prompt('请输⼊姓名')
    console.log(isPrompt) //  是 null 或者 用户输入的值

  • 页面跳转
html
<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 持久级存储和会话级存储


  • 会话级数据 : 内存型数据,是浏览器在内存上临时存储的数据,浏览器关闭后,数据失去,

    通过 windowsessionStorge 属性实现,使用 sessionStorage.getItem() 获取


  • 持久级数据 : 磁盘型数据,是浏览器在磁盘上持久存储的数据,浏览器关闭后,数据仍在,

    通过 windowlocalStorge 实现,使用 localStorage.getItem() 获取


  • 可以用于将来存储一些服务端响应回来的数据 , 比如: token 令牌,

    或者一些其他功能数据,根据数据的业务范围我们可以选择数据存储的 会话/持久 级别


  • 示例代码:
html
<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开发者工具应用程序栏 , 可以查看数据的状态
1690348134594

12.4 定时器


  • 延迟执行

    js
    setTimeout(function(){}, 毫秒)
    • 停止

      js
      clearTimeout(timer) // 参数必须是由 setTimeout() 返回的timer

  • 定时执行

    js
    setInterval(function(){}, 毫秒)
    • 停止

      js
      clearInterval(timer) // 参数必须是由 setInterval() 返回的timer

  • 示例代码:
html
<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>

  • 补充:定时器是一个异步任务,不会影响主线程任务的执行
html
<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 简介


正则表达式是描述字符模式的对象。

正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具。


  • 语法
js
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 验证


注意:这里是用 正则表达式对象 来调用方法

js
// 创建一个最简单的正则表达式对象
var reg = /o/;
// 创建一个字符串对象作为目标字符串
var str = 'Hello World!';
// 调用正则表达式对象的test()方法验证目标字符串是否满足我们指定的这个模式,返回结果true
console.log("/o/.test('Hello World!')="+reg.test(str)); 
// /o/.test('Hello World!')=true

13.2.2 (全局)匹配


js
// 创建一个正则表达式对象
// 修饰符 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 替换


注意:这里是使用 字符串对象 来调用方法


js
// 创建一个正则表达式对象
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 忽略大小写


js
//目标字符串
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 元字符使用


js
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)); // true

js
var 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)); // false

11.2.6 字符集合的使用


js
//n位数字的正则
var targetStr="123456789";
var reg=/^[0-9]{0,}$/;
//或者 : var reg=/^\d*$/;
var b = reg.test(targetStr);//true

js
//数字+字母+下划线,6-16位
var targetStr="HelloWorld";
var reg=/^[a-z0-9A-Z_]{6,16}$/;
var b = reg.test(targetStr);//true

11.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 中的网络编程技术来理解

    02c1eccf-8430-4181-9870-08ada5b07022
    • 异步交互 : 可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分网页 的技术

      • 如下图所示,当我们再百度搜索 java 时,下面的联想数据是通过 Ajax 请求从后台服务器得到的,

        在整个过程中,我们的 Ajax 请求不会导致整个百度页面的重新加载,

        并且只针对搜索栏这局部模块的数据进行了数据的更新,

        不会对整个页面的其他地方进行数据的更新,这样就大大提升了页面的加载速度,用户体验高。

    96dce349-59df-44f1-a3f3-025a6e414650

补充:同步异步是什么??


针对于上述 Ajax 的局部刷新功能是因为 Ajax 请求是异步的,与之对应的有同步请求


  • 同步请求如下图所示:
0f044f02-0356-4b4b-b202-2a30bf0add65

浏览器页面在发送请求给服务器,在服务器处理请求的过程中,浏览器页面不能做其他的操作。

只能等到服务器响应结束后才能,浏览器页面才能继续做其他的操作。


  • 异步请求如下图所示:
adc6c826-c48d-48df-b28e-24792034f1b5

浏览器页面发送请求给服务器,在服务器处理请求的过程中,浏览器页面还可以做其他的操作


14.2 Axios


使用原生的 Ajax 请求的代码编写起来还是比较繁琐的,

所以接下来我们学习一门更加简单的发送 Ajax 请求的技术Axios 。

Axios是对原生的 AJAX 进行封装,简化书写。

  • Axios 官网是:https://www.axios-http.cn

a31abb0d-0be9-4d4e-940e-63af75c564ea

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>

    微信截图_20250711005850

14.2.2 请求方式别名


  • 为了方便起见,Axios 已经为所有支持的请求方法提供了别名
  • 格式 : axios.请求方式(url[,data[,config]])

  • 所以在上述的入门案例中,我们可以将 get 请求代码改写成如下:
js
//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 请求改写成如下:
js
//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 键会自动生成成功及失败回调函数结构 。