JavaScript学习总结

目录

变量

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

变量的特点

    内存中数据存储的特点:1.读写速度快 2.临时数据的存储 3.内存的存储空间是比较小的

JavaScript中的数据类型

数据类型

// mdn 数据类型:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B

数据类型是什么?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 的一个原始数据类型,用来指代引用类型数据的空值

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

值类型:变量中直接存贮值本身

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

值类型和引用数据类型的存储方式

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

  //   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}

数据类型的转换

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

  强制转换:
  Number(x)函数 该函数可以将其他的数据类型强制转换为Number类型的数据

    console.log(typeof 100)
     String ==》 Number
    console.log(Number('123'), typeof Number('123'))
    console.log(Number('abc'), typeof Number('abc')) // NaN是一种特殊的number值,该值表示无法正确转换时的替代!其类型也是Number   not a number
    console.log(Number('你好'), typeof Number('你好'))
    console.log(Number('123a'), typeof Number('123a')) // NaN
    console.log(Number('3.14'), typeof Number('3.14'))   // 3.14
    console.log(Number('-100'), typeof Number('-100'))

    Boolean  ==> Number
    console.log(Number(true), typeof Number(true))
    console.log(Number(false), typeof Number(false))

    BigInt ==》 Number
    console.log(Number(100n), typeof Number(100n))

      Null ==> Number
    console.log(Number(null), typeof Number(null))  // 0

    Undefined ==》 Number
    console.log(Number(undefined), typeof Number(undefined))  // NaN

    Symbol ==> Number
    console.log(Number(Symbol('100')), typeof Number(Symbol('100'))) // 无法转换

    Object ==> Number
    console.log(Number({}), typeof Number({})) // NaN
    总结:NaN表示该类型无法找到对应的Number值与之匹配!Symbol类型无法被转换为Number
      实际的开发中,常常将 string Boolean 转换为 Number

  补充除了Number()函数可以强制转换其他数据为Number类型外,还有两种手段
   全局函数:  parseInt()   parseFloat
      parseInt() 将其他数据类型转换为number的整数值
    console.log(parseInt('100'), typeof parseInt('100'))
    console.log(parseInt('123a'), typeof parseInt('123a')) // 123
    console.log(parseInt('3.14'), typeof parseInt('3.14')) // 3
    console.log(parseInt(3.5))
    console.log(parseInt('123.11a'), typeof parseInt('123.11a'))

    parseFloat() 将其他数据类型转换为number的浮点数
    console.log(parseFloat('100'), typeof parseFloat('100'))
    console.log(parseFloat(200), typeof parseFloat(200))
    console.log(parseFloat('3.14'), typeof parseFloat('3.14'))
    console.log(parseFloat('123a'), typeof parseFloat('123a'))
    console.log(parseFloat('123.11a'), typeof parseFloat('123.11a'))

  ---------------------------------------------------

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

     Number ==> String
    console.log(String(100), typeof String(100))
    console.log(String(3.14), typeof String(3.14))
    console.log(String(-200), typeof String(-200))

     BigInt  ==> String
    console.log(String(200n), typeof String(200n))

  Boolean ==> String
    console.log(String(true), typeof String(true)) // true  ==>  'true'
    console.log(String(false), typeof String(false))

   null ==> String
    console.log(String(null), typeof String(null))

    undefinde ==> String
    console.log(String(undefined), typeof String(undefined))

    Symbol ==>  String
    console.log(String(Symbol('adbcdddf')), typeof String(Symbol('abcdddf')))

    Object ==>  String
    console.log(String({}), typeof String({})) // '[object Object]'   json对象转为字符串后,值固定为   '[object Object]'
    console.log(String([1, 2, 3]), typeof String([1, 2, 3]))

  ---------------------------------------------------
     Boolean()   该函数是将其他类型强制转换为布尔类型
     Number ==》 Boolean
      Number类型转换为Boolean时,除0,+0,-0,以外的值都转换为true
    console.log(Boolean(100), typeof Boolean(100))
    console.log(Boolean(-100), typeof Boolean(-100))
    console.log(Boolean(3.14), typeof Boolean(3.14))
    console.log(Boolean(0), typeof Boolean(0))
    console.log(Boolean(-0), typeof Boolean(-0))
    console.log(Boolean(+0), typeof Boolean(+0))

      String ==》 Boolean
      字符串类型在转换为布尔值时除 空字符''外的其他字符串都转换为true
    console.log(Boolean('100'), typeof Boolean('100'))
    console.log(Boolean('abc'), typeof Boolean('abc'))
    console.log(Boolean('3.14'), typeof Boolean('3.14'))
    console.log(Boolean('你好'), typeof Boolean('你好'))
    console.log(Boolean(' '), typeof Boolean(' '))
    console.log(Boolean(''), typeof Boolean(''))

       BigInt ==》 Boolean
    console.log(Boolean(100n), typeof Boolean(100n))
    console.log(Boolean(-100n), typeof Boolean(-100n))
    console.log(Boolean(0n), typeof Boolean(0n))

    null  ==》 Boolean
    console.log(Boolean(null), typeof Boolean(null)) //false

     undefined ==> Boolean
    console.log(Boolean(undefined), typeof Boolean(undefined)) // false

    Symbol ==> Boolean
    console.log(Boolean(Symbol(100)), typeof Boolean(Symbol(100)))

   Object ==> Boolean
    console.log(Boolean({}), typeof Boolean({}))
    console.log(Boolean({ name: 'zhagnsan' }), typeof Boolean({ name: 'zhangsan' }))

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

自增自减运算

自增、自减

// ++ 、 –

自增运算:

	 前自增、后自增 , 运算符都是 ++ 只是++的位置不同

自减运算:

	 前自减、后自减 , 运算符都是 -- 只是--的位置不同

自增,自减运算

     运算规则:  将变量自身的值取出来+1或-1,然后重新赋值给变量!

     let num = 50

     num = num + 1

     console.log(num)

     自增操作

     let num = 50

   num++  后自增  等价于  num = num +1  、 num+=1

   ++num  前自增  等价于  num = num +1  、 num+=1

    console.log(num)

前自增和后自增的区别:

前自增,是先将数据进行+1,然后再参与其他的运算

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

Json对象

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

   var obj1 = {
    name: 'obj1',
    'black-color': 'red',
     a: true,
    b: false,
    c: null,
   d: [1, 2, 3, 4],
    f: {
      x: 100,
   },
   } // console.log(obj1)

  // 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数据类型!

变量:变量就是存放数据的一个容器

声明变量:

​ var:可以同时声明多个变量,还可以重复声明相同的变量

​ let:可以同时声明多个变量,但是不可以在同一作用域重复声明相同变量

​ const:声明常量变量且声明的常量不可以被修改,在同一作用域下不允许声明相同的变量

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

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

​ 序列化:将一个合法的json对象,转换为一段标准的json格式的数据!

​ 反序列化:将一段标准的json格式的数据,字符串转化为一个合法的json对象!

​ 序列化:利用js提供的JSON对象上的方法进行操作 JSON.StringIfy

let  obj  = {
            name:'张三',
            age:18,
            a:[1,2,3],
            b:{
                x:100,
                y:100
            }
        }
var res = JSON.stringify(obj)
console.log(res,typeof res)

​ 反序列化:利用js提供的JSON对象上的方法进行操作 JSON.parse()

var newobj = JSON.parse(res)
console.log(newobj , typeod newobj)
console.log(newobj.name,newobj.age)


运算符

js中的运算符分类:
- 算术运算符
- 赋值运算符
- 比较运算符
- 逻辑运算符
- 三元运算符
- 单目运算符

算术运算符

算术运算符: + 、 - 、 * 、 / 、%(模运算,取余)、**(幂运算)

复合运算符

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

比较运算符

比较运算符:用于对两个数据进行数据的比较,其比较结果为布尔值!true 或 false
js中的比较运算符: > 、< 、 >= 、<=、 ==、 =、 != 、!

逻辑运算符

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中的逻辑与和逻辑或都是属于短路与,短路或!

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

自增、自减

  //  ++ 、 --

自增运算:

	 前自增、后自增 , 运算符都是 ++ 只是++的位置不同

自减运算:

	 前自减、后自减 , 运算符都是 -- 只是--的位置不同

自增,自减运算

     运算规则:  将变量自身的值取出来+1或-1,然后重新赋值给变量!

     let num = 50

     num = num + 1

     console.log(num)

     自增操作

     let num = 50

   num++  后自增  等价于  num = num +1  、 num+=1

   ++num  前自增  等价于  num = num +1  、 num+=1

    console.log(num)
前自增和后自增的区别:

前自增,是先将数据进行+1,然后再参与其他的运算

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

三目运算符

语法规则: 表达式?结果1:结果2
运算流程:计算表达式结果,如果表达式为真,则将结果1的数据进行返回,否则返回结果2的数据

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

流程控制结构

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——————')
多分支语句

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——————')
条件分支语句 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——————')

循环结构

  js的循环结构
      循环:重复的执行一段相同的代码
        js中提供了以下几个循环语句
        while语句 do…while语句 for语句

while循环

      while循环
语法:
while (表达式) {
循环体
}
while循环的注意事项:
1.必须设置循环控制变量
2.必须为循环控制变量赋初始值
3.每一次的循环体结束前应该更新循环控制变量

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

   while循环实现
  1.设置循环控制变量
    let count = 1 // 2.设置初始值

    while (count <= 5) {
      console.log('hello world!')
      //  3. 更新循环控制变量
      count++
    }
    console.log('end—————')

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循环

  let count = 1
  while(count<=5) {
      console.log('hello world!')
      count++
  }

    for (let count = 1; count <= 5; count++) {
      console.log('hello world!')
    }

    for (let count = 1; count <= 5; count++) {
      console.log('hello world!')
    }

  练习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…

continue和break关键字

   这两个关键字都可以让循环提前结束
   break关键字:直接结束当前包含break的循环语句进入下一个步骤
   continue:该关键字只结束该循环的本次内容,后续的循环继续执行

获取文档元素并修改其元素值

 浏览器为程序员提供了一个接口!
 该接口可以访问页面中的所有元素节点!
 该接口是一个对象!浏览器提供!   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')

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

 // 获取 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

可以使用Array.isArray()来判断一个变量是否为数组

修改数组元素的值

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

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

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

数组的常见操作

数组的常见操作
所有的数组实例对象,都来自于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 = ['a', 'b', 'c', 'd', 'f']

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

对数组进行倒置操作

倒置操作: [‘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'

文档树

文档树
    浏览器会将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)

删除节点

  <body>
    <ul>
      <li class="first-li">旧的li</li>
      <li>li</li>
      <li>li</li>
      <li>li</li>
    </ul>
  </body>

  <script>
    删除操作
    方式一:元素自删除!
    let firstli = document.querySelector('.first-li')

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

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

    let ul = document.querySelector('ul')
    ul.removeChild(firstli)

查询操作

<body>
    <ul>
      <li class="first-li">旧的li</li>
      <li>li</li>
      <li>li</li>
      <li>li</li>
    </ul>
  </body>

  <script>
    // 查询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)
  </script>

快速收集表单数据

1.先获取元素值
2.如果需要拿到的元素不止一个,那就先遍历所需要的元素,以input为例,先拿到所有的input元素,然后将其所有元素遍历,可以通过input元素的value值来进行数据的收集

通过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'))
    })

通过数组渲染表格

<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循环遍历,这里就不详细说了

foreach方法

    let arr = [100, 200, 300, 400]

      // forEach方法进行遍历数组
      //   forEach方法可以实现数组的遍历操作!对数组中每一个元素进行访问
      //  每访问一个元素就执行一次callback函数  , forEach方法没有返回值
      arr.forEach((el, index, myarr) => {
        // el : 代表当前正在访问的数组元素
        // index:代表当前正在访问的元素下标
        // myarr:操作数组本身 一般情况下会省略myarr参数
        // console.log(el, index, myarr)
        // console.log(myarr === arr)
      })

      // forEach和for循环的区别
      // forEach方法相对于for循环来说,更方便一些!
      // forEach它不允许打断,数组元素有多少个,callback就一定会执行多少次
      // for循环可以中途退出

every方法

 let arr = [100, 200, 300, 400]

      // every和forEach基本上是一样的!
      //  every方法同样可以遍历数组,但是允许中途退出!
      //   every方法会根据callback函数每次执行后的返回值进行判断,如果为true 则继续遍历操作,如果为false 则退出遍历操作!

      //    every具备返回值,返回值的类型是布尔值!
      //   let res = arr.every((el, index, myarr) => {
      //     return el >= 100
      //   })

      //   console.log(res)

      //   every方法的真正作用是,判断某一个数组中是否所有的元素都满足相应的要求!满足则返回 true  只要有一个不满足就返回false

      //   需求:判断一个数组中是否每一个元素都是偶数
      //   let myarr = [2, 2, 4, 8, 16, 10]

      //   let res = myarr.every((el) => {
      //     return el % 2 == 0
      //   })

      //   if (res) {
      //     console.log('该数组全部是偶数')
      //   } else {
      //     console.log('该数组中至少有一个元素不是偶数')
      //   }

some方法

    let arr = [1, 2, 301, 401]
      //   some方法和every方法是相反的
      //   some方法是遍历一个数组的所有元素,如果有一个元素满足条件,sonme就返回 true  否则返回false
      //   some方法在遍历元素中,每遍历一个元素执行一次回调函数,如果回调函数返回 false 则继续遍历! 直到遍历结束或返回true
      //   arr.some((el, index, myarr) => {
      //     console.log(el)
      //     return true
      //   })

      // 需求: 判断一个数组中是否至少有一个元素是偶数
      //   let res = arr.some((el) => {
      //     console.log(666)
      //     return el % 2 == 0
      //   })

      //   if (res) {
      //     console.log('符合要求')
      //   } else {
      //     console.log('不符合要求')
      //   }

map方法

  let arr = [100, 200, 300, 400]
      // map方法 它会返回一个新的数组
      // 新数组从每一次执行callback的返回值来,每一次callback返回值都会放到该数组中!

      //   let res = arr.map((el, index, myarr) => {
      //     console.log(el, index, myarr)
      //     return 100
      //   })

      //   console.log(res)

      //   需求:我想将arr数组中的每一个元素在原有的基础上增加一倍!

      //   let res = arr.map((el) => {
      //     return el * 2
      //   })

      //   console.log(res)

      //   map实现数组的浅拷贝
      //   let res = arr.map((el) => {
      //     return el
      //   })

      //   console.log(res === arr)

filter方法

 // 过滤器 filter方法 也会返回一个数组
      // 每一次callback函数执行后的函数值 如果true 则保留当前的元素到新数组中,如果是false 则继续下一次遍历

      //   let arr = [1, 2, 3, 4]

      //   //   需求:筛选出arr数组中的所有偶数 组成一个新的数组

      //   let res = arr.filter((el) => {
      //     return el % 2 == 0
      //   })

      //   console.log(res)

find方法

   // find 从数组中查找符合条件第一个元素并返回该元素,如果遍历完整个数组都没有找到符合添加的元素则返回 undefined

      //   let arr = ['张三', '李四', '王五', '李四']

      //   // 需求: 从arr数组中查询是否存在 一个元素值为 '李四' 存在则返回该元素

      //   let res = arr.find((el) => {
      //     console.log(666)
      //     return el === '李四xxxx'
      //   })

      //   console.log(res)

findIndex方法

 // findIndex  从数组中查找符合条件第一个元素并返回该元素的下标值,如果遍历完整个数组都没有找到符合添加的元素则返回 -1

      let arr = ['张三', '王五', '李四']

      // 需求: 从arr数组中查询是否存在 一个元素值为 '李四' 存在则返回该元素

      let res = arr.findIndex((el) => {
        return el === '李四xxxxxx'
      })

      console.log(res)

字符串操作

字符串基础

  // 字符串的基础内容
      // 设置字符串
      let str1 = 'hello world!'
      //   let str2 = 'hello world!'
      //   let str3 = `hello world!`

      //  读取字符串
      console.log(str1)

      //    将字符串看作是数组的形式,进行访问字符串中单个字符
      //    利用下标值,访问单个字符串的内容
      console.log(str1[0])
      console.log(str1[1])
      console.log(str1[2])

      // 字符串的其他操作! 字符串
      console.log(typeof str1) // string

      //    万物皆对象
      //    Object类型的对象 才具备属性和方法

      //   length属性  查看字符串的长度!
      console.log(str1.length)
      //  从常规来说,基本数据类型不是对象,是无法拥有属性和方法的!也不能像操作对象那样操作普通数据!
      // 但是在js中一个工具,js引擎在执行非对象类型的数据时,如果该数据类型并不是Object!那么js会进行包装!把它包装成一个Object类型!

      //   console.log(Object(str1))
      //   console.log(Object(100))
      //   console.log(Object(true))

charAt方法

let str = 'hello world!'

      //  charAt方法 是通过指定的下标进行访问对应的字符  类似于  str[0]

      let res = str.charAt(0) // 等价于 str[0]
      console.log(res)

split方法

   let str = 'hello world!'

      //   split方法 通过指定的字符将字符串分割成一个字符数组!
      let res = str.split('')
      console.log(res)

concat方法

 let str1 = 'hello'
      let str2 = 'world!'

      // concat是将多个字符串进行拼接  等价于  +
      let str3 = str1.concat(str2)
      console.log(str3)

includes方法

 let str = 'abcdefg'
      //  includes去字符串中查询是否存在一个指定的字符!存在则返回 true  不存在 返回 false
      let res = str.includes('a')
      console.log(res)

indexOf方法

 let str = 'abcdefg'
      //  indexOf去字符串中查询是否存在一个指定的字符!存在则返回 对应的下标值  不存在 返回 -1
      //    从左往右查找第一个匹配的字符下标
      let res = str.indexOf('axxxxx')
      console.log(res)

lastIndexOf方法

  let str = 'abcdefg'
      //  lastIndexOf去字符串中查询是否存在一个指定的字符!存在则返回 对应的下标值  不存在 返回 -1
      //    从右往左查找第一个匹配的字符下标
        let res = str.lastIndexOf('g')
        console.log(res)

trim方法

   let str = '   hello world!   '
      console.log(str)
      //   trim 去除字符串前后的空格
      let res = str.trim()
      console.log(res)

trimStart方法

 let str = '   hello world!   '
      console.log(str)
      //   trimStart 去除字符串前面的空格
      let res = str.trimStart()
      console.log(res)

trimEnd方法

 let str = '   hello world!   '
      console.log(str)
      //   trimEnd 去除字符串后面的空格
      let res = str.trimEnd()
      console.log(res)

toUpperCase方法

  let str = 'abcdefgABCDN'

      // toUpperCase该方法将字符串中所有的小写字母转换为大写字母
      let res = str.toUpperCase()
      console.log(res)

toLowerCase方法

 let str = 'abcdefgABCDN'

      // toLowerCase该方法将字符串中所有的大写字母转换为小写字母
      let res = str.toLowerCase()
      console.log(res)

substring方法

 let str = 'hello world!'
      //  substring(开始下标,结束下标) 方法是对字符串进行截取
      //  注意:截取区间是 左闭右开的区间
      //   let res = str.substring(0, 5)
      //   let res = str.substring(2) //如果省略结束下标,则从指定位置截取到字符串末尾!

      // 如果 indexStart 等于 indexEnd,substring 返回一个空字符串。
      // 如果省略 indexEnd,substring 提取字符一直到字符串末尾。
      // 如果任一参数小于 0 或为 NaN,则被当作 0。
      // 如果任一参数大于 stringName.length,则被当作 stringName.length。
      // 如果 indexStart 大于 indexEnd,则 substring 的执行效果就像两个参数调换了一样。见下面的例子。
      console.log(res)

startsWith方法

    let str = 'hello world!'
      //  startsWith 方法 判断该字符串是否以指定的字符串进行开头

      let res = str.startsWith('hello')
      console.log(res)

endsWith方法

    let str = 'hello world!'
        //  endsWith 方法 判断该字符串是否以指定的字符串进行结尾

        let res = str.endsWith('!')
        console.log(res)

replace方法

 let str = 'hello world!'
      //  replace 方法将指定的字符替换为其他字符
      let res = str.replace('hello', 'xxxx')
      console.log(res)

BOM对象

window

BOM (浏览器对象)对象 ==》 代表整个浏览器
浏览器环境中,为js提供了两个对象!这两个对象是浏览器提供的,也称为宿主对象!

  DOM (文档对象)  ===》 文档树

   BOM下的具体的对象!
   window对象  BOM的核心对象  windonw指向的是 浏览器的窗口!
   windo对象除了指向浏览器窗口,它还代表着js在浏览器环境下的全局对象!也称为顶层对象!
  console.log(window)

   window作为顶层对象,它的作用!在全局环境下所声明的全局var变量或者函数都是作为window对象的属性存在!
 var num = 200
      console.log(num)
      console.log(window.num)

      function fun() {
        console.log(666)
      }

      fun()
      window.fun()

      window.alert()
      window.prompt()

      console.log(window)

      let num = 500
      console.log(num)
      console.log(window.num) // let和const所声明的变量 不能作为window对象的属性存在!

location

 let data = 'hello world!'
    let btn = document.querySelector('button')
    btn.addEventListener('click', () => {
      // location.href = 'https://www.baidu.com'
      // location.replace('https://www.bilibili.com')
      // location.reload()
      location.href = './demo.html' + '?' + data

      console.log(location.search)
    })
    // location 代表浏览器地址栏
    // 文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Location

    // 跳转网页
    // location.href = 'https://www.baidu.com'

    // 跳转网页不计入历史,更新地址栏,不计入历史操作
    // location.replace('https://www.bilibili.com')

    // 刷新页面
    // location.reload()

    // 地址栏参数  ?后的数据  key=value 键值对的形式
    // location.search
    // console.log(location.search, location)

history

  let btns = document.querySelectorAll('button')
    btns[0].addEventListener('click', () => {
      history.forward() // 前进
    })
    btns[1].addEventListener('click', () => {
      console.log(666)
    })
    // history 浏览器浏览记录(历史记录)

    // 文档:https://developer.mozilla.org/zh-CN/docs/Web/API/History

    // 前进
    // history.forward()

    // 后退
    // history.back()

    // 转到指定位置
    // history.go(delta)
    // history.go(1) 等价于 history.forward()
    // history.go(-1) 等价于 history.back()

navigation

// navigator 用于查看设备信息,查询当前浏览器的信息、版本、名称等
// 文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator

  console.log(navigator)

执行上下文

//  执行上下文环境: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()

this关键字指向练习

    第一题 01
      var o = {
        a: 10,
        b: {
          a: 12,
          fn: function () {
            console.log(this.a) 
            console.log(this) 
          },
        },
      }

      o.b.fn()

      第一题 02
      var o = {
        a: 10,
        b: {
          fn: function () {
            console.log(a)
            console.log(this)
          },
        },
      }

      o.b.fn()

      第二题

      var o = {
        a: 10,
        b: {
          a: 12,
          fn: function () {
            console.log(this.a)
            console.log(this)
          },
        },
      }

      var j = o.b.fn

      // j == >  function(){}

      j() 

       第三题

      var point = {
          x : 0,
          y : 0,
          moveTo : function(x, y) {
            //  this  ==>   point {}
              this.x = this.x + x;
              this.y = this.y + y;
              console.log(this.x);
              console.log(this.y);
          }
      };
      point.moveTo(1, 1)

        4、第四题

      function someFun(x) {
        // this ==> window
        this.x = x // window.x
      }
      someFun(5)
      console.log(x) //

       5、第五题
      var point = {
        x: 0,
        y: 0,
        moveTo: function (x, y) {
          // var x = 1
          // var y = 1
          // this ==》 point
          var moveX = function (x) {
            // var x = 1
            // this ==》 window
            this.x = x
          }

          var moveY = function (y) {
            this.y = y
          }
          moveX(x) // 1  moveX 什么形式 函数  function函数 中this  ==》 window
          moveY(y) // 1
        },
      }

      point.moveTo(1, 1) // moveTo 方法形式执行, 该方法中的this 是 point{}

      console.log(point.x) // 
      console.log(point.y) // 
      console.log(x, y) // 

      第六题
      var point = {
        x: 0,
        y: 0,
        moveTo: function (x, y) {
          //  var x = 1
          //  var y = 1

          //  this ==》 point
          this.x = x // point.x = 1
          console.log(this.x) // 
          console.log(this) // 

          var moveX = function (x) {
            this.x = x // window.x  = 1
          }

          var moveY = function (y) {
            this.y = y // window.y = 1
          }
          moveX(x) // 
          moveY(y)
        },
      }
      point.moveTo(1, 1) // moveTo() 是以方法形式调用  this==》 找他的调用者
      console.log(point.x) // 
      console.log(point.y) // 
      console.log(x) // 
      console.log(y) // 

      第七题
      var point = {
        x: 0,
        y: 0,
        moveTo: function (x, y) {
          // var x = 1
          // var y = 1
          var that = this // that = point
          var moveX = function (x) {
            // var x = 1
            that.x = x // 
            this.x = x // 
          }
          var moveY = function (y) {
            that.y = y // 
            this.y = y // 
          }
          moveX(x) // moveX 函数  this ==》 window
          moveY(y)
        },
      }
      point.moveTo(1, 1) // moveTo 方法  调用者  point 对象  this ===》 point

      console.log(point.x) // 
      console.log(point.y) // 
      console.log(x) // 
      console.log(y) // 

      第八题
      var point = {
        x: 0,
        y: 0,
        moveTo: {
          moveX: function (x) {
            // var x = 1
            console.log(this) // 
            this.x = x // 
          },

          moveY: function (y) {
            // var y = 1
            this.y = y // 
          },
        },
      }

      point.moveTo.moveX(1) // moveX 方法形式调用  this==》 调用者 moveTO {}

      point.moveTo.moveY(1) // moveY 方法形式调用  this==》 调用者 moveTO {}

      console.log(point.moveTo) //   
      console.log(point.x) // 
      console.log(point.y) // 
      console.log(window.x) // 
      console.log(y) // 

      第九题
      var x = 10
      var y = 20

      //  var 声明的变量 会被挂载到 widow对象上作为window的属性存在
      var obj = {
        x: 0,
        y: 0,
        fun: () => {
          // 因为该箭头函数没有自己的this,它永远绑定的是被创建时,当时执行环境下的this,永不更改!
          //  this ==》 window
          this.x = this.x + 1
          this.y = this.y + 1
        },
      }

      obj.fun()
      console.log(x)
      console.log(y)

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)()

严格模式

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'
  • 不允许赋值只读属性

    • 'use strict'
      const obj = {}
      Object.defineProperty(obj, 'x', { value: 0, writable: false })
      obj.x = 3.14
      
    • '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

    • function fn() {
        console.log(this) // => undefined
      }
      

构造函数

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

      // 任何函数都可以作为构造函数使用!前提是 它是否被 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就不要写了!
function Person(name,age,sex){
            // console.log(this)
            this.name = name
            this.age = age
            this.sex = sex

            return {
                x:100
            }

        }

        var p1 = new Person('张三',20,'男')
        console.log(p1)

new关键字

new关键字是配合构造函数使用:
new关键字,做了几件事情:
1.new关键字会创建一个该构造函数的实例对象,一开始是空的{}
2.new关键字会将刚才创建的实例对象的__proto__属性指向为 构造函数所保存的 prototype指向的对象
3.new关键字会将构造函数中的this 指向为 该实例对象
4.最终返回这个 实例对象

js垃圾回收机制

垃圾回收:JavaScript程序在运行的过程中,随着运行时间的增加,那么会相应的产生一些垃圾数据,垃圾回收机制主要是
定时的去清理这些垃圾数据,避免内存溢出、泄露导致程序崩溃!

        内存的溢出:
        内存的泄露:

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


    如何定义垃圾:
        - 标记清除:假设所有的变量或者函数等其他数据,一开始就认为他们不是垃圾!当他们产生了某种变化后,就会被打上标记,标记为垃圾
        - 引用计数:当引用型数据,没有再被任何引用时,引用链条为0的时候!就自动判定为垃圾数据
    

        正常情况下:
            - 全局作用域下的变量、函数在程序执行完毕后才会销毁
            - 局部作用域下的变量、函数,当该局部作用域执行完了!结束后,就销毁对应的变量以及函数
            - 引用数据当引用链条数量为0的时候,也会被销毁
    
    
    小提示:
        垃圾回收机制并不是JavaScript语言独有,很多编程语言都具备
        - java、python、JavaScript
        - c c++  人为的控制,人为的定期去是释放内存

闭包(重点)

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

  如何才会产生一个闭包:
  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提供的一种针对处理时间相关的内容的对象!

  // 比如:一个商品的打折时间!  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())

elment元素offset&&client系列的属性

* {
        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

事件

 方式一
    <button onclick="test(100)">按钮</button> -->

    <!-- 方式二 -->
     <button>按钮</button> 

   方式三 
    <button>按钮</button>

    <script>
      事件:某个事情,特殊的一种情况!

      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('监听器方式')
      })
    </script>

事件的解绑

  <!-- 方式一: -->
    <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>

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

 事件属性 和 事件监听器的区别
        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)
        事件监听的方式可以为同一个元素绑定多个相同类型的事件,并且指定不同的处理方式

事件的分类

// 参考: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>

事件对象

 // 事件对象:一个事件被触发后!浏览器对应的会自动产生一个事件对象!并且该事件对象是具备类型的!
      // 鼠标事件、键盘事件、资源事件....
      // 事件对象的作用:事件对象上存放了一些与该事件产生时的一些数据或者方法!

      //   事件对象如何使用?
      //   事件对象在事件函数被执行时,浏览器会自动的向该事件函数传入对应的事件对象!只需要利用一个形参变量来接受即可!
      //   一般这个形参变量,命名为:  event ==> ev

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

      //  事件函数调用执行时,一般不会传入其他的参数!
      //  因为事件函数的第一个实参是传入的事件对象!

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

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

鼠标事件对象常用属性

<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
      })

事件执行机制—冒泡

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

事件执行机制—捕获

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

event对象的target&currentTarget属性的区别

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

事件的代理和委托

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

阻止js代码触发事件

<body>
    <button class="btn">按钮</button>
    <script>
      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)
    </script>

阻止事件冒泡

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

阻止元素默认事件

 <form action="">
      <button>提交</button>
    </form>

    <!-- button按钮在表单中 默认就存在一个 提交事件 -->
    <script>
      let btn = document.querySelector('button')
      btn.addEventListener('click', (ev) => {
        // 阻止默认事件的发生
        ev.preventDefault()
        console.log(6666)
      })
    </script>

# 鼠标移入移出事件

```javascript
    <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>

事件的捕获与冒泡过程

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()
    </script>

显示原型和隐式原型属性

// 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中的继承是利用了隐式原型对象
      //   以及原型链的概念,最终实现了实例对同一构造函数上的方法或者属性的继承!
    </script>

原型链

实例对象身上__proto__ 存储的是什么数据类型? Object

  原型链
  每一个实例对象上都具备  __proto__ 原型对象
  当一个对象在自身上找不到对应的属性或者方法时,会沿着__proto__原型对象身上去寻找!  obj {}
  如果往上一级的__proto__身上还是找不到想要的属性或者方法 obj.__proto__
  那么它会继续沿着当前这一级__proto__继续寻找!  obj.__proto__.__proto__
  ....  obj.__proto__.__proto__.__proto__
     沿着原型链最终找到 null  就结束! 如果已经访问到null 都还没有找到想要的属性或者方法 则放回 undefined

    以上描述寻找的过程就称为  原型链!

显示原型对象和隐式原型对象的区别

  <script>
      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
</script>

静态属性和静态方法

<script>
      // 静态属性和静态方法,指的是该属性和方法只能由类本身进行调用!
      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()
    </script>

子类和父类之前的继承语法

// 定义一个类 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)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值