【更新中】javascript

文章目录

一、JavaScript介绍

1.1 JS是什么

是一种运行在客户端的编程语言,实现人机交互效果。
轻量级 弱类型 脚本语言
js就是通过固定的语法来操作 浏览器和 页面结构 来实现网页中的各种效果

1.2 JS作用

  • 网页特效(监听用户的一些行为让网页做出对应的反馈)
  • 表单验证(针对表单数据的合法性进行判断)
  • 数据交互(获取后台的数据,渲染到前端)
  • 服务器编程(node.js)

1.3 JS的组成

  1. ECMAScript:定义了 javascript 的语法规范
  2. BOM:(browser object model)浏览器对象模型
    浏览器有一套成熟的 API 方法,通过 BOM 来操作浏览
  3. DOM(document objectmodel)文档对象模型
    简介有一套成熟的可以操作页面元素的 API 方法,通过 DOM 可以操作页面元素

1.4 javascript书写位置

行内式 (一般不使用)

在 a 标签的 href 属性中书写代码
javascript:js代码;
或者在标签里书写 onclick 然后书写 JS 代码

<!-此处的js写法常用于a标签的阻止浏览器默认跳转 -->
<a href="javascript:;">点一下但是不跳转</a>

<div onclick='alert("点击")'></div>

内嵌式

js代码书写在 script 标签内
浏览器打开的页面的时候,会立即执行script标签中的js代码
script标签中的 type=“text/javascript” 属性可以省略
一个页面中可以书写多对script标签,但是script不能嵌套使用

<script>
  alert('内嵌')
</script>

外链式

script标签的src属性中 书写js文件的路径地址
利于 HTML 页面代码结构化,把大段 JS 代码独立到 HTML 页面之外,既美观,也方便文件级别复用
引用外部 JS 文件的 srcipt 标签中间不可写代码
适用 JS 代码量比较大的情况,开发常用

<script src="./test.js"></script>

二、javascript基础语法

2.1 JS 注释

在 JS 中有两种注释方式
注释就是不会执行的内容,注释是给程序员看的

单行注释

// 编辑器快捷方式 Ctrl+/
// alert('666')

行注释

/*
编辑器中默认的是shift+Ctrl+a
alert('666')
alert('666')
alert('666')
alert('666')
*/

2.2 JS中常见的页面输出方式

弹窗输出

alert(内容)  提示弹窗输出内容

confirm(内容)  确认弹窗
返回值: 返回值就是 点击确定为true 取消为false

prompt(输入问题)  提问弹窗
返回值: 输入啥内容点击确定时候返回值就是什么内容,如果点击取消则返回null

控制台输出

console.log(内容) 在浏览器的控制台中输出内容

页面输出

document.write(内容) 在浏览器页面中输出内容,会将内容写到页面的body标签中
注意: 此方法会将内容写到body标签中,如果写入的内容有html标签,则页面可以识别对应的标签内容

2.3 变量

在 js 程序中,变量是用来帮我们存储中间值
注意: 在 js 中 一个等号表示赋值 将等号右边的值 赋值给左边

如何定义变量

定义变量:通过关键字 var 变量名
注意:一个变量中只能存储一个值

var age // 表示定义了一个变量,名字为 age
var age = 100 // 表示定义了一个 age 变量并且赋值为 100

var age,username,gender // 一次定义多个变量
    age = 100 // 给已经定义的变量赋值
    
var age = 17,username='小斌',gender='男' // 定义多个变量并赋值

// 使用定义的变量来接收 提问弹窗的返回值
var age = prompt('你多大?');
    // 在控制台中将变量中的值输出
    console.log( age );
    
// ! 注意 一个变量中只能存储一个值
var age = 100 // 定义 age 变量并赋值 100
    age = 666 // 此时将 666 赋值给变量 age 中,则 age 中原来的值被覆盖了
    console.log( age )

变量名命名规则和规范

规则:死的无条件遵守,服从命令为准则
规范:如同道德品行,尽量遵守

(1) 变量名的命名规则(必须遵守)

变量名只能由:数字 字母 下划线 $ 组成
变量名不能以数字开头
不能使用关键字和保留字为变量名
变量名不能有空格,不要使用中文
严格区分大小写

(2) 变量名的规范(大家默认遵守)

变量名尽量语义化
如果变量名由多个字母组成则尽量使用小驼峰命名(第一个单词首字母小写,后续单词首字母大写)

2.4 数据类型

基本数据类型:number string boolean undefined null
复杂数据类型(引用数据类型):object array function…(不展开讲)

基本数据类型

(1) number 数值类型

取值: 数字 小数,NaN…等等
NaN 表示这个数据 不是一个数,但 NaN 是 number 数据类型

(2) string 字符串类型

取值:string
在 js 中所有使用单引号或双引号包裹的都是字符串类型

(3) boolean 布尔类型

取值: true(真),false(假)

(4) undefined 未定义,未赋值

取值: undefined
当定义了一个变量,但是没有赋值,此时变量的值就是 undefined

(5) null 空

取值: null

判断数据类型

(1) 语法1: typeof 变量
// 定义了一个变量res来接收 typeof 变量 执行的结果(返回值)
var res = typeof num
console.log( res ) // 'number'
(2) 语法2: typeof(变量)
var res = NaN
console.log( typeof res ) // 'number'

返回值: 通过字符串表示变量中数据的类型
注意: typeof 语法只能获取基本数据类型,而且null类型获取不到

var num = 100
var str = '小斌'
var flag = true
var und = undefined
var nul = null
console.log( typeof(num) ) // 'number 字符类型'
console.log( typeof(str) ) // 'string 字符串类型'
console.log( typeof(flag) ) // 'boolean 布尔类型'
console.log( typeof(und) ) // 'undefined 未定义类型'
console.log( typeof(nul) ) // 'object 空'
(3) 判断一个值,是否是NaN

语法: isNaN(变量)

作用: 判断这个变量的值,是否是一个非数字

返回值: 布尔类型
如果变量是一个数,则返回 false
如果变量不是一个数,则返回 true

注意: 此语法默认会将变量中的值进行一次 隐式转换(转为数值)

var num = 100
console.log(isNaN(num)) // false

var num = 'xiaobin'
console.log(isNaN(num)) // true

数据类型的转换(转为数值类型)

(1) Number()

语法: Number(变量)
作用:将变量中的值转为数值类型(无论是整数还是小数都可以转换)
返回值: 转换后的数字,或者NaN
注意: 将变量中的值转为数值,只能转数字,或小数数字

var str = '1234'
var res = Number(str)
console.log( res ) // 1234
console.log( typeof res ) // number
(2) parseInt()

语法: parseInt(变量) 将变量中的值转为整数
返回值: 转换后的整数,或则NaN
转换的时候,从数据的第一位开始转换,直到遇见非数字,则停止转换

(3) parseFloat()

语法: parseFloat(变量)  将变量转为数值,可以转小数
返回值: 数字,或 NaN
和parseInt一样的转换规则,只不过parseFolat可以识别一个小数点

(4) 隐式转换

除了加号之外的 数学运算符(-,*,/,%) 会将变量中的值转为数值类型,然后再运算
运算中的隐式转换,就是在与那算之前会偷偷的将变量中的值 通过Number()方法转为数值然后再进行运算

var num = '1000';
var res = num 0;
// 在运算之前,会隐式的将num中的值转为数值类型,然后在运算
console.log( res ) //1000
console.log( typeof res ) // 'number'

数据类型的转换(转为字符串)

(1) String

语法: String(变量) 将变量中的值转为字符串类型的值
返回值: 转换后的字符串

var flag = true
console.log( String(flag) )// 'true'
(2) toString()

语法: 变量.toString() 将变量的值 转为字符串类型 然后返回
返回值: 转换后的字符串
注意: undefined 和 null 数据类型的变量不可以使用 toStirng 方法

(3) 加号

加号作用:1. 数学运算 2.字符串拼接

数据类型的转换(转为布尔类型)

(1) Boolean()

语法:Boolean(变量)  将变量中的值转为布尔值然后返回
返回值: 转换后的布尔值
在js中只有 1.空字符串 2.数值0  2.数值NaN 4.undefined 5.null 的值为false,其他别的数据转换都是true
以后遇见任何 其他数据类型隐式转为布尔值 都是隐式的调用了Boolean()方法类型转换

var num1 = 0
var num2 = NaN
var str = ''
var und
var nul = null
console.log( Boolean(num1) ) // false
console.log( Boolean(num2) ) // false
console.log( Boolean(str) ) // false
console.log( Boolean(und) ) // false
console.log( Boolean(nul) ) // false

2.5 运算符

算术运算符

(1) 运算符

加 +
减 -
乘 *
除 /
取余(取模) %
注意:遵循先乘除后加减有括号要先算的数学运算法则

(2) + 特殊运算符

注意: 在 js 中 + 两边都是数值的时候,才是算术运算
如果 + 两边有一边是字符串,则加号就是 字符串拼接的作用
如果 + 两边都是数值的之后,才进行加法运算

var res = 10 + 20
console.log( res ) // 30

var res1 = 10 + '20'
console.log( res1 ) // 1020

注意: 在js运算中,小数运算会有精度问题,在js中尽量避免小数运算
处理方式: 放大倍数的方式计算,然后缩小倍数得到结果

var res2 = (0.3*100 + 0.03*100)/100
console.log( res2 ) // 0.33

赋值运算符

(1) = 赋值

将等号右边的值 赋值 给左边的变量

var num = 100
(2) += 加等赋值
var n = 20
n += 20 // 等价于 n = n + 20
console.log( n )//40

var str = 'hello'
str += ' wolrd' // 拼接 str = str + ' wolrd'
console.log( str ) // hello wolrd
(3) -= 减等 *= 乘等 /=除等 %=取余等
var num = 100;
num -= 10; // 等价于 num = num 10
num *= 10; // 等价于 num = num * 10
num /= 10; // 等价于 num = num / 10
num %= 3; // 等价于 num = num % 3

比较运算符

(1) 大小比较

大于 >
小于 <
大于或者等于 >=
小于或者等于 <=

// 比较运算符,组成的表达式,返回值是布尔值
console.log(20 > 10)// true
console.log(20 < 10)// false
console.log(20 >= 20)// true
console.log(20 <= 20)// true
(2) == 等等比较
  1. 等等比较的时候,如果两边的数据类型不一致,则会发生隐式转换
  2. 只会比较两边的值(如果两边的值都一样的话则返回true)
  3. undefined和null 等等比较结果为true,其他任何数据和undefined或null比较都是false
  4. 数值NaN和任何数值比较都为false
  5. 如果是布尔值,则会先将布尔值转为数值,然后比较
    布尔值true 转为数值为1; 布尔值false 转为数值为0
  6. 如果一边是数值,则另一边的值 隐式转会为数值然后比较
console.log( 'leon' == 'leon' )// true
console.log( 10 == 10 ) // true
console.log( '10' == 10 ) // true
console.log( '10leon' == 10 ) // false
console.log( null == undefined ) // true
console.log( null == 0 ) // false
console.log( undefined == 0 ) // false
console.log( 1 == true ) // true
console.log( 0 == false )// true
console.log( '10leon' == true ) //  false
console.log( NaN == NaN ) //  false
(3) === 全等比较

只有两边的数据类型和值都相等的时候才为true

console.log( 10 === '10' ) // false
console.log( '10' === '10' ) // true
(4) != 不等比较

只有两边的值不相等,结果才为true

console.log( 10 != '10' ) // false
console.log( 100 != '10' ) // true
(5) !== 全不等比较

只要两边的数据类型和值有一个不相等,结果就是true

console.log( 10 !== '10' ) // true
console.log( '10' !== '101' ) // true
console.log( '10' !== '10' ) // false

逻辑运算符

(1) &&  逻辑与

作为条件判断的时候: && 符号两边都为true的时候,最终结果才为true,只要一边为false,最终结果就是false

var res1 = true && true;
var res2 = true && false;
console.log( res1 )// true
console.log( res2 )// false

作为表达式的时候:
&& 符号如果左边转为布尔值 为false,则左边的内容就是表达式的结果

&& 符号如果左边转为布尔值 为true,则右边的内容就是表达式的结果

var res1 = 0 && 100;
var res2 = undefined && NaN;
var res3 = 100 && null;
var res4 = 100 && 200;
var res5 = (100 < 20) && 200;
console.log(res1) // 0
console.log(res2) // undefined
console.log(res3) // null
console.log(res4) // 200
console.log(res5) // false
(2) || 逻辑或

作为条件判断的时候: || 符号两边都为false的时候,最终结果才为false,只要又一边为true,最终结果就是true

var res1 = false || false;
var res2 = true || false;
console.log( res1 )// false
console.log( res2 )// true

作为表达式的时候:
|| 符号如果左边转为布尔值 为true,则左边的内容就是表达式的结果

|| 符号如果左边转为布尔值 为false,则右边的内容就是表达式的结果

var r1 = 1000 || 666;
var r2 = 1000 || 0;
var r3 = 0 || NaN;
var r4 = 0 || 888;
console.log(r1) // 1000
console.log(r2) // 1000
console.log(r3) // NaN
console.log(r4) // 888
(3) !变量  取反 逻辑非

返回值: 布尔值
隐式的将变量的值转为布尔值,然后取反

console.log( !0 ) // true
console.log( !NaN )// true
console.log( !'' )// true
console.log( !undefined ) // true
console.log( !null ) // true
console.log( !'0' )// false

自操作运算符

(1) 自增 ++

++变量   前自增
先进行运算,然后才是别的操作

变量++   后自增
先进行别的操作,然后才是别的运算

(2) 自减 –

–变量   前自减
先进行运算,然后才是别的操作

变量 后自减
先进行别的操作,然后才是别的运算

var k = 10
var res = k++ + ++k + k++ + k--
// k = 10
// k++ + ++k + k++ + k-===> 10 + ++k + k++ + k-此时k=11
// 10 + ++k + k++ + k===> 10 + 12 + k++ + k此时k=12
// 10 + 12 + k++ + k ===> 10 + 12 + 12 + k 此时k=13
// 10 + 12 + 12 + k  ===> 10 + 12 + 12 + 13    此时k=12
// 47
console.log( res ) // 47
console.log( k )//12

2.6 分支结构

流程控制: 代码的执行顺序
顺序结构和分支结构
分会结构: 条件分支 循环分支

条件分支

(1) if 单分支

单分支,有可能不执行对应的代码
语法: if (条件) {条件为 true 执行的代码}

var money = 3000;
if(money < 8000){
  console.log( '上班通勤时间为2小时' )
}
(2) if-else 双分支

双分支,一定为执行一个分支代码
语法: if (条件) {条件为 true 执行的代码} else {条件为 false 执行的代码}

var age = 27;
if(age >= 18){
  console.log( '成年了' )
}else{
  console.log( '未成年' )
 }
(3) if-else if 多分支

多分支,有可能不执行对应的代码
语法: if (条件1) {代码1} else if (条件2) {代码2} …
条件1为true,执行代码1
条件1为false,条件2为true 执行代码2
如果前面的条件为true,则执行对应的代码块,但是不会再判断后面的条件
判断输入的年份是否是普通闰年 还是世纪闰年 还是平年

var year = prompt('请输入年份:')
if (year % 4 === 0 && year % 100 != 0) {
  console.log(year + '是普通闰年')
} else if (year % 400 === 0) {
  console.log(year + '是世纪闰年')
} else if (!(year % 4 === 0 && year % 100 != 0) && year % 400 != 0) {
  console.log( year + '是平年' )
}
(4) if-else if-…else 多分支

多分支,一定为执行一个分支代码
语法: if (条件1) {代码1} else if (条件2) {代码2} …else {代码n}
条件1为 true,执行代码1
条件1为 false,条件2为 true 执行代码2
如果前面的条件为 false,则执行代码 n
输入一个1~7,输出对应是星期几

var num = prompt('请输入1~7的数字')
if (num == 1) {
  console.log('星期一')
} else if (num == 2) {
  console.log('星期二')
} else if (num == 3) {
  console.log('星期三')
} else if (num == 4) {
  console.log('星期四')
} else if (num == 5) {
  console.log('星期五')
} else if (num == 6) {
  console.log('星期六')
} else if (num == 7) {
  console.log('星期天')
} else {
  console.log( '请输入1~7的数字' )
}
(5) switch-case 多分支

多分支,有可能不执行对应的代码
注意: 1. 变量和 case 后面的值比较,是全等比较(需要比较数据类型和值)
注意: 2. break 关键值在 switch 语句中可以不写,但是如果不写的话,则代码会继续往下执行,知道遇见break,或执行结果,省略 break 的写法叫做switch 穿透

switch(变量){
        case1:
          // 当变量 和 值1 全等的情况执行代码;
          break;
        case2:
          // 当变量 和 值2 全等的情况执行代码;
          break; // break的作用结束switch语句的执行
            // ...
        default:
        // 当变量和 上面的值都不相等的时候,执行的代码
      }
      
// 案列
var num = 11;
switch(num){
  case 10:
    console.log( '第一个case' );
    break;
  case 11:
    console.log( '第二个case' );
    // break;
    // 如果不写这个break,条件判断执行到此处,switch语句不会结束,而是继续往下执行代码(也不会进行判断比较),直到遇见break才会结束
  case 12:
    console.log( '第三个case' );
    break; 
  case 13:
    console.log( '第四个case' );
    break;      
}
(6) 三元表达式

当我们书写双分支的时候,可以使用三元表达式
双分支: if(条件){代码1}else{代码2}
三元表达式: 条件?代码1:代码2
条件为true则执行代码1,否则执行代码2

// 双分支案列
var num = 19
if (num >= 18){
  console.log( '成年了可以考驾照' );
} else {
  console.log( '未成年回家写作业' );
}
// 使用三元表达式
var num = 17
num >= 18 ? console.log( '成年了可以考驾照' ) : console.log( '未成年回家写作业' );
// 使用三元表达式判断奇偶数
var n = Number(prompt('输入一个数字判断奇偶数'))
if (isNaN(n)) {
  console.log('请输入一个数字')
} else {
  if (n === 0) {
    var res = '输入的是0'
  } else {
    var res = n % 2 == 0 ? '偶数' : '奇数';
  }
  console.log(res)
}

循环分支

(1) while 循环

语法: while (条件) {循环代码}
先条件判断,如条件为 true,则执行循环代码
在执行条执行判断,如果为 true,继续执行循环代码…
直到条件判断为 false 的时候,则循环结束
注意: 如果条件判断一致为 true,就会形成 死循环。所以一般会设置初始值,并且在循环体中改变初始值,而条件判断也是根据变量值判断的

// 案列1:和女朋友说 10次 对不起
// 1.设置初始值
var n = 0;
// 2.设置条件
while (n < 10){
  // 2.书写循环体
  console.log( '对不起' )
  // 4.在循环体内, 改变值
  n++;
}
console.log( n+'循环结束' )

// 案例2:第一次弹窗的结果 str是循环的初始值
var str = prompt('你爱不爱我?(yes/no)');
while(str != 'yes'){  // 条件成立则执行循环体
  // 循环体中代码就是 弹出提问弹窗,并修改str的值
  str = prompt('你爱不爱我?(yes/no)')
}
// 当输入yes的时候循环结束
alert('我也爱你');
(2) do-while循环

执行: 先执行一次大括号中的循环代码,然后条件判断
条件判断为true,则继续执行循环代码,然后再条件判断
条件判断为true,则继续执行循环代码,然后再条件判断
…直到 条件判断为false,则循环结束

特点: do-while语句 至少会执行一次循环代码
while循环有可能一次都不执行循环代码

do{
      循环代码
    } while (条件)
    
// 需求: 和女朋友说 我错了 5次
var n = 5;
do{
  console.log('我错了');
  n--;
}while(n>0)
(3) for语句循环
/*
语法:
for(初始值1; 条件2; 值改变3){
    循环代码4
}
  执行: 1243-->243-->243-->.....继续循环执行,直到 条件2 判断为false的时候,循环结束
*/

// 简单的for循环 输出1~5
 for (var i = 1; i <= 5; i++) {
   console.log(i)
 }
 
// for循环的变换写法1
// 可以将for循环小括号中的初始值放到外面,但是小括号的分号不能省略
var i = 1;
for (;i<=5;i++) {
  console.log( i )
}

// for循环的变化写法2
// 也可以将for选混中的值改变 在循环体内书写
var i = 1;
for (; i <= 5;) {
  console.log(i)
  i++
}

// 不要写死循环   条件判断一定要有
for(;;){
  console.log( 666 )
}

循环控制关键字

(1) break
// break 可以在循环中使用,表示结束循环
// 吃十个包子, 发现吃到第6个的时候,就饱了,就不吃了
for(var i = 1; i <= 10; i++){
  document.write('吃第' + i + '个包子<br>')
  if(i === 6){
    document.write('吃饱了')
    break// 结束循环
  }
}
(2) continue
// continue  结束本次循环代码的执行,直接跳到下一次的循环
// 吃十个包子, 发现吃到第6个的时候,第6个包子坏了,第6个包子不吃了,继吃后面四个包子
for(var i = 1; i <= 10; i++){
  if(i === 6){
    console.log('第6个包子坏了,不吃')
    continue // 结束本次循环代码的执行,继续下一次循环      
  }
  document.write('吃第' + i + '个包子<br>') 
}
(2) 循环小结
  1. while循环和for循环中的循环体,可能一次都不执行,do-while循环至少会执行一次循环体
  2. 如果知道循环次数 更多的时候使用 for循环,如果不知道循环次数,则使用while或do-while循环
  3. 循环控制关键字 break 和 continue 使用在循环体中的,无论是for循环还是while , do-while循环都可以使用

循环嵌套

循环嵌套: 循环体内 写循环

(1) 循环嵌套写法
// 需要记录多个跑圈,4个人,每人跑5圈
// 记录一个人跑步,每跑一圈记录一下
for (var i = 1; i <= 5; i++) {
  console.log('跑第' + i + '圈');
}
for (var j = 1; j <= 4; j++) {
  // 外层循环,记录的是第j个人的跑圈
  console.log('这是第' + j + '个人的跑圈记录');
  for (var i = 1; i <= 5; i++) {
    // 内层循环,记录 第j个人 跑的圈数
    console.log('这是第' + j + '个人跑第' + i + '圈');
  }
}


// 使用循环嵌套,在页面中打印表格4*5的表格
// 奇数行和偶数行的背景颜色不一样  
document.write('<table border="1">')
// 使用循环嵌套  输出tr-td
for (var i = 1; i <= 4; i++) {
    // 外层循环控制输出的行数
    if (i % 2 == 0) {
      document.write('<tr style="background:red">')
    } else {
      document.write('<tr style="background:skyblue">')
    }
    for (var j = 1; j <= 5; j++) {
      // 循环输出td;内层循环控制一行输出多个td
      document.write('<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>')
    }
    document.write('</tr>')
  }

  document.write('</table>')
(1) 循环嵌套案列
// 在页面中打印九九乘法表
/* 
  1*1=1
  2*1=1 2*2=4
  3*1=1 3*2=6 3*3=9
  4*1=1 4*2=8 4*3=12 4*4=16
  ...
  特点: 每一行输出的等式个数  就是行数
 */
for (var i = 1; i <= 9; i++) { // i就是第几行
  // 外层循环控制行数 ,9行
  for (var j = 1; j <= i; j++) {
    // 内层循环 控制 每一行输出 等式 个数
    document.write(i + '*' + j + '=' + i * j+'&nbsp;&nbsp;&nbsp;');
  }
  // 通过在页面中输出换行标签来实现 换行    
  document.write('<br>');
}

2.7 函数

函数特点: 封装, 复用, 即用

函数定义及调用

函数定义
使用关键字 function 来告诉浏览器, 要准备一个 盒子
这个 盒子 要有一个名字,这个盒子的名字也就是函数的名字—函数名称,和之前讲过的变量名的命名规则和规范一样
定义函数的过程 就是 将要执行的代码 放到 盒子 中的过程

(1) 声明式定义函数

语法:  function 函数名(){ 函数代码 }

  • function 关键字和 函数名 之间的空格不能省略
  • 函数名后面的小阔号不能省略: 小括号中是写 形参

声明式定义的函数,可以在定义之前或者定义之后调用该函数

赋值式定义的函数,只能在定义之后调用该函数

// 声明式定义函数
function fn() {
  console.log('这是fn函数')
}
fn() // 调用函数
(2) 赋值式定义函数----和之前讲的定义变量并赋值是一样

语法: var 变量名 = function (){ 函数代码 }

  • 赋值给变量的是一个没有名字的函数(匿名函数)
fn(); // 报错  ff is not a function
// 赋值式定义函数
var fn = function () {
  console.log('赋值式定义函数')
}
fn() // 调用函数
// 注意: 在js中代码执行报错,会终止主程序的执行(报错之后的代码不会执行)
(3) 函数定义阶段:
  1. 在内存中开辟一个空间(创建了一个盒子)
  2. 将函数中的代码一模一样的放到这个中间中,此时不会解析函数中的变量
    (相当于将函数中的代码,当做字符串一样放到开辟的空间中)
  3. 将开辟的空间的 地址赋值给 函数名(变量)

函数调用阶段:

  1. 先根据 函数名(变量)中存储的地址,找到 函数代码的存储空间
  2. 将函数存储空间中的代码拿出来执行,此时才会解析函数中的变量
  3. 函数调用的时候才会解析函数中的变量

函数的参数

(1) 形参:

在函数定义的时候 写到小括号中的变量----形式参数
形参 也是 在函数中使用的变量
如果有多个形参,使用逗号分隔
形参个数 比 函数调用时候的实参要多这个形参的值就是undefined(相当于在函数内部定义了一个变量,但是调用的时候没有赋值)

(2) 实参:

在函数调用的时候,写在小括号中的具体的数据,如果有多个也是用逗号分隔
实参 就是 在函数调用的时候,给对应的形参赋的值
实参没有赋值给对应的形参,则在函数内部不能通过形参得到这个实参的值

// 形参n 在函数内使用的变量
function fn(n, m, x, y) { 
  console.log(n, m, x, y)
}
// 函数调用,100就是实参
fn(100, 200); // 100 200 undefined undefined
(3) arguments 关键字
function ff(n1, n2) {
  // 1. arguments 会接收所有函数调用传递的实参
  console.log(arguments)
 
  // 2. 在函数内部,可以通过 arguments[下标] 获取对应的实参
  console.log(arguments[0])
  console.log(arguments[1])
  console.log(arguments[2])
 
  // 3. 可以通过arguments.length 获取实参的个数
  console.log(arguments.length)
}
// ff(10,20,30,40,50);
ff(10, 20, 30, 40, 50, 60);

函数的返回值

(1) 函数内的关键字 return
  1. return 会终止函数内代码的执行(return 关键字,下一行的代码不会执行)
  2. return 可以给函数创造一个返回值
    return 关键字后面跟的是什么内容,函数调用返回的结果就是什么
    注意: return 关键字后如果什么都没写,则函数的返回值就是 undefined
    如果函数内没有return,则函数调用的返回值也是undefined
// return 会终止函数的执行
function ff() {
  console.log( 1 )
  console.log( 2 )
  return;  // 函数内的代码执行到此处的时候,后面的代码就不会执行了
  console.log( 3 )
  console.log( 4 )
}
ff();

// return 返回的值
function fn(n) {
    var i = n + 10
    return i// 返回值return
}
// 函数调用也是一个表达式,执行后会有结果
// 将函数调用的结果 赋值给变量res
var res = fn(10); 
console.log(res) // 将 res 的值输出 i 的值 20
(2) 函数练习
// 编写一个函数,计算两个数值的和,差,积,商  并返回, 要求:使用传参的方式
function getRes(n1, n2, opt) {
  // 校验传入的是n1,n2的值,是否是数值
  if (isNaN(n1) || isNaN(n2)) {
    alert('请输入数字');
    return '输入数字有误'; // 终止函数代码的执行
  }
  var result; // 结果计算的结果
  // 使用switch-case多分支判断,输入的运算符,计算结果
  switch (opt) {
    case '+': result = n1 + n2; break;
    case '-': result = n1 n2; break;
    case '*': result = n1 * n2; break;
    case '/': result = n1 / n2; break;    
    default: 
      alert('请输入正确的运算符');
      return '输入的运算符有误';
  }
  return result;
}
var a = parseInt(prompt('输入第一个数字'))
var b = parseInt(prompt('输入第二个数字'))
var f = prompt('请输入计算方式:+,-,*,/')
// 函数调用
var res = getRes(a, b, f);
console.log(res)

函数的预解析

预解析: 在浏览器解析js代码之前,会解析的内容

(1) 预解析的内容
  1. var 关键字定义的变量
    变量提升: 在浏览器解析js中的代码之前,会将var 声明的变量提升(提升到代码的最前面)
  2. 声明式定义函数
    函数提升: 在浏览器解析js代码之前,会将 声明式定义的函数提升到代码的最前面
    注意: 函数调用,执行函数内部代码之前,也会在函数内部进行预解析,但是变量和函数,只会提升到函数内的最前面
(2) 预解析分析
console.log( num ); // undefined
var num = 100;
console.log( num ); // 100
/* 
  预解析分析:
    代码执行之前,只要发现代码中有var 申明变量,会将var 声明变量提升到最前面
    var num;
    console.log( num ) // undefined,声明变量没有赋值,变量就是undefined
    num = 100
    console.log( num ) // 100    
*/

/******************************************/
fn();
function fn() {
  console.log('fn')
}
fn();
/* 
  预解析分析:
    在代码执行之前,会将声明式定义的函数提升到最前面
    function fn() {
      console.log( 'fn' )
    }
    fn();
    fn();
 */
 
 /**************************************************/
  fn();
  var fn = function () {
    console.log('fn')
  }
  fn();
  /* 
    预解析分析:
      代码执行之前,发现有var 声明变量,会将变量声明提升到最前面
      var fn;
      fn(); // 此时变量fn中的值undefined,所以此处函数调用报错
      fn = function () {
        console.log('fn')
      }
      fn();
   */

  /**************************************************/
  function fn() {
    console.log(num)
    var num = 100;
    console.log(num)
  }
  fn();
  /* 
    预解析分析:
      fn函数调用,fn函数内部代码预解析
        function fn(){
          // 预解析
          var num;
          console.log(num) // undefined
          num = 100;
          console.log(num)// 100
        }
   */
(3) 预解析小结:
  1. 只有var 声明变量和声明式定义的函数才会预解析
  2. 赋值式定义的函数不会函数提升预解析,只有可能会变量提升预解析
  3. 函数调动的时候,函数内也会预解析,但是会先进行形参赋值,然后预解析
  4. 函数内的return 不会影响函数内的预解析
  5. 分支结构中的条件和大括号也不会影响预解析(变量提升)

作用域

作用域: 就是变量的使用范围
作用域分为全局作用域和局部作用域(私有作用域)

(1) 全局作用域: 一个页面就是一个全局作用域

全局作用域中定义的变量,在全局中都可以使用
全局作用域的生命周期: 从页面打开到页面关闭

// 全局中定义的变量在全局中都能使用
var num =100;
function fn() {
  console.log( num );
}
fn();
console.log( num )
(2) 局部作用域:

也叫做私有作用域,在js中只有函数的大括号才能确定一个局部作用域(if和for的大括号不行)
在局部作用域中定义的变量,只能在这个局部作用域中使用,在别的地方不能使用

// 局部作用域
function ff() {
  // 此处就是ff的局部作用域
}
function ff2() {
  // 此处就是ff2的局部作用域
}
(3) 作用域中的反问规则
  • 变量的方式就是获取这个变量的值

常见访问变量值的方式:

  1. 输出变量,
  2. 变量参与运算,
  3. 在函数中返回变量的值,
  4. 将变量当中的值赋值给别的变量
  5. 函数调用变量作为实参
  • 会先在当前自己作用域中查找,是否有定义这个变量,如果有则拿来使用
  • 如果当前作用域中没有这个变量,则去上一级作用域中查找,找到了则使用
  • 如果找不到,则再继续去上一级作用域中查找这个变量,找到了则使用
  • 如果还是找不到,则再继续去上一级作用域中查找这个变量,找到了则使用
  • 一直往上的作用域中查找,直到全局作用域查找,找到则使用
  • 如果在全局作用有中还是找到不到,则报错(变量 is not defined)
  • 注意: 变量的访问,找不到的时候只会去上一级作用域查找,不会往下的作用域中查找
function fn() {
  // fn的局部作用域
  var num = 200;
  function ff() {
    // ff局部作用域
    // console.log(username) // 报错 
    // 当前作用域没有username,则去上一级fn作用域中查找,没有找到,则继续去上一级作用域全局中查找,没有找到则报错  username is not defined 
    function f1() {
      // f1的局部作用域
      var username = 'leon';
      // num变量运算和num变量赋值都是属于 num变量访问
      console.log(num + 100) // 300
      var n = num
    }
    f1();
  }
  ff();
}
fn();
// f1作用域的上一级是ff作用域
// ff作用域的上一级是fn作用域
// fn作用域的上一级是全局作用域
(4) 作用域中变量的赋值规则:
  • 当作用域中有 给变量赋值的时候,
  • 会现在当前作用域中找,是否有声明这个变量,如果有声明则赋值
  • 如果没有,则去上一级作用域中查找,在上一级作用域中是否有声明这个变量,如果有则赋值
  • 如果还是没有,则继续往上一级作用域中找,如果还是没有,则继续往上的作用域中找
  • 如果直到全局作用域找,还是没有找到,则会将这个变量定义为 全局变量 ,并且赋值
  • 这中定义的变量 我们称之为 隐式全局变量
function fn() {
  function ff() {
    var num = 200;
    function f1() {
      num = 100;
    }
    f1()
    // f1函数调用 执行给变量num赋值, 在f1作用域中没有声明变量num,则去上一级作用域中找 是否有声明变量num
    // ff作用域中 有声明变量num, 则将100赋值给ff作用域中的num变量,f1函数执行结束
    console.log( num )//100  此处代码执行的时候,num变量中的值 已经是100了
  }
  ff();
}
fn();
(5) 作用域链

作用域链: 在变量的访问和赋值的时候,先当前作用域往上一级作用域,一直到到全局作用域,这样形成的一个链式称之为作用域链
作用域链的作用: 就是让js代码在执行的时候,变量的访问和赋值是有序的

事件

事件就是用户在页面的动作行为, 比如:鼠标点击,鼠标移动,键盘按下等等
在js中可以通过页面标签的id属性值,直接获取到页面元素
console.log( box )

事件组成三要素:

  1. 事件源: 绑定事件的页面元素
  2. 事件类型: 触发的什么事件(鼠标事件,键盘事件,表单事件…)
  3. 事件处理程序(函数): 事件触发后要执行的函数
1. 鼠标事件
  • click     鼠标左键单击事件
  • dblclick  鼠标左键双击事件
  • mouseover 鼠标移入事件
  • mouseout  鼠标移出事件
  • mousemove 鼠标移动事件
2. 键盘事件
  • keydown    键盘按下事件(键盘按下不松开则会一直触发此事件)
  • keyup      键盘弹起事件(键盘按下松开,只会触发一次keyup)
  • 一般键盘事件绑定给页面,js中docuemnt表示浏览器文本,文档包含浏览器中整个显示页面
3. 表单事件
  • blur     表单输入框的失去焦点(光标)事件
  • focus    表单输入框的获取焦点(光标)事件
  • input    表单输入框的输入事件
4. 浏览器事件
  • load    浏览器页面加载事件(一般给绑定给window)
  • scroll  浏览器滚动事件

语法: 页面元素.on+事件类型 = 函数

// box就是事件源
// click就是事件类型(click 是鼠标左键单击事件)
box.onclick = function () {
   console.log(666);
}
/* 
  此处我们可以理解为: 定义了一个匿名函数,并且赋值给了box元素的点击事件
  当鼠标点击box元素的时候,就会触发box的点击事件,并执行对应的事件处理函数
*/

对象

对象: 是 js 中的一种数据类型,是复杂数据类型
对象是一个数据的集合,无序数据集合
对象也叫做 键值对集合, 对象可以存储数据, 对象存储数据的形式----键值对形式存储

(1) 对象语法

(1) 对象

语法:{}表示一个空对象

var o = {};
console.log( o )

(2) 有数据的对象

语法:{键名:键值,键名:键值,键名:键值…}

var per = {
  name: 'zs',
  name: 'lisi', // 对象中的键名唯一的不能重复,所有后面的会将前面的覆盖
  'age': 17,
  'flag': true,
  eat:function(){
    console.log( '西红柿炒番茄' )
  }
}
console.log(per)

对象中的键名: 也叫做对象的属性名,属性名必须是字符串数据类型,属性名可以可以加引号,也可以不加引号书写,对象中属性名是唯一的(不能重复)
对象中的键值: 可以是任意数据类型的值 一组键值对组成了对象中的一个数据成员 对象中如果有多个数据成员(键值对),则使用逗号分隔

(2) 创建对象

1. 字面量创建对象

语法: 变量 = 对象

// 1. 字面量创建
// 字面量就是直接赋值的形式
var obj = {name:'zs',age:10};
console.log( obj )

2. 使用内置构造函数创建对象

Object是内置的构造函数,通过new 的方式调用该函数,可以创建对象
语法: 变量 = new Object()

// 2. 使用内置构造函数创建对象
var obj = new Object();
console.log( obj ) // 空对象
(3) 操作对象的成员:
  1. 添加对象成员
  • 点语法:   对象.属性名 = 值
  • 数组关联法: 对象[‘属性名’] = 值
    • 使用数组关联法,中括号里面的属性名必须加引号
  1. 修改对象对应的成员的值
  • 点语法:   对象.属性名 = 值
  • 数组关联法: 对象[‘属性名’] = 值
  1. 访问对象成
  • 点语法:   对象.属性名
  • 数组关联法: 对象[‘属性名’]
  1. 删除对象成员
  • 点语法:   delete 对象.属性名
  • 数组关联法: delete 对象[‘属性名’]
// 点语法操作
var obj = new Object();
// 添加
obj.name = 'zs';
obj.age = 17;
// 修改
obj.name = 'lisi'
// 删除
delete obj.age;
// 访问
console.log( obj.name ) // 'lisi'
// 访问对象成员的时候,如果没有这个属性名,则值为undefined
console.log( obj.abcd ) // undefined
console.log( obj ) // {name: 'lisi'}

// 数组关联法
var per = {};  
// 添加
per['name'] = 'zs';
per['age'] = 20;
// 修改
per['name'] = 'lisi';
// 删除
delete per['age']
// 访问
console.log( per['name'] ) // lisi
console.log( per['xyz'] ) // undefined  对象没有xyz这个属性名
console.log( per ) // {name: 'lisi'}

三、javascript数据操作

3.1 递归函数

在函数内部调用函数本身

// 使用递归函数,计算1+2+3+4+5的和
function ff(n) {
  // 结束条件
  if(n===1){
    return 1;
  }
  return n + ff(n - 1)
}
var res = ff(5);
console.log( res ) // 15

// 斐波那契数列
// 该数列的特点: 第一个和第二个的数值是1,从第三个开始,每一个数的值是前两个数的和
// 1 1 2 3 5 8 13 21 34 55..
// 请使用递归函数 求第10个斐波那契数列的值
function getFeiBo(n) {
  // 结束条件,第一个和第二个的数值是1
  if (n === 1 || n === 2) {
    return 1
  }
  return getFeiBo(n - 1) + getFeiBo(n - 2)
}
console.log( getFeiBo(10) ) // 55

3.2 数组

数据类型: 基本数据类型和复杂数据类型
基本数据类型: string boolean number undefined null
复杂数据类型: object array
数组 是js的复杂数据类型
数组就是一组有序的数据集合
因为数组中的每一个成员都有一个索引,这个索引就是成员在数组中的排序

数组: 使用中括号包裹,多个成员之间通过逗号分隔,数组中的数据成员可以是任意数据类型
数组的两种创建方式

字面量创建数组

语法:var arr = []

//  1. 字面量创建数组
var arr = [];
console.log( arr ) // 空数组 没有任何数据的数组
var arr1 = [1,2,3,4,'a','b','c',true,undefined,{}]
console.log( arr1 ) // [1, 2, 3, 4, 'a', 'b', 'c', true, undefined, {…}]

内置构造函数创建数组

  • var arr = new Array()  // 得到一个空数组
  • var arr = new Array(一个数字)  // 传入的数字必须是大于0的整数,得到一个有成员的数组,但是每一个成员的内容都是(empty)
  • var arr = new Array(多个参数)  // 多个参数可以是任意数据类型,得到一个有成员的数组,而且传入的参数就是数组成员
// 2. 通过内置构造函数创建数组
var arr = new Array();
console.log( arr ) // []
var arr = new Array(5); 
console.log( arr ) // [empty × 5]
var arr = new Array('a','b',1,2);
console.log( arr ) // ['a', 'b', 1, 2]
var arr = new Array('10');
console.log( arr ) // ['10']

数组的长度和索引

(1) 数组长度
  • 数组中的数组,我们也叫做数组的成员
  • 数组成员的个数 就是数组的长度
  • 数组长度获取
    • 数组.length
    • 返回数组成员的个数
(2) 数组的索引
  • 数组的每一个成员都有一个索引,表示成员在数组中序号
  • 数组的索引永远都是从0开始的连续自然数
  • 可以通过数组的索引,获取修改数组对应的成员数据
    • 语法: 数组[索引]     // 获取
(3) 数组索引和数组长度的关系
  • 数组索引的最大值 就是 数组长度减1
  • 数组长度 就是数组索引最大值加1
(4) 添加数组成员
  • 语法: 数组[索引] = 值
  • 注意: 如果数组中对应的索引有值了,通过这个方式只能修改数组中索引对应的值
(5) 数组的长度是可变的(可修改)
  • 随着数组成员数的编,数组的长度也会变化
  • 而知可以通过改变数组的长度,修改数组成员个数
  • 注意: 因为数组的索引是连续的自然数,如果跳过数组索引最大值添加成员,则长度以 最新的数组最大索引加1 为准
(6) 遍历数组: 循环的获取数组中的每一个成员
// for循环来遍历数组
for(var i = 0;i<数组.lengthl;i++){ // i 就是数组的索引
   数组[i]
}
var arr = ['a','b','c','d'];
console.log( arr.length ) // 4
console.log( arr ) // ['a','b','c','d']
console.log( arr[0] ) // ['a']
arr[3] = 4; // 修改
console.log( arr ) [4]
arr[4] = 'e'; // 添加
console.log( arr ) // ['a','b','c','d','e']

// 数组长度
var arr = ['a', 'b', 'c', 'd'];
// 直接修改数组长度 改变数组个数
arr.length = 2 
console.log(arr) // ["a", "b"]
arr.length = 0;
console.log(arr) // [] 

// 通过索引 添加数组成员,数组长度变化
arr[6] = 'f'; 
console.log(arr) // ['a', 'b', 'c', 'd', empty × 2, 'f']
console.log( arr.length ) // 7

// for 循环遍历数组
var arr = ['a', 'b', 'c', 'd'];
// 利用了数组索引和数组长度的关系特点 使用for循环遍历
// 数组的索引最大值 为 数组长度减1
for (var i = 0; i < arr.length; i++) {
  // i 就是数组的索引
  console.log( arr[i] ) // a b c d
}

数组常用方法

语法: 数组.数组方法()

数组方法作用
push()可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
pop()从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
unshift()方法能够在数组前面添加任意个项并返回新数组的长度。
shift()方法能够移除数组中的第一个项并返回该项,同时将数组长度减 1。
reverse()方法会反转数组项的顺序。
sort()方法按字母表升序排列数组项。
concat()方法可以基于当前数组中的所有项创建一个新数组,参数可以是数组项或者数组。
slice()方法它能够基于当前数组中的一或多个项创建一个新数组。可以接受一或两个参数。
splice()方法,对数组进行删除、插入、替换,是最强大的数组方法,返回值是数组,改变原数组。
join()方法,将数组转换成对应的字符串。参数就是连接符。
indexOf根据传入的参数,去数组中从前往后查找,如果找到一样的则返回索引,并停止查找,如果数组中没有一样的元素, 则返回-1
// 1. pop()
// 语法: 数组.pop();
// 作用: 删除数组的最后一个成员
// 返回值: 被删除的数组成员
// 会改变原数组
var arr = ['a', 'b', 'c'];
var res = arr.pop();
console.log(res) // 'c' 删除的元素作为返回值
console.log(arr) // ["a", "b"]

/***************************************/
// 2. unshift()
// 语法: 数组.unshift(参数);
// 作用: 将参数作为数组的成员,添加到数组里面的最前面
// 返回值: 返回添加之后的数组长度
// 会改变原数组
var arr = ['a', 'b', 'c'];
var res = arr.unshift(1);
console.log(res) // 4
console.log(arr) // [1, "a", "b", "c"]

/***************************************/
// 3. splice
// 语法1: 数组.spice(起始索引,删除几个元素)
// 作用: 从指定的索引开始删除数组元素
//  如果没有传入删除的个数,则默认从起始索引开始删除元素一直到最后一个元素
// 返回值: 以数组的形式返回删除的元素
// 会改变原数组
var arr = ['a', 'b', 'c', 'd', 'e'];
var resArr = arr.splice(2, 2);
console.log( '返回值:',resArr ) // ["c", "d"]
console.log( '原数组:',arr ) // ["a", "b", "e"]

// 语法2: 数组.spice(起始索引,删除几个元素,将这个东西替换删除的内容...)
// 作用: 从指定的索引开始删除数组元素,并替换为传入的内容
// 返回值: 以数组的形式返回删除的元素
var arr = ['a', 'b', 'c', 'd', 'e'];
var resArr = arr.splice(2, 2, 1, 2);
console.log('返回值:', resArr) // ["c", "d"]
console.log('原数组:', arr) // ["a", "b", 1, 2, "e"]

/**********************************************************/
// 4. sort
// 语法1: 数组.sort()
// 作用: 按照数组的元素的逐位数值排序
// 返回值: 排序后的数组
// 会改变原数组 
var arr = [11,31,4,2];
var resArr = arr.sort();
console.log('返回值:', resArr) // [11, 2, 31, 4]
console.log('原数组:', arr) // [11, 2, 31, 4]
// 语法2: 数组.sort(参数)
//  参数必须是一个函数,函数要有两个形参 函数内返回两个形参的差值
// 作用: 按照数组的元素的数值排序
// 返回值: 排序后的数组
// 会改变原数组 
var arr = [11, 31, 4, 2];
var resArr = arr.sort(function (a, b) {
  return a - b
});
console.log('返回值:', resArr) // [2, 4, 11, 31]
console.log('原数组:', arr) // [2, 4, 11, 31]
  
var arr = [11, 31, 4, 2];
var resArr = arr.sort(function (a, b) {
  return b - a
});
console.log('返回值:', resArr) // [31, 11, 4, 2]
console.log('原数组:', arr) // [31, 11, 4, 2]

遍历对象

因为数组是有序(索引)的数据集合
所以很容易使用for循环遍历数组
但是对象是一个无序的数据(键值对)集合
使用for循环则无法遍历对象,需要使用for-in语法遍历

// for-in语法:
// 对象有多少个成员就会循环多少次
for(var 变量 in 对象){
  // 变量就是对象的键名
}

var obj = {
  name: 'xiaobin',
  age: 18,
  yyds: '牛逼'
}
for (var key in obj) {
  // key就是对象的所有键名
  // console.log(key)
  // 此处的key是变量,通过对象获取键值,不能使用点语法
  console.log(key, obj[key])
}

二维数组

数组中存储数组,我们把大数组称之为多维数组
如果数组外面只嵌套了一层数组,那么就是二维数组

// 二维数组
var friends = [ // 模拟qq分组数据
  ['zs1','lisi1','ww1'],  // 好友
  ['zs2','lisi2','ww2'],  // 黑名单
  ['zs3','lisi3','ww3'],  // 陌生人
]
// 获取二维数组中所有的数据
console.log( friends[0][0] )
console.log( friends[0][1] )
console.log( friends[0][2] )
console.log( '--------------' )
console.log( friends[1][0] )
console.log( friends[1][1] )
console.log( friends[1][2] )
console.log( '--------------' )
console.log( friends[2][0] )
console.log( friends[2][1] )
console.log( friends[2][2] )

/********************************************/
var friends = [ // 模拟qq分组数据
  ['zs1', 'lisi1', 'ww1'], // 好友
  ['zs2', 'lisi2', 'ww2'], // 黑名单
  ['zs3', 'lisi3', 'ww3'], // 陌生人
]
// 使用循环遍历二维数组
for (var i = 0; i < friends.length; i++) {
  // 外层循环 大数组中的小数组
  console.log(friends[i])
  // console.log( '------' )
  for (var j = 0; j < friends[i].length; j++) {
    // 内层循环遍历小数组的每一个数据
    console.log(friends[i][j])
    // i表示小数组在大数组中的索引,j表示具体数据在小数组中的索引
  }
}

3.3 数据类型

数据类型的存储区别

存储内存分为:

  1. 栈内存
  2. 堆内存

js 中的数据类型 分为基本数据类型和复杂数据类型(引用数据类型)
基本数据类型的数据 存储在栈内存 中 栈内存中的标识对应的变量
复杂数据类型数据存储在 堆内存 中 而地址存储在栈内存中

小结: 给变量赋值一个基本数据类型,则在这个变量的 栈内存 中直接存储数据
给变量赋值一个复杂数据类型,则就是将 数据 存储在堆内存,然后将存储空间的地址 直接存储在变量的 栈内存中

不同数据类型之间变量赋值区别

  1. 基本数据类型之间的变量赋值, 赋值之后,变量之间互不影响
  2. 复杂数据类型之间的变量赋值,其实就是地址的赋值
  • 赋值后两个变量的地址指向同一个 存储空间,操作的也是同一个存储空间
  • 注意: 函数调用的实参赋值给形参,形参赋值也遵守上面两条 数据类型变量的赋值
// 基本数据类型之间的变量赋值
var n1 = 100;
var n2 = n1;
n2 = 200;
console.log(n1) // 100
console.log(n2) // 200

// 复杂数据类型之间的变量赋值
var arr = ['a', 'b', 'c'];
var arr2 = arr;
arr2[0] = 666;
console.log( arr ) // [666, "b", "c"]
console.log( arr[0] ) // 666

// 形参赋值1
function fn(n) {
 n = 200;
}
var num = 100
fn(num);
console.log(num) // 100

数据类型之间变量的比较

基本数据类型的变量比较  其实就是值的比较
复杂数据类型的变量比较  其实就是地址的比较

// 基本数据类型
var num1 = 100;
var num2 = 100;
console.log( num1 == num2 )//true

// 复杂数据类型  
var arr1 = [1, 2, 3];
var arr2 = [1, 2, 3];
console.log(arr1 == arr2) // false

数组排序

(1) 冒泡排序

遍历数组,在循环中用前一个数组元素和后一个数组元素来两两比较
如果前一个值比后一个更大,则交换位置,如果没有更大,则不交换位置
遍历结束后保证了数组中的最大值在最后面
进行多次的循环比较交换位置后,数组排序就完成了

var arr = [3, 5, 6, 4, 9, 7, 8, 2, 1];
for (var j = 0; j < arr.length - 1; j++) {
  for (var i = 0; i < arr.length - 1 - j; i++) {
    // 前一个和后一个比较,如果前一个更大,则交换位置
    if (arr[i] > arr[i + 1]) { // 交换位置
      var tmp = arr[i];
      arr[i] = arr[i + 1];
      arr[i + 1] = tmp;
    }
  }
}
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
(2) 选择排序

第一次假设数组下标为0是最小值
从数组下标1开始遍历数组,拿数组中的每一个元素逐个和假设的值比较
如果某一个值比假设的更小,则记录下索引
然后用记录索引的值和后面的数组元素继续比较,如果更小则替换索引
遍历结束之后,将记录的索引的值和下标为0 的值交换位置

第二次假设数组下标为1是最小值
从数组下标2开始遍历数组,拿数组中的每一个元素逐个和假设的值比较
如果某一个值比假设的更小,则记录下索引
然后用记录索引的值和后面的数组元素继续比较,如果更小则替换索引
遍历结束之后,将记录的索引的值和下标为1 的值交换位置
在进行多次选择比较(交换位置)后,数组排序完成

// 进行j次假设遍历后,数组排序完成
for (var j = 0; j < arr.length - 1; j++) {
  // 假设数组索引 j 就是最小值
  var minIndex = j; // 定义遍历记录最小值索引
  // 从数组索引 j+1 遍历
  for (var i = j + 1; i < arr.length; i++) {
    // 数组元素和索引为minIndex的值比较
    if (arr[i] < arr[minIndex]) { // 如果某一个值比minIndex的值更小则将索引记录在minIndex中
      minIndex = i;
    }
  }
  // 遍历结束,将索引为minIndex中的值和索引为j的值交换位置
  //  如果minIndex就是我们一开始假设的最小值的索引 j ,则不需要交换位置
  if (minIndex != j) {
    var tmp = arr[j];
    arr[j] = arr[minIndex]
    arr[minIndex] = tmp;
  }
}
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

3.4 字符串

严格模式

因为一开始的时候,js语法设计不是很严谨
js严格模式,主要是消除一些语法不合理的地方
也是为了未来新版本语法铺垫
开启严格模式,在代码的最前面直接书写 字符串 ‘use strict’

// 1. 严格模式下,没有var 声明的变量会报错
num = 200; // 报错
console.log( num )

// 2. 在严格模式下,定义的函数不能使用同名形参
function fn(num,num,num) {}  //报错

// 3. 在严格模式下,全局函数中的this没有任何指向
function ff() {
  console.log( this ) // undefined
}
ff(); // 函数中的this关键字,在函数普通调用方式的时候,this指向window

// 4. 在严格模式下,函数中不能使用arguments.callee 
function f2() {
  console.log( arguments )
  // console.log( arguments.callee ) // 报错
}
f2(1,2,3,4)

// 5. 在严格模式下,不能书写0开头的八进制写法
var n = 012;// 报错
var n1 = 0o12; // 标准的0o开头八进制数写法可以使用
var n2 = 0b11; // 0b开头的数字就是二进制数
var n3 = 0x11; // 0x开头的数字就是十六进制数
console.log( n1,n2,n3 )

ES5中的数组常用方法

语法作用
indexOf()从前往后查找数组元素,返回索引或-1
lastIndexOf()从后往前查找数组元素,返回索引或-1
forEach()数组遍历方法 没有返回值
map()数组映射方法 返回一个新数组
filter()数组过滤方法 返回一个新数组
every()数组判断方法 返回布尔值
some()数组判断方法 返回布尔值
(1) indexOf

语法: 数组.indexOf(参数)
根据传入的参数,去数组中从前往后查找,如果找到一样的则返回索引,并停止查找
如果数组中没有一样的元素, 则返回-1
作用: 查找数组元素
返回值: 索引 或 -1 不会改变原数组

var arr = [1,2,3,4,5,2];
var res1 = arr.indexOf(2)
console.log('返回值:', res1) // 1
(2) lastIndexOf

语法: 数组.lastIndexOf(参数)
作用: 从数组元素的后面开始查找,参数在数组中是否存在,存在则返回索引,否则返回-1
返回值: 索引或 -1

var arr = [1, 2, 6, 4, 5, 6];
console.log( arr.lastIndexOf(6) ) // 5
console.log( arr.lastIndexOf(7) ) // -1
(3) forEach 数组遍历方法

语法: 数组.forEach(参数)
参数是一个函数,函数有三个形参
作用: 遍历数组, 数组有多少个元素,则执行forEach中的函数多少次
这个方法没有返回值

var arr = ['a', 'b', 'c', 'd'];
var res = arr.forEach(function (item,index,ar) {
  // item 每次函数执行 表示数组的元素
  // index 每次函数执行 表示数组元素的索引
  // ar 每次函数执行 表示调用forEach的数组(原数组)
  console.log( item,index,ar )
})
console.log( res ) // undefined
(4) map 数组映射方法

语法: 数组.map(参数)
参数是一个函数,函数有三个形参
作用: 按照既定的条件操作数组的每一个元素, 数组有多少个元素,则执行map中的函数多少次
这个map方法有返回值: 一个新数组
函数中return的结果就是组成新数组的元素,所以此函数要有return
此方法不会改变原数组

var arr = [10, 20, 30, 40];
var res = arr.map(function (item, index, ar) {
  // item 每次函数执行 表示数组的元素
  // index 每次函数执行 表示数组元素的索引
  // ar 每次函数执行 表示原数组
  // console.log(item, index, ar)
  return item+5;
})
console.log( res ) // [15, 25, 35, 45]
console.log( arr ) // [10, 20, 30, 40]
(5) filter 数组过滤方法

语法: 数组.filter(参数)
参数是一个函数,函数有三个形参
作用:  数组有多少个元素,则执行filter中的函数多少次
这个filter方法有返回值: 一个新数组
函数中return的结果,如果为true,则对应的这个数组元素作为新数组的元素,如果为false,则对应的这个数组元素不会作为新数组的元素此方法不会改变原数组

var arr = [10, 20, 30, 40];
var res = arr.filter(function (item, index, ar) {
  // item 每次函数执行 表示数组的元素
  // index 每次函数执行 表示数组元素的索引
  // ar 每次函数执行 表示原数组
  // console.log(item, index, ar)
  return item>=30;
}) 
console.log( res ) // [30, 40]
console.log( arr ) // [10, 20, 30, 40]
(6) every 数组判断方法

语法: 数组.every(参数)
参数是一个函数,函数有三个形参
作用: 如果 函数return false ,则不再执行继续执行函数,不再遍历数组,every方法的返回值就是false, 如果数组遍历到结束 每次函数返回 都是true,则every方法的返回
为true
返回值: 布尔值
此方法不会改变原数组

var arr = [10, 20, 30, 40, 50];
var res = arr.every(function (item, index, ar) {
  // item 每次函数执行 表示数组的元素
  // index 每次函数执行 表示数组元素的索引
  // ar 每次函数执行 表示原数组
  // console.log(item, index, ar)
  // return item <= 30;
  return item>5;
})
console.log(res) // true
console.log( arr ) // [10, 20, 30, 40]
(7) some 数组判断方法

语法: 数组.some(参数)
参数是一个函数,函数有三个形参
作用: 如果 函数返回 true ,则不再执行继续执行函数,不再遍历数组,some方法的返回值就是true, 如果数组遍历到结束 每次函数返回 都是false,则some方法的返回值为false
返回值: 布尔值
此方法不会改变原数组

var arr = [10, 20, 30, 40, 50];
var res = arr.some(function (item, index, ar) {
  // item 每次函数执行 表示数组的元素
  // index 每次函数执行 表示数组元素的索引
  // ar 每次函数执行 表示原数组
  console.log(item, index, ar)
  // return item>5;
  // return item >= 30;
  return item>100
})
console.log(res) // false
console.log( arr ) // [10, 20, 30, 40]

字符串常用方法

(1) 字符串.charAt(索引)

根据索引查找字符串中对应的字符并返回,如果查找不到,则返回空字符串

var str = 'hello';
console.log( str.charAt(0) ); // 'h'
console.log( str.charAt(5) ); // ''
console.log( typeof str.charAt(5) ); // string
(2) 字符串.charCodeAt(索引)

根据索引查找字符串中对应的字符的编码值并返回,如果查找不到,则返回NaN

var str = 'hello';
console.log( str.charCodeAt(0) ); // 104 'h'的阿斯克编码是104
console.log( str.charCodeAt(5) ); // NaN
(3) 字符串.indexOf(参数)  参数也是一个字符串

查找参数在字符串中是否存在,从前往后查找,找到了则停止,存在则返回对应的索引,不存在则返回-1

var str = 'hel--lo';
console.log( str.indexOf('h') ); // 0
console.log( str.indexOf('l') ); // 2
// indexOf(参数,起始索引)  从起始索引开始 查找
console.log( str.indexOf('l',3) ); // 5
(4) 字符串.lastIndexOf(参数)  参数也是一个字符串

查找参数在字符串中是否存在,从后往前查找,找到了则停止,存在则返回对应的索引,不存在则返回-1

var str = 'hel--lo';
console.log( str.lastIndexOf('h') ); // 0
console.log( str.lastIndexOf('z') ); // -1
console.log( str.lastIndexOf('l') ); // 2
// lastIndexOf(参数,起始索引)  从起始索引开始往前 查找
console.log( str.lastIndexOf('l',3) ); // 2
(5) 字符串.substring(起始索引,结束索引)

起始索引和结束索引 是按照传入的索引 从小到大截取
从起始索引开始截取字符串中的内容到结束索引为止(不包含结束索引)

var str = 'abcdefg';
console.log(str.substring(2, 5)); // 'cde' 
console.log(str.substring(5, 2)); // 'cde'
(6) 字符串.substr(起始索引,截取个数)

从起始索引开始截取字符串中的内容

var str = 'abcdefg';
console.log(str.substr(2, 5)); // 'cdefg'
console.log(str.substr(5, 2)); // 'fg'
(7) 字符串.slice(起始索引,结束索引)

从起始索引开始截取字符串中的内容 到结束索引为止 (不包含结束索引)
结束索引可以是负数 -1表示最后一个字符,-2表示倒数第二个字符…

var str = 'abcdefg';
// console.log(str.slice(2, 5)); // 'cde'
console.log(str.slice(2, -1)); // 'cdef'
console.log(str.slice(0, 0)); // ''
(8) 字符串.split(参数)    参数也是字符串

根据传入的内容,将字符串打断(分割),组成一个数组返回
如果不传入参数,或传入的参数在字符串中不存在,则整个字符串就当做一个数组的元素,返回此数组
如果传入的是空字符串,则将字符串中的每一个字符,当做数组元素,返回数组

var str = '2022-6-30';
console.log(str.split('-')); // ["2022", "6", "30"]
console.log(str.split()); // ["2022-6-30"]
console.log(str.split('+')); // ["2022-6-30"]
console.log(str.split('')); //["2", "0", "2", "2", "-", "6", "-", "3", "0"]
(9) 字符串.toLowerCase()    将字符串全转为小写
var str = 'He-L-lo';
console.log(str.toLowerCase()); // he-l-lo
(10) 字符串.toUpperCase()    将字符串全转为大写
var str = 'He-L-lo123';
console.log(str.toUpperCase()); // HE-L-LO
(11) 字符串.replace(要替换的内容,替换为什么)
var str = 'He-L-lo';
// 替换字符串内容,只会替换第一部分内容
console.log(str.replace('-','+')); // He+L-lo
(12) 练习
// 需求: 假设上传商品图片的应用场景,需要判断上传文件后缀是图片
var allow = ['jpg', 'png'];
// 给btn一个点击事件
btn.onclick = function () {
  // 获取选择的完整文件名
  // input标签的值: 元素.value
  var fileName = img.value;
  // console.log( fileName )
  // 获取上传文件的后缀
  var arr = fileName.split('.');
  // arr数组的最后一个元素就是文件后缀
  var ext = arr[arr.length - 1];
  // 判断上传的文件是否是允许的图片类型
  if(allow.indexOf(ext) === -1){
    alert('不是图片')
  }else{
    alert('上传中');
  }
}

进制转换

(1) 数值变量.toString(参数)

参数就是我们要将数值转换为多少进制的数字字符串显示
默认不传参数就是十进制
参数的取值范围: 2~36
返回值:  进制转化后的字符串

// var num = 20;
console.log( num.toString() ); // '20'
console.log( num.toString(2) ); // '10100'
console.log( num.toString(8) ); // '24'
console.log( num.toString(16) ); // '14'
console.log( num.toString(3) ); // '202'
(2) parseInt(数字或字符串)

数字或字符串,将第一个参数当做几进制计算
如果第二个参数不写,就是当做十进制数转给十进制整数
返回值: 十进制的整数或者NaN

var str = '1010';
console.log( parseInt(str) ) // 1010
console.log( parseInt(str,2) ) // 10
console.log( parseInt('a1',2) ) // NaN
console.log( parseInt('a1',16) ) // 161

3.5 Math和Date

Math

Math是js中的内置一个对象,这个对象中有很多操作数值的方法

(1) random() 获取0~1的随机数
console.log( Math.random() )
(2) round(数值) 四舍五入取整
console.log( Math.round(5.1) ) // 5
console.log( Math.round(5.5) ) // 6
console.log( Math.round(5.9) ) // 6
(3) abs(数值)  求数值的绝对值
console.log( Math.abs(100) ) // 100
console.log( Math.abs(-100) ) // 100
(4) floor(数值) 对数值向下取整
console.log(Math.floor(5.1)) // 5
console.log(Math.floor(5.5)) // 5
console.log(Math.floor(5.9)) // 5
(5) ceil(数值) 对数值向上取整
console.log(Math.ceil(5.1)) // 6
console.log(Math.ceil(5.5)) // 6
console.log(Math.ceil(5.9)) // 6
(6) max(多个数值)  求多个数值中的最大值
console.log( Math.max(1,2,32,4,1000) ); // 1000
(7) min(多个数值)  求多个数值中的最小值
console.log(Math.min(1, 2, 32, 4, 1000)); //1
(8) sqrt(数值)  求数值的平方根
console.log( Math.sqrt(4) ); // 2
console.log( Math.sqrt(16) );// 4
console.log( Math.sqrt(25) ); // 5
(9) pow(数值,多少次方)  求数值的多次幂
console.log(Math.pow(2, 2)); // 4
console.log(Math.pow(2, 3)); // 8
console.log(Math.pow(2, 4)); // 16
console.log(Math.pow(5, 5)); // 3125
(10)  PI  求圆周率
console.log( Math.PI ); // 3.141592653589793
(11) 练习
// 数字字母混合验证码
// 获取一个四位的验证码
var str = '1234567890qwertyuiopasdfghjklmnbvcxz';
var code = ''; //四位验证码变量
// str字符串的索引范围 0~str.length-1
// 四次获取str随机的索引,并根据索引获取对应的字符然后拼接
for (var i = 0; i < 4; i++) {
  var index = getNum(0, str.length - 1); // 获取随机索引
  code += str.charAt(index); // 根据索引获取对应的字符,然后拼接在code中
}
console.log( code )
// 获取n~m的范围随机整数
function getNum(n, m) {
  var max = Math.max(n, m);
  var min = Math.min(n, m);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

date

Date是js的系统内置构造函数
可以通过 new Date() 创建一个时间对象

(1) new Date() 创建时间对象

在new Date() 创建时间对象的时候

  1. 不传参数,则返回当前的时间对象
  2. 传递参数,如果单独传递的是年月日时分秒参数,则至少要传递两个
  3. 如果传递的月份,日期,小时,分钟,秒超过了范围,则往上一级时间累计
    | 参数 | 表示 |
    | — | — |
    | YYYY | 年份 |
    | MM | 月份(0~11 0表示1月,11表示12月) |
    | DD | 日期(月份中的第几天) |
    | hh | 小时(0~23) |
    | mm | 分钟(0~59) |
    | ss | 秒(0~59) |
// 不传参数得到当前时间
// 在控制台输出的时候,会让时间对象调用toString()方法,转为时间字符串
console.log( new Date() ) // Mon Jul 04 2022 21:13:34 GMT+0800 (中国标准时间)
console.dir(数据)  可以查看到数据的结构
console.dir( new Date() ) // Object
console.log( new Date() == new Date() ) // false 说明是复杂数据结构

// 传递参数
// 星期 月份 日期 年份 时间
console.log( new Date('2022','6') ) // Fri Jul 01 2022 00:00:00 GMT+0800 (中国标准时间)
// 如果月份传递的超过11,则年份+1进行计算
console.log( new Date('2022','12') ) // Sun Jan 01 2023 00:00:00 GMT+0800 (中国标准时间)
console.log( new Date('2022','6','4','21','25','30') ) //Mon Jul 04 2022 21:25:30 GMT+0800 (中国标准时间)
(2) 时间字符串

如果传递的是时间字符串,则1就是表示1月份
‘YYYY-MM-DD hh:mm:ss’
‘YYYY/MM/DD hh:mm:ss’
‘YYYY.MM.DD hh:mm:ss’

console.log( new Date('2022-07-05 00:00:00') ) // Tue Jul 05 2022 00:00:00 GMT+0800 (中国标准时间)
console.log( new Date('2022/07/05 00:00:00') ) // Tue Jul 05 2022 00:00:00 GMT+0800 (中国标准时间)
console.log( new Date('2022.07.05 00:00:00') ) // Tue Jul 05 2022 00:00:00 GMT+0800 (中国标准时间)
(3) 时间方法

js给我们共了很多操作时间的方法
主要分为两套操作时间的方法

  1. get一套  是获取时间的方法
  2. set一套  设置时间的方法
  3. 注意:get和set方法一致
获取时间方法描述
getFullYear()获取年份
getMonth()获取月份 0~11
getDate()获取日期 1~31
getHours()获取小时数 0~23
getMinutes()获取分钟数 0~59
getSeconds()获取秒 0~1000
getMilliseconds()获取毫秒数
getDay()获取星期几 0~6
getTime()获取时间戳
var time = new Date();  
console.log( time )
// get一套
// 1. getFullyear() 获取年份
console.log( time.getFullYear() ) // 2022

// 2. getMonth()  获取月份 0~11
// 0表示1月份  11表示12月份
console.log( time.getMonth() ) // 6

// 3. getDate()  获取日期 1~31
console.log( time.getDate() ) // 4

// 4. getHours()  获取小时数 0~23
console.log( time.getHours() ) // 21

// 5. getMinutes()  获取分钟数 0~59
console.log( time.getMinutes() ) //35

// 6. getSeconds() 获取秒 0~1000
console.log( time.getSeconds() )

// 7. getMilliseconds()  获取毫秒数
console.log( time.getMilliseconds() )

// 8. getDay()  获取星期几 0~6
// 0 就是星期天 6就是星期六
console.log( time.getDay() ) // 1

// 9. getTime()  获取时间戳
// 时间戳: 就是从格林威治时间到 这个时间对象 经历过的时间毫秒数
// 格林威治时间: 1970-01-01 00:00:00 
console.log( time.getTime() )

// set一套 用于设置时间对象的方法
// 1. setFullYear()
time.setFullYear(2023)
console.log( time ) // Tue Jul 04 2023 21:42:02 GMT+0800 (中国标准时间

// 2. setMonth() 设置月份 0~11 
// 0表示1月份  11表示12月份
time.setMonth(0);
console.log( time ) // Tue Jan 04 2022 21:43:01 GMT+0800 (中国标准时间)
(4) 练习
// 求两个时间的 时间差,返回相差 多少天 多少小时 多少分 多少秒
// 定义一个获取两个时间的时间差函数,返回相差的 多少天 多少小时 多少分 多少秒
function getDiff(t1, t2) {
  // // 1. 将传入的两个时间对象转为时间戳(毫秒数)
  // t1 = t1.getTime()
  // t2 = t2.getTime()
  // console.log( t1,t2 )
  // // 2. 时间戳相减,得到个相差的毫秒数,除1000 然后取整 得到相差的秒数
  // var diff = Math.round((t2-t1)/1000);
  // console.log( diff )
  // 获取两个时间对象 相差的毫秒数可以直接将两个时间对象相减
  // 因为在相减的时候,会将两个时间对象转为 对应的时间戳然后相减
  // console.log( Math.round((t2-t1)/1000) )
  var diff = Math.round((t2 - t1) / 1000);
  // 根据diff计算出 相差的整 天数
  // diff/一天的秒数  然后取整
  var days = parseInt(diff / (24 * 60 * 60));
  // 根据diff计算出 相差的小时数(不足一天的小时数)
  // diff/60/60%24 取整
  var hours = parseInt(diff / 60 / 60 % 24);
  // console.log( hours )
  // 根据diff计算出 相差的分钟(不足一小时的分钟数)
  // diff/60%60 取整
  var minutes = parseInt(diff / 60 % 60);
  // console.log( minutes );
  // 根据diff计算出 相差的秒数(不足一分钟的秒数)
  // diff%60 
  var seconds = diff % 60;
  // 拼接字符串并返回
  return days + '天' + hours + '小时' + minutes + '分钟' + seconds + '秒';
}
// // 获取两个时间对象
// var time1 = new Date();
// var time2 = new Date('2023-01-22 00:00:00');
// // var time2 = new Date('2022-07-06 00:00:00');
// var str = getDiff(time1,time2);
// console.log( str )
// 在页面中显示距离春节的倒计时
// document.write(str)
// 可以使用定时器完成倒计时
// 语法: setInterval(函数,时间)
// 每过一段时间会执行一次函数
// 时间单位是毫秒
setInterval(function () {
  // 获取两个时间对象
  var time1 = new Date(); // 当前时间
  var time2 = new Date('2023-01-22 00:00:00'); // 春节时间
  var str = getDiff(time1, time2);
  // 设置页面元素中的内容: 语法:元素.inneText = 内容. 覆盖性的设置
  dv.innerText = '距离春节还有:'+str;
}, 1000)

四、BOM 和 DOM

4.1 BOM 浏览器对象模型

浏览器对象模型: 所有的浏览器操作方法都在对象中

  1. BOM中的顶级对象  是window
  2. 浏览器操作中的各个操作对象 都是window对象的属性成员
  3. 全局中的this和top关键字 指向window对象
  4. BOM操作中 可以省略window,比如: 浏览器弹窗  windiw.alert(),实际使用中可以省略window 直接 alert()
  5. 在全局中通过var定义的变量或函数 其实就是添加在window对象中的属性和方法

浏览器事件

(1) load  浏览器加载事件
// 1. load  浏览器加载事件(当浏览器页面中将所有资源[图片,cssm,js....]都加载完毕的时候触发)
window.onload = function () {
  // 这个函数会在 页面中所有内容都加载完毕后才触发 执行
  console.log( dv ) // <div id="dv"></div>
}
(2) scroll  浏览器页面滚动滚动事件
// 2. scroll  浏览器页面滚动滚动事件
window.onscroll = function () {
  console.log( '浏览器滚动了' )
}
(3) resize  浏览器可视窗口的尺寸变化事件
// 3. resize  浏览器可视窗口的尺寸变化事件
window.onresize = function(){
  console.log( '窗口大小变化了' );
}

BOM的相关操作

(1) 获取浏览器的窗口尺寸
  1. window.innerWidth    浏览器可视窗口的宽度(包含滚动条)
  2. window.innerHeight   浏览器可视窗口的高度(包含滚动条)
console.log( window.innerWidth )
console.log( window.innerHeight )

// 在窗口大小变化事件中获取窗口大小
// 当浏览器可视窗口大小小于 800的时候div不显示
window.onresize = function () {
  console.log(window.innerHeight)
  console.log(window.innerWidth)
  if (window.innerWidth < 800) {
    // div隐藏
    dv.style.display = 'none'
  } else {
    dv.style.display = 'block'
  }
}
(2) 浏览器信息

浏览器信息通过navigator对象获取,window.navigator

// 1. 获取浏览器整体信息
console.log( navigator.userAgent ) // Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
(3) 浏览器地址栏

1. location.href 获取设置浏览器地址url的

// location.href 获取设置浏览器地址url的,如果给location.href一个新的地址,则页面会发生跳转
btn1.onclick = function () {
  location.href = 'http://www.xiaobinw.cn';
}
// 地址中的 中文等特殊字符会被转为url编码格式展示
console.log( location.href )

2. location.reload()  刷新页面

// location.reload()  刷新页面,注意: 不要在全局中直接书写,不然会一直刷新页面
location.reload()
btn2.onclick = function () {
  location.reload();  
}
(4) 浏览器的历史记录信息

1. history.back()
回退到历史记录中的上一个页面,前提要有历史记录

2. history.forward()
前进到历史记录中的下一个页面,前提要有历史记录

3. history.go(n)
n为正数则前进到历史记录中的前几个页面
n为负数则回退到历史记录中的后几个页面
history.go(-1) 相当于 history.back()
history.go(1) 相当于 history.forward()
history.go() 会刷新页面

(5) 浏览器的滚动距离

浏览器水平滚动距离
浏览器垂直滚动距离

1.页面有DOCTYPE文档声明的时候
通过  document.documentElement.scrollLeft 获取
通过  document.documentElement.scrollTop 获取

console.log( window.document.documentElement.scrollLeft )
console.log( document.documentElement.scrollTop )

2.页面没有DOCTYPE文档声明的时候
通过  document.body.scrollLeft 获取
通过  document.body.scrollTop 获取
javascript

console.log(document.body.scrollLeft)
console.log( document.body.scrollTop )
(6) 顶部通栏
// 当浏览器滚动距离小于(800)的时候,隐藏顶部通栏,否则显示
window.onscroll = function () {
  // 获取滚动的距离
  var goLength = document.documentElement.scrollTop;
  if (goLength >= 800) {
    top1.style.height = '80px';// 显示顶部通栏
  } else {
    top1.style.height = '0px';// 隐藏顶部通栏
  }
}

4.2 定时器

在前端 js中 代码执行是单线程(同一时间只能做一件事)
JS 提供给我们一个 异步代码执行机制

异步代码执行机制(EventLoop)

  • 当代码执行过程中,遇见异步代码了
  • 不会立即执行异步代码,而是将异步代码放到 事件队列池 中等待
  • 继续向后执行同步代码
  • 等到所有同步代码执行完毕之后,调用栈清空了,再去异步事件队列池中拿到异步代码执行
    定时器中的函数是异步执行的代码

定时器开启

(1) 延时定时器
  • 语法: setTimeout(函数,数字,参数1,参数2,参数3,…)
    • 函数: 表示时间达到的时候要执行的函数
      第一个位置的参数,可以不写函数,写js代码字符串也行
    • 数字: 倒计时间, 单位毫秒
    • 第二位置之后的参数,是前面函数执行时候的实参
console.log( new Date() )
setTimeout(function () {
  console.log( '两秒过去了' )
  console.log( new Date() )
},2000)
(2) 间隔定时器
  • 语法: setInterval(函数,数字,参数1,参数2,参数3,…)
    • 函数: 表示每间隔一段时间要执行的函数
      第一个位置的参数,可以不写函数,写js代码字符串也行
    • 数字: 倒计时间, 单位毫秒
    • 第二位置之后的参数,是前面函数执行时候的实参
setInterval(function(){
  console.log( '2s过去了' )
},2000)

定时器返回值

  • 返回值为数字,表示开始定时器的标识
  • 两种定时器的返回值都是一个样
  • 不区分定时器种类,只是表示定时器的第几个定时器
var t1 = setTimeout(function () {})
var t2 = setInterval(function () {})
console.log( 't1:',t1 )
console.log( 't2:',t2 )

关闭定时器

  • 关闭销毁定时器的是,不区分定时器种类,只要给出的定时器标识数是对的,就可以销毁
  • 语法:
    • clearInterval(定时器标识)
    • clearTimeout(定时器标识)
var t1 = setTimeout(function () {console.log( 'timeout' )},3000);
var t2 = setInterval(function () {console.log( 'Interval' )},1000);
btn.onclick = function () {
  // clearInterval(t1)
  // clearInterval(t2)
  clearTimeout(t1)
  clearTimeout(t2)
}

4.3 DOM

认识DOM - Document Object Model 文档对象模型

  1. 如何找到页面中我们操作的元素
  2. 操作(操作文本,操作属性,操作样式,操作增删改查)

DOM获取元素

用一个变量保存页面中某一个或某些元素
获取元素方法分两类获取非常规元素和获取常规元素

(1) 获取非常规元素
  • html: document.documentElement
  • head: document.head
  • body: document.body
  • title: document.title
(2) 获取常规元素
语法名字返回值
.getElementById()id 获取元素,只能获取一个元素有则返回元素,无则返回null
.getElementsByTagName()标签名来获取元素, 返回类数组有则返回到数组中,无则返回空伪数组
.getElementsByClassName()类名来获取元素, 返回类数组有则返回数组,无则返回空伪数组,为数组有length属性
.querySelector()选择器来获取元素, 只能获取到匹配的第一个有则返回第一个元素,无则返回null
.querySelectorAll()选择器来获取元素, 返回类数组有则返回数组,无则返回空伪数组,可以使用forEach遍历数组方法
Array.from(伪数组)将伪数组转为真数组方返回一个真数组(元素和伪数组元素一模一样)

元素属性

标签属性分类有三种

(1) 原生属性
  • 在 W3C 规范中有的属性名
  • 比如: id class style type src href…
(2) 自定义属性
  • 在 W3C 规范中没有的属性名,是我们自己在书写在标签上的
(3) H5 自定义属性
  • 目的: 就是为了区分自定义属性和原生属性写在标签上的形式
  • 要求: 书写 H5 自定义属性的时候,都要 data- 开头
  • 比如: data-index = ‘888’
    • data- 表示这个是 H5 自定义属性
    • index 表示属性名
    • ‘888’ 表示属性值

操作元素属性

(1) 操作原生属性
  • 语法: 元素.属性名 = 属性值
  • 注意: 如果遇到布尔类型属性,可以使用false或true赋值
(2) 操作自定义属性(非H5)
<div id="box" index='666' data-index='888' data-id=999>hello</div>
<img src="https://img1.baidu.com/it/u=3923683862,2037492630&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=587"> <br>
<script>
//设置 语法: 元素.setAttribute(属性名,属性值)
dv.setAttribute('stock',99);

// 删除 语法: 元素.removeAttribute(属性名)
dv.removeAttribute('index')

// 获取 语法: 元素.getAttribute(属性名)
// 返回值: 该元素自定义属性的值
var res = dv.getAttribute('index');
console.log( res ) // '666'
</script>
(3) 操作H5自定义属性

每一个 元素节点 身上都有一个自带的属性名叫做 dataset
这个属性的值是一个类似于 对象的数据结构,存储的改标签上所有的H5自定义属性
H5 自定义属性的操作,就是对象dataset这个数据结构的操作(同对象操作语法)

// 获取元素
var dv = document.getElementById('box');
var img = document.querySelector('img');
// 查 语法: 元素.dataset.属性名
console.log( dv.dataset.size )

// 增 语法: 元素.dataset.属性名 = 属性值
dv.dataset.size = 999;

// 改 语法: 元素.dataset.属性名 = 属性值
dv.dataset.id = 777

// 删 语法: delete 元素.dataset.属性
delete dv.dataset.index;
(4) 练习全选案例
  1. 全选按钮的点击操作
  • 当全选按钮是选中的状态的时候,所有选项按钮都是选中状态
  • 当全选按钮是未选中的状态的时候,所有选项按钮都是未选中状态
  1. 点击每一个选项按钮的操作
  • 每一个选项按钮点击的时候,都要判断
  • 如果所有选项按钮都是选中状态,则全选按钮选中
  • 如果所有选项按钮有任何一个未选中,则全选按钮未选中
* {
  margin: 0;
  padding: 0;
}
.box {
  width: 100px;
  padding: 20px;
  margin: 50px auto;
  border: 2px solid #333;
  border-radius: 10px;
}
hr {
  margin: 10px 0;
}
<div class="box">
  <input type="checkbox" class="all">全选
  <hr>
  <input type="checkbox" class="item"> 选项一<br>
  <input type="checkbox" class="item"> 选项二<br>
  <input type="checkbox" class="item"> 选项三<br>
  <input type="checkbox" class="item"> 选项四<br>
</div>
// 获取元素
var allBtn = document.querySelector('.all');
// 将伪数组转为真数组方法
// 语法: Array.from(伪数组)
// 返回一个真数组(元素和伪数组元素一模一样)
// 为了后面方便操作,将获取的所有选项按钮伪数组转为真数组
var items = Array.from(document.querySelectorAll('.item'));
// 1. 全选按钮的点击操作
allBtn.onclick = function () {
  // 遍历选项按钮数组
  items.forEach(function (item) {
    // 将全选按钮的 选中 状态给每一个选项按钮
    item.checked = allBtn.checked;
  })
}
// 2. 点击每一个选项按钮的操作
items.forEach(function (item) {
  // 遍历选项按钮元素数组,给每一个选项按钮点击事件
  item.onclick = function () { // 事件处理函数
    // 通过数组的every方法 判断 是否 每一个选项按钮都是 选中
    //  选项按钮全选中为true,只有有一个没有选中则为false
    // 将结果 给全选按钮的checked属性
    allBtn.checked = items.every(function (item) {
      return item.checked;
    })
  };
})

操作元素的样式

在js中操作元素的样式有三种

  1. 获取元素行内样式(只能获取元素的行内样式)
  2. 获取元素的非行内样式(包含了行内样式和非行内样式)
  3. 设置元素的样式(只能设置元素的行内样式)

注意: 涉及到带中划线的样式名的时候

  • 转为驼峰写法
  • 使用数组关联法
(1) 获取元素的行内样式

语法: 元素.style.样式名

console.log( ele.style.width );// 100px
console.log( ele.style.height );// 非行内样式
console.log( ele.style.backgroundColor ) // green
console.log( ele.style['background-color'] ) // green
(2) 获取元素的非行内样式

语法: window.getComputedStyle(你要获取样式的元素).样式名

// 注意:getComputedStyle在低版本ie中不能使用
console.log( window.getComputedStyle(ele).width ) // 100px
console.log( window.getComputedStyle(ele).height ) // 200px
console.log( window.getComputedStyle(ele).fontSize ) // 20px
console.log( window.getComputedStyle(ele)['font-size'] ) // 20px

// 在低版本ie中获取元素的非行内样式
// 语法: 元素.currentStyle.样式名  在主流浏览器中不能使用
console.log( ele.currentStyle.fontSize ) // 20px
console.log( ele.currentStyle.width ) // 100px
(3) 设置元素的样式(只能设置行内样式)

语法: 元素.style.样式名 = 样式值

ele.style.backgroundColor = 'blue';
ele.style['background-color'] = 'blue';
ele.style.fontSize = '50px';

操作元素的类名

  1. className
  • 就是原生属性的操作 元素.属性名
  • 因为 JS中有一个关键字叫 class,为了避开改名叫做className
  • 注意: 类名的值 是字符串,可以多个类名一起
  1. classList
  • 每一个元素身上都有一个属性 叫做classList
  • 该属性对应的值是一个类似于 数组的解构,方法是该元素的所有类名
  • 增删改查都是对象元素的classList操作,有专门的方法
    • 增: 元素.classList.add(类名)
    • 删: 元素.classList.remove(类名)
    • 切换: 元素.classList.toggle(类名)
    • 原来有该类名则删除,没有则添加
// className
// 设置: 元素.className = "值"
// 因为是用的是 = 赋值,会把之前的类名覆盖
ele.className = 'box';
ele.className = '';
// 追加类名 元素.className += ' 值'
ele.className += ' box'

// classList
console.log( ele.classList )
// 增
ele.classList.add('box');
ele.classList.add('active');
// 删除
ele.classList.remove('b');
// 切换
ele.onclick = function () {
  ele.classList.toggle('active');
}

操作元素的内容

(1) innerText

  • 语法: 元素.innerText
  • 获取元素的所有文本内容
  • 语法: 元素.innerText = ‘值’
  • 作用: 完全覆盖是的书写标签文本内容
  • 注意: 没有办法识别解析 html格式的字符串
//  innerText
console.log(ele.innerText)
ele.innerText = '666';
ele.innerText = '<h1>888</h1>';
(2) innerHTML

是一个读写属性

  • 语法: 元素.innerHTML
  • 获取元素的所有内容(包含了超文本内容),以html格式字符串的形式返回
  • 语法: 元素.innerHTML = ‘值’
  • 作用: 完全覆盖是的书写标签 超文文本内容
  • 注意: 可以识别解析 html格式的字符串
//  innerHTML
console.log(ele.innerHTML)
ele.innerHTML = '666';
ele.innerHTML = '<h1>888</h1>';
(3) value

表单标签的内容操作
一个读写属性,其实就是原生属性value操作

  • 语法: 表单元素.value
  • 得到: 该表单元素的value值
  • 语法: 表单元素.vlaue = ‘值’
  • 作用: 设置表单元素的value值
// value
console.log( inp.value )
inp.value = 888

获取元素的尺寸

有两套语法offsetWdith和offsetHeight

(1) 语法
  • 元素.offsetWdith
    • 获取元素的 内容+padding+border 区域的宽度
  • 元素.offsetHeight
    • 获取元素的 内容+padding+border 区域的高度
  • 注意: 不管盒子是什么模型,区域不变
// // 1. offset方式
console.log( ele.offsetWidth )
console.log( ele.offsetHeight )
(2) 语法
  • 元素.clientWdith
    • 获取元素的 内容+padding 区域的宽度
  • 元素.clientHeight
    • 获取元素的 内容+padding 区域的高度
// 2. client方式
console.log(ele.clientWidth)
console.log(ele.clientHeight)

获取元素的偏移量

(1) 获取偏移量参考元素

语法: 元素.offsetParent
得到: 该元素的偏移量参考父级

  • 就是该元素的定位父级
  • 如果到body都没有定位父级,那么这里的offsetParent就是body
(2)第一套语法
  • 元素.offsetLeft
    • 获取元素相对于 offsetParent 的左侧距离
  • 元素.offsetTop
    • 获取元素相对于 offsetParent 的上方距离
(3)第二套语法
  • 元素.clientLeft
    • 获取元素(内容+padding区域) 相对于该元素border左边的尺寸
  • 元素.clientTop
    • 获取元素(内容+padding区域) 相对于该元素border上边的尺寸
console.log( 'offsetParent:',spanEle.offsetParent )

console.log( 'offsetLeft:',spanEle.offsetLeft )
console.log( 'offsetTop:',spanEle.offsetTop )

console.log( 'clientLeft:',spanEle.clientLeft )
console.log( 'clientTop:',spanEle.clientTop )

获取可视窗口尺寸

BOM 级别获取: 包含滚动条

  • innerWidth
  • innerHeight

DOM 级别获取: 不包含滚动条

  • document.documentElement.clientHeight
  • document.documentElement.clientWidth
// 获取可视窗口
console.log( 'BOM' )
console.log( '宽度',window.innerWidth )
console.log( '高度',window.innerHeight )

console.log( 'DOM' )
console.log( '宽度',document.documentElement.clientWidth)
console.log( '高度',document.documentElement.clientHeight)

4.4 节点属性

  • 节点: 文档中一个小小的组成部分
  • 我们的网页有若干个节点组成

常见的DOM节点

  1. 元素节点    特指页面中所有标签
  2. 属性节点    书写在表桥身上的属性
    • 属性节点不作为独立节点出现,只用于修饰标签使用
  3. 文本节点    所有的文本内容(包含了换行和空格)
  4. 注释节点    所有的注释内容(包含了换行和空格)

获取DOM节点的方法

1. childNodes
  • 语法: 父节点.childNodes
  • 得到: 该父节点下的所有子一级节点
2. children
  • 语法: 父节点.children
  • 得到: 该父节点下的所有子一级元素节点
3. firstChild
  • 语法: 父节点.firstChild
  • 得到: 该父节点下的第一个子节点
4. firstElementChild
  • 语法: 父节点.firstElementChild
  • 得到: 该父节点下的第一个子元素节点
5. lastChild
  • 语法: 父节点.lastChild
  • 得到: 该父节点下的最后一个子节点
6. lastElementChild
  • 语法: 父节点.lastElementChild
  • 得到: 该父节点下的最后一个子元素节点
7. previousSibling
  • 语法: 节点.previousSibling
  • 得到: 该节点的上一个兄弟节点
8. previousElementSibling
  • 语法: 节点.previousElementSibling
  • 得到: 该节点的上一个兄弟元素节点
9. nextSibling
  • 语法: 节点.nextSibling
  • 得到: 该节点的下一个兄弟节点
10. nextElementSibling
  • 语法: 节点.nextElementSibling
  • 得到: 该节点的下一个兄弟元素节点
11. parentNode
  • 语法: 节点.parentNode
  • 得到: 该节点的父节点
12. parentElement
  • 语法: 节点.parentElement
  • 得到: 该节点的父元素节点
13. attributes
  • 语法: 节点.attributes
  • 得到: 该节点的所有属性节点
  • 注意: 只需要记住元素节点的获取就行

DOM节点属性

  • 属性节点: 节点类型的一种
  • 节点属性: 描述节点的信息
  • 所有节点都共有的内容,只是不同节点不一样

常见的节点属性有三种

1. nodeType 节点类型
  • 用一个数字来区分不同的节点,给每一个节点做了一个编号
  • 元素节点: 1
  • 属性节点: 2
  • 文本节点: 3
  • 注释节点: 8
// nodeType
console.log('nodeType')
console.log('元素节点:', ele.nodeType)
console.log('属性节点:', attr.nodeType)
console.log('文本节点:', text.nodeType)
console.log('注释节点:', comment.nodeType)
2. nodeName  节点名称
  • 元素节点: 大写的标签名
  • 属性节点: 属性名
  • 文本节点: #text
  • 注释节点: #comment
// nodeName
console.log('nodeName')
console.log('元素节点:', ele.nodeName)
console.log('属性节点:', attr.nodeName)
console.log('文本节点:', text.nodeName)
console.log('注释节点:', comment.nodeName)
3. nodeValue 节点内容
  • 元素节点: null
  • 属性节点: 属性值
  • 文本节点: 文本内容(包含换行和空格)
  • 注释节点: 注释内容(包含换行和空格)
// nodeValue
console.log('nodeValue')
console.log('元素节点:', ele.nodeValue)
console.log('属性节点:', attr.nodeValue)
console.log('文本节点:', text.nodeValue)
console.log('注释节点:', comment.nodeValue)

创建节点

  • 就是使用 js 创建出一个节点来,但是没有插入到页面中
1. 创建元素节点
  • 语法: document.createElement(‘标签名’)
  • 返回值: 一个被创建的标签
  • 注意: 你可以自定义标签名
// 创建元素节点
var crEle = document.createElement('div');
console.log( crEle )
2. 创建文本节点
  • 语法: document.createTextNode(‘文本内容’)
  • 返回值: 文本节点
// 创建文本节点
var crText = document.createTextNode('我是文本节点');
console.log( crText )

插入节点:

  • 就是把一个节点方法哦另一个节点内当子节点使用
1. appendChild()
  • 语法: 父节点.appendChild(子节点)
  • 作用: 把子节点插入到父节点内,并放到最后的位置
2. insertBefore()
  • 语法: 父节点.appendBefore(要插入子节点,在哪个子节点前面)

删除节点

  • 就是把一个节点从本身位置删除
1. removeChild()
  • 语法: 父节点.removeChild(子节点)
  • 作用: 把子节点从父节点中移除
2. remove()
  • 语法: 节点.remove()
  • 作用: 把该节点自己干掉

替换及克隆节点

1. 替换节点 replaceChild()
  • 语法: 父节点.replaceChild(换上节点,换下节点)
  • 作用: 在父节点内,换上节点 替换掉 换下节点
2. 克隆节点 cloneNode()
  • 语法: 节点.cloneNode(参数)

  • 参数默认值为false: 表示不克隆后代节点

  • 如果参数值为true : 表示克隆后代节点

练习

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Leon</title>
  <style>
    table {
      width: 500px;
      margin: 50px auto;
    }
  </style>
</head>

<body>
  <table border="1" cellspacing='0' rules=all>
    <thead>
      <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>性别</th>
        <th>年龄</th>
      </tr>
    </thead>
    <tbody>
      <!-- <tr>
        <td>1</td>
        <td>lisi</td>
        <td>男</td>
        <td>18</td>
      </tr> -->
    </tbody>
  </table>
</body>

</html>
<script>
  // 准备数据
  var users = [{
      id: 1,
      name: 'lisi',
      gender: '男',
      age: 18
    },
    {
      id: 2,
      name: 'zs',
      gender: '女',
      age: 19
    },
    {
      id: 3,
      name: 'leon',
      gender: '男',
      age: 20
    },
    {
      id: 4,
      name: 'jack',
      gender: '女',
      age: 21
    },
    {
      id: 5,
      name: 'tom',
      gender: '男',
      age: 22
    },
  ];
  // 方案一
  // 获取元素
  // var tbody = document.querySelector('tbody');
  // // 1. 根据数组,遍历 创建节点 并追加到tbody中
  // users.forEach(function (item) {
  //   // 每一个 item  创建一个tr元素节点
  //   var tr = document.createElement('tr');
  //   tr.align = 'center'
  //   // 每一个tr内,根据item内有对少数据, 创建多少个td 元素节点并追加到tr中
  //   for (var k in item) {
  //     var td = document.createElement('td');
  //     td.innerText = item[k] // item[k]就是需要在表格中展示的数据
  //     tr.appendChild(td);
  //   }
  //   // 将tr追加到tbody中
  //   tbody.appendChild(tr);
  // })
  // 在循环中多次操作页面DOM节点并插入节点, 效率不高

  // 方案二
  // 获取元素
  var tbody = document.querySelector('tbody');
  // 创建一个文档碎片
  // 语法: document.createDocumentFragment()
  // 常见一个袋子
  var frg = document.createDocumentFragment()

  // 1. 根据数组,遍历 创建节点 并追加到tbody中
  users.forEach(function (item) {
    // 每一个 item  创建一个tr元素节点
    var tr = document.createElement('tr');
    tr.align = 'center'
    // 每一个tr内,根据item内有对少数据, 创建多少个td 元素节点并追加到tr中
    for (var k in item) {
      var td = document.createElement('td');
      td.innerText = item[k] // item[k]就是需要在表格中展示的数据
      tr.appendChild(td);
    }
    // 不直接将tr追加到tbody中,而是把tr放到一个容器中
    // 把tr 放到 '袋子' 中(文档碎片)
    frg.appendChild(tr);
  })

  // 最后 一次性将 '袋子' 中的内容 都放到 tbody中
  tbody.appendChild(frg);
</script>

五、事件对象和传播事件

5.1 事件对象

  • 事件就是 和 浏览器中某一个节点约定的事情
  • 当用户发生指定行为的时候,会执行的某一段代码

事件三要素

  1. 事件源         绑定在谁身上的事件
  2. 事件类型       绑定的什么事件
  3. 事件处理函数   当行为发生的时候,执行的函数
  4. 注意: 在事件处理函数中,this关键字就是事件源
    注意: 所有事件类型,都是只要行为发生,有事件处理函数,则会执行函数
    注意: 键盘事件所有元素都可以绑定,但是不是所有元素都可以触发(一帮绑定给window,documnet,表单元素)
    注意: 表单事件,不一定非得有form标签,只有一个表单标签就行

通过on+事件类型 给元素绑定事件
语法: 元素.on+事件类型 = 函数

/* 
   事件源: ele
   事件类型: click
   事件处理函数: handler      
 */
var ele = document.querySelector('div');
ele.onclick = handler;
function handler() {
  console.log(this)
  console.log('你点了我!!!')
}

鼠标事件

语法作用
click左键单击
dblclick左键双击
contextmenu右键单击
mousedown鼠标按键按下
mouseup鼠标按键松开
mousemove鼠标移动
mouseover鼠标移入
mouseout鼠标移出
mouseenter鼠标移入
mouseleave鼠标移出

注意:

  • over 和 out 是一套,鼠标的移入移出 后代元素的时候,也会触发
  • enter 和 leave 是一套,鼠标的移入移出 后代元素的时候,不会触发,不会进行事件传播
var ele = document.querySelector('div');
//  mouseover    鼠标移入
//  mouseout     鼠标移出
ele.onmouseover = function () {console.log( '鼠标移入 over' )}
ele.onmouseout = function () {console.log( '鼠标移出 out' )}
  
//  mouseenter   鼠标移入
//  mouseleave  鼠标移出
ele.onmouseenter = function () {console.log( '鼠标移入 enter' )}
ele.onmouseleave = function () {console.log( '鼠标移出 leave' )}

键盘事件

keydown  键盘按键按下事件

  • 按键按下一直不松开,则一直触发
  • 键盘任何按键按下都会触发

keyup    键盘按键抬起事件

keypress 键盘按键按下事件

  • 按键按下一直不松开,则一直触发
  • 键盘上的一些系统功能按键不会触发: 比如 ESC等
<body>
  <div></div>
  <input type="text">
</body>

window.onkeydown = function () {
  console.log('按键按下 keydown')
}
window.onkeypress = function () {
  console.log('按键按下 keypress')
}
var inp = document.querySelector('input');
var div = document.querySelector('div');
div.onkeyup = function () {console.log( 'div-up' )} // div无法触发键盘按下
inp.onkeyup = function () {console.log( 'div-inp' )}

表单事件

  • 专门为表单元素(form,input,button,select…)准备的事件
  • 其他元素可以绑定,但是无法触发
    | 语法 | 作用 |
    | — | — |
    | blur | 失焦 |
    | focus | 聚焦 |
    | input | 输入 |
    | change | 改变 |
    | reset | 重置 |
    | submit | 提交 |

reset 重置

  • 需要绑定给 form 标签
  • 当你点击reset按钮的时候,会触发form标签的默认重置行为

submit  提交

  • 需要绑定给 form 标签
  • 当你点击submit按钮的时候,会触发form标签的默认提交
  • 当你点击button标签按钮的时候,会触发form标签的默认提交
  • 当你输入框回车的时候,会触发form标签的默认提交
var inp = document.querySelector('input[type=text]');
var form = document.querySelector('form');
// input
inp.oninput = function () { 
  // 事件处理函数中的this就是事件源---inp--输入框
  console.log( this.value )
}

// reset
form.onreset = function(){
  console.log( '重置' )
}

// submit
form.onsubmit = function () {
  console.log( '提交' )
  alert('666')
}

触摸事件

专门用于在触摸屏幕设备(手机,平板,…)

Col1Col2
touchstart触摸开始
touchmove触摸移动
touchend触摸结束
document.ontouchstart = function () {console.log( '触摸开始' )}
document.ontouchmove = function () {console.log( '触摸移动' )}
document.ontouchend = function () {console.log( '触摸结束' )}

其他事件

  1. selectstart   鼠标按下移动选中内容
  2. visibilitychange 离开页面
document.onselectstart = function () {
  console.log( '你要选中复制吗?' )
 //  return false; // 为 flase 时禁止点击
}
document.onvisibilitychange = function () {
  console.log( '你要离开页面吗?' )
}

事件绑定

在js中有两种事件绑定的方式

DOM 0级 事件绑定

  • 使用on 语法进行绑定
  • 语法: 事件源.on事件类型 = 事件处理函数
  • 特点: 同一个事件源的同一个事件类型只能绑定一个事件处理函数
    • = 赋值 会覆盖之前的函数
// DOM0级 事件
ele.onclick = function () {console.log( '点击事件处理函数1' )}
ele.onclick = function () {console.log( '点击事件处理函数2' )}

DOM 2级 事件绑定(事件侦听器/事件监听器)

  • 标准浏览器
    • 语法: 事件源.addEventListener(‘事件类型’,事件处理函数)
    • 特点: 同一个事件源的同一个事件类型可以绑定多个事件处理函数,触发的时候按绑定顺序执行函数
// DOM2级 事件
ele.addEventListener('click', function () {
  console.log('处理函数1')
});
ele.addEventListener('click', function () {
  console.log('处理函数2')
});
ele.addEventListener('click', function () {
  console.log('处理函数3')
});
  • 低版本IE浏览器
    • 语法: 事件源.attachEvent(‘on事件类型’,事件处理函数)
    • 特点: 同一个事件源的同一个事件类型可以绑定多个事件处理函数,触发的时候按绑定倒序执行函数
// DOM2级 事件
ele.attachEvent('onclick', function () {
  console.log('处理函数1')
});
ele.attachEvent('onclick', function () {
  console.log('处理函数2')
});
ele.attachEvent('onclick', function () {
  console.log('处理函数3')
});

案例

<body>
  <input type="text" id="num1">
  <select id="opt">
    <option value="+">+</option>
    <option value="-">-</option>
    <option value="*">*</option>
    <option value="/">/</option>
  </select>
  <input type="text" id="num2">
  <button id="btn">=</button>
  <input type="text" id="res">
  <script>
    // 获取元素
    var num1Inp = document.querySelector('#num1')
    var num2Inp = document.querySelector('#num2')
    var optSle = document.querySelector('#opt')
    var resInp = document.querySelector('#res')
    var btn = document.querySelector('#btn')

    // 给=按钮绑定点击事件
    btn.addEventListener('click', fn);
    function fn() { // 点击事件的处理函数
      var result; // 计算的结果
      switch (optSle.value) {
        case '+': result = Number(num1.value) + Number(num2.value); break;
        case '-': result = Number(num1.value) - Number(num2.value); break;
        case '*': result = Number(num1.value) * Number(num2.value); break;
        case '/': result = Number(num1.value) / Number(num2.value); break;
      }
      // 将计算结果 给 resInp的value
      resInp.value = result;
    }
  </script>
</body>

事件解绑

取消已绑定的事件处理函数

DOM 0级 事件解绑

  • 语法: 事件源.on事件类型 = null
  • 因为 赋值符号 覆盖的原因,就可以实现解绑
  • ele.onclick = null;

DOM 2级 事件解绑—标准浏览器

  • 语法: 事件源.removeEventListener(‘事件类型’,你要解绑的事件处理函数)
  • 注意: 2级事件, 如果你要解除绑定,那么在绑定时候,需要把函数单独书写,以函数名的形式绑定
  • ele.removeEventListener('click', f2);

DOM 2级 事件解绑—低版本IE浏览器

  • 语法: 事件源.detachEvent(‘on事件类型’,你要解绑的事件处理函数)
  • 注意: 2级事件, 如果你要解除绑定,那么在绑定时候,需要把函数单独书写,以函数名的形式绑定
  • ele.detachEvent('onclick',f2)

5.1.8 事件对象–Event

  • 是一个对象数据类型,对象内存储的内容是对当前事件的描述信息
  • 概念: 事件触发的时候,对这次事件细节的所有描述信息的一个对象数据类型

标准浏览器

  • 直接在事件处理函数接受一个形参,形参就是事件对象
  • 会在事件触发的时候,浏览器自动传递实参
  • xxxx.onclick = function(xx){}
  • xxx.addEventListener(‘click’,function(xx){})

低版本IE

  • 在事件处理函数中,window.event获取
var ele = document.getElementsByTagName('div')[0];
//  ele点击事件的事件对象获取
ele.onclick = function (e) {
    // e 是事件处理函数的形参
    // 事件处理函数和浏览器约定好了,当用户触发这个元素的点击事件的时候,浏览器帮我们调用事件处理函数,并且在函数调用的时候传递一个实参 ---- 就是事件对象
    // console.log( e )
    // console.log(window.event);
  
    // 兼容获取
    e = e || window.event;
    console.log(e);
}

鼠标事件

(1) 鼠标按键信息
  • 通过事件对象button属性获取
    • 左键按下: 0
    • 滚轮按下: 1
    • 右键按下: 2
      坐标信息
  1. client 一组
  • 事件对象.clientX

  • 事件对象.clientY

  • 鼠标光标相对于浏览器可视窗口左上角的坐标位置

  1. page 一组
  • 事件对象.pageX

  • 事件对象.pageY

  • 鼠标光标相对于文档左上角的坐标位置

  • 相对于浏览器的距离

  1. offset 一组
  • 事件对象.offsetX

  • 事件对象.offsetY

  • 鼠标光标相对于 触发事件的元素 左上角的坐标位置

  • 相对于自身元素的距离

(2) 小结:client家族和 offset家族

offset家族

  • offsetParent  获取元素的定位父级
  • offsetWidth
  • offsetHeight  获取元素 内容+padding+border 区域的尺寸
  • offsetLeft
  • offsetTop     获取元素相对于 offfsetParent 的偏移量(上/左边的距离)
  • offsetX
  • offsetY   鼠标光标相对于 触发事件元素 左上角的坐标

client家族

  • clientWidth
  • clientHeight  获取元素 内容+padding 区域的尺寸
  • clientLeft
  • clientTop     获取元素左/上边框的 宽度
  • clientX
  • clientY   鼠标光标相对浏览器可视窗口左上角的坐标

键盘事件

(1) keyCode

语法: 事件对象.keyCode
得到: 一个编码,一个按键有一个自主的独立编码(Unicode)
注意:

  • keydown事件对象获取的 字母按键的编码 不区分大小写

  • keypress事件对象获取的 字母按键的编码 区分大小写
    在低版本获取浏览器中获取按键编码

  • 事件对象.which

(2) 组合按键

每一个键盘事件对象中都有四个信息

  • altKey
  • ctrlKey
  • shiftKey
  • metaKey(win: win键,mac:command)
  • 以上四个按键的值都是布尔值,true表示按下,false表示没有按下
var inp = document.getElementsByTagName('input')[0];
inp.onkeydown = function (e) {
  e = e || window.event;
  // console.log( e.keyCode )
  // 组合按键
  console.log( 'shift:',e.shiftKey )
  console.log( 'ctrl:',e.ctrlKey )
  console.log( 'alt:',e.altKey )
  console.log( 'meta:',e.metaKey )
  if(e.ctrlKey && e.keyCode === 65){
    alert('你按下的是ctrl+a')
  }
}

案例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Leon</title>
  <style>
    span {
      display: block;
      width: 30px;
      height: 30px;
      background-color: red;
      border-radius: 50%;
      text-align: center;
      line-height: 30px;
      color: white;
      font-weight: bold;
      position: absolute;
    }
  </style>
</head>
<body>
  分数: <b>0</b>
  <!-- <span>w</span> -->
  <script>
  /* 
    打字游戏
      1. 页面布局(假设有一个span)
      2. 创建span元素,并随机获取字符或数字,并设置span水平随机定位,top:0
      3. 让span移动(垂直移动),当移动 出浏览器高度的时候,游戏结束
      4. 页面键盘按下事件,当键盘按下字符和span中的字符一致的时候,span消失,得分+1,并创建新的span 移动
  */
  // 定义变量
  // 获取随机字符或数字的字符串
  var str = 'qwertyuiopasdfghjklzxcvbnm0987654321';
  // 随机字符变量
  var s;
  // 定时器标识
  var timer;
  // span元素
  var span;
  // 分数
  var score = 0;

  // 定义一个创建span元素的函数(span中的字符随机,且水平定位随机,显示在也页面中)
  function createSpan() {
    // 获取随机字符
    // 获取随机索引 0~(str.length-1)
    var index = Math.floor(Math.random() * str.length);
    s = str[index];
    // 创建span元素
    span = document.createElement('span');
    span.innerHTML = s;

    // 获取水平随机坐标
    var xMax = window.innerWidth - span.offsetWidth;
    var x = Math.floor(Math.random() * xMax);
    // 赋值给span的定位样式
    span.style.left = x + 'px';
    span.style.top = 0;

    // 将创建的span追加到body中
    document.body.appendChild(span);
    // 当span在页面中显示之后,再调用span的移动函数
    spanMove()
  }

  // 打开页面创建并显示span
  createSpan()


  // 定义一个函数span移动(垂直移动)
  function spanMove() {
    // 获取span移动的最大距离
    var yMax = window.innerHeight;
    var goLength = 0; // span移动距离
    timer = setInterval(function () {
      goLength += 5; // 每过一段事件span移动5px
      // 判断游戏结束
      if (goLength >= yMax) {
        clearInterval(timer);
        span.remove();
        alert('游戏结束')
      } else {
        span.style.top = goLength + 'px';
      }
    }, 50)
  }

  // 页面键盘按下事件
  document.addEventListener('keypress',pressFn);
  function pressFn(e) {
    e = e || window.event;
    // 随机字符的编码 和 按键按下的编码 一致则得分
    if(e.keyCode === s.charCodeAt(0)){
      // 清除定时器 移除span 并创建新的span移动,得分+1
      clearInterval(timer);
      span.remove();
      createSpan();
      document.querySelector('b').innerHTML = ++score;
    }
  }
</script>
</body>
</html>

保留小数toFixed()

toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。

NumberObject.toFixed(num)
参数描述
num规定小数的位数,是 0 ~ 20 之间的值,包括 0 和

事件目标

  • 就是当事件触发的时候,准确 触发事件的那个元素
  • 当你给一个元素板顶事件后
    • 该元素的后代元素上触发行为,是可以触发事件的函数
    • 在该元素的后代元素身上触发行为,也可以触发事件处理函数
  • 获取事件目标:
    • 标准浏览器中: 事件对象.target
    • 低版本IE:事件对象.srcElement
  • 注意:mouseenter和mouseleave天生获取不到事件目标

5.2 事件对象

当我们在某一个元素上触发事件的时候,会把同类型事件向结构父级上传递,直到window为止
事件目标-> ··· -> body -> html -> document -> window
注意:可以通过 事件对象.path 获取事件目标 的所有结构父级

事件传播机制

(1)目标

准备触发事件的那个元素,在事件队形里面有个属性叫做 target 表示本次事件触发的时候,准确的元素,也叫目标元素
同类型事件的话先捕获后冒泡
特点:IE 低版本不支持
IE 用 STCElement

var div = document.getElementById('box')
div.onclick = function (e) {
    e = e || window.event
    var target = e.target || e.srcElement
    console.log(target)
}
(2)冒泡 false 默认

就是按照从 目标 到 window 的顺序依次触发事件

(3)捕获 true

就是按照从 window 到 目标 的顺序依次触发事件

(4)addEventListener()

阻止事件传播

  • 因为事件的传播,会导致我在另一个元素上触发行为
  • 会执行多个元素的事件处理函数
  • 阻止事件传播
    • e.stopPropagation()
    • e.cancelBubble = true
div.onclick = function(e) {
    e = e || window.envent
   
    // 阻止事件传播 兼容方式一
    if (e.stopPropagation) {
        e.stopPropagation()
    } else {
        e.cancelBubble = true
    }
    
    // 阻止事件传播 兼容方式二
    try {
        e.stopPropagation()
    } catch (err) {
        e.cancelBubble = true
    }
}

5.3 事件委托

利用了事件的冒泡,把子元素的事件绑定在一个共同的父元素身上

注意:

  • 事件委托要委托给一个共同的结构父级
  • 注意在父级的事件处理函数里面用事件目标去判断
  • 事件目标的兼容处理
  • 如果判断 nodeName 或 tagName 注意是大写标签名
ul.oncklick = function (e) {
    e = e || window.event
    var target = e.target || e.srcElement
    // target 就是你点击的那个元素
    if (target.nodeName === 'LI') {
        console.log('点击的是 li',targer)
    }
}

5.4 阻止浏览器的默认行为:

  • 不需要事件绑定, 天生自带的行为
  • 比如:
    • a标签的点击,只要点击.会默认进行跳转或锚点点位
    • 表单提交,只要点击submit按钮,那么表单会自动提交
    • 鼠标右键点击,只要行为发生,会默认出现菜单

阻止默认行为:

  • 不让本身应该发生的事件发生
  • 在同类型的事件中进行默认事件的阻止

DOM 2级事件

  • 标准浏览器: 事件对象.preventDefault()
  • 低版本IE: 事件对象.returnValue = false
document.getElementsByTagName('a')[0].attachEvent('onclick', function (e) {
    e = e || window.event
    // 阻止默认事件
    e.preventDefault()
    
    // IE 低版本
    retrunValue = false
}

DOM 0级事件

  • 通用语法: 在事件处理函数中 return false; 返回一个false布尔值

  • 注意:

    • return false; 一定要写在处理函数代码的最后
    • 保证前面的代码不报错
    • 在0级事件中也可以使用 事件对象.preventDefault() 来实现阻止默认行为
// 阻止鼠标右键单击--默认出现菜单
document.oncontextmenu = function () {
  console.log( '鼠标右键了但是没有菜单' )
  return false;
}

六、正则表达式

认识正则表达式 - Regular Expression

  • 作用: 检验字符串是否符合规则
  • 是一个 复杂数据类型
  • 正则表达式是复杂数据类型,reg3和reg4中存储的是正则表达的存储空间的地址

6.1 正则表达式的创建

量方式

var reg = /qwer/

构造函数

var reg = new RegExp('qwert');

6.2 正则表达式常用方法

text()  匹配方法

  • 语法: 正则.text(检测的字符串)
  • 作用: 检测字符串是否符合规则
  • 返回值: 布尔值
    • true   表示检测的字符串符合 正则表达式的规则
    • fasle  表示检测的字符串不符合 正则表达式的规则
var reg = /qwert/; // 检测的字符串中要包含 'qwert' 字符串片段
// var res = reg.test('qqwqwqqqrrrrtttttt');
var res = reg.test('qqwqwqqqrrrrttqwerttttt');
console.log( res )

exec()  捕获方法

  • 语法: 正则.exec(原字符串)
  • 作用: 从传入的字符串中, 获取到符合正则表达式规则的 部分字符串片段

6.3 正则表达式的组成

  1. 普通字符(文本)
  2. 元字符 (具有特殊含义)
  • 在正则表达式中 用一个字符来表示一类内容
  1. 标识符
  • 用于修饰正则表达式

元字符----基本元字符

基本元字符作用
\d表示 一位 数字
\D表示 一位 非数字
\s表示 一位 空白内容(空格,缩进,\n换行 也是空白)
\S表示 一位 非空白内容
\w表示 一位 数字字母下划线任意内容
\W表示 一位 非数字字母下划线任意内容
.表示 一位 非换行的任意内容
\表示转义
// 使用\ 转义符, 把 具有特殊函数的内容转义为普通内容
// 使用\ 将具有特殊函数的 .  转为了普通字符串 . 文本
var reg = /\d\.\d/;// 表示字符串中必须包含 数字.数字
console.log( reg.test('qwer.qwer') ) // false
console.log( reg.test('123.qwer') ) // false
console.log( reg.test('q123666ewrq') ) // false
console.log( reg.test('q123.qwer') ) // false
console.log( reg.test('q123.666ewrq') ) // true

元字符----限定元字符

作用: 限定前一个符号连续出现的次数

限定元字符作用
*表示连续重复出现 0~无数次
+表示连续重复出现 1~无数次
?表示连续重复出现 0~1次
{n}表示连续重复出现 n次
{n,}表示连续重复出现 n~无数次
{n,m}表示连续重复出现 n~m无数次
var reg = /\d{2,4}/ // 表示字符串中 必须包含连续的数字2~4个数字
console.log(reg.test('')) // false
console.log(reg.test('1')) // false
console.log(reg.test('12')) // true
console.log(reg.test('1wr2')) // false

元字符 - 边界元字符

边界元字符作用
^表示字符串开始 (写在正则表达式的最前面)
$表示字符串结束 (写在正则表达式的最后面)

注意: 当^ 和 $ 一起使用的时候,表示 从开始到结束

  • 没有 ^ 和 $ 叫做包含
  • 有^ 和 $ 叫做 只能
var reg = /^\d{3}$/; // 表示字符串 从开始到结尾 只能是三位连续的数字
console.log( reg.test('qwrew') ) // false
console.log( reg.test('q123etre') ) // // false
console.log( reg.test('123etre') )// false
console.log( reg.test('123') )// true
console.log( reg.test('123123') )// false

元字符–特殊元字符

  1. ()
  • 表示一个整体
  • 单独捕获(捕获方法中体现)
var reg = /^(abc){2}$/; // 表示 只能匹配 'abcabc' 的字符串
console.log( reg.test('abcabc') ) // true
console.log( reg.test('abc666abc') ) // false
  1. |
  • 或者
  • 一般和()联用
  • 只有() 和^ 和 $ 能区分 或 边界
var reg = /^(asd|qwe)$/; // 表示字符串 只能是 asd或qwe
console.log( reg.test('qwe') ) // true
  1. []
  • 写在 [] 中的内容任意一个字符都行
  • 注意: 一个[]只占一个位置
var reg = /^[asd]$/; // 字符串开头到结尾只能是一位字符串组成,可以是a或s或d
console.log(reg.test('a')) // true
  1. [^]
  • 书写在 [^]内的任意一个字符都不行
  • 注意: 一个[^]只占一个位置
var reg = /^[^asd]$/; // 字符串开头到结尾只能是一位字符串组成,不可以是a或s或d中的任意一个
console.log(reg.test('a'))// false
  1. -
  • 表示范围 到,至
  • 一般和[]联用
  • 注意: 必须保持Unicode码是连续的
    • 比如:
      • [0-9] 表示一位 数字
      • [a-z] 表示一位 小写字母
      • [A-Z] 表示一位 大写字母
      • [\u4e00-\u9fa5]  表示一位 中文
var reg = /^[0-9]$/; // 字符串只能是一位数字
console.log(reg.test('0')) // true
console.log(reg.test('1')) // true

元字符–重复元字符

语法: \n  比如: \1 \2 \3 …

  • 重复出现
  • 需要 第n个小括号中的内容一模一样重复
  • 注意: 不是重复多少次,而是重复第几个小括号的内容
// \2 的位置,要重复第二个括号中的内容
// ((ab|cd)|(ef|gh))
var reg = /^((ab|cd)|(ef|gh))\2$/;
console.log( reg.test('abcd') ) // false
console.log( reg.test('abab') ) // true

6.4 正则 - 断言(预查)

  • 需要匹配前后跟特定模式的内容
  • 分为 前瞻断言 和 后瞻断言

前瞻断言

  1. 前瞻肯定断言
  • 语法: x(?=y)
  • 作用: 匹配x,仅当后面跟着y
// 1. 前瞻肯定断言
var reg = /\d(?=d)/; // 字符串中的包含 数字,且数字后必须跟个字符d
console.log( reg.test('ddddd') ) // false
  1. 前瞻否定断言
  • 语法: x(?!y)
  • 作用: 匹配x,仅当后面不跟着y
// 2. 前瞻否定断言
var reg = /\d(?!d)/; // 字符串中的包含 数字,且数字后必须不能跟个字符d
console.log( reg.test('ddddd') ) // false

后瞻断言

  1. 后瞻肯定断言
  • 语法: (?<=y)x
  • 作用: 匹配x,仅当跟在y后面的
// 1. 后瞻肯定断言
var reg = /(?<=d)\d/; // 字符串中的包含 数字,且数字必须跟在字符d后面
console.log( reg.test('ddddd') ) // false
  1. 后瞻否定断言
  • 语法: (?<!y)x
  • 作用: 匹配x,仅当不跟在y后面的
// 2. 后瞻否定断言
var reg = /(?<!-)\d+/; // 字符串中的包含 数字,且数字前不能有-字符
console.log( reg.test('-1 -2 -3 -4 -5') ) // false

6.5 标识符

  • 书写在正则外面,正则表达式后面
  • 用来修饰整个正则表达式使用
标识符作用
i忽略大小写 ignore
g全局标识符 global 和捕获一起使用才有效果
var reg = /^[a-z]$/i
console.log( reg.test('a')  )
console.log( reg.test('b')  )
console.log( reg.test('A')  )

6.6 正则表达式–捕获方法

语法: 正则.exec(原字符串)
返回值:

  1. 原字符串中没有符合正则要求的字符片段
  • 返回值 null
  1. 原字符串中有符合正则要求的字符片段
  • 返回值一个数组,[0]位置就是 从原字符串中捕获的内容

2.1 正则表达式中没有() 也没有全局标识符g

  • 返回值 数组 只有[0] 是正则捕获的字符片段
  • 不管捕获多少次,正则都是从原字符串的开始位置进行检索捕获

2.2 正则表达式中有()

  • 返回值 数组 [0] 依旧是正则捕获的字符片段
  • 从 [1] 开始 依次是每一个小括号的单独捕获的内容
var str = '身份证号: 10010820010202001x ';
// 10 省份
// 01 市
// 08 县
// 2001 年份
// 02 月份
// 02 日期
// 倒数第二位表示性别 奇数是男 偶数女
var reg = /(\d{2})(\d{2})(\d{2})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|x)/
console.log( reg.exec(str) );

2.3 正则表达式中有 g

  • 返回值 数组 [0] 依旧是正则捕获的字符片段
  • 但是从第二次捕获开始,或从前一次捕获结束位置开始检索
  • 依次类推,直到检索捕获不到为止,返回null
  • 再下一次又从原字符串的开始位置进行检索
var str = 'qw123erty456uewrt789yuere';
var reg = /\d{3}/g;
console.log( reg.exec(str) );
console.log( reg.exec(str) );
console.log( reg.exec(str) );
console.log( reg.exec(str) );// null

6.7 正则表达式的两大特性

懒惰性

  • 当你捕获内容的时候,每次默认从字符串开头位置开始检索
  • 解决方案: 加一个全局标识 g
贪婪限定符含义
*表示0~多个
+表示1~多个
表示0~1次
{n}表示指定n次
{n,}表示n次,n多次
{n,m}表示n~m次

贪婪性

  • 贪婪匹配 能拿少拿少 尽可能的捕获更多
  • 非贪婪匹配 能拿多拿多 尽可能的捕获更少
    贪婪匹配 指的都是 限定符
非贪婪限定符含义
*?表示0~多个
+?表示1~多个
??表示0~1次
{n}?表示指定n次
{n,}?表示n次,n多次
{n,m}?表示n~m次

6.8 创建正则表达式方式的区别

书写正则表达式标识符的区别

  • 字面量: 直接书写正则的后面
  • 内置构造函数: 以第二个参数的形式书写
var reg1 = /qwer/gi;
var reg2 = new RegExp('qwer','ig');
console.log( reg1 )
console.log( reg2 )

拼接变量的能力

  • 字面量: 不能进行变量拼接
  • 内置构造函数: 可以进行变量拼接
    • 内置构造函数的第一个参数就是字符串类型
var s = 'QQ';
var reg2 = new RegExp('^'+s+'$');
console.log( reg2.test('QQ') ) // true

书写基本元字符

  • 字面量: /\s\d\w/
  • 内置构造函数: new RegExp(‘\s\d\w’)
// js中 \ 在字符串里面,具有转义的作用
// \\ 前一个\ 是将后一个\转义为普通的文本字符
var reg1 = /\s\d\w/;
var reg2 = new RegExp('\\s\\d\\w');
console.log( reg1 )
console.log( reg2 )

6.8 能够和正则联用的 字符串方法

1. replace()

  • 语法:
    • 字符串.replace(换下的字符,换上的字符)
    • 字符串.replace(正则表达式,换上的字符)
  • 返回值: 替换好的字符串
    • 当你传递的是正则表达式,并且有g全局标识,会全部替换
var str = 'qwQQeryQQteyrQQytruQQtrurQQewtert'
console.log( str.replace(/QQ/g,'**') )

2. search()

  • 语法:
    • 字符串.search(字符片段)
    • 字符串.search(正则表达式)
  • 返回值:
    • 如果有该字符片段,则字符片段的开始索引位置
    • 如果没有则返回 -1
var str = 'qwQQeryQQteyrQQytruQQtrurQQewtert'
console.log( str.search('QQ') )//2
console.log( str.search(/QQ/) )//2
console.log( str.search(/QQ/g) ) // 2

3. match()

  • 语法:
    • 字符串.match(字符片段)
    • 字符串.match(正则表达式)
  • 返回值:
    • 当你参数传递的是 字符片段 或者 没有g的正则,这返回值和exec一模一样
    • 当你参数传递的是有g的正则,则返回值是一个数组,里面是捕获的所有内容
var str = 'qwQQ66eryQQ77teyrQQ88ytruQQ99trurQQewtert'
console.log(str.match('QQ'))
console.log(str.match(/QQ/))
console.log(str.match(/\d{2}/g)) //['66', '77', '88', '99']

6.9 练习

/* 
  1. 用户名验证
    + 要求: 只能包含数字 字母 下滑线   \w
            不能以下划线开头          ^[0-9a-zA-Z]
            一共只能 6~12             {6,12}
 */
var reg = /^[0-9a-zA-Z]\w{5,11}$/

/* 
  2 大陆手机号验证  +86 13512345612
    + 要求: 
      可以有 +86 ,也可以没有
      手机号 号段  136 135 188 开头 
 */
var reg = /^(\+86 )?(136|135|188)\d{8}$/;

七、ES6

7.1 定义变量

定义变量的关键字

  • let 变量
  • const 常量

let/const 和 var 的区别

  1. 预解析
  • var 会进行预解析
  • let 和const不会,必须要在定义后使用变量
    2. 重复变量名
  • var 可以定义重复变量名, 重复定义没有意义
  • let 和 const 不能定义重复变量名
    3. 块级作用域
  • var 是没有块级作用域,只会被私有作用域限制使用范围(函数内)
  • let/const 是可以被块级作用域显示使用范围
    • 块级作用域: 任何一个可以书写代码的{ } 就是块级作用域
  • 注: let /const 定义变量的 {} 也称之为 暂时性死区

let 和 const的区别

  1. 定义时候的赋值
    - let 定义变量的时候,可以不赋值
    - const 定义的时候,必须赋值
  2. 值修改
    - let 定义的变量值可以修改
    - const 定义的变量不可以修改

循环绑定

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Leon</title>
</head>
<body>
  <button>索引为0</button>  <br>
  <button>索引为1</button>  <br>
  <button>索引为2</button>  <br>
  <button>索引为3</button>  <br>
  <button>索引为4</button>  <br>
  <button>索引为5</button>  <br>
</body>
</html>
<script>
  var btns = document.querySelectorAll('button');
  // 给所有的button按钮点击事件,并要求在事件处理函数中获取点击按钮的索引
  // 通过for循环遍历btns 给每一个按钮点击事件
  for (var i = 0; i < btns.length; i++) {
    // 在for循环 中 i 就是按钮的索引
    // var i 在for循环的{} 中会进行预解析,变量提升到全局中
    btns[i].onclick = function () {
      // 当点击事件触发的时候,for循环已经执行结束,此时i的值为6
      // 所以点击的时候,处理函数执行 去到全局中才找到变量i值为6
      console.log( i ) // 6
    }    
  }
  for (let i = 0; i < btns.length; i++) {
    // 在for循环 中 i 就是按钮的索引
    // let i 在for循环的{} 会让{}变为块级作用域
    btns[i].onclick = function () {      
      console.log( i )
    } 
  }
</script>

7.3 箭头函数

ES6中定义函数的一种方式,只能用来定义匿名函数表达式(匿名函数)

  • var fn = function(){}
  • var obj = {f:function(){}}
  • setInterval(function(){},1000)
  • 事件源.on事件类型 = function(){}

语法:

  • ()=>{}
  • () 书写形参的位置
  • => 箭头函数的标志
  • {} 函数的函数体
let ff = ()=>{
console.log( '888' )
}
ff()

箭头函数特点

1. 可以省略小括号不写
  • 当函数只有一个形参的时候,可以不写小括号
  • 如果没有形参或有两个以上形参,则必须写小括号
2. 可以省略大括号不写
  • 当你的函数内 代码只有一句话(一个表达式)的时候,可以省略大括号哦
  • 并且会自动返回这一句的结果,自动把这句代码的结果当做函数的返回值(return 也不写)
3. 箭头函数中没有arguments
  • 箭头函数内没有arguments 使用
4. 没有 this
  • 官方: 箭头函数内的 this 就是上下文的this
  • 私人: 箭头函数内的 this 就是定义箭头函数外部作用域的 this
  • 箭头函数内的 this和调用函数的方式没有关系,和调用箭头函数的位置有关系

7.4 函数形参默认值

以前 给函数形参默认值的方式 是在函数内通过短路写法 实现
但是 如果实参的值 是布尔值false,或0 或NaN的时候有问题

let fn = function(n1){    
  n1 = n1 || 10;
  console.log(n1);
}
fn(); // 10
fn(200) // 200
fn(0) //10

ES6中新增了 给函数形参默认值的方式
直接在函数定义的小括号中,直接给形参赋值,这个值就是函数内形参使用的默认值

let ff = function (n1=10,n2=20) {
  console.log( n1,n2 )
}
// ff();// 形参使用默认值  n1为10  n为20
// ff(66);// 形参n1使用实参传递的66 形参n2使用默认值  n1为66  n2为20
ff(66,88);// 形参n1使用实参传递的66 形参n2使用实参传递的88  n1为66  n2为88
注意: 箭头函数中如果形参使用了默认值,则不能省略小括号
let f1 = (n=30)=>{console.log( n )}
// f1()
f1(10)

7.5 模板字符串

js中单引号或双引号包裹的就是字符串
1.使用单引号或双引号包裹的字符串 中不能换行

let str = 'hello
world';
  1. 使用单引号或双引号包裹的字符串中不能解析变量
let user = 'leon';
let str = 'hello user';
console.log( str )

ES6中新增了字符串的形式 模板字符串
模板字符串: 使用反引号包裹 ``

  1. 在模板字符串中可以换行书写
  2. 在模板字符串中可以解析变量, 变量在 {变量} 在 变量{} 中可以执行一些简单的代码
var n = 10;
var str = `hello ${n}
  world;
`
console.log( str )
document.body.innerHTML = str;

7.6 对象简写

let username = '张三'
let userage = 18;
// 对象
let obj = {
  // username: username,
  // 如果对象的属性名 和属性值的变量名一样,则可以简写
  // 将属性名和冒号省略
  username,
  userage,
  // 在对象中如果属性对应的值只一个匿名函数,也可以简写
  // 将冒号也function省略
  // fn:function(){console.log( 666 )}
  fn(){console.log( 666 )}
}
console.log( obj )
obj.fn()

7.7 解构赋值

  • 作用: 快熟的获取数据解构中某一个值
  • 两种:
    • 解构数组
    • 解构对象

解构数组

  • 语法: 解构 = 数组
  • 数组解构使用 []
    • [] 在赋值等号的 左边的叫做 解构
    • [] 在赋值等号的 右边的叫做 数组
  • 在 [] 内按照索引书写变量即可
let [n1, n2, n3, n4, n5] = ['a', 'b', 'c', 'd']
console.log(n1, n2, n3, n4)
console.log( n5 )// undefined

// 使用解构数组 来交换变量的值
let n1 = 100;
let n2 = 200;
console.log('交换前:', n1, n2);
// 等号右边是数组,等号左边是解构
[n2, n1] = [n1, n2]
console.log('交换后:', n1, n2)
  • 多维数组的解构
    • 把原数组赋值一份一模一样的放在解构位置
    • 把数据换为变量
// 解构多维数组
const arr = [100, 200, [300, 400, [500]]]
let [n1, n2, [n3, n4, [n5]]] = arr;  
console.log(n1, n2, n3, n4, n5)

解构对象

  • 语法: 解构 = 对象
    • 解构对象使用 {}
    • {} 书写在 赋值等号的左边 表示 解构
    • {} 书写在 赋值等号的右边 表示 对象
  • 在左边的 {} 中 你需要书写解构的key(属性名)
  • 对象解构可以起别名
    • 在解构中,除了书写可以获取的key,可以在 key:别名
// 解构对象
let obj = {username:'leon',age:18};

let { username, age } = obj// 等价于 let username = obj.username = obj.age
console.log( username, age ) //  leon 18

let { age:userage} = obj // 等价于 let userage = obj.age
console.log( userage )  // 18
  • 对象的多维解构
    • 解构多维对象数据
    • 直接把对象数据类型复制一遍,放在解构位置
      • 如果你需要用别名,那么把值换成别名
      • 如果不需要别名,那么直接去掉值
let o = {
  name:'xiaobin',
  age:20,
  info:{
    height:180,
    weight:180,
    addr:{
      city:'云南'
    }
  }
}
let {name:username,age,info:{height,weight,addr:{city}}} = o;
console.log(username,age,height,weight,city );// xiaobin 20 180 180 云南

7.8点点点运算符

展开运算符

  • 可以展开数组和对象
  • 注意:
    • 展开的对象需要放在 {}
    • 展开的数组需要放在 [] 或方法函数实参位置
// 展开数组
let arr = [1,2,3,4,5,'a','b','c'];
console.log(...arr ) // 1 2 3 4 5 "a" "b" "c"

// 合并数组
let arr1 = [1, 2, 3, 4];
let arr2 = [5, 6, 7, 8];
var newArr = [...arr1,...arr2]
console.log( newArr )// [1, 2, 3, 4, 5, 6, 7, 8]

// 合并对象使用
let o = {name:'leon',age:18};
let obj = {
  gender:'男',
  ...o
}
console.log( obj )

合并运算符

  • … 既可以展开数组, 也可以合并多个数据,得到数组
  • 当 …用于函数形参位置的时候,就是一个合并运算符,可以将多个实参合并起来得到一个数组
let fn = function (...arr) {
  console.log( arr ) // [1, 2, 3, 4, 5, 6]
}
fn(1, 2, 3, 4, 5, 6);

// 合并运算符,可以适当的弥补箭头函数内没有argumenst的缺陷
let ff = (...ars) => {
  console.log( ars ) // [1, 2, 32, 34, 4]
}
ff(1, 2, 32, 34, 4)
let ff = (n1, n2, n3, ...ars) => {
  console.log( n1,n2,n3 ) // 1 2 3
  console.log(ars) //  [4, 5, 6, 7]
}
ff(1, 2, 3, 4, 5, 6, 7)

7.9 模块化开发

  • 2015 年发布。ES6 语法自带了一个模块化标准,默认是严格模式
  • 2016 年开始,Vue 出现了,搭建脚手架,内置 ES6 模块化标准
  • 2018 年中,Chrome 率先原生支持 ES6 模块化
  • 语法:变成了 JS 的语法,和关键字,不需要任何第三方文件的引入
  • 特点:页面必须在浏览器打开
    • live server 插件
    • 如果你想使用模块化语法,script 标签要加一个属性 type=‘module’

模块化语法规则

  • 当你开始使用模块化语法的时候
  • 每一个 js文件都会变成独立的js文件,相互之间没有任何关系
    • 文件和文件之间不共通
    • 我们把每一个独立文件 叫做文件作用域(模块作用域)
  • 当你需要互通数据的时候
    • 需要使用导入导出语法

导出语法:

  • 在当前自己文件内,把某一些数据向外暴露
    • 语法1: export default 导出的内容
    • 语法2: export 数据 export let num = 200
const num = 666;
const foo = 'bar';
// 导出语法1
export default { num, foo };
// 导出语法2
export const msg = '你好';
export const code = 1;
/* 
  相当于向外暴露了
  {
    default:{num,foo},
    msg,
    code
  }
*/

导入语法:

  • 在当前自己文件内,把某一些文件导入到自己内部(目的: 为了使用改文件暴露的数据,并执行改文件)
    • 语法1: import 变量名 form 文件名
    • 语法2: import { } from 文件名
    • 语法3: import('./a.js').then(function (res) { console.log(res) }
      • import 文件名
      • 此语法导入不接受数据
// 语法1: 
import moduleA from './modules/a.js';
console.log( moduleA )

// 语法2:
import {msg,code} from './modules/a.js';
console.log( msg )
console.log( code )

// 语法3 
import ff from'./modules/b.js';
ff();
  • 注意:
    • 导入语法1 只能导入 导出语法1 的内容
    • 导入语法2 只能导入 导出语法2 的内容

7.10 this 指向 — this是js中的关键字

  • 官方: 指当前代码执行的上下文环境(context)
  • 个人解释:
    • 就是在一个作用域中(全局/局部)的关键字
    • 全局作用域 this 在 全局作用域中,this就是 window(就是指前端)
  • 函数使用:不管函数怎么定义,不管函数在哪定义,只看函数的调用(箭头函数除外)

函数作用域 this (背诵)

1. 普通调用
  • 函数名()
  • 函数内的this 指向window
2. 对象调用
  • 对象.函数名()
  • 函数内的 this 指向 调用函数的对象
3. 事件处理函数
  • 事件源.on事件类型 = 函数
  • 事件源.addEventListener(‘事件类型’,函数)
  • 事件触发的时候,执行的函数中 this指向事件源(事件绑定在哪个身上 )
4. 定时器调用
  • setTimeout(函数,毫秒)
  • setInterval(函数,毫秒)
  • 函数中的 this 指向 window
5. 自调用函数
  • ;(函数)()
  • 函数中的this指向 window
  • 调用函数的语法
    • ;(函数)()
    • !(函数)()
    • ~(函数)()
    • 特点: 函数定义好之后立即执行
;(function(){
  console.log( this )
  console.log( this===window ) // true
})()
6. 箭头函数
  • 唯一一个不看调用方式,只看函数定义位置决定this指向
  • 箭头函数的this 指向定义(书写)箭头函数 所在作用域中this的指向
const obj = {
  ff: function () {
    console.log(this)
  },
  fn: () => { // 箭头函数定义在全局中
    console.log(this)
    console.log(this === window) // true      
  }
}
obj.fn()

强行改变this的指向

  • this 的指向是由函数的调用方式决定的
  • 在本次调用函数的时候,改变函数中this 的指向
  • 箭头函数中不能通过call,apply,bind方法改变this的指向

函数的方法(背诵)

1. call
  • 语法: 直接在函数名后面连接使用
    • 函数名.call()
    • 对象.函数名.call()
  • 参数:
    • 第一个参数: 此次执行函数内 this的指向
    • 第二个参数开始: 依次给该函数的实参
  • 特点: 立即调用函数
2. apply
  • 语法: 直接在函数名后面连接使用
    • 函数名.apply()
    • 对象.函数名.apply()
  • 参数:
    • 第一个参数: 此次执行函数内 this的指向
    • 第二个参数: 是一个数组或伪数组集合,集合中的每一个数据按顺序作为此次函数执行的实参
  • 特点: 立即调用函数
3. bind
  • 语法: 直接在函数名后面连接使用
    • 函数名.bind()
    • 对象.函数名.bind()
  • 参数:
    • 第一个参数: 新函数内 this的指向
    • 第二个参数开始: 依次给该函数的实参
  • 特点: 不会立即调用函数,而是返回一个新的函数,新函数和原函数代码一模一样,只不过新函数中的this指向 第一个参数(此函数中的this锁死了)
  • 一般用在定时器和事件处理函数上
// 通调用
console.log( '普通调用' )
fn(10,20);

console.log('通过函数的call方法调用')
fn.call(obj,100, 200);

console.log('通过函数的apply方法调用')
fn.apply(obj,[100, 200]);

console.log('通过函数的bind方法调用返回一个新函数')
let ff = fn.bind(obj,20,30);
// 此时变量ff中得到一个新函数的地址,且新函数中的this锁死了 执行obj
ff();

7.11 万能检测数据类型

语法: Object.prototype.toString.call(你要检测的数据)
返回值: ‘[object 数据类型]’

console.group('Object.prototype.toString.call')
console.log(Object.prototype.toString.call(1)) // [object Number]
console.log(Object.prototype.toString.call('1')) // [object String]
console.log(Object.prototype.toString.call(false)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(function () {})) // [object Function]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(new RegExp())) // [object RegExp]
console.log(Object.prototype.toString.call(new Date())) // [object Date]

json格式字符串

约定: 按照字符串内存储的内容对字符串进行一些类的分类划分

  • 普通字符串: ‘werewte’ “werwetre” etreytess
  • 数字字符串: ‘124235436457243235’
  • html格式字符串: <div>666</div>
  • 查询字符串: ‘key1=value1&key2=value2’
json格式字符串:
  • 字符串内写的是对象: ‘{“name”:“leon”,“age”:18}’
  • 字符串内写的是数组: ‘[{“name”:“leon”,“age”:18},{“name”:“jessica”",“age”:19}]’
格式要求:
  1. json 格式字符串内: 键名 的位置必须是使用 双引号 包裹
  2. json 格式字符串内: 将值的位置,如果是数值或布尔,不需要包裹
  3. json 格式字符串内: 必须是对象或数组(数组内一般是一个一个对象)的形式
  4. json 格式字符串内: 不会包含函数数据类型
  5. json 格式字符串内: 最后一条数据不要用逗号
  6. json 格式字符串内: 除了引号以外的内容,只能用冒号 逗号 大括号 中括号 书写的就是 js对象或数组数据
注意
  • js 给我们提供了json格式 转换的方法
    • json格式数据和对象或数组的转换
  • json格式 的数据一般都是 我们前端请求后端接口,后端返回的数据(字符串)
    • 目前前后端交互的数据中json格式的数据占大部分
  • json 格式 是一种独立的格式,我们也已直接书写 .json文件
// 1. js 的数据类型 转为 json格式字符串
// 语法: JSON.stringify(要转的js格式数据)
// 返回值: json格式字符串
let obj = {name:'zs',age:18,gender:true};
let resStr = JSON.stringify(obj);
// console.log( resStr );// '{"name":"zs","age":18,"gender":true}'
let arr = [{name:'zs',age:18,gender:true},{name:'ls',age:19,gender:false}];
console.log( JSON.stringify(arr) )
// '[{"name":"zs","age":18,"gender":true},{"name":"ls","age":19,"gender":false}]'

// 2. json格式字符串 转为js数据类型
// 语法: JSON.parse(json格式字符串)
// 返回值: 数组或对象
// 注意: 参数只能是一个合法的 json格式字符串 否则报错
let str1 = '{"name":"zs","age":18,"gender":true}';
let str2 = '[{"name":"zs","age":18,"gender":true},{"name":"ls","age":19,"gender":false}]';
console.log( JSON.parse(str1) )
console.log( JSON.parse(str2) )

7.12 运动函数

/**
 * @description: 简单的运动函数
 * @param {Object} ele     运动的元素
 * @param {Object} target 多个运动的样式和目标值组成的对象
 * @param {Function} fn  运动结束后执行的回调函数
 */
function move(ele, target, fn = () => { }) {
    let count = 0
    // target 有多少开启几个定时器
    for (let key in target) {
        count++
        if (key === 'opacity') target[key] *= 100
        let timer = setInterval(function () {
            let current
            if (key === 'opacity') {
                current = getStyle(ele, 'opacity') * 100
            } else {
                current = parseInt(getStyle(ele, key))
            }
            // 计算本次移动的距离
            let distance = (target[key] - current) / 50
            // 取整
            distance = distance > 0 ? Math.ceil(distance) : Math.floor(distance)
            if (target[key] === current) {
                clearInterval(timer)
                count--
                // 当 count === 0 时表示结束
                if (!count) { fn() }
            } else {
                if (key === 'opacity') {
                    ele.style[key] = (current + distance) / 100
                } else {
                    ele.style[key] = current + distance + 'px'
                }
            }
        }, 30)
    }
}
// 获取元素的非行内样式 样式元素.样式名
function getStyle(ele, style) {
    if (getComputedStyle) {
        return window.getComputedStyle(ele)[style]
    } else {
        return window.currentStyle[style]
    }
}

7.13 轮播图

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>love life</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        ul,
        ol {
            list-style: none;
        }

        .banner {
            width: 600px;
            height: 400px;
            border: 10px solid #333;
            position: relative;
            overflow: hidden;
            margin: 0 auto;
        }

        .banner ul {
            width: 500%;
            overflow: hidden;
            height: 100%;
            position: absolute;
            left: 0;
            top: 0;
        }

        .banner ul li {
            width: 600px;
            height: 100%;
            float: left;
            font-size: 100px;
            color: #fff;
            text-align: center;
            line-height: 400px;
        }

        .banner ol {
            display: flex;
            justify-content: space-around;
            align-items: center;
            height: 30px;
            background-color: rgba(0, 0, 0, .5);
            position: absolute;
            left: 50%;
            bottom: 30px;
            transform: translateX(-50%);
            border-radius: 15px;
        }

        .banner ol li {
            width: 20px;
            height: 20px;
            background-color: #fff;
            border-radius: 50%;
        }

        .banner ol li.active {
            background-color: red;
        }

        .banner div {
            display: none;
            width: 100%;
            height: 60px;
            position: absolute;
            left: 0;
            top: 50%;
            transform: translateY(-50%);
        }

        .banner:hover.banner div {
            display: block;
        }

        .banner div p {
            float: left;
            width: 40px;
            height: 60px;
            font-size: 30px;
            color: #fff;
            text-align: center;
            line-height: 60px;
            background-color: rgba(0, 0, 0, .3);
            border-radius: 0 30px 30px 0;
            cursor: pointer;
        }

        .banner div p:last-child {
            float: right;
            border-radius: 30px 0px 0px 30px;
        }

        .banner div p:hover {
            background-color: rgba(0, 0, 0, .5);
        }
    </style>
</head>

<body>
    <div class="banner">
        <ul class="img_box">
            <li style="background-color: pink;">1</li>
            <li style="background-color: red;">2</li>
            <li style="background-color: blue;">3</li>
            <li style="background-color: yellow;">4</li>
            <li style="background-color: green;">5</li>
        </ul>
        <!-- 焦点盒子 -->
        <ol>
            <!-- <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li> -->
        </ol>
        <!-- 左右切换盒子 -->
        <div>
            <p class="left">&lt;</p>
            <p class="right">&gt;</p>
        </div>
    </div>
    <script src="./运动函数.js"></script>
    <script>
        let ul = document.querySelector('.banner ul')
        let ol = document.querySelector('.banner ol')
        let banner = document.querySelector('.banner')

        // 准备变量
        // 获取可视区域的宽度
        let bannerWidth = banner.clientWidth
        // 定时器
        let timer = 0
        // 记录第几张
        let index = 1
        // 开关
        let flag = true

        // 生成焦点
        setPoint()
        function setPoint() {
            // 拿到ul下的元素
            let num = ul.children.length
            // 创建文档碎片 就是一个袋子
            let frg = document.createDocumentFragment()
            for (let i = 0; i < num; i++) {
                // 创建元素节点
                let li = document.createElement('li')
                // 操作自定义属性(非H5)
                li.setAttribute('type', 'point')
                li.setAttribute('point_index', i + 1)
                if (i === 0) li.classList.add('active')
                // 插入节点到文档碎片
                frg.appendChild(li)
            }
            // 把文档碎片里面的内容添加到 ol
            ol.appendChild(frg)
            ol.style.width = 30 * num + 'px'
        }

        // 赋值元素
        copyEle()
        function copyEle() {
            // 克隆第一个节点
            let first = ul.firstElementChild.cloneNode(true)
            // 克隆最后一个节点
            let last = ul.lastElementChild.cloneNode(true)
            // 第一个克隆节点插入到最后面
            ul.appendChild(first)
            // 最后一个克隆节点插入到最前面
            ul.insertBefore(last, ul.firstChild)
            // 获取ul下的元素个数 在设置宽度
            ul.style.width = ul.children.length * 100 + '%'
            ul.style.left = -bannerWidth + 'px'
        }

        // 自动轮播
        autoPlay()
        function autoPlay() {
            timer = setInterval(function () {
                index++
                move(ul, { left: -index * bannerWidth }, moveEnd)
            }, 2000)
        }

        // 运动结束
        function moveEnd() {
            // 判断到最后一张时瞬间定位回第一张
            if (index === ul.children.length - 1) {
                index = 1
                // 瞬间定位就是直接赋值
                ul.style.left = -index * bannerWidth + 'px'
            }
            if (index === 0) {
                index = ul.children.length - 2
                ul.style.left = -index * bannerWidth + 'px'
            }

            // 焦点配套
            for (let i = 0; i < ol.children.length; i++) {
                ol.children[i].className = ''
            }
            ol.children[index - 1].className = 'active'
            // 解决鬼畜 开启开关
            flag = true
        }

        // 移入移除
        overOut()
        function overOut() {
            // 移入停止定时器,不在轮播
            banner.addEventListener('mouseover', () => clearInterval(timer))
            // 移除开启定时器,开启轮播
            banner.addEventListener('mouseout', () => autoPlay())
        }

        // 点击事件
        bindEvent()
        function bindEvent() {
            banner.addEventListener('click', function (e) {
                // 获取对象处理函数
                e = e || window.event
                // 事件传播机制 目标元素 target就是点击的属性
                let target = e.target || e.srcElement
                if (target.className === 'right') {
                    // 解决鬼畜 判断开关是否关闭 关闭则不执行
                    if (flag === false) return
                    index++
                    move(ul, { left: -index * bannerWidth }, moveEnd)
                    // 解决鬼畜 关闭开关
                    flag = false
                }
                if (target.className === 'left') {
                    // 解决鬼畜 判断开关是否关闭 关闭则不执行
                    if (flag === false) return
                    index--
                    move(ul, { left: -index * bannerWidth }, moveEnd)
                    // 解决鬼畜 关闭开关
                    flag = false
                }
                // 判断点击的是焦点
                if (target.getAttribute('type') === 'point') {
                    // 解决鬼畜 判断开关是否关闭 关闭则不执行
                    if (flag === false) return
                    index = target.getAttribute('point_index') - 0
                    move(ul, { left: -index * bannerWidth }, moveEnd)
                    // 解决鬼畜 关闭开关
                    flag = false
                }
            })
        }

        // 解决鬼畜 切换标签时关闭定时器
        chuangeTab()
        function chuangeTab() {
            // 页面离开
            document.addEventListener('visibilitychange', () => {
                // visibilityState 里面有两个值,离开hidden,回来visible
                if (document.visibilityState === "hidden") {
                    clearInterval(timer)
                } else if (document.visibilityState === 'visible') {
                    autoPlay()
                }
            })
        }
    </script>
</body>

</html>

7.14 渐隐渐显轮播图

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>love life</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        ul,
        ol {
            list-style: none;
        }

        .banner {
            width: 600px;
            height: 400px;
            border: 10px solid #333;
            margin: auto;
            /* overflow: hidden; */
            position: relative;
        }

        .banner ul {
            width: 100%;
            height: 100%;
            position: absolute;
        }

        .banner ul li {
            width: 100%;
            height: 100%;
            float: left;
            font-size: 100px;
            color: #fff;
            text-align: center;
            line-height: 400px;
            transition: opacity .5s linear;
            position: absolute;
            left: 0;
            top: 0;
            opacity: 0;
        }

        .banner ul li.active {
            opacity: 1;
        }

        .banner ol {
            display: flex;
            justify-content: space-evenly;
            align-items: center;
            height: 30px;
            background-color: rgba(0, 0, 0, .5);
            position: absolute;
            left: 50%;
            bottom: 30px;
            transform: translateX(-50%);
            border-radius: 15px;
        }

        .banner ol li {
            width: 20px;
            height: 20px;
            background-color: #fff;
            border-radius: 50%;
        }

        .banner ol li.active {
            background-color: red;
        }

        .banner div {
            display: none;
            width: 100%;
            height: 60px;
            position: absolute;
            left: 0;
            top: 50%;
            transform: translateY(-50%);
        }

        .banner:hover.banner div {
            display: block;
        }

        .banner div p {
            float: left;
            width: 40px;
            height: 60px;
            font-size: 30px;
            color: #fff;
            text-align: center;
            line-height: 60px;
            background-color: rgba(0, 0, 0, .3);
            border-radius: 0 30px 30px 0;
            cursor: pointer;
        }

        .banner div p:last-child {
            float: right;
            border-radius: 30px 0px 0px 30px;
        }

        .banner div p:hover {
            background-color: rgba(0, 0, 0, .5);
        }
    </style>
</head>

<body>
    <div class="banner">
        <ul class="img_box">
            <li class="active" style="background-color: pink;">1</li>
            <li style="background-color: red;">2</li>
            <li style="background-color: blue;">3</li>
            <li style="background-color: orangered;">4</li>
            <li style="background-color: green;">5</li>
        </ul>
        <!-- 焦点盒子 -->
        <ol></ol>
        <!-- 左右切换盒子 -->
        <div>
            <p class="left">&lt;</p>
            <p class="right">&gt;</p>
        </div>
    </div>
    <script src="./运动函数.js"></script>
    <script>
        let banner_ul = document.querySelector('.banner>ul')
        let banner_ol = document.querySelector('.banner>ol')
        let banner = document.querySelector('.banner')
        let banner_div = document.querySelector('.banner_div')

        let index = 0
        let timer = 0

        setPoint()
        function setPoint() {
            let num = banner_ul.children.length
            // 文档碎片
            let frg = document.createDocumentFragment()
            for (let i = 0; i < num; i++) {
                let li = document.createElement('li')
                li.classList.add('point')
                li.dataset.point = i
                if (i === 0) {
                    li.classList.add('active')
                }
                frg.appendChild(li)
                banner_ol.appendChild(frg)
                banner_ol.style.width = num * 35 + 'px'
            }
        }

        function changOne(type) {
            banner_ul.children[index].classList.remove('active')
            banner_ol.children[index].classList.remove('active')
            if (type === true) {
                index++
            } else if (type === false) {
                index--
            } else {
                index = type
            }
            if (index >= banner_ul.children.length) {
                index = 0
            } else if (index < 0) {
                index = banner_ul.children.length - 1
            }
            banner_ul.children[index].classList.add('active')
            banner_ol.children[index].classList.add('active')
        }

        autoPlay()
        function autoPlay() {
            timer = setInterval(function () {
                changOne(true)
            }, 1000)
        }

        overOut()
        function overOut() {
            banner.addEventListener('mouseover', () => clearInterval(timer))
            banner.addEventListener('mouseout', () => autoPlay())
        }

        bindEvent()
        function bindEvent() {
            banner.addEventListener('click', function (e) {
                e = e || window.event
                let target = e.target || e.srcElement
                if (target.className === 'left') {
                    changOne(false)
                }
                if (target.className === 'right') {
                    changOne(true)
                }
                if (target.className === 'point') {
                    let pointIndex = target.dataset.point - 0
                    changOne(pointIndex)
                }
            })
        }

        changeTab()
        function changeTab() {
            document.addEventListener('visibilitychange', () => {
                // visibilityState 里面有两个值,离开hidden,回来visible
                if (document.visibilityState === "hidden") {
                    clearInterval(timer)
                } else if (document.visibilityState === 'visible') {
                    autoPlay()
                }
            })
        }
    </script>
</body>

</html>

八、javascript高级

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值