javascript基础

一、javascript简介

轻量级 弱类型 脚本语言
js就是通过固定的语法来操作 浏览器和 页面结构 来实现网页中的各种效果

1.1 javascript组成部分

  1. ECMAScript:定义了 javascript 的语法规范

  2. BOM:(browser object model)浏览器对象模型
    浏览器有一套成熟的 API 方法,通过 BOM 来操作浏览

  3. DOM(document objectmodel)文档对象模型
    简介有一套成熟的可以操作页面元素的 API 方法,通过 DOM 可以操作页面元素

1.2 javascript书写位置

1.2.1 行内式 (一般不使用)

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

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

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

1.2.2 内嵌式

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

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

1.2.3 外链式

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

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

二、javascript基础语法

2.1 JS 注释

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

2.1.1 单行注释

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

2.1.2多行注释

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

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

2.2.1 弹窗输出

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

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

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

2.2.2 控制台输出

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

2.2.3 页面输出

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

2.3 变量

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

2.2.1 如何定义变量

定义变量:通过关键字 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 )    

2.2.2 变量名命名规则和规范

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

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

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

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

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

2.4 数据类型

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

2.4.1 基本数据类型

(1) number 数值类型

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

(2) string 字符串类型

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

(3) boolean 布尔类型

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

(4) undefined 未定义,未赋值

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

(5) null 空

取值: null

2.4.2 判断数据类型

(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

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

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

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

(1) String

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

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

(2) toString()

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

(3) 加号

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

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

(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 运算符

2.5.1 算术运算符

(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

2.5.2 赋值运算符

(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

2.5.2 比较运算符

(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

2.5.3 逻辑运算符

(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

2.5.4 自操作运算符

(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 分支结构

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

2.6.1 条件分支

(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(变量){
        case 值1:
          // 当变量 和 值1 全等的情况执行代码;
          break;
        case 值2:
          // 当变量 和 值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)
}

2.6.2 循环分支

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

2.6.3 循环控制关键字

(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循环都可以使用

2.6.3 循环嵌套

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

(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 函数

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

2.7.1 函数定义及调用

函数定义
使用关键字 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. 函数调用的时候才会解析函数中的变量

2.7.2 函数的参数

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

2.7.3 函数的返回值

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

2.7.4 函数的预解析

预解析: 在浏览器解析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. 分支结构中的条件和大括号也不会影响预解析(变量提升)

2.7.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代码在执行的时候,变量的访问和赋值是有序的

2.7.6 事件

事件就是用户在页面的动作行为, 比如:鼠标点击,鼠标移动,键盘按下等等

在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的点击事件,并执行对应的事件处理函数
*/

2.7.7 对象

对象: 是 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的复杂数据类型
数组就是一组有序的数据集合
因为数组中的每一个成员都有一个索引,这个索引就是成员在数组中的排序

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

3.2.1 字面量创建数组

语法: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, {…}]

3.2.2 内置构造函数创建数组

  • 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']

3.2.3 数组的长度和索引

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

3.2.4 数组常用方法

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

数组方法 作用
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]

3.2.5 遍历对象

因为数组是有序(索引)的数据集合
所以很容易使用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])
}

3.2.6 二维数组

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

// 二维数组
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 数据类型

3.3.1 数据类型的存储区别

存储内存分为:

  1. 栈内存
  2. 堆内存

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

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

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

  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

3.3.3 数据类型之间变量的比较

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

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

3.3.4 数组排序

(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 字符串

3.4.1 严格模式

因为一开始的时候,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 )

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

3.4.3 字符串常用方法

(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('上传中');
  }
}

3.4.4 进制转换

(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

3.5.1 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;
}

3.5.2 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对象中的属性和方法

4.1.1 浏览器事件

(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( '窗口大小变化了' );
}

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

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

4.2.1 定时器开启

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

4.2.2 定时器返回值

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

4.2.3 关闭定时器

  • 关闭销毁定时器的是,不区分定时器种类,只要给出的定时器标识数是对的,就可以销毁
  • 语法:
    • 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. 操作(操作文本,操作属性,操作样式,操作增删改查)

4.3.1 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遍历数组方法

4.3.2 元素属性

标签属性分类有三种

(1) 原生属性

  • 在 W3C 规范中有的属性名
  • 比如: id class style type src href...

(2) 自定义属性

  • 在 W3C 规范中没有的属性名,是我们自己在书写在标签上的

(3) H5 自定义属性

  • 目的: 就是为了区分自定义属性和原生属性写在标签上的形式
  • 要求: 书写 H5 自定义属性的时候,都要 data- 开头
  • 比如: data-index = '888'
    • data- 表示这个是 H5 自定义属性
    • index 表示属性名
    • '888' 表示属性值

4.3.3 操作元素属性

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

4.3.4 操作元素的样式

在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';

4.3.5 操作元素的类名

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

4.3.6 操作元素的内容

(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

4.3.7 获取元素的尺寸

有两套语法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)

4.3.8 获取元素的偏移量

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

4.3.9 获取可视窗口尺寸

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)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容