一、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代码;
或者在标签里书写 onlick 然后书写 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. 堆内存

image

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.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;
}
最后修改:2022 年 07 月 01 日
谢谢老板打赏