JavaScript

javascript的简介

###javascript 是什么

java: 类似 java 计算机编程语言

script: 脚本

类似 java 计算机编程语言的脚本语言,缩写为 js

## java 和 javascript 的区别

- 静态编译: 将 java 的程序编译生成为一个.class 文件

- 编译型语言(java、c)和解释型语言(js)

- java 静态编译,js 作为脚本动态编译
- java 是强类型语言,js 是弱类型语言

- 蹭热度 JavaScript 蹭 java 的热度

## javascript 的作用

提供了一个和 html 进行动态交互的功能

什么是变量?如何声明变量

变量就是存放数据的一个容器 (盒子)
变量所保存的数据本质是存储在计算机的内存中的!  内存是通过硬件内存条所产生一块虚拟空间!通常称为内存空间!

声明变量
在js中 通过  var 关键字 就可以声明一个变量
声明变量语法
 var a
声明变量的同时赋值,变量的初始化
 var num = 100
同时声明多个变量 多个变量之间用逗号分隔
 var a, b, c, d
var 关键字可以重复声明相同的变量
js在ES6之前,变量的声明只能通过 var关键字进行声明
js在ES6之后可以采用 let 或者 const 关键字进行变量的声明
let 语法规则
 let a
 a = 200
变量的初始化
 let A = 200
同时声明多个变量
 let b, c, d
ncaught SyntaxError: Identifier 'a' has already been declared
  a这个变量你怎么重复声明呢,之前不是声明过了吗????
  let关键字不能在同一作用域重复声明相同的变量
 const 语法规则
 const关键字是用于声明 常量的!
 常量:就表示该变量中保存的数据不允许更改!
 Uncaught SyntaxError: Missing initializer in const declaration
 const num = 200
 利用赋值运算符 重新修改num的值
 num = 300 // Uncaught TypeError: Assignment to constant variable.
 const声明的常量变量,是不允许修改的!
 const同样在同一作用域下不允许声明相同的变量

变量数据的使用和修改

<!-- src属性是引入外部的js文件 -->
<!-- type引入文件的类型, 默认是指向 text/JavaScript -->
<script type="text/JavaScript">
      // 变量数据的使用和修改
      //  声明一个变量
      var  num
      // 对num变量进行赋值
      num = 200
      // 使用num变量中的数据
      console.log(num)
      console.log(num + 100)  // 1.首先读取num的值 200 2. 进行运算  200 + 100   3.输出 300
    //    修改变量的值
    //  在js中修改一个变量的值必须通过 '=' 赋值运算符
  //   num  = 450
      num + 300
    console.log(num) // 200

标识符的命名规则

变量(标识符)
标识符就是表示该环境下,作用域的一些数据!
标识符能表示 变量  函数  类  方法  对象  .....
标识符的命名大部分是由程序员自定义命名的
自定义命名的时候,应该遵循标识符的命名规范
js中的标识符命名规范:
  1.标识符只能由字母 数字 下划线 以及$符号组成
  2.标识符不能以数字进行开头
  3.标识符严格区分大小写
  4.不能采用关键字或者保留字进行标识符的定义
    -- 关键字:js语法规范中,提前规定好了一些单词!并且js也赋予其特殊的含有!这些单词称为关键字! 比如 if  else break for var let const....
    -- 保留字:js语法规范中,以前收录了一些单词!但目前这些单词未真正的投入使用,测试阶段!并没有任何的含义! 这些单词称为保留字
   注意:以上4条就是js标识符命名规范的硬性准则!
  5.标识符如果由多个单词构成!应遵循 '小驼峰命名法' : blackcolor ==> blackColor
  6.标识符的命名应该遵循 '见名知义'  : name ,age ,sex   不要取这些名字: a1 b2 abc  ......

数据类型

数据类型是什么?js 中对不同种类数据的一个类型分类
什么是数据?数据就是对现实事物的一种抽象表达!

程序:利用程序代码去解决实际的问题!处理数据!

对于事物的不同表达,数据的类型是有划分的!在 js 中数据是明确具备类型的!

mdn 为数据类型一共定义了 8 种:

7 种原始类型

- undefined
- Boolean
- Number
- String
- BigInt
- Symbol
- null
- 注意:在 ES6 之前只有 undefined、Boolean 、 Number 、 String、null
- ES6 后新增加了两种数据类型, BigInt 、 Symbol(符号)
  最后一种类型是:
- Object (对象类型,在 js 中使用构造函数所创建的对象!都属于 Object 对象类型)

数据按照参数的传递方式,可以分为 `值传递类型` 和 `引用传递类型`

## 值类型

- Boolean 布尔型
- Number 数字型
- String 字符串
- BigInt 长整数

## 引用类型

- Object 对象类型
- Symbol 符号类型

## undefined 未定义

undefined 是一个单独的类型,用于给已经定义的变量设置的初始值

## null 空引用

null 值的是空引用,是 js 的一个原始数据类型,用来指代引用类型数据的空值

## 值类型和引用类型数据的区别

值类型:变量中直接存贮值本身
引用类型:变量中存储的是引用地址,而值是存在引用地址所指向的内存中的某个对应位置

值类型

Number  数值类型,不再进一步的去区分int或者float  100 3.14
Stirng  使用单引号'' 或者 "" 或者 `` 包裹起来的字符串  字符串类型,js中没有字符类型
Boolean  布尔类型,表示事物的两种状态, 真(true) 和 假(false)
BigInt  长整型,该类型是用于表示,超出基本number能表示的数值范围! bigint通常用于描述巨大的值

引用类型

Object对象类型
Symbol符号类型
null 类型 特殊的一种引用类型

值类型与引用类型的存储方式

值类型  、 引用类型
这两者的区别体现在哪里?
体现在内存中存储的方式不同!


 var num = 100
 console.log(num) // 100
 num = 300
 console.log(num) // 300
 var num = 100
 var num2 = num
 num = 300
 console.log(num2) // 100
创建一个花括号对象,{}
花括号对象中具备,属性和方法,以键值对的形式进行存储表示!
  ‘=’赋值运算符,运算顺序是从右往左!
将赋值号右边表达式的值赋值给左边的变量
 var person = {
   name: '张三',
   age: 18,
 }
 var p2 = person
 //   访问 p2 对象
 console.log(p2) // {name:张三,age:18}
 //    修改p2的age属性
 p2.age = 20
 console.log(p2) // {name:张三,age:20}
 console.log(person) // {name:张三,age:20}

json对象

json对象,花括号对象
json对象的创建利用{}进行创建
json对象中数据的存储采用键值对的方式, key:value ,其中key的数据类型必须是字符串,value可以是任何的合法数据类型!
json对象中多个属性值使用逗号分隔
大多数时候,js允许省略key的引号或者双引号,省略的前提是属性名的命名符合标识符的命名规则

// Object类型的数据都可以采用以下方式进行操作对象
var person = {
  name: '张三',
  age: 18,
  sayHello: function () {
    console.log('你好!')
  },
  'black@color': 'red'
}
console.log(person)

 1. 属性值的读取操作
 - 方式一:通过.语法   语法规则:  对象.属性  可以把.理解为语文上'的'
 - 方式二:通过[属性名]
 获取person对象的name值  点语法
 console.log(person.name)
 console.log(person.age)
 console.log(person.sayHello)
 console.log(person.black@color)
 读取对象中不存在的属性
 console.log(person.xxx) // undefined  读取对象中不存在的属性时其返回数据为 undefined
  []  语法规则 对象[属性名]
 console.log(person['black@color']) //
 2.添加属性  利用赋值运算符,为一个不存在的属性进行赋值!就是在为该对象添加一个新的属性并且赋值!
 console.log(person.xxxx)
 person.xxxx = '我是xxxx属性值'
 console.log(person.xxxx)
 console.log(person)
 person['xxxx']  = '我是xxx数据'
 console.log(person)
 3.修改属性值  利用赋值运算符
 person.age  = 300
 console.log(person)

4.对象类型中不允许存在两个同名属性!后一个会覆盖前面的属性值!
注意:以上这些操作,适用于任何的Obeject数据类型!

undefined

undefined类型  
该类型的值有且仅有一个  undefined
 undefined用于表示未赋值变量的状态!

null类型

null类型,是一种特殊的引用类型,
null类型的值有且只有一个  null
 null是一个特殊的Object对象类型  typeof检测的返回值是 Object
null类型通常用于指向一个不存在的对象空间! 表示为'空'
切断变量的引用链!释放空间,也可以使用null

检测数据类型

js中的数据类型有8种
原始数据类型(7) 和 对象类型(1)
 口诀: 四基两空双一
 四基: Number、String、 Boolean 、 BigInt
 两空:  undefined 、 null
 双一: 一个符号 Symbol  一个对象 Object
对于js种数据类型的多样化,如何快速的检测其数据的类型!
利用js所提供一个运算符 typeof 类型检测
 语法: typeof 数据  
typeof 运算后的数据属性什么数据类型!
typeof 的返回值是字符串类型!

var res1  =  typeof 100
var res2 =  typeof 'hello world!'
var res3 = typeof {}
var res4 = typeof true
// res1 res2 res3 res4 所存储的数据是什么类型?字符串、
console.log(typeof res1,typeof res2,typeof res3 , typeof res4)

json对象的序列化和反序列化

json格式的数据本质上一段字符串!该书写格式是于js中的json对象书写是一致的!
json对象的序列化和反序列化
序列化:将一个合法的json对象,转换为一段标准的json格式的数据!
反序列化:将一段标准的json格式的数据,字符串转化为一个合法的json对象!

// 序列化  利用js提供的JSON对象上的方法进行操作 JSON.StringIfy
// console.log(obj,typeof obj)
// 对obj对象进行序列化操作
// JSON.stringify(arg)  arg:json对象
var res = JSON.stringify(obj)
console.log(res, typeof res)
// 反序列化 利用js提供的JSON对象上的方法进行操作 JSON.parse()
// JSON.parse(arg) arg ==> json数据
var newobj = JSON.parse(res)
console.log(newobj, typeof newobj)
console.log(newobj.name, newobj.age)
newobj.age = 20

json数据的作用是方便数据的传输!json数据是一种轻量的js数据格式!该数据不仅仅用于前端!后端!也会使用!嵌入式!
json对象的数据格式  JSON数据
json(JavaScript Object Notation, JS 对象简谱) : 轻量化js数据结构
为什么使用json对象,作用是什么?json 对象主要用于存储数据,有着简洁的数据结构,正由于这样的特点,json 对象常用于网络信息传输中,传递数据

数据类型的转换

数据的类型转换:将某个数据的类型通过某种方式转换为另一种数据类型! 比如:  number ==》 string
数据转换通过方式不同可以分为:强制转换、隐式转换
强制转换:人为的通过手段去改变一个数据的类型
隐式转换:没有人为的参与,是程序解析自动进行转换
强制转换:
Number(x)函数 该函数可以将其他的数据类型强制转换为Number类型的数据

NaN表示该类型无法找到对应的Number值与之匹配!Symbol类型无法被转换为Number
 实际的开发中,常常将 string Boolean 转换为 Number

补充:除了Number()函数可以强制转换其他数据为Number类型外,还有两种手段
 全局函数:  parseInt()   parseFloat
    parseInt() 将其他数据类型转换为number的整数值

String()   该函数是将其他类型强制转换为字符串类型

Boolean()   该函数是将其他类型强制转换为布尔类型

js中的假值,假值就是通过强制转换或者隐式转换该数据为布尔类型后,其结果为false的数据称为假值!
js中的假值: false 、 0 、 -0 、 +0 、 null 、 undefined 、 ''

运算符

  运算符的作用,利用各种运算符,对已有数据进行运算然后得到新的数据!
 js中的运算符分类:
 - 算术运算符
 - 赋值运算符
 - 比较运算符
 - 逻辑运算符
 - 三元运算符
 - 单目运算符

运算符

 算术运算符: + 、 - 、 * 、 / 、%(模运算,取余)、**(幂运算)
  算数运算符主要用于两个number数据的计算!
 如果算术运算符一侧数据类型不为number则js将进行隐式转换!转换为numnber类型然后进行运算!

// +法运算
 //注意:如果+号的一侧数据类型为字符串类型,则+号就自动变成字符的拼接运算
console.log(5 + 5)
console.log(100 + 3.14)
console.log(-100 + 785)
console.log(5 + true) // 5 + true 等价于 5 + Number(true)
console.log(100 + {}) // 100 + '[object Object]'   100[object Object]
console.log(100 + null)
console.log(100 + undefined)  // 100 + Number(undefined) ==>  100 + NaN  = NaN
  //-法运算
console.log(100 - 20)
console.log(100 - 3.14)
console.log(-20 - 30)
console.log(20 - {})
console.log(20 - null)
console.log(20 - undefined)
  //*法运算
console.log(5 * 5)
console.log(5 * 3.14)
console.log(5 * -7)
console.log(5 * {})
console.log(5 * null)
 // /法运算,是数学意义上的除法运算!
console.log(6 / 2)
console.log(6 / 3)
console.log(7 / 3)
//  % 模运算,取余运算
 //5 % 3  ==》  商1 余2   结果:2
console.log(100 % 10)
console.log(7 % 3)
//** 幂运算
console.log(5 ** 3)
console.log(2.1 * 3)
console.log(0.1 + 0.2)

赋值运算符

 赋值运算符
 '=' 强调赋值运算符,不是数学意义上的等于符号!它是赋值运算!
 运算流程: 将赋值运算符的右侧表达式的结果,赋值给左边的变量! 从右往左
 let num = 30 // 将30赋值给左侧的 num变量
 注意:赋值运算符可以改变变量或者对象属性的具体值!
 表达式:表达式是js语句的最小单位!
 表达式是可以通过计算得到一个确定的值!类似的式子就可以称为表达式!
 比如:  5+5 算术表达式  、300 > 200 比较表达式 、 Number('123') 函数表达式 、 200 常量表达  式 、 num 变量表达式!

复合赋值运算符

复合赋值运算符:该运算符是 赋值运算符 与 算数运算符的结合书写!
注意:复合赋值运算符也具备赋值运算的功能!
+=  、 -= 、 /= 、 %= 、 **= 、*=

let num = 30
// num = num + 30
num += 30 // 等价于 num = num + 30
num -= 20
num /= 2
num *= 5
num %= 10
num **= 7

比较运算符

 比较运算符:用于对两个数据进行数据的比较,其比较结果为布尔值!true 或 false
    js中的比较运算符: >  、<  、 >= 、<=、 ==、 ===、 != 、!==
   >
   console.log(100 > 1) // true
   console.log(100 > 200) // false
   console.log(100 > '300') // 隐式转换  等价于  100 > Number('300')
   console.log('a' > 'b') // 字符的ASCII码  a==>97  b==>98
   <
   console.log(100 < 30)
   console.log(100 < 50 - 20)
  >=
   console.log(100 >= 100)
  <=
   console.log(100 <= 200 - 100)
 == 等于,注意这才是js的等于!
 双等于,特点是,进行比较时如果两侧的数据类型不一致!首先进行隐式转换!
   console.log(100 == 100)
   console.log(100 == '100') // 隐式转换  100 == Number('100') ==>  100 == 100 ==> true
 === 全等于,三等,
  全等的特点是,只会比较数据的值,如果数据的类型都不同!直接返回false!
  1. 先检测两边的类型是否一致  如果不一致则直接返回 false
  2. 进行值的比较,查看是否相等
   console.log(100 === 100)
   console.log(100 === '100')
   console.log(100 === 100)
   console.log({} === {}) //注意:引用数据类型进行比较时,比较的是地址值!
  != 不等于
  不等于,特点,如果两侧数据类型不同还是会进行隐式转换!
   console.log(100 != 100)   false
   console.log(100 != '100')  false
   !== 不全等 特点,不会进行隐式转换,只要!==的两边数据类型不同直接返回! true
   console.log(100 !== 100) // false
   console.log(100 !== '100') // true
   建议:以后如果遇到需要比较两个值是否相等的情况下,请采用三等!尽量不要采用双等!

逻辑运算符

逻辑运算符:用于对两个逻辑值的运算!
js中的逻辑运算符:  &&(与) ||(或) !(非,取反)
逻辑运算表达式的返回结果是什么类型?结果:可以是任何数据类型!
 && 与运算
 运算语法:   表达式1 && 表达式2  如果&&运算两侧的数据类型不是布尔类型,则先进行隐式转换! Boolean()
 与运算口诀: 全真为真,一假即假

console.log(false && false) // false
console.log(true && false) // false
console.log(true && true) // true
console.log(false && true) // false
console.log(false && false && true) // false
console.log(100 > 200 && 200 < 300) // false && true  ==> false
console.log(100 && true) // Boolean(100) && true  true && true
console.log(false && '')
console.log(100 && 200) // Boolean(100) && Boolean(200)   true && true  ==> true  200

 注意:js中的逻辑与和逻辑或都是属于短路与,短路或!
短路与:
特点在运算时,从左往右计算,如果碰到其中一个表达式的值为,false,那么后续的表达式将不再进行计算!并且返回当前表达式的原始值作为整个与运算的结果!
    如果从左往右计算,都没有遇到false,那么将最后一个表达式的原始值作为整个与运算的结果!

var num = 100
console.log(100 < 30 && (num += 10))
console.log(num) // 1. 110  2. 100
console.log(0 && true) //Boolean(0) ==》 false    0  原始值
console.log(0 && true)
console.log('' && true)
console.log(null && true)
console.log(undefined && true)
console.log(true && true && true && '')

  逻辑或(短路或)
 运算语法:   表达式1 || 表达式2  如果||运算两侧的数据类型不是布尔类型,则先进行隐式转换! Boolean()
 或运算口诀: 全假为假,一真即真

console.log(false || false) // false
console.log(false || true) // true
console.log(true || true) // true
console.log(true || false) //true

  短路或的运算特点
特点在运算时,从左往右计算,如果碰到其中一个表达式的值为,true,那么后续的表达式将不再进行计算!并且返回当前表达式的原始值作为整个或运算的结果!
  如果从左往右计算,都没有遇到true,那么将最后一个表达式的原始值作为整个或运算的结果!

 console.log(100 || false) // 100
 console.log({} || false) // {}
 console.log([] || false) // []
 console.log(true || false) // true
 console.log(false || false || false || [])

 !取反运算 非运算
口诀: 真变假,假变真

 console.log(!false)
 console.log(!true)

自增自减运算

自增、自减
 ++ 、 --
自增运算: 前自增、后自增 , 运算符都是 ++ 只是++的位置不同
自减运算: 前自减、后自减 , 运算符都是 -- 只是--的位置不同
   自增,自减运算
   运算规则:  将变量自身的值取出来+1或-1,然后重新赋值给变量!

let num = 50
num = num + 1
console.log(num)

前自增和后自增的区别:
前自增,是先将数据进行+1,然后再参与其他的运算
后自增,是先利用原始数据参与其他的运算,然后再进行+1操作

let num = 50
console.log(++num + 50) // 101
//   console.log(num++ + 50) // 100
console.log(num)
// 注意:表达式一旦计算完毕!值已经确定!则表达式的结果不能更改!

条件(三目)运算符

  三目:操作数,为3
 *   双目
 !  单目
 语法规则:  表达式?结果1:结果2
   运算流程: 计算表达式结果,如果表达式为真,则将结果1的数据进行返回,否则返回结果2的数据

let res = 100 < 50 ? 100 : 200
let num = false ? 10 + 1 : 20 + 1
console.log(num)

单目运算符

js中的单目运算符
!  、 typeof  ...
单目运算的操作数只有一个!

逗号运算和括号运算

// 逗号运算符,始终将最后一个,号表达式的值最后整个逗号表达式的运算结果
let a, b, c
let res = (1, 2, 3, 4)
console.log(res)
//   小括号()
//  js中利用()可以改变运算符的优先级!以及将一些表达式看作为一个整体进行计算!
console.log((5 + 5) * 8)

流程控制语句

js的代码如果没有人为的干预,那么js的执行顺序为从上往下,顺序执行!
作用:流程控制语句后,可以对js的代码执行顺序进行干预,完成更复杂的程序逻辑!
 js中的三大流程控制结构:
 顺序结构  选择结构  循环结构
 顺序结构: 整体代码的解析执行是按照从上往下!的规律进行执行!也是最基础的控制结构
 选择结构: 为代码的执行提供可能的路径!影响代码的走向!
 循环结构: 重复的执行某一段代码!

选择结构

选择结构
 js中的选择结构控制语句:
 if  单分支
 if...else 双分支
 if...else...if  多分支
 switch 条件多分支
   if 语句
语法:
  if (表达式) {
    代码段
  }
 执行流程,判断if后表达式的值!如果为true,则执行{}中的代码段!false则不执行{}中的代码段,直接回到程序的主路径
 

 let age = 17
 if (age >= 18) {
   console.log('恭喜你成年了!')
 }
 console.log('end___')

if...else 语句  双分支
语法:
  if (表达式) {
    代码段1
  } else {
    代码段2
  }
执行流程,判断if后表达式的值!如果为true,则执行代码段1的内容,如果为false,则执行代码段2中的内容!,最终回到主路径!

let age = 18
if (age >= 18) {
  console.log('恭喜成年,可以上网!')
} else {
  console.log('抱歉,未成年!滚')
}
console.log('end——————')
 //练习1: 随机从键盘上输入一个数值,判读奇偶!
 js的输入语句,浏览器提供的一个api 全局函数
 let res = prompt('请输入数据:')
 console.log(res)
 let num = prompt('请输入数字:')
 if (num % 2 == 0) {
   console.log('偶数')
 } else {
   console.log('奇数')
 }
//练习2: 随机输入一个年份,判断平润年
  能被4整除但不能被100整除或者能被400整除   闰年
  let year = prompt('输入一个年份:')
  if ((year % 4 === 0 && year % 100 != 0) || year % 400 === 0) {
    console.log('闰年')
  } else {
    console.log('平年')
  }

多分支语句

js多分支语句,if...else的嵌套

      //  双分支,是两条路径选择!

      //  多分支,是多条路径进行选择!

      //  多分支的本质就是利用if和else嵌套!

      // 语法:

      // if(表达式){

      //     路径1

      // }else if(表达式){

      //     路径2

      // } else {

      //     路径3

      // }

   let num = prompt('请输入一个成绩:')
         if (num >= 90) {
           console.log('优秀')
         } else if (num >= 80) {
           cosole.log('良好')
         } else {
           console.log('一般')
         }

           console.log('end——————')

条件分支语句

// js的条件分支语句  switch语句

      // 语法:

      //   switch (表达式) {

      //     case '值':

      //       语句块

      //     case '值':

      //       语句块

      //     case '值':

      //       语句块

      //     case '值':

      //       语句块

      //     case '值':

      //       语句块

      //     case '值':

      //       语句块

      //     ....

      //     default:

      //         不满足其他case分支时,进行default!

      //   }

let res = prompt('请输入一个字符:')
      switch (res) {
        case 'A':
          console.log('我是A')
          break
        case 'B':
          console.log('我是B')
          break
        case 'C':
          console.log('我是C')
          break
        case 'D':
          console.log('我是D')
          break
        default:
          console.log('没有任何case匹配')
      }
      console.log('end——————')

//   switch执行流程,计算表达式的值,然后与{}中case进行匹配!匹配一旦成功则从对应的case段落开始执行,一直到{}的末尾!

      //    switch在执行的过程中如果没有被break语句进行打断则一直执行到语句末尾结束!如果遇到break打断则马上退出switch语句,执行主路径的内容!

      //    注意: switch语句的匹配是基于三等来匹配!  类型和值相等!

循环结构

// js的循环结构

      // 循环:重复的执行一段相同的代码

      // js中提供了以下几个循环语句

      //  while语句   do...while语句  for语句

while循环

// while循环

      // 语法:

      //   while (表达式) {

      //     循环体

      //   }

      // while循环的注意事项:

      // 1.必须设置循环控制变量

      // 2.必须为循环控制变量赋初始值

      // 3.每一次的循环体结束前应该更新循环控制变量

      //   执行流程,首先判断表达式的true或者false,如果为true则执行循环体的代码,否则结束while循环

      //   如果判断为true则执行循环体的代码,当循环体代码执行完毕时,再次判断表达式的真假!重复以上操作!直到表达式为false,退出while语句

      //   打印以下 文本信息  hello world!  五次

      //   console.log('hello world!')

      //   console.log('hello world!')

      //   console.log('hello world!')

      //   console.log('hello world!')

      //   console.log('hello world!')

      //  while循环实现

      // 1.设置循环控制变量

      //   let count = 1 // 2.设置初始值

      //   while (count <= 5) {

      //     console.log('hello world!')

      //     //  3. 更新循环控制变量

      //     count++

      //   }

      //   console.log('end—————')

// 练习1: 求1-100的和  利用while循环!
      //   let num = 1
      //   let sum = 0 // 求和变量 sum 保存每次+的结果
      //   while (num <= 100) {
      //     sum = sum + num
      //     num++
      //   }

      //   console.log(sum)

      // 练习2: 求1-100的偶数和
      //   let num = 1
      //   let sum = 0
      //   while (num <= 100) {
      //     if (num % 2 === 0) {
      //       sum += num
      //     }
      //     num++
      //   }
      //   console.log(sum)

      // 练习3: 从键盘上输入一个整数,求该数各位之和  如:758  ==》 7+5+8=20  |  1234 ==》 1+2+3+4 = 10
      // 难点: 你不知道用户输入的数值位数!

      // 758 ==》 7 5 8

      // 758 ==》 8
      //  75 ==》 5
      // 7 ==》 7

      // 758
      // 758%10 == 8
      // parsInt(758/10) == 75

      // 75
      // 75%10 == 5
      // parsInt(75/10) == 7

      // 7
      // 7%10 == 7
      // parsInt(7/10) == 0

      //   let num = Number(prompt('请输入一个大于0的正整数:'))
      //   let sum = 0
      //   while (num != 0) {
      //     sum = (num % 10) + sum
      //     num = parseInt(num / 10)
      //   }
      //   console.log(sum)

do while循环

//  do...while循环语句

      //  语法:

      // do {

      //     循环体

      // }while(表达式)

      // do...while循环的注意事项:

      // 1.必须设置循环控制变量

      // 2.必须为循环控制变量赋初始值

      // 3.每一次的循环体结束前应该更新循环控制变量

      // 执行流程,do...while循环,一开始先进行一次循环体代码的执行!然后进行表达式的判断!

      // 如果为true 则继续执行循环体内容,重复操作!

      // 如果为false 则结束do...while循环

      // 打印hello world  5次

      //   let count = 1

      //     do {

      //       console.log('hello world!')

      //       count++

      //     } while (count <= 5)

      //     console.log('end——————')

      //   while (count <= 5) {

      //     console.log('hello world!')

      //     count++

      //   }

      //   do...while 和 while 的区别

      //   如果do...while和while执行相同的代码逻辑!do...while至少比while多一次循环!

      //   do {

      //     console.log('hello world!')

      //     count++

      //   } while (false)

      //   while (false) {

      //     console.log('hello world!')

      //     count++

      //   }

for循环

//  for 循环

      // 语法:

      // for(语句1;语句2;语句3){

      //     循环体

      // }

      // 执行流程,

      // for循环执行开始,先执行语句1,然后执行语句2,进行判断!

      //   根据语句2的判读结果,true or false

      //   如果为true,则执行循环体的内容,循环体内容执行完毕,跳到语句3并执行语句3的内容,语句3执行完毕后,又回到语句2再次进行判断!

      //   直到语句2的结果为false,才会结束for循环

// 练习1: 利用for循环  求 1-100的偶数和

         for (var sum = 0, i = 1; i <= 100; i++) {
           if (i % 2 === 0) {
             sum += i
           }
         }

      //   let 具备块级作用域
      //   var 不具备块级作用域

循环的嵌套

// 循环的嵌套,就是指多个循环语句进行嵌套使用!常见的 双重循环 , 三重循环!

      // 注意:在实际应用中基本上不会超过三重循环!

      // 双重循环, while和for  while和while  for和for   while和do while....

      //   for (var day = 1; day <= 5; day++) {

      //     console.log(`今天是周${day}`)

      //     for (var count = 1; count <= 8; count++) {

      //       console.log(`这是今天的第${count}节课`)

      //     }

      //   }

      //   console.log('放假拉!.....')

//   练习1:打印矩形 利用双重循环
      // *****
      // *****
      // *****
      // *****

      // 方法一: 利用 document.write()
       for (var row = 1; row <= 4; row++) {
         for (var col = 1; col <= 4; col++) {
           document.write('*')
         }
         document.write('<br>')
       }
        //document.write()  浏览器中dom对象上方法!

      // 方法二: console.log()
       let str = ''
       for (var row = 1; row <= 5; row++) {
         for (var col = 1; col <= 5; col++) {
           str += '*'
         }
         // 转义字符: 换行 '\n' 斜线 '\\'   水平制表符 '\t' tab键
         str += '\n'
         console.log(str)
         console.log('')
         str = ''
       }

      // 练习2:利用* 打印直角三角形   document.write(),console.log()
      // *
      // **
      // ***
      // ****
      // *****

       for (let i = 1; i <= 5; i++) {
         for (let j = 1; j <= i; j++) {
           document.write('*')
         }
         document.write('<br>')
       }

      //console.log() // 它就是一个api   利用它 实现 输出日志到控制台!  浏览器提供的!

continue和break

// continue 和 break关键字

      // 两个关键字可以使得循环提前结束!

      // break关键字

      // 特点:直接结束当前包含break的循环语句!进入下一个步骤!

      // continue关键字

      // 特点:该关键字也可以结束循环,但是它只是结束该循环的本次内容!后续的循环继续执行!

      // 拟人化

    for (let num = 1; num <= 10; num++) {
           if (num === 3) {
             break
           }
           console.log(num)
         }

         for (let num = 1; num <= 10; num++) {
           if (num === 3) {
             continue
           }
           console.log(num)
         }

         console.log('end ____')

alert、confirm、prompt

// alert 弹窗提示   浏览器提供的  api  弹窗提示

    // alert 它会阻塞页面的渲染和执行

    // alert('请问????')

    // console.log('3333')

    // confirm 确认模态  弹出框 浏览器提供的  还具备返回值

    // let res = confirm('确定还是取消??')

    // console.log(res)

    // 输入模态

    // result = prompt('请输入姓名')

    // console.log(result)

    // alert(result)

    // 上述api多用于调试

获取文档元素并修改其属性

 //  浏览器为程序员提供了一个接口!

    //  该接口可以访问页面中的所有元素节点!

    //  该接口是一个对象!浏览器提供!   document

    //  document是对象,那么对应的对象上是存在 属性或者方法的!

    // console.log(document, typeof document)

    // 利用document身上的方法可以获取到页面中的元素!

    //  1.document.querySelector()

    // 方法中的参数:是字符串类型,css的合法选择器

    //  css的选择器书写:  .box  div  #01  div>span  div span .....

    //  该方法的返回值是符合css选择器下的第一个页面元素对象

    // let elment = document.querySelector('div')

    // let elment = document.querySelector('div>span')

    // console.log(elment)

    //  2.document.querySelectorAll()

    // 方法中的参数:是字符串类型,css的合法选择器

    //  css的选择器书写:  .box  div  #01  div>span  div span .....

    //  该方法的返回值是符合css选择器的所有页面元素对象,不管元素有多少个始终以 NodeList,伪数组的形式返回

    // let arr = document.querySelectorAll('.box')

    // console.log(arr)

    // 3.document.getElementById()  通过元素的id属性值进行获取!

    // let elment = document.getElementById('span01')

    // console.log(elment)

    // 4.document.getElementsByClassName() 通过元素的class属性值进行获取! 以 HTMLCollection 伪数组形式返回

    // let arr = document.getElementsByClassName('box')

    // console.log(arr)

    // 5. document.getElementsByTagName()  通过元素的标签名进行获取!   div  span  p  h1.....

    // let arr = document.getElementsByTagName('span')

    console.log(arr)

通过元素对象获取和修改元素的属性

// 获取 div 元素
    let div = document.getElementById('div01')

    // 注意页面的元素都是对象类型,那么对象身上是具备属性和方法的!
    console.log(div, typeof div)

    // 通过api 查看 当前页面元素的所有属性!
    console.dir(div)

    // 页面是对象
    console.log(div.title)
    console.log(div.id)

    // 注意:利用点语法或者[]是可以获取元素对象上所有已知或未知属性的值!除了class属性的值不能正常获取!
    // 通过  class ==》  className来获取元素的class值
    console.log(div.className)
    console.log(div['id'])

    // 修改页面元素的属性值
    // div.title = 'xxxxxxxxxxxxxxxxxxx'
    // div.className = 'box2'

元素对象的标签体属性

 let box = document.querySelector('.box')

    // console.log(box.className)

    // 元素对象身上具备已知的属性 未知的属性!

    // console.dir(box)

    // innerHTML  和 innerText

    // console.log(box.innerHTML) //

    /*

   

      <span>我是div的儿子</span>

   

    */

    // console.log(box.innerText) //  我是div的儿子

    // innerHTML是用于更改元素的标签体的内容,合法的字符会被浏览器自动解析为标签!

    // innerText是用于更改元素的文本信息,该文本信息是不会被浏览器当作标签来正常解析的

    // innerHTML

    // box.innerHTML = '<span>我是div的儿子</span>'

    // innerText

    // box.innerText = '<span>我是div的儿子</span>'

通过简单的点击事件修改元素的标签体内容

  let box = document.querySelector('.box')
    let btn = document.querySelector('button')

    // 为btn元素绑定一个点击事件
    // 事件就是某个事情被触发了! 比如:  下雨 ==》 打伞     下课 ==》 吃饭  点击事件

    // addEventListener(事件类型,事件处理函数)
    btn.addEventListener('click', function () {
      //   console.log('我被点击了....')
      // 当btn被点击时,修改box元素的标签体内容!
      box.innerHTML = '我是被修改后的内容!'
    })

数组的基础

      // 数组,是存储一组数据的一个容器!

      // 数组,是属于Object类型,通过typeof 检测是 'Object'

      // 数组中可以同时存储不同类型的数据!只要是合法的js数据即可!多个数据之间使用',' 分隔!

      //   数据中的每个数据可以称为 元素!数组的存储是有序存储的!利用元素的下标值进行访问!

      //    1. 创建一个数组

      // 方式一: 利用直接量进行创建, []   推荐使用!

      //   let arr = ['a', 'b', true, 100]

      //   console.log(arr, typeof arr)

      //   方式二: 利用构造函数 new Array()

      // let arr = new Array(100, 200, 'HELLO', true)

      // console.log(arr, typeof arr)

      //  2. 获取数组中对应元素的值, 利用元素的下标!注意下标值是从 0 开始的!

      //    语法: 数组[下标值]

      //   let arr = [100, 'a', 'b', true]

      //   console.log(arr[0])

      //   console.log(arr[1])

      //   console.log(arr[2])

      //   console.log(arr[3])

      //   console.log(arr[4]) // 如果超过下标范围,则返回undefined

修改数组元素的值

      // 修改数组中对应元素的值 利用  赋值运算

      //   let arr = [100, 200, 300, 'hello']
      //   console.log(arr)

      //   arr[0] = 500
      //   console.log(arr)

      //   思考:以下代码
      let arr1 = [100, 200, 300, 'hello']
      let arr2 = arr1
      console.log(arr1, arr2) // [100,200,300,'hello']  [100,200,300,'hello']

      arr1[2] = 800
      console.log(arr1) //  [100,200,800,'hello']
      console.log(arr2) //  [100,200,800,'hello']

      //   解释?
      //   数组是引用数据类型!数组的赋值本质上是地址值的赋值!所以利用地址值可以改变对应的数据!

数组的常见操作

      // 数组的常见操作
      // 所有的数组实例对象,都来自于Array构造函数! 所以所有的数组实例对象都具备相同的操作方法和属性

      //   let arr = [100, 200, 300, 'hello', true]

      //   1.获取数组的长度 length属性
      //   console.log(arr.length) //返回当前数组的长度 元素的个数  数组的下标值范围:0  ---  (arr.length-1)

      //   2.添加元素到数组的末尾  push方法
      //    push方法是改变原数组的操作!
      //   arr.push('world!')
      //   console.log(arr)

      //   arr.push('xxx')
      //   console.log(arr)

      //   3.删除数组末尾的元素  pop方法
      //    pop方法具备返回值,返回的就是被移除的末尾元素
      //   let res = arr.pop()
      //   console.log(res, arr)
      //   res = arr.pop()
      //   console.log(res, arr)

      //     4.添加元素到数组的头部  unshift方法 原数组上操作数据! 无返回值
      //   arr.unshift('xxxx')
      //   console.log(arr)
      //   arr.unshift('yyyy')
      //   console.log(arr)

      //     5. 删除数组头部的元素 shift方法  原数组上操作数据! 有返回值,被删除的元素
      //   let res = arr.shift()
      //   console.log(res, arr)

      //    6. 在数组的任意位置实现,元素的添加,修改,删除! splice方法  在原数组上操作数据!
      //    有返回值,返回值默认是一个空数组,如果有其他被删除的元素统一添加到该数组中
      //   splice(x,y,args...)
      //  x : 确定在数组的那个位置进行操作
      //  y : 是确实从对应位置开始删除的元素个数
      //  args: 用于更新的新元素列表
      //   let arr = [100, 200, 300, 'hello', true]

      //   需求,我想在1索引的位置,删除2个元素,并用 新的元素进行替换
      //   arr.splice(1, 2, 'x', 'y', 'z')
      //   console.log(arr)

      //   需求,我想在2索引的位置,删除1个元素,没有新元素替换
      //   arr.splice(2, 1)
      //   console.log(arr)

      //    需求,我想替换索引3位置元素的值,更新
      //   arr.splice(3, 1, 'world!')
      //   console.log(arr)

      //  需求,我想在索引0的位置,添加两个新元素
      //   let res = arr.splice(0, 0, 'x', 'y', 'z')
      //   console.log(arr, res)

      //    7. concat 连接多个数组,并返回连接后的新数组,没有操作原数组!
      //   let arr1 = [100, 200]
      //   let arr2 = ['hello']

      //   [100,200,'hello']
      //   let arr3 = arr1.concat(arr2)
      //   console.log(arr3, arr1, arr2)

      //   let arr3 = arr1.concat(['a', 'b'], [1, 2], 'xxxxx')
      //   console.log(arr3)

      // 利用concat方法实现数组浅拷贝
      //   let arr3 = arr1.concat([])
      //   console.log(arr3, arr1, arr3 === arr1)

      //   以下方式为深拷贝
      //   let arr3 = arr1
      //   console.log(arr3, arr1, arr3 === arr1)

      //  8. join方法  将数组中的各元素分散通过指定的字符进行连接,返回一个字符串数据!
      //   let arr = [100, 200, 300, 'hello', true]
      //   let str = arr.join('*&^*(^*&*&(')
      //   console.log(str)

      //  9. includes  判断一个元素是否存在于当前数组中,存在则返回 true  不存在返回 false
      // let arr = [100, 200, 'hello']
      // let res = arr.includes(100)
      // console.log(res)

      //  10.判断元素是否存在,如果存在则返回第一个匹配的元素值的下标!  如果不存在则返回 -1
      //  indexOf  从左往右依次进行匹配!
      // let arr = [100, 200, 'a', '3000', 'a']

      // let res = arr.indexOf('a')
      // console.log(res)

      //  11. 判断元素是否存在,如果存在则返回第一个匹配的元素值的下标!  如果不存在则返回 -1
      //  lastindexOf  从右往左进行匹配
      // let arr = [100, 200, 'a', '3000', 'a']
      // let res = arr.lastIndexOf('a')
      // console.log(res)

      // 12. 数组的切片,获取数组的子数组   返回值,一个子数组!
      //  slice(开始下标,结束下标)   左闭右开的区间

      // let arr = [100, 200, 300, 'a', 'b']
      // let res = arr.slice(2, 3) // 0 1
      // console.log(res)

      // 总结: 注意以上的方法或者属性全都是数组的实例对象才能使用的!

多维数组

     // 多维数组就是由多个数组进行嵌套表示!所形成的一个多维的数组形式!

      // 二维数组

      //   let arr = [

      //     [100, 200],

      //     [300, 400],

      //     [500, 600],

      //   ]

      //   console.log(arr[0]) // [100,200]

      //   console.log(arr[1]) // [300,400]

      //   console.log(arr[2]) // [500,600]

      //   console.log(arr[0][0])

遍历数组

      //  遍历: 就是去一个容器中,将该容器中的所有元素,访问一遍! 这种行为叫做遍历!

      let arr = ['a', 'b', 'c', 'd', 'f']

      // 需求遍历 arr
      // 利用for循环来进行遍历 最传统的方式
      for (let i = 0; i < arr.length; i++) {
        // console.log(i)
        console.log(arr[i])
      }

对数组进行冒泡排序

      let arr = [100, 2, 4, 56, 88, 91, 77]

      //   冒泡排序:元素两两之间进行比较,根据大小的判断交换位置!最终实现数组的排序
      //   双重循环

      //   for (let i = 0; i < arr.length; i++) {
      //     for (let j = 0; j < arr.length - i; j++) {
      //       if (arr[j] > arr[j + 1]) {
      //         let temp = arr[j]
      //         arr[j] = arr[j + 1]
      //         arr[j + 1] = temp
      //       }
      //     }
      //   }
      //   console.log(arr)

      //   选择排序
      //   for (let i = 0; i < arr.length; i++) {
      //     for (let j = i + 1; j < arr.length; j++) {
      //       if (arr[i] < arr[j]) {
      //         let temp = arr[i]
      //         arr[i] = arr[j]
      //         arr[j] = temp
      //       }
      //     }
      //   }
      //   console.log(arr)

对数组进行倒置操作

      // 倒置操作:  ['a','b','c','d']  ==> ['d','c','b','a']

      let arr = ['a', 'b', 'c', 'd']

      //  方式一: 利用一个空数组
      // let arr2 = []
      // let len = arr.length
      // for (let i = 0; i < len; i++) {
      //   arr2.push(arr.pop())
      // }

      // console.log(arr2)

      //  方式二: 长度一半 首尾交换
      // let temp
      // for (let i = 0; i < parseInt(arr.length / 2); i++) {
      //   temp = arr[i]
      //   arr[i] = arr[arr.length - 1 - i]
      //   arr[arr.length - 1 - i] = temp
      // }
      // console.log(arr)

      //  方式三:
      // let arr2 = []
      // for (let i = arr.length - 1; i >= 0; i--) {
      //   console.log(arr[i])
      //   arr2.push(arr[i])
      // }
      // console.log(arr2)

对数进行去重操作

      // 去重操作
      // ['a','b','c','a','y','b']  ==> ['a','b','c','y']

      let arr = ['a', 'b', 'c', 'a', 'y', 'b'] // ['a','b','c','y','b']

      //  方式一:利用空数组
      // let arr2 = []
      // for (let i = 0; i < arr.length; i++) {
      //   if (!arr2.includes(arr[i])) {
      //     arr2.push(arr[i])
      //   }
      // }
      // console.log(arr2)

      // 方式二:利用 lastindexOf()  splice()
      // let index
      // for (let i = 0; i < arr.length; i++) {
      //   index = arr.lastIndexOf(arr[i]) //index = 3
      //   while (index != i) {
      //     arr.splice(index, 1)
      //     index = arr.lastIndexOf(arr[i])
      //   }
      // }
      // console.log(arr)

js获取页面元素的样式属性值

    let box = document.querySelector('.box')

    // 需求,获取该元素的样式属性  比如 获取它的高度值!

    // className title id .....value name ....  style

    // 1. 利用 elment.style 获取元素的样式对象!

    // console.log(box.style, typeof box.style)

    // //  利用样式对象 去获取 当前元素的属性值!

    // console.log(box.style.width)

    // console.log(box.style.height)

    // console.log(box.style.backgroundColor) //注意:如果样式属性名是-连接的,则采用小驼峰方式获取!

    // // 注意:利用element.style这种方式只能用于获取元素的 style属性中设置的样式值!

    // console.log(box.style.border)

    // 2.利用全局函数 getComputedStyle(elment) 返回 对象

    // let res = getComputedStyle(box)

    // console.log(res, typeof res)

    // 利用 getComputedStyle(elment) 返回 对象 去获取当前元素正在应用的样式属性值

    // console.log(res.width)

    // console.log(res.height)

    // console.log(res.backgroundColor)

    // console.log(res.border)

    // 总结: style属性的方式 和  getComputedStyle(elment)区别

    //  1. style属性的方式只能去获取元素自身的style属性所设置的样式值

    //  2.  getComputedStyle(elment) 获取元素正在应用的样式属性值!

    //  3. style属性是可读 可写  而   getComputedStyle(elment)是只读的不能重新赋值

    // box.style.width = '500px'

    // getComputedStyle(box).width = '500px'

点击元素更换背景色

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 200px;
        height: 200px;
        background-color: red;
      }

      #div01 {
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <div class="box" id="div01"></div>
  </body>
  <script>
    let box = document.querySelector('.box')

    box.addEventListener('click', function () {
      //   box.style.backgroundColor = 'pink'
      //   box.style.width = '500px'
    })
  </script>
</html>

文档树

     //  文档树

      //    浏览器会将html文档中的标签,解析并转换为文档树

      //  文档树是一个树形结构!html根标签,最外层的节点!其余的标签(节点)都是挂载于根节点上!

      //    文档树中,由很多的节点类型!不只元素节点(标签节点)! 除了元素节点之外,还存在 一些其他的节点类型! 注释节点  文本节点 ....

      //   注意:学习的重点以及操作是在元素节点!

创建节点

     // 需求: 通过js代码在body中添加一个div元素节点

      //  添加一个元素节点到已存在的节点中时,前提是 已经创建了对应的元素节点

      //   1. 创建一个 div 元素节点  document.createElement()

      //   参数要求, 数据类型 为 字符串  该字符串表示标签的名字!

      //   创建一个div   'div' 'p'  'button'

      let mydiv = document.createElement('div')

      console.log(mydiv, typeof mydiv)

      //   注意: 被创建出来的元素节点,它是不存在于文档树中! 需要把它插入或者添加到文档树中!

      //  对元素节点的  操作: 创建 增 删  查  改

添加&插入节点

    //  创建一个 div 元素

    let mydiv = document.createElement('div')

    // 在挂载前 初始化 元素节点

    // mydiv.id = 'div01'

    // mydiv.className = 'box'

    // mydiv.innerHTML = '我是div元素'

    //  挂载操作

    //  添加节点 利用节点进行添加 , 把新创建的元素节点添加到具体的某个元素节点的内部!作为该元素节点的子节点!

    //  获取body元素  document.body

    // 方式一: appendChild(elment)

    // 特点:该方法只会将元素添加到当前元素节点的末尾!

    // 在挂载后 初始化 元素节点

    // document.body.appendChild(mydiv)

    // mydiv.id = 'div01'

    // mydiv.className = 'box'

    // mydiv.innerHTML = '我是div元素'

    // 方式二:  insertBefore(新节点元素,旧节点元素)

    // 特点: 插入新的元素节点 到指定旧节点元素的前面

    let box = document.querySelector('.box')

    // document.body.insertBefore(mydiv, box)

    //  如果insertBefore的第二个参数为 null  则将新元素节点添加到该父节点的末尾 等价于  appendChild

    document.body.insertBefore(mydiv, null)

替换节点

   // 需求:创建一个新的li元素把旧的li元素给替换掉!

    // 1.创建一个新的li元素

    let newli = document.createElement('li')

    // let newli2 = document.createElement('li')

    // let newli3 = document.createElement('li')

    // let newli4 = document.createElement('li')

    // let newli5 = document.createElement('li')

    // newli.innerHTML = '新的li'

    // 2.获取即将被替换的旧元素

    // let oldli = document.querySelector('.first-li')

    // 3.获取父节点,由父节点来完成替换操作!  ul

    let ul = document.querySelector('ul')

    // 进行替换操作

    // replaceChild(newnode,oldnode)

    // ul.replaceChild(newli, oldli)

    // 利用指定的新元素,去替换当前父元素下所有的子元素

    // 指定的新元素个数不限!

    ul.replaceChildren(newli1)

删除节点

    // 删除操作

    // 方式一:元素自删除!

    let firstli = document.querySelector('.first-li')

    // firstli.remove() // 元素自我删除

    // 方式二: 利用父节点进行删除指定的子元素节点

    // let ul = document.querySelector('ul')

    // ul.removeChild(firstli)

查询操作

    // 查询1: 通过指定元素,查询该元素的父节点

    // let firstli = document.querySelector('.first-li')

    //  通过元素节点的属性查询自己的父元素节点

    // let res = firstli.parentNode   // 节点

    // console.log(res)

    // console.log(firstli.parentElement === res)

    // 查询2: 通过父元素查询它的所有子元素节点

    let ul = document.querySelector('ul')

    // 元素的属性  children 属性返回的是一个 伪数组

    console.log(ul.children)

快速收集form表单数据

    // 通过js动态获取 input的数据  value

    // 1.获取所有的input元素

    // 2.遍历input元素 拿到每一个input的value值

    // let btn = document.querySelector('.btn')

    // btn.addEventListener('click', function () {

    //   //   console.log(666)

    //   let inputs = document.querySelectorAll('input[name]')

    //   for (let i = 0; i < inputs.length; i++) {

    //     console.log(inputs[i].value)

    //   }

    // })

    //  快速的方式  new  FormData(表单对象)

    // let btn = document.querySelector('.btn')

    // btn.addEventListener('click', function () {

    //   //   console.log(666)

    //   let formdata = new FormData(document.querySelector('form'))

    //   console.log(formdata.get('id'))

    //   console.log(formdata.get('name'))

    //   console.log(formdata.get('sex'))

    //   console.log(formdata.get('age'))

    // })

通过数组渲染表格

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <table border>
      <thead>
        <tr>
          <th>学号</th>
          <th>姓名</th>
          <th>性别</th>
          <th>年级</th>
          <th>班级</th>
        </tr>
      </thead>
      <tbody></tbody>
    </table>
  </body>

  <script>
    // 创建表格的数据
    let data = [
      {
        id: 0,
        name: '张三',
        sex: 'male',
        grade: '1年级',
        clazz: '2班',
      },
      {
        id: 1,
        name: '李四',
        sex: 'female',
        grade: '6年级',
        clazz: '3班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
      {
        id: 2,
        name: '隔壁老王',
        sex: 'male',
        grade: '3年级',
        clazz: '2班',
      },
    ]

    // 后端返回的json数据
    let resdata = JSON.stringify(data)
    console.log(resdata)

    // 1.拿到json数据,应该先进行反序列化操作
    data = JSON.parse(resdata)
    console.log(data)

    // 1.获取tbody元素
    let tbody = document.querySelector('tbody')

    // 2.遍历后端返回的数组
    // for (let i = 0; i < data.length; i++) {
    //   console.log(data[i])

    //   tbody.innerHTML += `
    //   <tr>
    //       <td>${data[i].id}</td>
    //       <td>${data[i].name}</td>
    //       <td>${data[i].sex}</td>
    //       <td>${data[i].grade}</td>
    //       <td>${data[i].clazz}</td>
    //     </tr>
    //   `
    // }

    for (let i in data) {
      if (i == 2) break
      console.log(i)
    }

    // for in 会遍历原型链所有可枚举的属性!   遍历对象  数组

    //  for of  只能用于遍历 自身拥有 迭代器的数据!    string  array  map  set  ....

    //  数组实例对象的各种遍历器

    data.forEach((el) => {
      console.log(el);
      break
    })
  </script>
</html>

元素事件绑定问题

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div>
      <button>按钮</button>
    </div>
  </body>

  <script>
    let btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      console.log(666)
    })
    document.querySelector('div').innerHTML = '<button>按钮</button>'
  </script>
</html>

通过动态指定class改变元素的样式

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <style>
      .box {
        width: 200px;
        height: 200px;
        background-color: pink;
        color: red;
      }
    </style>
  </head>
  <body>
    <div>我是div元素</div>

    <button>改变</button>
    <button>移除</button>
  </body>

  <script>
    let div = document.querySelector('div')

    let btns = document.querySelectorAll('button')

    btns[0].addEventListener('click', function () {
      //   console.log(666)
      //   div.style.width = '300px'
      //   div.style.color = 'red'
      //   div.style.backgroundColor = 'yellow'
      div.className = 'box'
    })

    btns[1].addEventListener('click', function () {
      //   console.log(666)
      //   div.style.width = '300px'
      //   div.style.color = 'red'
      //   div.style.backgroundColor = 'yellow'
      div.className = ''
    })
  </script>
</html>

函数介绍和声明创建函数

  // 函数:一段被封装好的可重复使用的代码段!并且可以实现某种功能!

      // js中的函数,函数在js中在不同的环境下对其的叫法是不同!

      // 函数的其他称呼:

      // 方法

      // 回调函数(callback)

      // 构造函数

      // 自执行函数(IIFE)

      // 匿名函数

      // 箭头函数 (lamda表达式)

      // 无参函数

      // 有参函数

      // 比较函数  (主要用于对数据之间的比较,返回结果)

      // 事件函数

      //   .....

  // js中可以通过关键字  function进行函数的声明创建

      //  语法结构

      //  1.声明创建一个函数

      // function 函数名(形参列表){

      //     函数体

      // }

      //   函数的声明,在声明时,函数体内部的代码是不会执行的!

      function sayHello() {

        console.log('hello world!')

      }

      //   2.调用函数\执行函数

      //  语法:  函数名()

      //  注意:函数名 + () 才能执行对应的函数!

      sayHello()

无参函数和有参函数

无参函数:对应的某个函数,不具备形参列表!不需要额外的参数传入!该函数称为无参函数

有参函数:函数在定义时,明确指定了形参列表!后续的函数执行中也需要外部的数据!

     // 注意: a ,b 就是形参变量 (形式参数)

      function add(a, b) {

        console.log(a + b)

      }

      //   调用函数

      //    200,300 就是 实参 实际参数!一一对应关系

      add(200, 300)

函数的返回值

// 函数的返回值,大部分函数都是具备返回值!

      // 返回值代表的是该函数执行后,对数据的处理结果! 然后利用 return语句 进行返回!外部可以使用函数的返回值!

      // 如果函数,没有return语句,默认该函数的返回值是 undefined

      //   注意: return语句的特点

      // 1.return语句后可以是任何合法的js数据类型!

      // 2.return语句具有中断函数的功能,函数的执行过程中如果碰到return 那么直接结束该函数的执行!

函数的值传递和引用传递

     // 值传递,内部函数的数据操作不会影响外部传入的数据!

      //   let num = 100

      //   function change(mynum) {

      //     mynum += 10

      //   }

      //   change(num)

      //   console.log(num)

      //   ---------------------------------------------

      //   引用传递,实参和形参的改变会相互影响!

      //   let obj = {

      //     num: 100,

      //   }

      //   function change(myobj) {

      //     myobj.num += 100

      //   }

      //   //   调用 函数  change

      //   change(obj)

      //   console.log(obj.num)

方法

     // 方法:方法的本质其实也是一个函数,只不过该函数是作为某个对象的属性存储的!

      let person = {
        name: '张三',
        age: 18,
        sayHello: function () {
          console.log('你好我是张三!')
        },
      }

      console.log(person)

      //   调用方法  方法名()
      //   person.sayHello()

函数表达式

函数表达式 : 函数也是一种数据类型!引用数据类型!Object ==> typeof  检测出来  Function

 注意:函数是一种数据,该数据是存储在堆空间中的!

回调函数

     // 回调函数:函数最终通过某种手段执行了,但是不是你让它执行的!这种就是回调函数!

      //   function fun(callback) {

      //     for (let i = 0; i < 10; i++) {

      //       if (i === 5) {

      //         callback()

      //         break

      //       }

      //     }

      //   }

      //   //   fun函数 需要传入一个 函数(回调函数)作为参数

      //   fun(function () {

      //     console.log('哈哈我执行了,你不知道吧!')

      //   })

      //   let btn = document.querySelector('button')

      //   btn.addEventListener('click', function () {

      //     console.log('66666')

      //   })

自执行函数

  //   let fun = function () {
      //     console.log(666)
      //   }

      let num = 100

      //   IIFE  自执行函数
      ;(function () {
        console.log(666)
      })()

      //   声明并创建
      function fun() {}

      //   创建函数
      let res = function () {}

      let obj = {
        // 创建一个函数 函数表达式
        fun: function () {},
      }

箭头函数

      // 箭头函数: 书面称呼, lamda表达式  ,俗称是箭头函数

      // 箭头函数是在es6中新加入的一种特殊的函数形式!

      // 与function函数的不同

      // 1. 创建方式不同 不需要function关键字进行声明修饰!

      // 箭头函数的创建 语法

      // (形参列表)=>{

      //     函数体

      // }

      // 2.箭头函数,通常不能像function函数那样,直接进行声明然后利用函数名进行调用

      //    箭头函数通常是依附于某个变量或者某个对象上的属性

      //   2.1 作为某个变量的值,保存箭头函数

      //   let fun = () => {

      //     console.log(666)

      //   }

      //   fun()

      //   //   2.2 作为某个对象的方法

      //   let obj = {

      //     say: () => {

      //       console.log('say...')

      //     },

      //   }

      //   obj.say()

      //   //    2.3 作为一个callback进行 函数的传递使用

      //   btn.addEventLisener('click', () => {})

      //   function fun() {}

      //   ;() => {}

      //   ;(() => {

      //     console.log(666)

      //   })()

      //   3. 箭头函数中不具备自己的this关键字

      //   箭头函数的书写小知识:

      //   ;() => {

      //     console.log(6666)

      //   }

      //  第一个: 当形参个数有且只有一个时,()是可以省略的

      // let fun = num => {

      //   console.log('fun....', num)

      // }

      // fun(50)



 

      // 第二个: 当函数体内部只有 一条语句时, {} 是可以省略的

     

      // let fun = num => console.log('fun....', num)

     

      // fun(5000000)


 

      // 第三个: 当函数体内部只有 一条语句时,该函数的返回值默认就是 该语句的结果

           

      // let fun = num => num+200

      // let fun = (num)=>{

      //   return num+200

      // }

     

      // let res = fun(100)

      // console.log(res)

作用域

      // 作用域: 指变量或者函数的作用范围!

      // js中目前存在三种作用域

      // 1. 全局作用域  es6之前

      // 2. 函数作用域  es6之前

      // 3. 块级作用域 {} es6之后才有 块级作用域

      //    全局作用域

      //    在函数外的区域,整个 script 脚本区域 所在区域,称为全局作用域

      //   显式的全局变量

      //   var num = 100

      //   console.log(num)

      //   function fun() {

      //     console.log(num)

      //   }

      //   fun()

      //    隐式的全局变量

      //   num = 200 //  js自动的在全局的作用域  声明了 一个变量   var  num

      //   console.log(num)

      //    函数作用域

      //    函数体内部声明的变量或者函数,具备函数作用域

      //   var num = 500

      //   function fun() {

      //     var num = 300 // 函数作用域下 num 变量

      //     console.log(num)

      //   }

      //   console.log(num) // 500  全局作用域下  num

      //   fun() // 函数作用域下的 num

      //    块级作用域

      //    块级作用域的变量 只能通过 let 或者 const 进行声明 才会存在块级作用域

      //   {

      //     const num = 100

      //     // num 在那个 块下声明  {}

      //     console.log(num)

      //   }

      //   console.log(num)

      //   for (var i = 0; i < 5; i++) {}

      //   console.log(i)

      //   let btns = document.querySelectorAll('button')

      //   for (let i = 0; i < btns.length; i++) {

      //     btns[i].addEventListener('click', () => {

      //       console.log(i)

      //       btns[i].style.width = '200px'

      //     })

      //   }

      //   //   {

      //   //     btns[i].addEventListener('click', () => {

      //   //       console.log(i)

      //   //     })

      //   //   }

      //   //   {

      //   //     btns[i].addEventListener('click', () => {

      //   //       console.log(i)

      //   //     })

      //   //   }

      //   //   i = 3

作用域链

      // 作用域链:通过作用域的链式关系,进行查找对应的 变量 和 函数

      //   如果沿着作用域链一直寻找,直到在全局作用域下都没有找到对应的变量或者函数 就会报错!

      //   注意:作用域链的查找规则是 从内到外!查找!

      //   var num = 500

      //   function fun() {

      //     console.log(num)

      //   }

      //   fun()

      //   var num = 500

      //   function fun() {

      //     var num = 400

      //     function fun2() {

      //       var num = 300

      //       console.log(num)

      //     }

      //     fun2()

      //   }

      //   fun()

      //   {

      //     let num = 500

      //     {

      //       let num = 300

      //       console.log(num)

      //     }

      //   }

预解析

      // 预解析: js代码在真正的执行前,会先进行一个操作!该操作称为 预解析!

      // 预解析主要涉及两个内容

      // 当前作用域下的var变量 和 function 函数进行提升!

      //   console.log(num)

      //   var num = 500

      // js引擎在真正执行代码前会将以上的代码先进行预解析

      //  预解析后的代码情况

      //   var 变量的提升操作

      //   var num

      //   console.log(num)

      //   num = 500

      //   console.log(num)

      //   function函数的提升

      //   fun()

      //   function fun() {

      //     console.log(666)

      //   }

      // 预解析操作

      //   function fun() {

      //     console.log(666)

      //   }

      //   fun()

      // 变量和函数的提升谁更靠前 !  函数的提升优于变量的提升

      //   console.log(fun)

      //   var fun = 300

      //   function fun() {

      //     console.log(666)

      //   }

      //   console.log(fun)

      //   预解析

      //   function fun() {

      //     console.log(666)

      //   }

      //   var fun

      //   console.log(fun)

      //   fun = 300

      //   console.log(fun)

      //   var num = 200

      //   var num // js看来是无效的声明 因为在全局作用域下已经声明过一个 num 变量了 并且有值了!

      //   console.log(num)

let和const的暂时性死区

     // let 的变量不具备提升操作

      //   console.log(num)  // Cannot access 'num' before initialization

      //   let num = 200

      //   console.log(num) // num is not defined

      //   //   let num = 200

      //  let 和 const 所修饰的变量不具备提升操作!但是存在一个'暂时性死区'

      //   console.log(num) // Cannot access 'num' before initialization

      //   const num = 500

var、let、const的区别

总结:

      var 、 let 、const 三者的区别

      1. var存在变量提升操作,var不具备块级作用域,var可以在同一作用域重复声明同名的变量或者函数

      2. let不存在变量提升操作,let具备块级作用域,let存在暂时性死区,let不允许在同一作用域声明同名的变量和函数

      3. const不存在变量提升操作,const具备块级作用域,const存在暂时性死区,const不允许在同一作用域声明同名的变量和函数,const声明的同时必须赋!

函数的封装

      //  技术  技巧

      //  技术: 每一个知识点的学习和掌握  for语句  定时器  while循环 if  闭包  多敲!多练!多问!多思考!

      //  技巧: 编程的思想 思维!代码的冗余问题!高内聚,低耦合原则! 经验的积累!项目的累积!错误叠加!

      // 高内聚: 代码语句不是零散,散落!而是高度集中的! 函数封装!

      // 低耦合: 耦合功能快之间是否连接的特别缜密!低耦合就是要求你不要连接的特别缜密!

      // 编程高手,菜鸟的区别?

      // 看你的代码 if switch 出现的频率!

      // let arr = []

      // let num = 200

      // // if (num > 100) {

      // //   arr.push(200)

      // // }

      // // num > 100 ? arr.push(200) : ''

      // num > 100 && arr.push(200)

      // console.log(arr)

      // 将某段重复执行的代码,利用函数进行封装!方便后续的使用!

      // 需求: 求两个数的和

      //   let num1 = Number(prompt('请输入第一个数:'))

      //   let num2 = Number(prompt('请输入第一个数:'))

      //   let sum = num1 + num2

      //   alert(sum)

      //  把以上的代码封装成一个函数

      //   function add() {

      //     let num1 = Number(prompt('请输入第一个数:'))

      //     let num2 = Number(prompt('请输入第一个数:'))

      //     let sum = num1 + num2

      //     alert(sum)

      //   }

      //   add()

封装一个求任意圆面积的函数

      function getArea() {
           let r = Number(prompt('请输入一个圆的半径:'))
           let area = r ** 2 * 3.14

           return area
         }

         alert(getArea())

      // 注意:
      // 1.封装时尽可能的不要将代码段写在函数外!然后再移植到函数内!应该直接将该函数的内容,写在函数体内!
      // 2.函数命名规范!函数是为了实现某种功能!应该给使用它的人清晰的表达!见名知义!
      // 3.函数内部所使用的数据,大部分都是通过函数调用时传入的参数!
         function getArea(r) {
           return r ** 2 * 3.14
         }

         let res = getArea(5)
         console.log(res)

localStorage

    // localStorage 数据持久化到浏览器中,关闭窗口或浏览器,都不会消失

    //   localStroage 将数据永久性的存储到 浏览器中! 以键值对的形式

    // 设置数据, key 和 value 数据类型必须是字符串类型
    localStorage.setItem('name', '张三')

    // 设置数据 []
    localStorage['age'] = 18

    // 读取数据
    console.log(localStorage.getItem('name'))
    console.log(localStorage['age'])

    // http://127.0.0.1:5501/JavaScript/day07/04_localStorage.html

    //  域名 127.0.0.1
    //  域名 www.bilibili.com

sessionStorage

    //  sessionStorage 将数据临时性的存储到 浏览器中! 以键值对的形式 ! 窗口关闭 数据就消失

    // 设置数据, key 和 value 数据类型必须是字符串类型

    sessionStorage.setItem('name', '张三')

    // 设置数据 []

    sessionStorage['age'] = 18

    // 读取数据

    console.log(sessionStorage.getItem('name'))

    console.log(sessionStorage['age'])

    // http://127.0.0.1:5501/JavaScript/day07/04_localStorage.html

    //  域名 127.0.0.1

    //  域名 www.bilibili.com

执行上下文

      //  执行上下文环境:js代码在执行的过程中,会存在一个环境!js引擎会将js代码,分别设置一个执行环境!
      //  执行上下文环境的分类:三大类
      //  1. 全局执行上下文
      //  2. 函数执行上下文
      //  3. eval()执行上下文  (忽略,了解!)

      var num = 100

      console.log(666)

      10 + 20

      function say1() {
        // 函数的执行上下文
        console.log(666)
      }

      function say2() {
        // 函数的执行上下文
        console.log(666)
      }

      say1()

      console.log(66666)

      say2()

      console.log(777777)

this关键字

      //  this关键字,可以理解为 语文的 '这' 代词!  是一个变化的东西!

      //  this的变化是有规律的!

      // 寻找this,判断this

      // 1.在浏览器环境中的全局上下文中,this永远指向的是 顶层对象 window

      // 2.在浏览器环境中函数执行上下文中,this指向的也是 顶层对象  window , 严格模式除外! 函数执行上下文,设置了严格模式,那么this指向的 undefined

      // 3.如果函数是被某个对象进行调用时,那么该函数(方法)中的this指向的是,它的调用对象(调用者)!而非持有者!

      // 4.构造函数中this,指向的是新创建出来的一个该构造函数类型的 实例 {} 空对象!

      // 5.事件对象的事件处理函数中的this,指向的是 当前被绑定的元素对象,仅适用于 function函数

      // 6.箭头函数,它不存在自己的this,它的this是该箭头函数被创建时,当时的执行上下文环境中的this!并且永不改变包括this的三大劫持手段!(类似于‘印随效应’)

      //   小技巧: 如何判断函数形式执行还是方法形式执行
      //    调用时的表达式形式,
      //    函数名()
      //    对象.方法()

      //   function fun() {
      //     'use strict'
      //     console.log(this, typeof this)
      //   }

      //   fun()

      //   var obj = {
      //     name: 'obj1',
      //     fun: function () {
      //       'use strict'
      //       console.log(this)
      //     },
      //   }

      //   obj.fun()

      //   new fun()  把函数作为构造函数进行调用、使用

      let btn = document.querySelector('button')

      // btn.addEventListener('click', function () {
      //   console.log(this)
      // })

      // let obj = {
      //   name: 'obj',
      //   fun: function () {
      //     console.log(this) // obj
      //     btn.addEventListener('click', () => {
      //       console.log(this)
      //     })
      //   },
      // }

      // obj.fun()



      // var obj1 = {
      //   name:'obj1',
      //   fun:function(){
      //     console.log(this.name)
      //   }
      // }



      // var obj2 = {
      //   name:'obj2',
      //   myfun:obj1.fun
      // }


      // obj2.myfun()

严格模式

# 严格模式

w3school 文章: https://www.w3schools.cn/js/js_strict.asp

## 为什么要使用严格模式

为了代码更规范,避免一些错误与 `js` 历史问题所产生的歧义

## 严格模式中不准做的事情

开启严格模式

- 全局执行上下文 开启

- 函数执行上下文 开启

- 未声明变量就直接赋值

  - `x = 12`

  - 对象也一样 `x = {a: 1, b: 2}`

- 直接删除变量

  - `delete x`

- 重复定义参数名

  - `function fn(a, a) {}`

- 不允许使用 8 进制数

  - `let x = 010`

- 不允许使用八进制转义字符

  - `let x = '\010'`

- 不允许赋值只读属性

  - ```js

    'use strict'

    const obj = {}

    Object.defineProperty(obj, 'x', { value: 0, writable: false })

    obj.x = 3.14

    ```

  - ```js

    'use strict'

    const obj = {}

    const obj = {

      get x() {

        return 0

      },

    }

    obj.x = 3.14

    ```

- 删除不允许删除的属性

  - `delete Object.prototype`

- `eval` `arguments` 不能重复定义变量

  - `let eval = 3.14`

- 不能使用 `with` 语法

  - with (Math){x = cos(2)};

- 不能用 `eval()` 函数创建变量

  - `eval ("let x = 2")`

- \*`this` 关键字在函数内,默认为 `undefined`

  - ```js

    function fn() {

      console.log(this) // => undefined

    }

    ```

严格模式测试

      'use strict'

      // 隐式的声明 全局的作用域  var num

      //   num = 100

      //   console.log(num)

      //   delete 关键字 对象 属性 非严格模式 delete 变量

      //   var num = 100

      //   delete num

      //   var obj = {

      //     a: 100,

      //     b: 200,

      //   }

      //   console.log(obj.a)

      //   delete obj.a

      //   console.log(obj.a)

      //   function fun(a, a) {}

      //   js中表示各进制的数据

      //  0b ==》 二进制

      //  0  ==》 八进制

      //  0x ==》 十六进制

      //   var num = 053

      //   console.log(num)

构造函数

      // 构造函数:构造函数也是函数!构造函数的本义是用于构造一个实例对象!并不是像普通函数一样为了实现某种功能!

      // 任何函数都可以作为构造函数使用!前提是 它是否被 new 关键字调用!

      // 构造函数的命名:软性规定,建议构造函数的首字母大写

      //   平时创建一个对象  json对象

      //   以下的方式创建对象的问题已经暴露

      //  解决方式:通过自定义的构造函数,去构建对象!

      //   var person1 = {

      //     name: '张三',

      //     age: 18,

      //     sex: '男',

      //   }

      //   // 使用以上方式,构建一个对象比较慢!不灵活!

      //   // 如果需要批量的构建一个相同类型的对象?该方式的效率很低!

      //   var person2 = {

      //     name: '李四',

      //     age: 20,

      //     sex: '男',

      //   }

      //   var person3 = {

      //     name: '小红',

      //     age: 20,

      //     sex: '女',

      //   }

      //   //   typeof  检测的时候 无法辨别该实例对象的 具体类型

      //   console.log(typeof person1)

      //   console.log(typeof person2)

      //   console.log(typeof person3)

      //   var car = {

      //     color: 'black',

      //     price: 2000,

      //   }

      //   console.log(typeof car)

      //   //    以上的创建对象的方式 底层是 这样实现

      //   var obj = new Object()

      //   console.log(obj, typeof obj)

      function Person(name,age,sex) {

        this.name = name

        this.age = age

        this.sex = sex

      }

      function Car(color,price) {

        this.color = color

        this.price = price

      }

      // var p1 = new Person() // 构造函数中,new关键字会自动创建一个该类型的实例对象!

      // var car1 = new Car()

      // //   如果调用构造函数 不需要额外的参数时 可以省略括号 ,建议不要省略

      // var p2 = new Person

      // console.log(p1, car1, p2)

      var p1 = new Person('张三',18,'男')

      var p2 = new Person('李四',20,'男')

      var car1 = new Car('black',2000)

      console.log(p1,p2,car1)

      Person()

构造函数和普通函数的区别

       // 构造函数和普通函数的区别:

        // 1.构造函数中默认返回值已经构造完成的对象

        // 2.普通函数默认返回值是 undefined

        // 3.构造函数需要搭配new关键字进行使用

        // 4.构造函数的目的不是为了实现某种功能,而是去快速构造某一类型的实例对象

        // 5.构造函数中的this指向的是new关键字所创建的该构造函数类型的对象实例! {}空对象

        // 注意:构造函数的返回值问题

        //  1.构造函数中默认是省略 return语句  ,并且默认返回的是构建完成的实例对象 {}

        //  2.如果存在return语句,则返回的内容分情况讨论:

        //   情况一: return 返回的 数据类型是 基本值数据类型 Number、Boolean 、Sring...,则完全无视这个return,

        //   情况二: return 返回的 数据类型是 引用数据类型 则正常的将该数据进行返回

        // 为了避免上述问题的存在,建议大家构造函数中的return就不要写了!

new关键字

       // new关键字是配合构造函数使用:

        // new关键字,做了几件事情:

        // 1.new关键字会创建一个该构造函数的实例对象,一开始是空的{}

        // 2.new关键字会将刚才创建的实例对象的__proto__属性指向为 构造函数所保存的 prototype指向的对象

        // 3.new关键字会将构造函数中的this 指向为 该实例对象

        // 4.最终返回这个 实例对象

this的劫持

     // 正常情况下,this的指向都是符合 六大规则!

      // 非正常情况下,人为可以将this强行的改变!

      // 强行改变this的手段,我们称为对this的劫持!

      // this的劫持方法: call() 、 apply() 、 bind()
      // 以上的三个方法都属于,Function实例对象身上的方法!

      //  call劫持
      // var obj1 = {
      //     name:'obj1',
      //     fun:function(a,b){
      //         console.log(this,a,b)
      //     }
      // }

      // var obj2 = {
      //     name:'obj2'
      // }

      // 正常情况调用
      // obj1.fun(100,200)

      // call方法调用
      // obj1.fun.call(obj2,100,200)

      // apply劫持
      // var obj1 = {
      //     name:'obj1',
      //     fun:function(a,b){
      //         console.log(this,a,b)
      //     }
      // }

      // var obj2 = {
      //     name:'obj2'
      // }

      // // 正常情况调用
      // obj1.fun(100,200)

      // // apply()
      // obj1.fun.apply(obj2,[100,200])

      // 总结:call和apply方法的区别
      // call和apply使用方式基本一致,唯一的不同在于实参的传递方式
      // call是将多个实参以逗号分隔,依次传入函数中
      // apply是将多个实参以数组的形式,一次性传入函数中

      //  思考:利用call和apply可以劫持箭头函数中的this吗?
      // var obj1 = {
      //     name:'obj1',
      //     fun:()=>{
      //         console.log(this)
      //     }
      // }

      // var obj2 = {
      //     name:'obj2'
      // }

      // // 正常情况调用
      // obj1.fun()

      // // call 或 apply
      // obj1.fun.call(obj2)
      // obj1.fun.apply(obj2)
      // 总结:箭头函数this不能被劫持

      // bind劫持是利用bind方法返回一个新的函数!
      // 劫持该函数中的this,相对于call和apply来说,它们是操作自身的函数!bind的操作新的函数!
      var obj1 = {
        name: 'obj1',
        fun: function (a, b) {
          console.log(this, a, b)
        },
      }

      var obj2 = {
        name: 'obj2',
      }

      // 正常情况下
      // obj1.fun()

      // bind劫持
      // let res = obj1.fun.bind()
      // console.log(res) res是bind方法执行后返回来的新函数, 新的函数 ,这个新函数长的和fun函数一模一样
      obj1.fun.bind(obj2, 100, 200)()

js垃圾回收机制

        垃圾回收:JavaScript程序在运行的过程中,随着运行时间的增加,那么会相应的产生一些垃圾数据,垃圾回收机制主要是

            定时的去清理这些垃圾数据,避免内存溢出、泄露导致程序崩溃!

           

            内存的溢出:

            内存的泄露:

        注意:垃圾回收这个动作不需要人为的去管控,它是由js引擎垃圾回收的模块负责


 

        如何定义垃圾:

            - 标记清除:假设所有的变量或者函数等其他数据,一开始就认为他们不是垃圾!当他们产生了某种变化后,就会被打上标记,标记为垃圾

            - 引用计数:当引用型数据,没有再被任何引用时,引用链条为0的时候!就自动判定为垃圾数据

       

            正常情况下:

                - 全局作用域下的变量、函数在程序执行完毕后才会销毁

                - 局部作用域下的变量、函数,当该局部作用域执行完了!结束后,就销毁对应的变量以及函数

                - 引用数据当引用链条数量为0的时候,也会被销毁

       

       

        小提示:

            垃圾回收机制并不是JavaScript语言独有,很多编程语言都具备

            - java、python、JavaScript

            - c c++  人为的控制,人为的定期去是释放内存

闭包

    <script>
      // 闭包,这个东西! 道可道,非常道!名可名,非常名!

      // 闭包:把一些数据进行封装!包裹!形成一个独立的空间,该空间只会被能访问到该空间的‘人’使用

      // 如何才会产生一个闭包:
      // 1.假设有两个函数,A函数,B函数,并且B函数是在A函数的内部进行声明的!B函数是被嵌套声明的!
      // 2.B函数内部,使用了一些A函数中才会存在的数据!通过作用域链找到了A函数中的一些数据!
      // 3.最终当A函数执行完毕后,把B函数 ‘交’出去了! 交出去的方式有多种:1.return 2.直接把它赋值给外部的变量
      // 这个时候,就产生了闭包!

      // 什么是闭包?
      // 闭包就是一个函数在其他函数的内部嵌套声明,并且该函数内部使用了上级函数的数据!并且该函数被返回出去了!

      // 闭包的优点:
      // 1.使用闭包可以形成对立的空间,避免变量名污染问题
      // 2.利用闭包,可以在函数外,也能访问到函数内部的数据!
      // 闭包的缺点:
      // 1.闭包的产生有很多时候是隐式产生的,最终会造成内存泄漏

      // function A(){
      //     var num = 100
      //     function B(){
      //         // console.log(num)
      //         num++
      //         console.log(num)
      //     }

      //     return B
      // }

      // 正常的情况下
      // A()
      // A函数执行完毕后,根据垃圾回收机制, num变量 和 B函数 应该被销毁
      // 能够通过任何的手段访问到 num 和 B吗? 正常情况下是不能有任何的手段访问到num和B函数的

      // 产生了闭包的情况下

      // let C  = A()
      // 清除闭包 让变量指向为null 空
      // C = null

      // C()  //  101
      // C() //   102
      // C() //   103
      // let  F = C
      // F() // 104

      // let D = A()
      // D() //  101

      // 情况二:
      // function A(){
      //     var num = 100
      //     function B(){
      //         var num = 200
      //         num++
      //         console.log(num)
      //     }

      //     return B
      // }

      // //  产生闭包没有? 没有产生闭包

      // let C = A()
      // C() // 201
      // C() // 201
      // C() // 201

      // 情况三:
      //  function A(){
      //     var num = 100
      //     function B(){
      //         num++
      //         console.log(num)
      //     }

      //     C = B
      // }

      // var  C
      // A()
      // C()  // 101
      // C()  // 102
      // C()  // 103
      // //  产生闭包没有?产生了闭包

      // 情况四:
      // function A(){
      //     var num = 100
      //     function B(){
      //         console.log(num)
      //     }

      //     return B
      // }

      // let C = A()
      // 帮助理解闭包的例子:
      // 1.把被交出去的那个函数 ,  拟人化
      // 2.这个人,离家出走, 打包东西!
      // 3.人出去了,打包的东西也跟着走了!

      // 情况五:
      // function A(){
      //     var num1 = 100
      //     function B(){
      //         var num2 = 200

      //         function C(){
      //             console.log(num1++,num2++)
      //         }

      //         return C
      //     }

      //     return B()
      // }

      // let D = A()
      //   产生闭包没有?产生了闭包
      // D()
      // D()
      // D()
      // D()

      // 假设

      function A() {
        let num = 10
        function B() {
          console.log(num)
        }

        return B
      }

      let C = A()
      let D = C

      //  刚才A执行时在内存中所创建的  num 和 B 被销毁没有?
      //   1. 被销毁  2.没有被销毁

      //  1.如果没有被销毁 num 和 B函数空间  存在的
      //    利用了闭包这个空间 保存了 函数中运行时所需要用到的数据!防止函数无意义的执行!
      C()

      //    2.如果被销毁 num 和 B函数空间  不存在的
      C // UNDEFINDE    C()  报错

定时器

     // js中的定时器、计时器:

      // 作用:规定一段时间,然后执行某一段代码!

      // js提供了两种创建定时器的方式:

      // 全局函数:setTimeout(一次性的)  setInerval(永久性的)

      //  这两个方法都是同步方法!而传入的callback是异步的,不会阻塞主线程

      // 以上两个函数都需要传入两个参数:

      // 参数一:规定时间后,需要执行的函数 callback

      // 参数二:规定多少时间开始执行代码 单位是ms 是忽略!

      //   setTimeOut()  一次性的

      //   setTimeout(() => {

      //     console.log(666)

      //   }, 5000)

      //   setInerval() 重复的执行

      //   setInterval(() => {

      //     console.log(666)

      //   }, 1000)

      //   计时器被创建后都会产生一个唯一的计时器编号,可以利用对应的编号关闭计时器

      //   计时器的id,由   setTimeOut() 或者  setInerval()  的返回值决定

      //   1.关闭setTimeout所创建的计时器

      //   let timer = setTimeout(() => {

      //     console.log(666)

      //   }, 1000)

      //   //   console.log(timer)

      //   // 利用计时器编号关闭,setTimeout创建的计时器

      //   //     clearTimeout(timer)

      //   //     timer:对应的计时器编号

      //   clearTimeout(timer)

      //  2.关闭setInerval所创建的计时器

      //   let timer = setInterval(() => {

      //     console.log(666)

      //   }, 1000)

      //   clearInterval(timer)

      //-----------------------------------------------------

      //   感受:异步

      //   console.log(111)

      //   console.log(222)

      //   console.log(333)

      //   setTimeout(() => {

      //     console.log(444)

      //   }, 0)

      //   console.log(555)

      //   444 111 222 333 555

      //   for (var i = 0; i < 5; i++) {

      //     setTimeout(() => {

      //       console.log(i)

      //     }, 0)

      //   }

元素的自定义属性

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div class="box" id="div01" add="100" data-reduce="200" data-a="200" data-b="300">
      我是div元素
    </div>

    <!-- 
        标签元素,是具备属性的! 标签属性

        在html中大部分使用的都是,官方预定义的属性!比如: id  title  class  src......
        但是,在实际的开发中,需要用到一些,自定义的属性!
        自定义的属性作用: 保存数据! 临时保存到页面中,浏览器中!
     -->

    <!-- 如何创建自定义属性 -->
    <!-- 1. 直接在元素的开始标签中,书写自定义的属性名以及赋值操作 -->
    <!-- 2. 利用data-* 形式创建自定义属性  -->

    <!-- 如何读取自定义属性的值 -->
    <script>
      // 如何读取官方的元素属性?
      let box = document.querySelector('.box')
      // 需求:读取class的属性值
      console.log(box.className)
      //   需求:读取id的属性值
      console.log(box.id)

      //   读取自定义的元素属性值
      // 需求:读取add自定义属性的值
      //   注意:自定义属性的值,不能通过点语法进行获取,需要使用元素对象的方法进行获取!
      console.log(box.getAttribute('add'))

      //   需求:读取data-reduce自定义属性的值
      console.log(box.getAttribute('data-reduce'))

      console.log(box.getAttribute('class'))
      console.log(box.getAttribute('id'))

      //   修改、设置一个元素的自定义属性
      //   利用setAttribute会将数据,隐式的转换为string类型
      box.setAttribute('add', true)
      //   box.getAttribute('add')
      //   console.log(typeof box.getAttribute('add'))
      box.setAttribute('class', 'boxxxxx')
      box.setAttribute('data-reduce', '700')

      //   利用setAttribute为元素添加新的自定义属性
      box.setAttribute('mydata', 100)

      //   特别的data-属性的读取方式
      console.log('------------------------')
      console.log(box.dataset.reduce)
      console.log(box.dataset.a)
      console.log(box.dataset.b)
    </script>
  </body>
</html>

classList数组

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      div {
        width: 200px;
        height: 200px;
        background-color: red;
      }

      .box1 {
        background-color: pink;
      }

      .box2 {
        font-size: 50px;
      }

      .box1box2 {
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <div>div元素</div>
    <button>还原</button>
    <button>变色</button>
    <button>增大字体</button>
    <script>
      // 动态的改变box元素样式
      let box = document.querySelector('div')

      // 拿到三个按钮
      let btns = document.querySelectorAll('button')

      console.log(box.classList)
      // 为按钮绑定事件
      btns[0].addEventListener('click', () => {
        // box.className = ''
        // classList 当前元素的样式类列表
        box.classList.remove('box1')
        box.classList.remove('box2')
      })
      btns[1].addEventListener('click', () => {
        // box.className = 'box1'
        box.classList.add('box1')
      })
      btns[2].addEventListener('click', () => {
        // box.className += ' box2'
        box.classList.add('box2')
      })
    </script>
  </body>
</html>

时间对象

      //  时间对象:JS提供的一种针对处理时间相关的内容的对象!

      // 比如:一个商品的打折时间!  1.31 - 2.7  打折时间!  倒计时!

      // 需求:获取当前用户的 星期数

      // 需求:获取今年的年份,判断闰年平年!

      // .....

      //  以上和时间相关的操作!信息!都需要利用js所提供的时间对象来获取!或者操作!

      // 创建一个时间对象  利用 JS提供的 Date(构造函数、类)   ===》 new  Date类 实例化一个时间对象

      // 四种创建方式

      // 方式一:无任何构造参数

      //   let date = new Date() // 返回一个当前系统时间的,时间对象

      //   console.log(date, typeof date) // Tue Jan 31 2023 11:11:54 GMT+0800 (中国标准时间)

      //   //   注意:不要过多的关注,浏览器所展示的时间对象的样式!

      //   方式二: value  时间戳 返回一个由当前时间戳与格林威治时间(标准时间的插值)所代表的一个时间对象

      //   let date = new Date(59000)

      //   console.log(date, typeof date) // 1970-1-1-8:00:59

      // 时间戳:当前时间与标准的格林威治时间的毫秒差值,称为时间戳!

      // 格林威治:英国的小镇   1970-1-1-00:00:00    之间的差值     1970-1-1-00:00:58     58秒==》58000毫秒  这个58000毫秒就是我们说的时间戳

      //    1970-1-1-00:00:00        1970-1-1-00:00:01  08:00:01

      //   方式三: dataString 时间字符串  该方式不推荐使用!各浏览器之间对时间字符串的定义有些差入!

      // 时间字符串的参考:https://www.w3.org/TR/NOTE-datetime

      //   let date = new Date('1997-10-19')

      //   console.log(date)

      //  方式四: 依次传入  年  月 日  时  分  秒 毫秒 进行构造时间对象

      //   let date = new Date(2023, 1, 31, 11, 34, 48, 1500)

      //   let date = new Date(2023, 10)

      //   console.log(date)

      // 特殊的使用方式 不使用 new Date()

      //   Date()函数 返回的是当前时间对象的字符串形式!

      //   let date1 = Date()

      //   let date2 = new Date()

      //   console.log(date1, typeof date1, date2, typeof date2)

操作时间对象

      // 操作时间对象
      // 1.获取对应的时间数据
      let date = new Date() // 14:23:1:578
      console.log(date)
      // 1- 获取时间对象的年份
      console.log(date.getFullYear())

      //   2- 获取时间对象的月份, 注意月份是从 0开始计算的  0代表1月
      console.log(date.getMonth() + 1)

      //    3- 获取时间对象的日 一个月的第几天
      console.log(date.getDate())

      //    4-  获取时间对象的  星期数 注意:范围是0-6  0代表星期天
      console.log(date.getDay())

      //   5- 获取时间对象的 小时数
      console.log(date.getHours())

      //    6- 获取时间对象的 分钟数
      console.log(date.getMinutes())

      //    7- 获取时间对象的  秒数
      console.log(date.getSeconds())

      //    8- 获取时间对象的 毫秒数
      console.log(date.getMilliseconds())

      // 2.设置对应的时间数据
      //   将以上的所有方法的 get 替换为 set
      //   date.setFullYear(2000)
      //   console.log(date)

      //   获取时间对象的时间戳
      //   console.log(Date.now()) //获取当前系统时间的时间戳    14:23:1:804
      console.log(date.getTime()) // 获取当前时间对象的时间戳

数学函数

      // js还提供了,数学相关的运算方法!这些方法都是基于 Math 全局函数的!
      // 注意:Math函数不能作为构造函数使用! 所有是无法实例化一个数学对象!

      console.log(Math)

      //  abs()  求一个数的绝对值
      console.log(Math.abs(-10))
      console.log(Math.abs(10))

      //   cos sin  tan 三角函数   acos  asin  atan 反三角函数 求弧度值!

      //   floor 地板数,对一个数向下取整
      console.log(Math.floor(3.8)) // 3
      console.log(Math.floor(3.12)) // 3
      console.log(Math.floor(-2.8)) // -3

      //   ceil 天花板数, 对一个数向上取整
      console.log(Math.ceil(3.8)) // 4
      console.log(Math.ceil(3.12)) // 4
      console.log(Math.ceil(-2.8)) // -2

      //   max 返回当前参数列表中最大值
      console.log(Math.max(100, 200, 300, 1, 77, 800))

      //   min 返回当前参数列表中最小值
      console.log(Math.min(100, 200, 300, 1, 77, 800))

      //    pow 求一个数的幂
      console.log(Math.pow(2, 5))

      //   sqrt 开平方根
      console.log(Math.sqrt(9))

      //   round 对一个数四舍五入
      console.log(Math.round(3.1))
      console.log(Math.round(3.8))
      console.log(Math.round(4.5))

      //    random 随机数, 随机返回一个 0-1之间的小数  包括0,不包括1  伪随机!
      console.log(Math.random())

扑克牌游戏

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 斗地主
      // 游戏规则
      // 一副扑克牌: 54张
      // 三位玩家:两个农民,一个地主
      // 发牌!
      // 农民1: 17
      // 农民2: 17
      // 地主:  17
      // 地主牌: 3

      //   开始游戏!
      //   1. 准备一副54张扑克牌
      //   2. 洗牌  打乱顺序    排序是随机的
      //   3. 发牌  17张 17张 20张
      //   4. 打印农民1的牌
      //   5. 打印农民2的牌
      //   6. 打印地主的牌

      //    ♥  ♠  ♣ ♦
      //   console.log('♥5')
      //   console.log('♠5')
      //   console.log('♦5')
      //   console.log('♣5')

      //   红桃2 - 红桃A
      //   黑桃2 - 黑桃A
      //   方片2 - 方片A
      //   梅花2 - 梅花A

      let color = ['♥', '♠', '♣', '♦']
      let numbers = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
      let puker = ['joker', 'JOKER']
      let prepare_puker = []
      for (let i = 0; i < color.length; i++) {
        for (let j = 0; j < numbers.length; j++) {
          puker.push(color[i] + numbers[j])
        }
      }
      console.log(puker)
      //   洗牌

      //    洗牌方式一:
      //   for (let i = 0; i < 54; i++) {
      //     let index = Math.floor(Math.random() * puker.length)
      //     prepare_puker.push(puker[index])
      //     puker.splice(index, 1)
      //   }

      //   洗牌方式二:
      //   let arr = [1, 0, -1]
      //   puker.sort((a, b) => {
      //     // let index = Math.floor(Math.random() * arr.length)
      //     // return arr[index]
      //     return Math.random() - 0.5
      //   })
      //   console.log(puker)

      //   let player01 = puker.slice(0, 17)
      //   let player02 = puker.slice(17, 34)
      //   let player03 = puker.slice(34)
      //   console.log(player01, player02, player03)

      //    不洗牌直接发牌:
      let player01 = []
      let player02 = []
      let player03 = []
      for (let i = 0; i < 54; i++) {
        let index = Math.floor(Math.random() * puker.length)
        prepare_puker.push(puker[index])
        if (!(player01.length === 17)) {
          player01.push(puker[index])
        } else if (!(player02.length === 17)) {
          player02.push(puker[index])
        } else {
          player03.push(puker[index])
        }
        puker.splice(index, 1)
      }

      console.log(player01, player02, player03)
    </script>
  </body>
</html>

elment元素offset、client系列的属性

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .box {
        width: 200px;
        height: 200px;
        padding: 5px;
        border: 2px solid red;
        background-color: pink;

        margin-top: 50px;
        margin-left: 50px;
      }

      .father {
        /* position: relative; */
      }

      /* .big_box {
        position: relative;
      } */
    </style>
  </head>
  <body>
    <!-- <div class="big_box">
      <div class="father">
        <div class="box"></div>
      </div>
    </div> -->
    <div class="box"></div>

    <script>
      let box = document.querySelector('.box')
      // box ==> 是一个dom元素 elment元素
      console.dir(box)

      //   offset系列的属性   5个   获取当前元素的相关的偏移数据
      //   注意:offset属性具体的内容! 首先明确,该元素的 offsetParent 是谁?
      //  1. offsetParent
      console.log(box.offsetParent) // 默认情况下,元素是相对于 body元素进行偏移的!
      //   注意:offsetParent 指向的是距离当前元素最近的并且开启了定位的祖先元素!如果没有任何的元素符合这一条件那么,offsetParent默认指向body元素

      //   2. offsetTop  当前元素的顶部距离offsetParent元素的距离  返回值是 number类型
      console.log(box.offsetTop)

      //   3. offsetLeft  当前元素的左侧部分距离offsetParent元素的距离 返回值是 number 类型
      console.log(box.offsetLeft)

      //    4. offsetWidth 当前元素的可见框的  宽度大小    width + padding  + border
      console.log(box.offsetWidth) // 200 + 2 + 10

      //    5. offsetHeight 当前元素的可见框的  高度大小    height + padding  + border
      console.log(box.offsetHeight) // 200 + 2 + 10

      console.log('--------------------')

      //   client系列属性  4个  当前元素自身的一些数据值
      //   1. clientTop   当前元素的顶部边框大小  number类型  元素的padding 到 边框的之间 间距
      console.log(box.clientTop)

      //   2. clientLeft   当前元素的左侧边框大小  number类型
      console.log(box.clientLeft)

      //   3. clientWidth  当前元素的除border属性外的可见框宽度大小  width + padding
      console.log(box.clientWidth)

      //    4. clientHeight  当前元素的除border属性外的可见框高度大小  height + padding
      console.log(box.clientHeight)

      // 特殊情况:一个元素 只设置了 宽和高
      // box.offsetWidth   ===  box.clientWidth
    </script>
  </body>
</html>

事件

      // 事件:某个事情,特殊的一种情况!

      // js中可以利用某个事件的触发!去相应的执行某一断代码!

      //   事件的三要素:

      //   1. 事件源

      //   2. 事件类型

      //   3. 事件处理函数

      //   JS中如何为一个元素绑定事件

      //  绑定事件的三种方式:

      //  1. 利用 元素标签 自身的 on* 属性(事件属性) 进行事件的绑定  完全不建议使用

      //    语法:  onclick  = '函数形式的字符串' ==》  onclick = 'add()'

      function test(num) {

        console.log('html标签的事件属性')

      }

      //   2. 在js脚本中利用元素对象的事件属性进行,数据的绑定 偶尔用!

      //   let btn = document.querySelector('button')

      //   //   btn元素对象上自身的事件属性进行绑定事件

      //   console.dir(btn)

      //   btn.onclick = function () {

      //     console.log('事件属性方式')

      //   }

      //    3. 通过事件监听器,进行元素的事件绑定  强烈推荐的!

      // let btn = document.querySelector('button')

      // btn.addEventListener('click', () => {

      //   console.log('监听器方式')

      // })

事件的解绑

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 方式一: -->
    <button onclick="test()">按钮一</button>
    <!-- 方式二: -->
    <button class="btn2">按钮二</button>
    <!-- 方式三: -->
    <button class="btn3">按钮二</button>

    <script>
      // 解绑一:
      //   let count = 1
      //   let btn = document.querySelector('button')
      //   function test() {
      //     // 需求:button点击事件只能 有效的点击5次
      //     if (count === 5) {
      //       // 解绑事件, 对button按钮进行事件的解绑
      //       btn.onclick = null
      //     }
      //     console.log('按钮一', count++)
      //   }

      //   解绑二:
      //   let btn2 = document.querySelector('.btn2')
      //   let count = 1
      //   // 为btn2绑定事件
      //   btn2.onclick = function () {
      //     console.log(this)
      //     // 需求:button点击事件只能 有效的点击5次
      //     if (count === 5) {
      //       // 解绑事件
      //       this.onclick = null
      //     }
      //     console.log('按钮二', count++)
      //   }

      // 解绑三:
      let btn3 = document.querySelector('.btn3')
      let count = 1
      btn3.addEventListener('click', function test() {
        // console.log(this)
        if (count === 5) {
          // 解绑事件   监听移除函数
          btn3.removeEventListener('click', test)
        }

        console.log('按钮三', count++)
      })

      //   function mytest() {
      //     console.log(456456)
      //     console.log(456456)
      //     console.log(456456)
      //     console.log(456456)
      //     console.log(456456)
      //     console.log(456456)
      //     console.log(456456)
      //   }

      //   btn3.addEventListener('click', mytest)
      //   btn3.removeEventListener('click', mytest)
    </script>
  </body>
</html>

为一个元素绑定多个相同的事件

     // 事件属性 和 事件监听器的区别

      //   let btn = document.querySelector('button')

      //   btn.onclick = function () {

      //     console.log('click1')

      //   }

      //   btn.onclick = function () {

      //     console.log('click2')

      //   }

      //   btn.onmousemove = function () {

      //     console.log('mousemove')

      //   }

      //   事件属性的方式相同类型的事件,只能绑定一个事件处理函数

      // let btn = document.querySelector('button')

      // function test1() {

      //   console.log('click1')

      // }

      // function test2() {

      //   console.log('click2')

      // }

      // btn.addEventListener('click', test1)

      // btn.addEventListener('click', test2)

      //   事件监听的方式可以为同一个元素绑定多个相同类型的事件,并且指定不同的处理方式

事件分类

<!DOCTYPE html>
<html>
  <head>
    <style>
      .box {
        width: 300px;
        height: 300px;
        background-color: gold;
      }

      .box2 {
        width: 100px;
        height: 100px;
        background-color: pink;
        position: absolute;
        bottom: 0;
      }
    </style>
  </head>

  <body>
    <div>
      <!-- <img src="./事件冒泡与捕获过程.png" /> -->
    </div>
    <form onsubmit="return false">
      <div>
        <label>姓名:<input name="name" /></label>
      </div>
      <div>
        <label
          >班级:
          <select name="clazz">
            <option>1班</option>
            <option>2班</option>
            <option>3班</option>
          </select>
        </label>
      </div>
    </form>
    <div>
      <button class="btn1">点我</button>
    </div>
    <div class="box" tabindex="1">
      <!-- <div class="box2"></div> -->
      Lorem ipsum dolor, sit amet consectetur adipisicing elit. Exercitationem soluta minima sequi
      consectetur voluptatum reprehenderit, dolorem explicabo quaerat quo porro, omnis quia mollitia
      repudiandae labore veritatis ad nam, quibusdam sapiente.
    </div>
  </body>
  <script>
    // 参考:https://developer.mozilla.org/zh-CN/docs/Web/Events
    // https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener

    // 函数 获取页面的 元素 通过选择器的方式
    function $(selector) {
      return document.querySelector(selector)
    }

    // 资源事件
    // load: 资源加载完成后就会触发
    // $('img').addEventListener('load', () => {
    //   console.log('图片加载完成')
    // })

    // // error: 资源加载失败时触发
    // $('img').addEventListener('error', () => {
    //   console.log('图片加载失败')
    // })

    // // 焦点事件
    // // 焦点事件只用于表单元素

    // focus : 获取焦点事件
    let inp = $('form input[name=name]')
    // inp.addEventListener('focus', () => {
    //   console.log('获取到焦点了')
    // })

    // // blur: 失去焦点事件
    // inp.addEventListener('blur', () => {
    //   console.log('失去焦点了')
    // })

    // // 鼠标事件
    // // click:鼠标左键点击事件
    // let btn1 = $('.btn1')
    // btn1.addEventListener('click', () => {
    //   console.log('点击事件触发了')
    // })

    // // contextmenu: 鼠标右键点击事件,右键点击改元素是触发的事件
    let box = $('.box')
    let box2 = $('.box2')
    // box.addEventListener('contextmenu', () => {
    //   console.log('鼠标右键被点击了')
    // })

    // // dblclick: 双击事件
    // box.addEventListener('dblclick', () => {
    //   console.log('双击事件触发了')
    // })

    // // mousedown: 鼠标按下事件
    // box.addEventListener('mousedown', () => {
    //   console.log('mousedown')
    // })

    // // mouseup: 鼠标松开事件
    // box.addEventListener('mouseup', () => {
    //   console.log('mouseup')
    // })

    // // mouseenter\mouseover 鼠标移入事件
    // box2.addEventListener('mouseenter', () => {
    //   console.log('mouseenter')
    // })

    // box2.addEventListener('mouseover', () => {
    //   console.log('mouseover')
    // })

    // // mouseleave\mouseout 鼠标移出事件
    // box2.addEventListener('mouseleave', () => {
    //   console.log('mouseleave')
    // })

    // box2.addEventListener('mouseout', () => {
    //   console.log('mouseout')
    // })

    // // mousemove: 鼠标移动事件
    // box.addEventListener('mousemove', () => {
    //   console.log('mousemove')
    // })

    // // wheel : 鼠标滚轮事件
    // box.addEventListener('wheel', () => {
    //   console.log('wheel')
    // })

    // change:当值发生变化时触发,多用于表单元素
    // let select = document.querySelector('form select')
    // select.addEventListener('change', () => {
    //   console.log('change')
    // })

    // inp.addEventListener('change', () => {
    //   console.log('input change')
    // })

    // // input: 输入框输入字符时触发该事件
    // inp.addEventListener('input', () => {
    //   console.log('正在输入....')
    // })

    // // 按键事件

    // // keydown: 按钮按下事件
    // document.body.addEventListener('keydown', () => {
    //   console.log('keydown')
    // })

    box.addEventListener('keydown', () => {
      console.log('keydown')
    }) //????

    // inp.addEventListener('keydown', () => {
    //   console.log('keydown')
    // })

    // keyup:按钮抬起事件
    // document.body.addEventListener('keyup', () => {
    //   console.log('keyup')
    // })

    // // keypress: 按钮按住不放事件
    // document.body.addEventListener('keypress', () => {
    //   console.log('keypress')
    // })
  </script>
</html>

事件对象

    // 事件对象:一个事件被触发后!浏览器对应的会自动产生一个事件对象!并且该事件对象是具备类型的!

      // 鼠标事件、键盘事件、资源事件....

      // 事件对象的作用:事件对象上存放了一些与该事件产生时的一些数据或者方法!

      //   事件对象如何使用?

      //   事件对象在事件函数被执行时,浏览器会自动的向该事件函数传入对应的事件对象!只需要利用一个形参变量来接受即可!

      //   一般这个形参变量,命名为:  event ==> ev

      //   注意:不同类型的事件对象,所保存的数据的不一样的!有些属性是任何事件对象都具备的,比如: type

      //    也有很多的属性是该事件对象所独有的!

      //  事件函数调用执行时,一般不会传入其他的参数!

      //  因为事件函数的第一个实参是传入的事件对象!

      document.addEventListener('click', (event) => {
        console.log(event)
      })

      document.addEventListener('keydown', (event) => {
        console.log(event)
      })

鼠标事件对象常用属性

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 200px;
        height: 200px;
        background-color: yellow;
        position: absolute;
        left: 30px;
        top: 80px;
      }

      body {
        width: 5000px;
        height: 2000px;
      }
    </style>
  </head>
  <body>
    <!-- <button>按钮</button> -->

    <div class="box"></div>
    <script>
      // 鼠标事件相关类型  click  dbclick  mousemove ...
      document.addEventListener('click', (ev) => {
        // console.log(ev)
        // button 属性  判断点击的左键还是右键  0左键  1右键
        // console.log(ev.button)
        // type 属性 返回的是当前 事件对象的具体事件类型: click  dbclick   keydown  keyup....
        // console.log(ev.type)
        //
      })

      //   box绑定点击事件
      let box = document.querySelector('.box')
      box.addEventListener('click', (ev) => {
        ev.preventDefault()
        ev.stopPropagation()
        console.log(ev)
        // console.log(ev)
        // offsetX offsetY
        // console.log('offsetX:', ev.offsetX) //返回点击的目标点,与当前元素的左侧距离  number
        // console.log('offsetY:', ev.offsetY) //返回点击的目标点 与当前元素的顶部距离  number
        // console.log('-------------')
        // clientX  clientY
        console.log('clientX:', ev.clientX) //返回点击的目标点,与当前页面窗口的左侧距离  number
        console.log('clientY:', ev.clientY) //返回点击的目标点 与当前页面窗口的顶部距离  number
        // console.log('-------------')
        //  screenX  screenY
        // console.log('screeX:', ev.screenX) //返回点击的目标点,与当前电脑屏幕,设备屏幕的左侧距离  number
        // console.log('screenY:', ev.screenY) //返回点击的目标点 与当前电脑屏幕,设备屏幕的顶部距离  number
        // console.log('-------------')
        //  pageX  pageY
        console.log('pageX:', ev.pageX) //返回点击的目标点,与当前页面窗口的实际间隔的左侧距离! 滚动条区域也算进去!  number
        console.log('pageY:', ev.pageY) //返回点击的目标点,与当前页面窗口的实际间隔的左侧距离! 滚动条区域也算进去!  number
      })
    </script>
  </body>
</html>

事件执行机制-冒泡

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <style>
      .father {
        width: 300px;
        height: 300px;
        background-color: yellow;
      }

      .son {
        width: 200px;
        height: 200px;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      // 事件的冒泡:事件的传递过程! 事件是可以进行传递的!
      // father 盒子  son 盒子  绑定了点击事件
      // son盒子在father盒子的内部    点击son时候   son点击会触发  father触发  事件会传递!传递的过程是 由内到外 !  冒泡!

      //   事件的冒泡是建立在统一个事件类型上的! 比如  click...bdlclick...等
      let father = document.querySelector('.father')
      let son = document.querySelector('.son')

      father.addEventListener('click', (ev) => {
        console.log('father-click')
      })
      son.addEventListener('click', (ev) => {
        console.log('son-click')
      })

      document.body.addEventListener('click', (ev) => {
        console.log('body-click')
      })
    </script>
  </body>
</html>

事件执行机制-捕获

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .father {
        width: 300px;
        height: 300px;
        background-color: yellow;
      }

      .son {
        width: 200px;
        height: 200px;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      // 事件的捕获:事件的传递过程! 事件的传递过程分为 捕获阶段和冒泡阶段
      //  默认情况下所有的事件都是的冒泡阶段触发的,但也有例外可以让事件提前在捕获阶段触发!
      // 冒泡过程: 从内到外
      // 捕获过程: 从外到内
      // 一个完整的事件触发,必定经历两个阶段,1.捕获阶段  2.冒泡阶段

      //   如果想实现某个元素的事件处理函数在捕获阶段就执行,需要利用 addEventListenter 方法的第三个参数
      //   第三个参数是一个布尔值,默认情况是 false  代表不在捕获阶段执行!  true 代表在捕获阶段执行!

      let father = document.querySelector('.father')
      let son = document.querySelector('.son')

      father.addEventListener(
        'click',
        (ev) => {
          console.log('father-click')
        },
        true
      )
      son.addEventListener('click', (ev) => {
        console.log('son-click')
      })

      document.body.addEventListener(
        'click',
        (ev) => {
          console.log('body-click')
        },
        true
      )
      //   1. body-click  2. son-click 3.father-click

      //    1.body-click 2.father-click  3.son-click
    </script>
  </body>
</html>

envent对象的targetcurrentTarget属性的区别

<!DOCTYPE html>

<html lang="en">

  <head>

    <meta charset="UTF-8" />

    <meta http-equiv="X-UA-Compatible" content="IE=edge" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>Document</title>

    <style>

      .box {

        width: 200px;

        height: 200px;

        background-color: red;

      }

      .box1 {

        width: 100px;

        height: 80px;

        background-color: yellow;

      }

      .box2 {

        width: 100px;

        height: 80px;

        background-color: pink;

      }

    </style>

  </head>

  <body>

    <div class="box">

      <button>按钮</button>

      <div class="box1">

        <span>xxxxxxxx</span>

      </div>

      <div class="box2"></div>

    </div>

    <script>

      // target 和  currentTarget 存储的也是两个elment元素

      let box = document.querySelector('.box')

      box.addEventListener('click', (ev) => {

        console.log(this) // box  自身  box

        console.log(ev.target)

        console.log((ev.currentTarget === this) === box)

        console.log('---------------')

        // this ==>  当前处理函数的事件源   this指向是被绑定事件的元素

        // ev.target ==> 触发该事件产生的对象元素

        // ev.currentTarget ==》 正在处理该事件的元素对象

      })

    </script>

  </body>

</html>

事件的代理_委托

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <style>
      * {
        list-style: none;
      }
      .bigbg li {
        width: 200px;
        height: 100px;
        background-size: 200px 100px;
        border-radius: 15px;
        margin: 0 10px;
      }
      .bg1 {
        background-image: url(img/bg1.jfif);
        float: left;
      }
      .bg2 {
        background-image: url(img/bg2.jfif);
        float: left;
      }
      .bg3 {
        background-image: url(img/bg3.jfif);
        float: left;
      }
    </style>
  </head>
  <body>
    <ul class="bigbg">
      <li class="bg1" data-src="img/bg1.jfif"></li>
      <li class="bg2" data-src="img/bg2.jfif"></li>
      <li class="bg3" data-src="img/bg3.jfif"></li>
    </ul>
  </body>
  <script>
    var body = document.querySelector('body')
    // var lis = document.querySelectorAll('li')

    // lis[0].addEventListener('click', function () {
    //   body.style.backgroundImage = "url('./bg1.jfif') "
    // })
    // lis[1].addEventListener('click', function () {
    //   body.style.backgroundImage = "url('./bg2.jfif') "
    // })
    // lis[2].addEventListener('click', function () {
    //   body.style.backgroundImage = "url('./bg3.jfif') "
    // })

    // li[下标值]  ===> xxxxxxx 循环的方式,统一设置! 没必要单独为每一个元素设置相同的逻辑操作!

    // 利用冒泡机制,实现了 事件的委托 事件的代理!
    let ul = document.querySelector('.bigbg')
    ul.addEventListener('click', (ev) => {
      console.log('click')
      console.log(ev.target)
      body.style.backgroundImage = `url(${ev.target.dataset.src})`
    })
  </script>
</html>

通过js代码触发事件

      let btn = document.querySelector('button')

      btn.addEventListener('click', (ev) => {
        console.log('btn-click', ev)
      })

      //   click由‘谁’触发?  当前的用户

      //   不需要用户来触发 btn-click 事件
      //  需求: 代码开始执行后 2秒  主动触发  btn的click事件
      setTimeout(() => {
        // 时间到了  触发btn的点击事件
        console.log('希望触发btn的点击事件')
        // js代码主动触发元素的事件
        // 方式一:
        // btn.click()  // 真实的去触发了某个事件  会产生相应的事件对象
        // btn.onclick() // 本质上没有触发事件  只是调用一次  事件函数! 不存在事件对象

        //方式二:
        // 1.手动创建一个事件对象的实例
        // 2.为某个元素派遣该事件对象
        let event = new Event('click')
        btn.dispatchEvent(event)
      }, 2000)

阻止事件的冒泡

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <style>
      .father {
        width: 300px;
        height: 300px;
        background-color: yellow;
      }

      .son {
        width: 200px;
        height: 200px;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      // 事件默认是允许冒泡的!冒泡的过程会传递给上层的元素,导致上层元素不得不触发该事件,并且执行事件函数
      let father = document.querySelector('.father')
      let son = document.querySelector('.son')

      father.addEventListener('click', (ev) => {
        ev.stopPropagation()
        console.log('father-click')
      })
      son.addEventListener('click', (ev) => {
        // 阻止当前的事件继续向上冒泡
        ev.stopPropagation()
        console.log('son-click')
      })

      document.body.addEventListener('click', (ev) => {
        console.log('body-click')
      })
    </script>
  </body>
</html>

阻止元素的默认事件

   let btn = document.querySelector('button')
      btn.addEventListener('click', (ev) => {
        // 阻止默认事件的发生
        ev.preventDefault()
        console.log(6666)
      })

自定义事件

      let btn = document.querySelector('button')

      btn.addEventListener('click', (ev) => {
        console.log('btn-click')
      })

      //   绑定一个自定义的事件,我自己创建的一个事件
      btn.addEventListener('xxx', (ev) => {
        console.log('btn-xxx')
      })

      //   注意: 自定义的事件,用户是无法触发的!
      //  自定义的事件,只能通过 js代码进行触发!
      setTimeout(() => {
        // 触发 btn身上的  xxx 事件
        // 1.创建一个自定义的事件对象  通过 构造函数 CustomEvent
        // let event = new Event('')   // new Event 本质是用于创建官方的事件类型的 click  dblclike  keyup ....
        let myevent = new CustomEvent('xxx')
        // 2.派遣该事件对象
        btn.dispatchEvent(myevent)
      }, 2000)

鼠标的移入移出事件的区别

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 200px;
        height: 200px;
        background-color: yellow;
      }

      .father {
        width: 300px;
        height: 300px;
        background-color: #bfa;
      }
      .son {
        width: 100px;
        height: 100px;
        background-color: #cfcfcf;
      }

      .test_box {
        width: 200px;
        height: 200px;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>

    <div class="test_box"></div>
  </body>
  <script>
    let father = document.querySelector('.father')
    let son = document.querySelector('.son')
    let test_box = document.querySelector('.test_box')

    // mouseover(移入) 和 mouseout(移出)
    // test_box.addEventListener('mouseover', (ev) => {
    //   console.log('鼠标移入..')
    // })
    // test_box.addEventListener('mouseout', (ev) => {
    //   console.log('鼠标移出..')
    // })

    // mouseenter(移入) 和 mouseleave(移出)
    // test_box.addEventListener('mouseenter', (ev) => {
    //   console.log('鼠标移入..')
    // })
    // test_box.addEventListener('mouseleave', (ev) => {
    //   console.log('鼠标移出..')
    // })

    //  mouseover & mouseout 和 mouseover 和 mouseout 区别如下:
    // mouseover(移入) 和 mouseout(移出)
    // 进入或者离开当前元素都会触发
    // 进入或者离开当前元素的后代元素也会触发
    // father.addEventListener('mouseover', (ev) => {
    //   console.log('鼠标移入..')
    // })
    // father.addEventListener('mouseout', (ev) => {
    //   console.log('鼠标移出..')
    // })

    // mouseenter(移入) 和 mouseleave(移出)
    // 进入或者离开当前元素才会触发
    // 进入或者离开当前元素的后代元素不会触发
    father.addEventListener('mouseenter', (ev) => {
      console.log('鼠标移入..')
    })
    father.addEventListener('mouseleave', (ev) => {
      console.log('鼠标移出..')
    })
  </script>
</html>

js的对象继承问题

      // 语法糖:方便程序的开发,简洁一些语法!
      // 直接量的方式创建
      let arr1 = []
      let arr2 = [1, 2, 3]
      let arr3 = ['a', 'b', 'c']

      // 以下三个语句打印的是什么?
      console.log(arr1.push) // function
      console.log(arr2.push) // function
      console.log(arr3.push) // function

      //   arr1 arr2  arr3 是什么数据类型?   Object
      //   JS中 万物皆对象!
      //   对象是一种实例化的数据!描述的是现实中的事物! 对象是具体的!
      //   对象是类的实例! 类是抽象的 对象是具像的!   ‘人’ 类 ==》 ‘张三’ 、 ‘王五’ 、

      //   对象身上具备属性和方法!
      //   属性:描述该对象的数据
      //   方法:描述该对象的行为
      //   人类   == 》 属性:  五官的大小 身高  性别 年龄  肤色 .....
      //   人类   ==》  方法(行为): 吃饭  睡觉 打豆豆....

      //  arr1  arr2  arr3  对象
      //  肯定就是具备 属性 和 方法
      //  arr1  arr2  arr3 他们是同一类型的对象 !他们都属于同一个类!  数组  Array

      // 创建数组真实的语法:
      //   let arr = new Array(1, 23, 500, 800)  本质的数组创建

      //   arr1 arr2 arr3  同属于 Array类
      //   arr1  arr2 arr3 具备相同的属性 和 方法

      //   console.log(arr1.push) // function
      //   console.log(arr2.push) // function
      //   console.log(arr3.push) // function

      console.log(arr1.push === arr2.push)
      console.log(arr2.push === arr3.push)

      //   为什么数组实例对象  一经创建 就具备了一些属性和方法 即使该数组是空的!
      //    继承!  不要重复的为每个实例添加属性和方法! 他们属于共同的类 可以直接继承 类的  属性 和 方法!

      let str = 'hello world!'

      console.log(str.push) // undefinde  new String()

      console.log(str.split()) // str  =  new String()

显示原型和隐式原型属性

      // JS中任何的一个实例对象都具备一个 隐式原型属性  __proto__
      // JS中所有的function函数身上都具备一个 显示原型属性  prototype

      //   function是不是一个对象?
      //   function身上同时具备  __proto__  和  prototype

      //   prtotype 和 __proto__ 是两个属性!存储的是 对象类型的数据!  Obeject类型

      //   创建一个构造函数 Person
      function Person(name, age, sex) {
        this.name = name
        this.age = age
        this.sex = sex
        this.sayHello = () => {
          console.log(this.name, '@')
        }
      }

      // 创建一个Person类型的实例对象
      let p1 = new Person('张三', 18, '男')
      let p2 = new Person('李四', 20, '男')
      console.log(p1, p2)

      //   p1 和 p2 共同的属性  name,age,sex
      //    p1 和 p2 具备一些公有的属性 或者 公有的方法

      //   打印查看 Person 显示原型属性
      console.log(Person.prototype, 'Person函数对象的显示原型属性')
      //   console.log(Person.__proto__, typeof Person.__proto__)

      //   打印查看  P1 实例对象  隐式原型属性
      console.log(p1.__proto__, 'p1的隐式原型属性指向的对象')

      //   证明:  Person.prototype  === p1.__proto__
      console.log(Person.prototype === p1.__proto__)
      console.log(Person.prototype === p2.__proto__)
      console.log(p1.__proto__ === p2.__proto__)

      console.log('----------------------------')

      let arr1 = new Array(100, 200)
      let arr2 = new Array(500, 200)
      console.log(arr1.__proto__ === Array.prototype)
      console.log(arr2.__proto__ === Array.prototype)
      console.log(arr1.__proto__ === arr1)
      console.log(arr1.push === arr1.__proto__.push)
      //   继承!  js中的继承是利用了隐式原型对象
      //   以及原型链的概念,最终实现了实例对同一构造函数上的方法或者属性的继承!

演示继承

      function Person(name, age, sex) {
        this.name = name
        this.age = age
        this.sex = sex
      }

      console.log(Person.prototype)
      Person.prototype.sleep = function () {
        console.log('睡觉....')
      }

      console.log(Person.prototype)

      // 构建实例对象
      let p1 = new Person('张三', 18, '男')
      let p2 = new Person('李四', 20, '男')
      console.log(p1, p2)
      //   console.log(p1.__proto__)
      //   console.log(p2.__proto__)
      p1.sleep()
      p2.sleep()

      //   console.log(p1.sleep === p2.sleep)

原型链

      // 实例对象身上__proto__ 存储的是什么数据类型?  Object

      // 原型链

      // 每一个实例对象上都具备  __proto__ 原型对象

      // 当一个对象在自身上找不到对应的属性或者方法时,会沿着__proto__原型对象身上去寻找!  obj {}

      // 如果往上一级的__proto__身上还是找不到想要的属性或者方法 obj.__proto__

      // 那么它会继续沿着当前这一级__proto__继续寻找!  obj.__proto__.__proto__

      // ....  obj.__proto__.__proto__.__proto__

      //    沿着原型链最终找到 null  就结束! 如果已经访问到null 都还没有找到想要的属性或者方法 则放回 undefined

      //   以上描述寻找的过程就称为  原型链!

显示原型对象和隐式原型对象的区别

      function Person() {}

      console.log(Person.prototype)
      console.log(Person.__proto__)

      console.log(Person.prototype === Person.__proto__)

      //   函数中的prototype属性是为了保证所有经过该函数所构造的实例对象能够继承该prototy属性所指向的对象!临时的保存!

      let p1 = new Person()
      // 1.P1你长大了被创建出来了!
      // 2.我这里有一样要交给你!  prototype 钥匙  ===》  ‘东西’
      // 3.这个东西你准备放在哪里,不要搞丢了!
      // 4.p1说你放在我的 __proto__ 上面吧!  不会搞丢的!

      let p2 = new Person()
      // 1.P2你长大了被创建出来了!
      // 2.我这里有一样要交给你!  prototype 钥匙  ===》  ‘东西’
      // 3.这个东西你准备放在哪里,不要搞丢了!
      // 4.p2说你放在我的 __proto__ 上面吧!  不会搞丢的!

      //   let Person = new Function()
      // 1.Person你长大了被创建出来了!
      // 2.我这里有一样要交给你!Function 身上  prototype 钥匙  ===》  ‘东西’
      // 3.这个东西你准备放在哪里,不要搞丢了!
      // 4.Person说你放在我的 __proto__ 上面吧!  不会搞丢的!

      console.log(Object.prototype.__proto__, '@')

      console.log(p1.__proto__.__proto__ === Object.prototype)

      //  注意:prototype 属性 可以随意的更改 使用!
      //  JS代码中,尽可能的不要去 动  __proto__  隐式元素对象

私有属性和私有方法

      // 私有属性或者私有方法,指的是该属性和方法只能在类中使用,不能够通过实例化的方式进行点语法的获取

      class Person {
        country = '中国'
        // 定义私有属性  #变量名
        #num = 100
        // 构造器,构造函数  必须的!
        constructor(name, sex, age) {
          this.name = name
          this.sex = sex
          this.age = age
          this.mynum = this.#num
          this.xxx = this.#sayHello()
          this.#xxx = 500
        }

        // Person类中 {}
        // 所定义的方法直接就挂载到 Person.prototype对象上
        eat() {
          console.log('eat...')
          console.log(this.#num)
          this.#sayHello()
        }

        sleep() {
          console.log('sleep...')
        }

        play_dd() {
          console.log('play_dd...')
        }

        // 定义私有的方法  #方法名
        #sayHello() {
          console.log('hello...', '@')
          return 200
        }
      }

      let p1 = new Person('张三', '男', 18)
      console.log(p1)
      //   console.log(p1.country)
      //   console.log(p1.name)
      //   console.log(p1.sex)
      //   console.log(p1.age)
      p1.eat()
      //   p1.sleep()
      //   p1.play_dd()

      //   console.log(p1.#num)
      //   p1.#sayHello()  err

静态属性和静态方法

      // 静态属性和静态方法,指的是该属性和方法只能由类本身进行调用!
      class Person {
        // 定义一个静态属性 static 属性名
        static name = 'Person'
        // 构造器,构造函数  必须的!
        constructor(name, sex, age) {
          this.name = name
          this.sex = sex
          this.age = age
        }

        // Person类中 {}
        // 所定义的方法直接就挂载到 Person.prototype对象上
        eat() {
          console.log('eat...')
        }

        sleep() {
          console.log('sleep...')
        }

        play_dd() {
          console.log('play_dd...')
        }

        // 定义一个静态方法  static 方法名
        static test() {
          console.log('test...')
        }
      }

      let p1 = new Person()
      //   console.log(p1.xxx)
      //   p1.test()

      console.log(Person.name)
      Person.test()

子类与父类之间的继承语法

      // 定义一个类 Person  人类   name  sex age  ...   吃饭 睡觉 ....
      class Person {
        constructor(name, sex, age) {
          this.name = name
          this.sex = sex
          this.age = age
        }

        eat() {
          console.log('eat....')
        }
      }

      // 定义一个类 Stutent  学生类 name  sex  age  sid  clazz.. 吃饭  睡觉  上课....
      //   没有继承的写法
      //   class Student {
      //     constructor(name, sex, age, sid, clazz) {
      //       this.name = name
      //       this.sex = sex
      //       this.age = age
      //       this.sid = sid
      //       this.clazz = clazz
      //     }

      //     eat() {
      //       console.log('eat....')
      //     }

      //     // 学生类的方法  上课
      //     study() {
      //       console.log('上课中....')
      //     }
      //   }

      //  继承  子类与父类的继承!
      //  Student 继承于  Person
      // 继承的写法
      class Student extends Person {
        constructor(name, sex, age, sid, clazz) {
          // 调用一次 父类的 constructor
          // 关键字 super
          super(name, sex, age)
          this.sid = sid
          this.clazz = clazz
        }

        study() {
          console.log('上课中...')
        }
      }

      let stu1 = new Student('张三', '男', 18, 01, '三班')
      console.log(stu1)
      stu1.study()
      stu1.eat()

super关键字

      // super 关键字 它在js中特用于!class的情况!
      //   console.log(super)  err

      //   super关键字有点类似于this  this==》指代某个对象!
      //   super也有指代的意思,也是指代某种东西!
      //   super的指代,或者使用的三种情况
      //    1. cconstructor 构造函数中使用时  super 就是指代了 父类的constructor函数本身
      //    2. 在子类的'普通'方法中(除静态方法外) super 指代的是 父类的 prototype属性指向的对象 Student extents Person  ==》  super  ==》 Person.prototype
      //    3. 在子类的静态方法中, super 指代的是 父类 本身    Student extents Person  super ==》 Person

      // 类中有那些方法?  静态方法 static 私有方法 # 普通方法 无任何修饰

      class Person {
        static myname = 'Person'
        constructor(name, sex, age) {
          this.name = name
          this.sex = sex
          this.age = age
        }

        eat() {
          console.log('eat....')
        }
      }

      class Student extends Person {
        constructor(name, sex, age, sid, clazz) {
          // 调用一次 父类的 constructor
          // 关键字 super
          super(name, sex, age) // super代表的是父类的 constructor函数
          this.sid = sid
          this.clazz = clazz
        }

        study() {
          console.log('上课中...')
          // console.log(super)  //注意: 使用super 不能查看super  super.sss super()
          //   console.log(Person.prototype.eat === super.eat)  //  true
          //   super.eat()
        }

        // 定义一个私有方法
        #mytest() {
          //console.log(Person.prototype.eat === super.eat)  //  true   Person.prototype === super
          super.eat()
        }

        test() {
          this.#mytest()
        }

        // 定义一个静态方法
        static fun() {
          console.log('fun...')
          console.log(super.myname) // Person   super === Person
        }
      }

      //    1. cconstructor 构造函数中使用时  super 就是指代了 父类的constructor函数本身
      let stu1 = new Student('张三', '男', 18, 01, '三班')

      //    2. 在子类的'普通'方法中(除静态方法外) super 指代的是 父类的 prototype属性指向的对象 Student extents Person  ==》  super  ==》 Person.prototype
      stu1.study()
      stu1.test()

      //    3. 在子类的静态方法中, super 指代的是 父类 本身    Student extents Person  super ==》 Person

      //   console.log(Person.myname)    super.myname ==> Person

      Student.fun()

面向过程&面向对象编程思想

      // 编程思想
      // 面向过程:以过程为导向!  step  by step  ‘一步一步的’
      // 面向对象:以对象为导向! 面向对象进行编程! 主要是构建类! 一般用于比较复杂的抽象的问题!该思想更方便!

      //   计算器  计算两个数据的 加减乘除:
      //   面向过程
      //   let num1 = Number(prompt('请输入数字1:'))
      //   let num2 = Number(prompt('请输入数字2:'))
      //   let symbol = prompt('请输入运算符:+ - * /')
      //   switch (symbol) {
      //     case '+':
      //       alert(num1 + num2)
      //       break
      //     case '-':
      //       alert(num1 - num2)
      //       break
      //     case '*':
      //       alert(num1 * num2)
      //       break
      //     case '/':
      //       alert(num1 / num2)
      //       break
      //     default:
      //       break
      //   }

      //   面向对象
      //   计算器对象    属性: num1  num2
      //   方法1: 运算两个数的值
      //   方法2: 输入一个数
      class Jsq {
        num1
        num2
        symbol
        // 如果不需要构建任何的实例对象的属性,constructor可以为空但是不能不写
        constructor() {}

        // 接受一个数
        getNum() {
          return Number(prompt('请输入一个数据:'))
        }

        // 接受一个符合
        getSymbol() {
          return prompt('运算符:')
        }

        // 运算两数之和
        computer() {
          let res
          switch (this.symbol) {
            case '+':
              res = this.num1 + this.num2
              break
            case '-':
              res = this.num1 - this.num2
              break
            case '*':
              res = this.num1 * this.num2
              break
            case '/':
              res = this.num1 / this.num2
              break
            default:
              break
          }
          return res
        }
      }

      let jsq = new Jsq()
      console.log(jsq)
      jsq.num1 = jsq.getNum()
      jsq.num2 = jsq.getNum()
      jsq.symbol = jsq.getSymbol()
      let res = jsq.computer()
      console.log(res)

面向对象-闹钟

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .clock {
        text-align: center;
      }
    </style>
  </head>
  <body>
    <div class="clock"></div>
    <script>
      // 面向对象- 闹钟 类

      // 属性:时间节点...  时针  分针 秒针  颜色  大小 ....
      // 方法: 开始 结束 实时倒计时更新  到点提醒  ....

      //

      class Clock {
        // 闹钟的实例
        // 那些属性是需要构造
        //  颜色,大小(宽 、 高)、时间节点(终点日期)
        // 时分秒三个属性,计数属性 不需要构造的
        hours = 0
        minutes = 0
        seconds = 0
        // 定时器编号
        timer

        constructor(color, width, height, endTime, elment) {
          // endTime  介质日期的时间对象
          this.endTime = endTime.getTime() //用户传入的时间对象节点所代表的时间戳
          this.elment = elment
          this.elment.style.width = width
          this.elment.style.height = height
          this.elment.style.lineHeight = height
          this.elment.style.backgroundColor = color
        }

        // 闹钟开始计时
        begin() {
          // 初始化 闹钟的 时分秒
          this.render()
          // 利用JS引擎 创建一个定时器
          this.timer = setInterval(() => {
            this.render()
          }, 1000)
        }

        // 闹钟结束
        stop() {
          // 清除的定时器
          clearInterval(this.timer)
        }

        // 实时的更新  时 分 秒 实时倒计时  渲染时分秒  倒计时
        render() {
          // 获取系统
          let date = new Date().getTime()
          // 计算节点时间和当前系统时间之间的差值
          let difference = this.endTime - date
          // 将差值转换为闹钟的 时 分 秒
          this.hours = Math.floor((difference / 1000 / 60 / 60) % 24)
          this.minutes = Math.floor((difference / 1000 / 60) % 60)
          this.seconds = Math.floor((difference / 1000) % 60)
          this.elment.innerHTML = `${this.hours}时${this.minutes}分${this.seconds}秒`
          if (this.hours === 0 && this.minutes === 0 && this.seconds === 0) {
            this.stop()
          }
        }

        // 判断用户的节点日期是否正确!待完善
      }

      //   获取clock 元素
      let clock = document.querySelector('.clock')
      //   实例化这个类
      let clock1 = new Clock('red', '200px', '200px', new Date(2023, 1, 6, 12, 13, 0), clock)
      // 闹钟  开始计时
      clock1.begin()
      clock1.stop()
    </script>
  </body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值