目录
- JavaScript的简介
- js的三种使用方式
- 变量
- 数据类型
- json对象
- json数据的系列化和反序列化
- 数据类型检测
- 数据类型的转换
- 运算符
- 多分支语句
- 条件分支语句
- while与do while
- for循环
- break与continue
- API
- 获取文档元素
- 修改页面元素属性
- 数组
- 数组的常用操作
- 数组遍历
- 冒泡和选择
- 数组的倒置和去重
- 获取页面元素样式属性值
- 为元素绑定点击事件
- 文档树
- 创建节点并添加插入
- 替换节点和删除节点
- 快速获取表单数据
- 函数的声明
- 函数的返回值
- 函数的值传递和引用传递
- 箭头函数
- 作用域 和 作用域链
- 预解析
- var、let、const三者的区别
- BOM
- localStorage
- sessionStorage
- 函数的封装
- 执行上下文
- this
- 构造函数
- 构造函数和普通函数的区别
- new关键字
- this的劫持
- js的垃圾回收机制
- 闭包
- 定时器
- 元素的自定义属性
- 时间对象
- 数学函数
- element元素的offset和client
- 事件
- 事件的解绑
- 事件分类
- 事件对象
- 事件执行机制 ——冒泡
- 事件的执行机制——捕获
- event对象 target 、currentTarget
- 阻止冒泡
- 显示原型、隐式原型
- 原型链
- ES5类的写法
- ES6 类的写法
- 私有属性和方法、静态属性和方法
- super
JavaScript的简介
JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。
js的三种使用方式
1.在浏览器控制台上直接运行js语言片段
2.在html文件的script标签中
3.使用.js文件书写js代码,然后再html中通过script标签的src属性引入js代码文件
变量
变量就是存放数据的一个容器 (盒子)
变量所保存的数据本质是存储在计算机的内存中的! 内存是通过硬件内存条所产生一块虚拟空间!通常称为内存空间!
内存中数据存储的特点:
1.读写速度快 2.临时数据的存储 3.内存的存储空间是比较小的
变量的声明
1.var var 关键字可以重复声明相同的变量
2.let let关键字在同一作用域下不能声明相同的变量
3.const const关键字是用于声明 常量的!在同一作用域下也不可以声明相同的变量,并且在声明时必须赋初值
变量的命名规则
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 …
数据类型
值类型
- Boolean 布尔型
- Number 数字型
- String 字符串
- BigInt 长整数
引用类型
- Object 对象类型
- Symbol 符号类型
undefined 未定义
undefined 是一个单独的类型,用于给已经定义的变量设置的初始值
null 空引用
null 值的是空引用,是 js 的一个原始数据类型,用来指代引用类型数据的空值加粗样式
值类型和引用类型数据的区别
值类型:变量中直接存贮值本身
引用类型:变量中存储的是引用地址,而值是存在引用地址所指向的内存中的某个对应位置
json对象
json对象,花括号对象
json对象的创建利用{}进行创建
json对象中数据的存储采用键值对的方式, key:value ,其中key的数据类型必须是字符串,value可以是任何的合法数据类型!
大多数时候,js允许省略key的引号或者双引号,省略的前提是属性名的命名符合标识符的命名规则
// 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数据类型!
json数据的系列化和反序列化
序列化:将一个合法的json对象,转换为一段标准的json格式的数据! 利用js提供的JSON对象上的方法进行操作 JSON.StringIfy
反序列化:将一段标准的json格式的数据,字符串转化为一个合法的json对象!反序列化 利用js提供的JSON对象上的方法进行操作 JSON.parse()
数据类型检测
// js中的数据类型有8种
// 原始数据类型(7) 和 对象类型(1)
// 口诀: 四基两空双一
// 四基: Number、String、 Boolean 、 BigInt
// 两空: undefined 、 null
// 双一: 一个符号 Symbol 一个对象 Object
语法: typeof 数据
数据类型的转换
数据的类型转换:将某个数据的类型通过某种方式转换为另一种数据类型! 比如: number ==》 string
数据转换通过方式不同可以分为:强制转换、隐式转换
强制转换:人为的通过手段去改变一个数据的类型
隐式转换:没有人为的参与,是程序解析自动进行转换
Number(x)函数 该函数可以将其他的数据类型强制转换为Number类型的数据
除了Number()函数可以强制转换其他数据为Number类型外,还有两种手段
全局函数: parseInt() 、parseFloat
parseInt() 将其他数据类型转换为number的整数值
parseFloat() 将其他数据类型转换为number的浮点数
String() 该函数是将其他类型强制转换为字符串类型
Boolean() 该函数是将其他类型强制转换为布尔类型
js中的假值,假值就是通过强制转换或者隐式转换该数据为布尔类型后,其结果为false的数据称为假值!
js中的假值: false 、 0 、 -0 、 +0 、 null 、 undefined 、 ‘’
运算符
运算符的作用,利用各种运算符,对已有数据进行运算然后得到新的数据!
js中的运算符分类:
算术运算符
赋值运算符
比较运算符
逻辑运算符
三元运算符
单目运算符
算术运算符
算术运算符:+ 、 - 、 * 、 / 、%
(模运算,取余)、**(幂运算)
算数运算符主要用于两个number数据的计算!
如果算术运算符一侧数据类型不为number则js将进行隐式转换!转换为numnber类型然后进行运算!
注意:如果+号的一侧数据类型为字符串类型,则+号就自动变成字符的拼接运算
赋值运算符
‘=’ 强调赋值运算符,不是数学意义上的等于符号!它是赋值运算!
运算流程: 将赋值运算符的右侧表达式的结果,赋值给左边的变量! 从右往左
注意:赋值运算符可以改变变量或者对象属性的具体值!
复合运算符
复合赋值运算符:该运算符是 赋值运算符 与 算数运算符的结合书写!
注意:复合赋值运算符也具备赋值运算的功能!
+= 、 -= 、 /= 、 %= 、 **= 、*=
比较运算符
用于对两个数据进行数据的比较,其比较结果为布尔值!true 或 false
> 、< 、 >= 、<=、 ==、 ===、 != 、!==
逻辑运算符
逻辑运算符:用于对两个逻辑值的运算!
js中的逻辑运算符: &&(与) ||(或) !(非,取反)
逻辑运算表达式的返回结果是什么类型?结果:可以是任何数据类型!
&& 与运算
运算语法: 表达式1 && 表达式2 如果&&运算两侧的数据类型不是布尔类型,则先进行隐式转换! Boolean()
与运算口诀: 全真为真,一假即假
短路与:
特点在运算时,从左往右计算,如果碰到其中一个表达式的值为,false,那么后续的表达式将不再进行计算!并且返回当前表达式的原始值作为整个与运算的结果!如果从左往右计算,都没有遇到false,那么将最后一个表达式的原始值作为整个与运算的结果!
逻辑或(短路或)
运算语法: 表达式1 || 表达式2 如果||运算两侧的数据类型不是布尔类型,则先进行隐式转换! Boolean()
或运算口诀: 全假为假,一真即真
短路或:
特点在运算时,从左往右计算,如果碰到其中一个表达式的值为,true,那么后续的表达式将不再进行计算!并且返回当前表达式的原始值作为整个或运算的结果!如果从左往右计算,都没有遇到true,那么将最后一个表达式的原始值作为整个或运算的结果!
!取反运算 非运算
口诀: 真变假,假变真
流程控制语句
js的代码如果没有人为的干预,那么js的执行顺序为从上往下,顺序执行!
作用:流程控制语句后,可以对js的代码执行顺序进行干预,完成更复杂的程序逻辑!
js中的三大流程控制结构:
顺序结构 选择结构 循环结构
顺序结构: 整体代码的解析执行是按照从上往下!的规律进行执行!也是最基础的控制结构
选择结构: 为代码的执行提供可能的路径!影响代码的走向!
循环结构: 重复的执行某一段代码!
多分支语句
js多分支语句,if…else的嵌套
// 双分支,是两条路径选择!
// 多分支,是多条路径进行选择!
// 多分支的本质就是利用if和else嵌套!
// 语法:
// if(表达式){
// 路径1
// }else if(表达式){
// 路径2
// } else {
// 路径3
// }
条件分支语句
js的条件分支语句 switch语句
// 语法:
// switch (表达式) {
// case '值':
// 语句块
// case '值':
// 语句块
// case '值':
// 语句块
// case '值':
// 语句块
// case '值':
// 语句块
// case '值':
// 语句块
// ....
// default:
// 不满足其他case分支时,进行default!
// }
while与do while
while循环
语法:
while (表达式) {
循环体
}
while循环的注意事项:
1.必须设置循环控制变量
2.必须为循环控制变量赋初始值
3.每一次的循环体结束前应该更新循环控制变量
执行流程,首先判断表达式的true或者false,如果为true则执行循环体的代码,否则结束while循环
如果判断为true则执行循环体的代码,当循环体代码执行完毕时,再次判断表达式的真假!重复以上操作!直到表达式为false,退出while语句
do…while循环
语法:
do {
循环体
}while(表达式)
do…while循环的注意事项:
1.必须设置循环控制变量
2.必须为循环控制变量赋初始值
3.每一次的循环体结束前应该更新循环控制变量
执行流程,do…while循环,一开始先进行一次循环体代码的执行!然后进行表达式的判断!
如果为true 则继续执行循环体内容,重复操作!
如果为false 则结束do…while循环
for循环
for 循环
语法:
for(语句1;语句2;语句3){
循环体
}
执行流程,
for循环执行开始,先执行语句1,然后执行语句2,进行判断!
根据语句2的判读结果,true or false
如果为true,则执行循环体的内容,循环体内容执行完毕,跳到语句3并执行语句3的内容,语句3执行完毕后,又回到语句2再次进行判断!
直到语句2的结果为false,才会结束for循环
break与continue
continue 和 break关键字
两个关键字可以使得循环提前结束!
// break关键字
// 特点:直接结束当前包含break的循环语句!进入下一个步骤!
// continue关键字
// 特点:该关键字也可以结束循环,但是它只是结束该循环的本次内容!后续的循环继续执行!
API
API 是 Application Programming Interface 的缩写,翻译过来就是 “应用程序编程接口”
API 是提供一些用于编程工具,对于 js 来说,API 可以是某框架提供的变量或函数,也可以是网络上的一个可访问的 url 地址,称为网络接口
API 既然是编程工具,那么其作用是通过调用 API 来实现一个功能,例如:排序,查询,添加数据等操作
alert 弹窗提示 浏览器提供的 api 弹窗提示
alert 它会阻塞页面的渲染和执行
confirm 确认模态 弹出框 浏览器提供的 还具备返回值
prompt输入模态prompt(‘请输入姓名’)
上述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')
修改页面元素属性
注意:利用点语法或者[]是可以获取元素对象上所有已知或未知属性的值!除了class属性的值不能正常获取!
通过 class ==》 className来获取元素的class值
修改页面元素的属性值
div.title = ‘xxxxxxxxxxxxxxxxxxx’
div.className = ‘box2’
# 元素对象的标签体属性
innerHTML是用于更改元素的标签体的内容,合法的字符会被浏览器自动解析为标签!
innerText是用于更改元素的文本信息,该文本信息是不会被浏览器当作标签来正常解析的
// innerHTML
// box.innerHTML = '<span>我是div</span>'
// innerText
// box.innerText = '<span>我是div</span>'
数组
数组,是存储一组数据的一个容器!
数组,是属于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 开始的!
// 语法: 数组[下标值]
如何判断一个变量是否为数组!
//利用js提供的api通过 构造函数对象 Array的静态方法 isArray() 进行判断 返回值true或false
数组的常用操作
数组的常见操作
// 所有的数组实例对象,都来自于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])
}
冒泡和选择
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)
数组的倒置和去重
倒置:
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 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)
获取页面元素样式属性值
// 需求,获取该元素的样式属性 比如 获取它的高度值!
// 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)是只读的不能重新赋值
为元素绑定点击事件
let box = document.querySelector(‘.box’)
box.addEventListener('click', function () {
})
文档树
文档树
浏览器会将html文档中的标签,解析并转换为文档树
文档树是一个树形结构!html根标签,最外层的节点!其余的标签(节点)都是挂载于根节点上!
文档树中,由很多的节点类型!不只元素节点(标签节点)! 除了元素节点之外,还存在 一些其他的节点类型! 注释节点 文本节点 …
注意:学习的重点以及操作是在元素节点!对元素节点的 操作: 创建 增 删 查 改
创建节点并添加插入
-
创建一个 div 元素节点 document.createElement()
let mydiv = document.createElement(‘div’)
console.log(mydiv, typeof mydiv)注意: 被创建出来的元素节点,它是不存在于文档树中! 需要把它插入或者添加到文档树中!
插入:
获取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')
// 2.获取即将被替换的旧元素
// let oldli = document.querySelector('.first-li')
// 3.获取父节点,由父节点来完成替换操作! ul
let ul = document.querySelector('ul')
// 进行替换操作
// replaceChild(newnode,oldnode)
// ul.replaceChild(newli, oldli)
删除:
// 删除操作
// 方式一:元素自删除!
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)
快速获取表单数据
// 通过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'))
// }
函数的声明
函数:一段被封装好的可重复使用的代码段!并且可以实现某种功能!
js中可以通过关键字 function进行函数的声明创建
// 语法结构
// 1.声明创建一个函数
// function 函数名(形参列表){
// 函数体
// }
// 函数的声明,在声明时,函数体内部的代码是不会执行的!
function sayHello() {
console.log('hello world!')
}
// 2.调用函数\执行函数
// 语法: 函数名()
// 注意:函数名 + () 才能执行对应的函数!
函数的返回值
函数的返回值,大部分函数都是具备返回值!
返回值代表的是该函数执行后,对数据的处理结果! 然后利用 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)
箭头函数
// 箭头函数: 书面称呼, 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之前 只能通过 let 或者 const 进行声明 才会存在块级作用域
3. 块级作用域 {} es6之后才有 块级作用域
作用域链:通过作用域的链式关系,进行查找对应的 变量 和 函数
如果沿着作用域链一直寻找,直到在全局作用域下都没有找到对应的变量或者函数 就会报错!
注意:作用域链的查找规则是 从内到外!查找!
预解析
预解析: js代码在真正的执行前,会先进行一个操作!该操作称为 预解析!
预解析主要涉及两个内容 当前作用域下的var变量 和 function 函数进行提升!
函数的提升高于var变量
let 和 const 所修饰的变量不具备提升操作!但是存在一个’暂时性死区’
var、let、const三者的区别
var 、 let 、const 三者的区别
1. var存在变量提升操作,var不具备块级作用域,var可以在同一作用域重复声明同名的变量或者函数
2. let不存在变量提升操作,let具备块级作用域,let存在暂时性死区,let不允许在同一作用域声明同名的变量和函数
3. const不存在变量提升操作,const具备块级作用域,const存在暂时性死区,const不允许在同一作用域声明同名的变量和函数,const声明的同时必须赋值!
BOM
BOM (浏览器对象)对象 ==》 代表整个浏览器
浏览器环境中,为js提供了两个对象!这两个对象是浏览器提供的,也称为宿主对象!
window
window对象 BOM的核心对象 windonw指向的是 浏览器的窗口!
windo对象除了指向浏览器窗口,它还代表着js在浏览器环境下的全局对象!也称为顶层对象!
console.log(window)
window作为顶层对象,它的作用!在全局环境下所声明的全局var变量或者函数都是作为window对象的属性存在!
location
// 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
// 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()
navigator
// navigator 用于查看设备信息,查询当前浏览器的信息、版本、名称等
// 文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator
localStorage
// localStorage 数据持久化到浏览器中,关闭窗口或浏览器,都不会消失
// localStroage 将数据永久性的存储到 浏览器中! 以键值对的形式
// 设置数据, key 和 value 数据类型必须是字符串类型
localStorage.setItem('name', '张三')
// 设置数据 []
localStorage['age'] = 18
// 读取数据
console.log(localStorage.getItem('name'))
console.log(localStorage['age'])
sessionStorage
// sessionStorage 将数据临时性的存储到 浏览器中! 以键值对的形式 ! 窗口关闭 数据就消失
// 设置数据, key 和 value 数据类型必须是字符串类型
sessionStorage.setItem('name', '张三')
// 设置数据 []
sessionStorage['age'] = 18
// 读取数据
console.log(sessionStorage.getItem('name'))
console.log(sessionStorage['age'])
函数的封装
将某段重复执行代码利用进行封装,方便以后的执行
编程的思想 思维!代码的冗余问题!高内聚,低耦合原则! 经验的积累!项目的累积!错误叠加!
高内聚: 代码语句不是零散,散落!而是高度集中的! 函数封装!
低耦合: 耦合功能快之间是否连接的特别缜密!低耦合就是要求你不要连接的特别缜密!
执行上下文
// 执行上下文环境:js代码在执行的过程中,会存在一个环境!js引擎会将js代码,分别设置一个执行环境!
执行上下文环境的分类:三大类
1. 全局执行上下文
2. 函数执行上下文
3. eval()执行上下文 (忽略,了解!)
this
this关键字,可以理解为 语文的 ‘这’ 代词! 是一个变化的东西!
// this的变化是有规律的!
// 寻找this,判断this
// 1.在浏览器环境中的全局上下文中,this永远指向的是 顶层对象 window
// 2.在浏览器环境中函数执行上下文中,this指向的也是 顶层对象 window , 严格模式( 'use strict')除外! 函数执行上下文,设置了严格模式,那么this指向的 undefined
// 3.如果函数是被某个对象进行调用时,那么该函数(方法)中的this指向的是,它的调用对象(调用者)!而非持有者!
// 4.构造函数中this,指向的是新创建出来的一个该构造函数类型的 实例 {} 空对象!
// 5.事件对象的事件处理函数中的this,指向的是 当前被绑定的元素对象,仅适用于 function函数
// 6.箭头函数,它不存在自己的this,它的this是该箭头函数被创建时,当时的执行上下文环境中的this!并且永不改变包括this的三大劫持手段!(类似于‘印随效应’)
构造函数
构造函数:构造函数也是函数!构造函数的本义是用于构造一个实例对象!并不是像普通函数一样为了实现某种功能!
任何函数都可以作为构造函数使用!前提是 它是否被 new 关键字调用!
构造函数的命名:软性规定,建议构造函数的首字母大写
例: var obj = new Object()
构造函数和普通函数的区别
构造函数和普通函数的区别:
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和apply使用方式基本一致,唯一的不同在于实参的传递方式
call是将多个实参以逗号分隔,依次传入函数中
apply是将多个实参以数组的形式,一次性传入函数中
bind劫持是利用bind方法返回一个新的函数!
劫持该函数中的this,相对于call和apply来说,它们是操作自身的函数!bind的操作新的函数!
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.闭包的产生有很多时候是隐式产生的,最终会造成内存泄漏
定时器
js中的定时器、计时器:
作用:规定一段时间,然后执行某一段代码!
// js提供了两种创建定时器的方式:
// 全局函数:setTimeout(一次性的) setInerval(永久性的)
// 这两个方法都是同步方法!而传入的callback是异步的,不会阻塞主线程
// 以上两个函数都需要传入两个参数:
// 参数一:规定时间后,需要执行的函数 callback
// 参数二:规定多少时间开始执行代码 单位是ms 是忽略!
计时器被创建后都会产生一个唯一的计时器编号,可以利用对应的编号关闭计时器
例如:
1.关闭setTimeout所创建的计时器
let timer = setTimeout(() => {
console.log(666)
}, 1000)
clearInterval(timer)
2.关闭setInerval所创建的计时器
let timer = setInterval(() => {
console.log(666)
}, 1000)
元素的自定义属性
<!-- 如何创建自定义属性 -->
<!-- 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)
时间对象
// 创建一个时间对象 利用 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()) // 获取当前时间对象的时间戳
数学函数
注意:Math函数不能作为构造函数使用! 所有是无法实例化一个数学对象!
console.log(Math)
abs() 求一个数的绝对值
floor 地板数,对一个数向下取整
ceil 天花板数, 对一个数向上取整
max 返回当前参数列表中最大值
min 返回当前参数列表中最小值
pow 求一个数的幂
console.log(Math.pow(2, 5))
sqrt 开平方根
console.log(Math.sqrt(9))
round 对一个数四舍五入
random 随机数, 随机返回一个 0-1之间的小数 包括0,不包括1 伪随机!
console.log(Math.random())
element元素的offset和client
let box = document.querySelector(‘.box’)
box ==> 是一个dom元素 elment元素
// 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
事件
事件:某个事情,特殊的一种情况!
// 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('监听器方式')
// })
事件的解绑
// 解绑一:
// 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 $(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')
// })
事件对象
事件对象:一个事件被触发后!浏览器对应的会自动产生一个事件对象!并且该事件对象是具备类型的!
鼠标事件、键盘事件、资源事件…
事件对象的作用:事件对象上存放了一些与该事件产生时的一些数据或者方法!
// 事件对象如何使用?
// 事件对象在事件函数被执行时,浏览器会自动的向该事件函数传入对应的事件对象!只需要利用一个形参变量来接受即可!
// 一般这个形参变量,命名为: event ==> ev
// 注意:不同类型的事件对象,所保存的数据的不一样的!有些属性是任何事件对象都具备的,比如: type
// 也有很多的属性是该事件对象所独有的!
// 事件函数调用执行时,一般不会传入其他的参数!
// 因为事件函数的第一个实参是传入的事件对象!
事件执行机制 ——冒泡
// 事件的冒泡:事件的传递过程! 事件是可以进行传递的!
// father 盒子 son 盒子 绑定了点击事件
// son盒子在father盒子的内部 点击son时候 son点击会触发 father触发 事件会传递!传递的过程是 由内到外 ! 冒泡!
// 事件的冒泡是建立在统一个事件类型上的! 比如 click...bdlclick...等
事件的执行机制——捕获
// 事件的捕获:事件的传递过程! 事件的传递过程分为 捕获阶段和冒泡阶段
// 默认情况下所有的事件都是的冒泡阶段触发的,但也有例外可以让事件提前在捕获阶段触发!
// 冒泡过程: 从内到外
// 捕获过程: 从外到内
// 一个完整的事件触发,必定经历两个阶段,1.捕获阶段 2.冒泡阶段
// 如果想实现某个元素的事件处理函数在捕获阶段就执行,需要利用 addEventListenter 方法的第三个参数
// 第三个参数是一个布尔值,默认情况是 false 代表不在捕获阶段执行! true 代表在捕获阶段执行!
event对象 target 、currentTarget
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 ==》 正在处理该事件的元素对象
阻止冒泡
// 事件默认是允许冒泡的!冒泡的过程会传递给上层的元素,导致上层元素不得不触发该事件,并且执行事件函数
通过ev.stopPropagation()
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')
})
显示原型、隐式原型
JS中任何的一个实例对象都具备一个 隐式原型属性 proto
JS中所有的function函数身上都具备一个 显示原型属性 prototype
因为function也是一个对象所以function身上同时具备 proto 和 prototype
prtotype 和 proto 是两个属性!存储的是 对象类型的数据! Obeject类
原型链
实例对象身上__proto__ 存储的是什么数据类型? Object
原型链
每一个实例对象上都具备 __proto__ 原型对象
当一个对象在自身上找不到对应的属性或者方法时,会沿着__proto__原型对象身上去寻找! obj {}
如果往上一级的__proto__身上还是找不到想要的属性或者方法 obj.__proto__
那么它会继续沿着当前这一级__proto__继续寻找! obj.__proto__.__proto__
.... obj.__proto__.__proto__.__proto__
沿着原型链最终找到 null 就结束! 如果已经访问到null 都还没有找到想要的属性或者方法 则放回 undefined
以上描述寻找的过程就称为 原型链!
ES5类的写法
类:抽象的概念! 类中会定义属性和方法,实例化的对象会拥有该类的属性和方法
// ES5中
// 类的定义方式
// 函数式定义方式 function
// 定义一个 Person 类
// 属性: 姓名 性别 年龄....
// 方法: 吃饭 睡觉 打豆豆...
function Person(name, sex, age) {
this.name = name
this.sex = sex
this.age = age
this.country = '中国'
}
Person.prototype.eat = function () {
console.log('eat...')
console.log(this, '@')
}
Person.prototype.sleep = function () {
console.log('sleep...')
}
Person.prototype.play_dd = function () {
console.log('play_dd...')
}
// 实例化:创建一个person类的实例对象
let p1 = new Person('张三', '男', 20)
let p2 = new Person('小明', '男', 25)
console.log(p1, p2)
// p1.eat()
// p1.sleep()
// p1.play_dd()
ES6 类的写法
class Person {
country = '中国'
// 构造器,构造函数 必须的!
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...')
}
}
// 实例化一个Person类的对象
let p1 = new Person('张三', '男', 20)
let p2 = new Person('张三', '男', 20)
console.log(p1, p2)
// p1.eat()
// p1.sleep()
// p1.play_dd()
私有属性和方法、静态属性和方法
私有属性或者私有方法,指的是该属性和方法只能在类中使用,不能够通过实例化的方式进行点语法的获取由#属性名、#方法名 创建。
静态属性和静态方法,指的是该属性和方法只能由类本身进行调用!由static 属性名、static 方法名创建
super
super 关键字 它在js中特用于!class的情况!
// super关键字有点类似于this this==》指代某个对象!
// super也有指代的意思,也是指代某种东西!
// super的指代,或者使用的三种情况
// 1. constructor 构造函数中使用时 super 就是指代了 父类的constructor函数本身
// 2. 在子类的'普通'方法中(除静态方法外) super 指代的是 父类的 prototype属性指向的对象 Student extents Person ==》 super ==》 Person.prototype
// 3. 在子类的静态方法中, super 指代的是 父类 本身 Student extents Person super ==》 Person
// 类中有那些方法? 静态方法 static 私有方法 # 普通方法 无任何修饰