js中的注释

  • 单行注释 //被注释的内容

  • 多行注释 /* 被注释的内容 多行 */ 快捷键 alt+shift+A

alt+shift+鼠标选择多个

control+shift+左右健 按单词的边界选中

js输入输出语句

  • alert(123); 在浏览器中弹出提示框 只要不是纯数字都用引号括起来,单引号双引号都可以

  • prompt(‘提示信息’); 浏览器弹出输入框,用户可以输入,返回值就是用户输入的内容,且不论用户输入的是什么内容,返回结果都是一个字符串,用户点击取消返回值为null

  • confirm(‘需要用户确认的信息’) 弹出一个确认消息对话框,有确认和取消两个按钮,用户点击后会分别返回布尔值true和false

    alert,comfirm ,prompt弹出框会阻断程序的继续执行,因为js是单线程的,弹出框弹出后,如果用户没有点击按钮表示弹出层没有结束,直到用户操作之后,才会继续向下执行代码

  • console.log(在浏览器控制台打印的输出信息);

  • document.write() 可以向页面的body中字写入一个内容

    直接把内容书写在页面上,所以可以解析html标签

js的书写规则

  • js中是严格区分大小写的 在html中没有严格区分 在html中` <a>a标签</A>可以正常显示,单是js中不可以

  • js中的每一条语句后面以;结尾

    如果不写分号,浏览器会自动添加,但是会消耗一些系统化资源,有时候也会加错分号,所以我们必须写分号

  • js中会忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化

字面量

作用:字面量都是一些不可改变的值,是用于表达一个固定值的表示法,又叫做常量

通俗的讲字面量就是所见即所得,当我们看到字面的时候就会知道他是什么类型的数据,值是多少

比如说:数字字面量:1,2,3

字符串字面量:”你好吗”

布尔值字面量:true false

字面量都是可以直接使用的,但是我们一半都不会直接使用字面量

语句和表达式

js程序执行单位为行,也就是一行一行的执行,一般情况下,每一行就是一个语句

语句是为了完成某种任务而进行的操作,比如var a=1+3;就是一行赋值语句,这条语句先用var命令声明了变量a,然后再将1+3的运算结果赋值给变量a

1+3叫做表达式,是一个为了得到返回值的计算式,语句和表达式的区别语句是为了执行某种操作,一般情况下不需要返回值;表达式式为了得到返回值,一定会返回一个值。凡是js中预期为值的地方,都可以使用表达式。比如,赋值语句的等号右边预期是一个值,因此可以放置各种表达式;

语句以分号结尾,一个分号就是一个语句结束,多个语句可以写在一行如var a=1+3;var b='abc';

分号前面可以没有任何内容,js引擎将其视为空语句;;;,这表示3个空语句,表达式不需要分号结尾,一但在表达式的后面添加分号,则js引擎就将表达式视为语句,这样就产生了一些没有意义的语句。如1=3;’abc’;这两行语句只是单纯的产生一个值,并没有任何实际的意义

表达式 返回值

  • 表达式是由数字,变量,运算符所组成的式子

  • 表达式最终都会有一个结果,这个结果就是返回值

  • 在js中,我们是将左边表达式计算出的结果返回给右边

    var userName=1+1;

变量

变量就是在内存中开辟一块存储空间用来存放数据,变量在程序运行中保存一个中间值使用

通俗的将变量就像个存储东西盒子,变量可以用来存储字面量,而且变量的值是可以直接改变的

变量可以更加方便我们使用字面量,所以在开发中我们都是通过变量去保存一个字面量,而很少使用字面量

(可以通过变量对字面量进行描述,即给变量起名时就可以知道是干啥用的)

  • 声明变量 使用关键字 var 变量名;

    //声明单个变量
    var box;  声明了一个名为box的变量本质是在内存中为box变量开辟一块存储空间
    //声明多个变量只使用一个var关键字,多个变量之间使用英文逗号,分隔
    var box1,box2,box3;
  • 关键字:系统规定有特殊意义的字,我们不能再使用关键字和保留字声明变量

    保留字:系统现在没有用上但是留作日后再用的字,等同于关键字

    标识符:我们为变量,函数,属性,参数等所起的名就是标识符(标识符不能使用关键字或保留字命名,表示要有意义)

  • 变量的命名规则,必须遵守,不遵守会报错

    • 由字母(a-zA-Z),数字(0-9),下划线,美元符号$符号组成

    • 不能以数字开头

    • 严格区分大小写 即 box 和 Box是俩个不同的变量

    • 不能使用关键字和保留字 如var

  • 变量命名规范,建议遵循,因为大家都这样nj

    • 由多个单词组成的变量名采用驼峰命名法,即第一个单词的首字母小写后面单词的首字母要大写myNameIsApple

    • 不要使用中文命名

    • 见名知意

    变量名虽然是任意起的,但是要尽量通俗易懂有意义,即 userName passWord

    可以通过变量对字面量进行描述,即给变量起名时就可以知道是干啥用的

  • 尽量不要使用name命名,name虽然不是关键字但是在许多系统中都有特殊的作用(这是扩展)

    name在没有声明且没赋值的情况下使用不报错,返回结果为空

    • window是js的全局对象,有一个天生自带的name属性,作用是:在iframe标签和name属性合作进行跨域的,特点是:被固定为字符串类型了,不管 你给name属性赋值为什么数据类型,他都会自动转换成字符串

  • 变量赋值 使用赋值符号= box=123; 表示将=右边的值放入到box变量中

  • 变量的初始化 就是声明一个变量并赋值,声明和赋值同时进行

    var box=123;  声明变量box,并给box变量赋值为123;
    var box1=123, 同时      mkk
    同时定义多个变量,有的赋值,有的不赋值
    var n1=100,n2,n3=300  //逗号表示这一句话没有结束,没有符号的换行或者分号表示这句话结束了
    等价于
    var n1=100,
        n2,
        n3=300
  • 我们通过变量名获取变量

  • 变量声明与赋值的特殊情况

    • 只声明不赋值,使用变量,返回结果是undefined

    • 不声明直接赋值 使用变量可以正常使用,不会报错,弱类型,不推荐这样使用,建议先定义后使用

    • 不声明不赋值直接使用变量的情况下,会报错

  • 更新变量

    新赋的值会覆盖原来的值,原来的值就没办法 获取了

    box=456; box变量的最终结果是456,原来的123已经被覆盖掉了

    作业:两个变量交换值

  • 当使用输出语法的时候,只要不用引号,就表示输出一个变量

    console.log(box1)

数据类型

数字 文字 (汉字,英文,日文)

ji中数据也是有分类,不同的数据类型所占的内存空间大小不同,且可执行的操作不同,比如数字类型的数据可以进行比较运算,字符串类型的数字可以进行字符串的链接

  • js的是弱类型的语言,我们在声明变量时并没有指定变量的类型,变量的类型由所赋给他的值的类型决定

  • 基本数据类型

    • 数字类型 Number 包含整形值和浮点型值

    • 字符串类型 String

    • 未定义类型 Undefined undefined

    • 空类型 Null null

    • 布尔类型 Boolean true false

  • 复杂数据类型(地址数据类型/引用数据类型)

    Object

    Function

    Array

typeof 检测数据类型

使用typeof 运算符来判断给定变量或数据的数据类型,返回结果是操作数的数据类型名称,是一个字符串类型的结果

  • 有两种格式

    • typeOf 要检测的数据 (空格) 只能检测紧跟着的一个变量

    • typeof(要检测的数据) 先运算小括号里的结果,然后再使用typeof 去检测结果的数据类型

需求:想要计算100+200 的结果的数据类型
var a=100,b=200;
var res=typeof a+b; //先匀速typeof a ,然后用返回值去和b进行加法运算
console.log(res)
var res2=typeof(a+b); //先运算a+b的值,然后再用typeog去检测300的数据类型
  • typeof的返回值

    • 返回值是一个字符串

    • 当两个及以上typeof 连用的时候,一定得到string

    var res3=typeof typeof b;
    //从右向左进行运算 ,先检测b的数据类型,再检测右侧检测后的返回值的数据类型
    • 只能准确的检测基本数据类型

      • 数值 Numbe console.log(typeof 122)

      • 字符串 String

      • 布尔值 Boolean

      • undefined undefined

      • null Objet *

console.log(typeof 12);  //"number"

数据类型总结:

  • 简单数据类型 null ,

var a=null;
typeOf(a)   //返回结果是object

如果一个变量打算以后存储为对象,暂时没有想好放啥,就可以先将变量赋值为null

  • 简单数据类型即基本数据类型或者值类型,复杂类型又叫做引用类型

    • 值类型:基本数据类型在存储时变量中存储的是值本身,因此叫做值类型

    string number boolean undefined null

    • 引用数据类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型

    通过new关键字创建的对象(系统对象,自定义对象),如Object,Array,Date等

数值型Number

  • 一切十进制表示的数字

  • 一切浮点数(小数)

  • 其他进制表示的数字 16 8 2进制

  • 科学计数法

    比较大的数字使用科学计数法表示 2e5 2*10的5次方

  • NaN

    not a Number 非数字

  • 数字型

    包含整形值和浮点形值。2 和2.3

  • 数字型进制

    • 十进制

      • 0~9 逢10进1

    • 二进制

      • 0和1 逢2进1

      • js中2进制的数字在前面加0b 0b100

    • 八进制

      • 0~7 逢8进1

      • 在js中表示八进制在数字前加0

      • 010 转换为十进制就是8

    • 十六进制

    • 0~9 a~f

      • 在js中表示十六进制在数字前加0x

      • 常见的颜色取值#dddddd就是十六进制

        var a=0xff a变量存储的是一个十六进制的ff

    在控制台输出的都是转为十进制的数值

  • 数字型的范围.

    js中数值的最大值和最小值

    Number.MAX_VALUE

    Number.MIN_VALUE

  • 数字型的三个特殊值.

    Infinity 正无穷

    -Infinity 负无穷

    NaN 待代表一个非数字

    NaN不和任何数相等,包含它自身 NaN==NaN 返回false

    isNaN现在不讲

    • isNaN(参数) 这个函数用来判断参数是否为非数字,如果是非数字的数据返回true,是数字的话返回false

    • isNaN(参数) 参数要先通过Number()进行数据转换再判断

字符串

  • 在js中使用引号包围的文本就是字符串,引号可以是双引号也可以是单引号,反引号

    因为在html中的标签属性值使用的是双引号,所以我们推荐在js中的字符串使用单引号

  • 字符串嵌套的原则是:外单内双,或外双内单

  • 字符串是一段文本内容,是一个字符一个字符链接起来的内容

    当引号包围的是纯数字是,也不是数值类型,而是字符串类型 ‘123’ 是1,2,3三个字符的链接在一起

  • 在字符串里空格也是占位的 ”hello word“ 11个字符的链接

    var a='123',b=123;
    console.log(a,b) 不同的数据类型在控制台打印出来的颜色是不同的
  • 字符串转义字符

    在字符串中有些字符是没有办法直接使用的,需要使用转义字符,转义字符是以\开头的字符

    \\ 在衣字符串内输入\前要使用转义字符转义一下
    \n 在字符串中换行  n是值newline
    \t tab缩进
    \b 空格 blank
  • 字符串拼接

    字符串拼接使用+

    字符串与任何类型的数据拼接时,都是将其他类型的数据先转为字符串,再进行拼接,返回一个拼接后的新字符串

    console.log('你好'+'小明')  // 你好小明
    console.log('你好'+12) 			//你好12
    console.log('你好'+true)		//你好true
    console.log('100'+100)	   //100100
    console.log(100+100)			//200
    console.log(100+true);     *//101
    console.log(100+false);     *//100
    console.log(100+null) //100
    console.log(100+undefinde) //NaN
    console.log(10+10+'20');  //2020
    console.log('10'+10+20);	//101020
    console.log(10+''); //10加上一个空的字符串 返回一个字符串 '10'
    console.log(1+2+'0'+2*3);//'306'

加号+,两端都是数值或者布尔值的时候则执行相加的操作

加号+,两端只要有一个是字符串就执行字符串拼接的操作

  • 字符串与变量拼接

    变量不能写在引号内

    var userName='小明';
    console.log('你好'+userName);
    console.log('你好'+userName+',你吃饭了吗');

    经常遇到字符串与变量拼接,因为变量可以动态改变里面的值

  • 字符串的长度。

    • 字符串的长度就是字符串里面字符的个数 包含空格

    • 使用字符串的length属性获取

    var userName='小明';
    console.log(userName.length); //2

布尔值

  • 布尔数据类型有两个值,true 和 false true为真在计算机存储是为1 false为假在计算机存储是为0

  • 布尔值在运算过程中的意义:主要用来做判断

    • a>b 如果得到true 表示这个表达式成立 真的

    • a>b 如果得到false 表示这个表达式不成立 假的

布尔值有字符串相加,进行字符串的拼接

console.log(true+"你好") //true你好

布尔值和数值进行运算时,true为1,false为0

console.log(true+1) //1
console.log(false+1) //0
console.log(false-1) //-1
console.log(true-1) //0

Undefined

undefined表示未定义

变量被声明但是没有被赋值,变量会有一个默认值就是undefined,表示未知数据类型即未定义类型

即这里本该有一个值,但是没有,就是undefined

undefined与字符串相加,任何类型的数据与字符串相加都是执行字符串拼接的操作

console.log(undefined+'你好') //undefined你好

undefined与数值相加最后结果是NaN。 undefined 转为数字时为NaN

console.log(undefined+11) //NaN

undefined与布尔值相加

console.log(undefined+true) //NaN

Null

null表示空值,即该处现在的值为空,即这里有一个空值

一个变量声明后赋值为null,表示变量里面存储的值是空的

var b=null;

null要赋值为null才能得到空值

console.log(true+null)  //1
console.log(2+null)  //2

null转为数字时为0

在最初的js中只设置了null来表示“无”,但是null在转为数值的时候会自动转为0,这样很不容易发现错误,因此又设计了undefined,区别是 null表示真正的空值,转为数值是为0,undefined表示 此处无定义的原始值,转为数值是为NaN

字面量

作用:字面量是用于表达一个固定值的表示法,又叫做常量

通俗的讲字面量就是所见即所得,当我们看到字面的时候就会知道他是什么类型的数据,值是多少

比如说:数字字面量:1,2,3

字符串字面量:”你好吗”

布尔值字面量:true false

数据类型转换

js是弱类型的语言,变量没有数据类型的限制,可以随时赋任何类型的值。变量的类型只有在运行时才知道。

虽然变量的类型是不确定的,但是各种运算符对运算子的数据类型是有要求的,当运算符发现运算子的类型与预期的不符,就会自动转换运算子的类型。

"66"-"2"   减法运算符预期左右两侧的都是数字类型,发现不是,就会将他们自动转换为数值类型
  • 转换分为:强制转换 自动转换

    • 强制转换是使用Number(),String(),Boolean(),这三个函数将各种类型的值分别转为数值,字符串,布尔值

    • 自动转换,比如"123"+123数值与字符串相加,js是自动发生的数据转换,用户是不可见的

    常见的三种方式转化:

转为字符串类型

  • 你要转换的数据.toString() var userName=17; console.log(userName.toString())

    • undefined和null不能转换

      (在js中.表示的)

  • String(你要转换的数据) 强制转换 String(234)

    • 任何数据类型都可以转换

  • 使用加号拼接字符串。 (自动转换)”你好”+234

转为数值类型 (重点)

  • Number() Number()函数可以将任意类型的值转换为数值类型

    格式:Number(你要转换的数据); 返回转换好的数据

    特点:

    • 会把你要转换的内容当作一个整体来看待

    • 能转换成数字结果,就转换成数字结果

    • 不能转换成数字结果就返回NaN ,NaN也是数值类型,表示非数字

    Number(null);//0
    Number(undefined);//NaN
    Number(true);//1
    Number(false);//false
    Number(NaN);//NaN
  • paseInt(string)函数 解析一个字符串,返回一个整数

    (先看参数字符串的首个字符是否是数字,如果是数字就对该字符串进行解析,直到到达数字的末端为止然后以数值的形式返回数字)

    • 只有字符串的一个数字会被返回 parseInt('12dr5') //12

    • 如果字符串的第一个字符不能被转换为数值,则返回NaN parseInt('dr5') //NaN

    • 开头和结尾的空格是允许的 parseInt(' 88 99')

    (千)语法:parseInt(你要转换的数据),返回转换好的数据

    特点:把你要转换的任何内容一位一位的看,如果第一位不能转换为数字,那么直接返回NaN

    如果第一位可以转换为数字,就继续向后看第二位,第一位保留,以此类推直到一个不能转换成合法数字的位置为止

    案例:一个数字,只保留三位小数,就是从小数点后数三位,剩下的不要,使用paseInt()方法,能取整,首先将数字扩大1000倍,然后再取整,取整后,再缩小1000倍(不考虑四舍五入的情况)

    作业:

    // 把一个四位数字,拆成一位一位的,即通过计算得到4个变量
    var num=2345;
    // 方法1
    var a=parseInt(num/1000);//得到千位的数字
    var b=parseInt((num-a*1000)/100)//num-a*1000得到百位数再除以100后取整,得到百位的上数字
    var c=parseInt((num-a*1000-b*100)/10)//得到十位的上数字
    var d=parseInt((num-a*1000-b*100-c*10))//得到个位上的数字
    console.log(num,a,b,c,d);
    // 方法2
    var a2=parseInt(num/1000);//得到千位的数字
    var b2=parseInt(num%1000/100)//num和1000取余,剩下的就是不能整除1000的部分,再除以100,取整得到百位的数字
    var c2=parseInt(num%100/10)//得到十位的数字
    var d2=parseInt(num%10)//得到个位的数字
    console.log(num,a2,b2,c2,d2);
    给定一个随机秒数,换算成多少天多少小时多少分钟多少秒
    结果为xx天xx小时xx分钟xx秒
    
    var time=1234567;//要进行计算的总s数
    // 一天有多少秒 :24*60*60 一小时有60*60秒 一分钟有60秒
    // 得到天数
    var d=parseInt(time/(24*60*60));  //用变量除以一天的总秒数,然后取整,得到一共有多少天
    // 得到小时
    var h=parseInt(time%(24*60*60)/(60*60));//用取余的方式去掉整天的秒数后,在计算
    //得到分钟
    var s=parseInt(time%(60*60)/60);//用取余的方式去掉整天和整小时的秒数后小时后,再计算
    // 得到秒钟即得到不够一分钟的秒数
    var m=time%60;//直接取余60,得到不够一分钟的秒数
    console.log(time+"s为:"+d+"天"+h+"小时"+s+"分钟"+m+"秒")//1234567s为:14天6小时56分钟7秒
  • parseFloat(string)函数 解析一个字符串,返回一个浮点数

    ``parseInt('234.4') //234

    parseFloat('234.4') //234.4

    (千)和parseInt()的解析模式一样,只不过多认识一个小数点

  • 除了加法运算符有可能会将运算子转换为字符串,其他非加法的数学运算符都会把运算子自动转为数值

    减 乘 除 取余,和Number的解析规则一样将数据看作一个整体解析

    '5' - '2' // 3
    '5' * '2' // 10
    true - 1  // 0
    false - 1 // -1
    '1' - 1   // 0
    '5' * []    // 0
    false / '5' // 0
    'abc' - 1   // NaN
    null + 1 // 1
    undefined + 1 // NaN
    • 一元运算符也会把运算子转成数值(扩展??)

      取正负值 ,和Number的解析规则一样 (千)

      格式:+变量 -变量

    +'abc' // NaN
    -'abc' // NaN
    +true // 1
    -false // 0

注意:null转为数值时为0 undefined转为数值是为NaN

转为布尔类型

Boolean(参数 你要转换的数据) 函数可以将任意类型的值转为布尔值

它的转换规则是:

  • 代表空,否定的值会被转换为false ;undefined ,null, NaN, 0, “”(空字符串)

  • 其他的值都会被转换为true

  • true和false这两个布尔值不会发生变化

  • !取反 返回数据所对应的布尔值的取反后的布尔值

  • 双取反 返回数据所对应的布尔值

运算符

做算术运算时的符号

运算符的运算子个数不同分为:一元运算符 ,二元运算符,三元运算符

运算符的分类:赋值运算符,算术运算符,比较运算符,逻辑运算符,递增递减运算符,三元运算符

书写规范:除了一元运算符外,运算符左右两侧要有空格

一元运算符:一个运算符只有一个匀运算子就能运算

  • 取正运算符 +

    • 取负运算符 –

    • 取反运算符 !

    • 自增运算符 ++

    • 自减运算符 —

算术运算符

  • + — * / %(取余,返回除法的余数)

  • +运算符有两个含义 字符串拼接,数学运算

  • %取余的主要用途是判断一个数是否可以被整除,余数为0就是可以被整除

    被除数/除数=商…余数,得到不能被整除的部分

    3%10 得到3 ;10%3 得到1

  • ** 取幂 a**b就是a的b次方

  • 浮点数在运算里会有精确度的问题,这是因为小数本身就很小了在准换为二进制时容易出现误差

console.log(0.1+0.2); //0.300000000004
0.1+0.2==0.3; //false

不要判断两个浮点数是否相等

算术运算的顺序,先乘除后加减,如果有括号先算括号里的

自增和自减运算符

  • 自增自减运算符只有一个运算子是一元运算符

  • 自增自减运算符必须和变量配和 使用

  • 自增自减运算符是仅有的可以改变变量值的运算符,其他的运算符都不会改变变量的值

自增运算符

之前我们想要给一个变量加1时要

var a=1;
a=a+1;

有了自增运算符我们可以 ++a或a++。最终a都是2 他们等同于a=a+1;

自增运算符放在变量的前面叫做前置自增运算符(++变量),放在变量的后面叫做后置自增运算符(变量++)

前置自增和后置自增单独运行时的结果是一样的,但前置或后置自增运算符和其他变量或数字一起参与运算时所起的效果就不同了

var a=1;
console.log(++a + 2); //4 a为2
console.log(a++ + 2); //3 a为2

前置自增是,变量先自增返回自增后的变量再参与运算(先己后人)

后置自增是,变量先返回原值参与运算,再自增(先人后己)

练习

var a=10,b=20;
console.log(++a + b++);
console.log(a++ + ++b);


var n=5;
var res=n++ + ++n + ++n +n++
console.log(res);//28 n此时为9
.....
....多练

推荐在开发时使用后置自增(自减),并且代码独占一行。a++或1–

比较运算符

是两个数据进行比较时所用的运算符,比较后会返回一个布尔值(true或false)作为结果

小于< 左边小于右边,返回true,左边不小于右边返回false

console.log(10<20);
console.log(10<10);
console.log(10<5);

大于>;<=;>=;==(等于);!=(不等于);===(全等);!==(不全等);

  • == 等于会转换数据类型,将其他数据类型转换为数值 ,只要求值相等,不管数据类型是否一样

    true==1 //true
    '2'==1 //false
    '1'==1 //true
    ''==0 //true
    null==undefined //true
    null===undefined //false
    NaN==NaN //false
    NaN===NaN //false
  • === 全等要求数值和数据类型都一致

  • !== 不全等,比较两边的数据是不是不等,值和数据类型都要比较,当数据类型不一样的时候,返回false

    console.log(1!=='1'); //false

  • = 赋值运算符,将右边的值赋值给左边

逻辑运算符 或且非

逻辑运算符对数据进行逻辑运算

(阮一峰:

布尔运算符用于将表达式转为布尔值,一共包含四个运算符。

  • 取反运算符:!

  • 且运算符:&&

  • 或运算符:||

  • 三元运算符:?:

取反运算符

  • 符号是! (非),用来取一个布尔值的相反值

!true //false
!false //true
  • 对于非布尔值,取反运算符会将其转为布尔值取反

  • 将表示否定或空的值取反操作后为true

undefined

null

false

0

NaN

“” 空字符串

  • 其他的值取反后都为false

  • !! 两个取反操作等于将其转为对应的布尔值

且运算符

&& 简称与,和的意思

语法:表达式1 && 表达式2

  • 且运算符在左右两侧都是布尔值时的规则:

    两侧都为true才返回true,否则返回false;即只要有一侧是false则返回false

    true && true
    false && false
    true && false==false //true
    3>2 && 2<4 //true
    3<2 && 2<4 //false
  • 左右两侧不是直接的布尔值,的规则:(数字,字符串,表达式参与运算)

    如果第一个运算子的布尔值是true则返回直接返回第二个运算子的值(注意是值,不是隐性转换后的布尔值)

    (如果第一个表达式的值为真,则返回表达式2)

    如果第一个运算子的布尔值是false则直接返回第一个运算子的值(不是隐性转换后的布尔值),且不在对第二个运算子求值(短路)

    (如果第一个表达式的值为假,则返回表达式1)

    3-1 && '2' //'2'
    3-3 && '2' //0
    
    var x=1;
    3-3 && x++  //0 此时X为1

    上面代码的最后一个例子,由于且运算符的第一个运算子的布尔值为false,则直接返回它的值0,而不再对第二个运算子求值,所以变量x的值没变。

    这种跳过第二个运算子的机制被称为段路,即只通过第一个运算子的值,控制是否运行第二个表达式的机制就是短路

    短路:当有多个表达式(值),左边表达式的值可以确定结果时,不在继续运算右边表达式的值了(bz)

    且运算符可以多个连用,这时返回第一个布尔值为false的表达式的值。如果所有表达式的布尔值都为true,则返回最后一个表达式的值。(如果有空值或表示为否定的值,返回第一个表示 空值或表示为否定的值。没有的话则返回最后一个表达式的值)

    true && 'foo' && '' && 4 && 'foo' && true
    // ''
    
    1 && 2 && 3
    // 3

    上面代码中,例一里面,第一个布尔值为false的表达式为第三个表达式,所以得到一个空字符串。例二里面,所有表达式的布尔值都是true,所以返回最后一个表达式的值3

或运算符

符号为:||

  • 或运算符在左右两侧都是布尔值时的规则:

    两边都是false时才返回false,即只要有一个为true则返回true

  • 左右两侧不是直接的布尔值,的规则:

    • 如果第一个运算子的布尔值为true,则返回第一个运算子的值,且不在对第二个运算子求值(短路)

    var a=10;
    3-2 || a++  //1   a此时为10 ,第二个运算子没有求值 造成短路了
    3-2 || (a=a*100) //1 a此时为10

    如果第一个运算子的布尔值为false,则返回第二个运算子的值(运算后的值)

    var a=10;
    3-3 || a*10 //100

    扩展:不讲

    或运算符可以多个连用,这时返回第一个布尔值为true的表达式的值。如果所有表达式都为false,则返回最后一个表达式的值。

    false || 0 || '' || 4 || 'foo' || true
    // 4
    
    false || 0 || ''
    // ''

赋值运算符

  • = 直接将右边的值赋值给左边

  • += -= 变量自身加减一个数后再赋值给自身,在自身的基础上进行叠加,是加法和赋值的合作

  • *= /= %= 变量乘 ,除,取余 后再赋值给自身

    var a=10;
    		a += 10;   等同于 a=a+10;
    		a -= 10; 	 等同于 a=a-10;
    		a *= 10; 	 等同于 a=a*10;
        a+="10";         a=a+"10";

运算符优先级

优先级由高到低:

.点运算符 []方括号运算符 new
()小括号

 一元运算符   ++  --  !  一元运算符里的逻辑非优先级很高

算术运算符  先* / %后+ -   (* / %的优先级是一样的,从左向右计算)

关系运算符  > >= < <= 

相等运算符  == != === !==

逻辑运算符  先&& 后||   逻辑与比逻辑或优先级高

三元运算符

赋值运算符 = += -= *= /= %=

逗号运算赋 ,

流程控制

流程控制就是控制我们所写的代码按照什么的结构顺序执行,代码执行的顺序对最后返回的结果起着很关键的作用

流程控制分为3类:

顺序结构

就是按照代码书写的先后顺序依次执行

分支结构:

是根据不同的条件,代码按不同的路径执行,返回相对应的结果

if语句

(1)if语句的格式 书写规范,()两端要有空格

if (条件表达式) {
    当条件表达式成立时所执行的代码
}

if语句是当条件成立(条件表达式为true)时执行大括号内的代码,条件不成立(false)则不执行{}里的语句

案例:输入成绩,根据不同的成绩,弹出不同的提示框

(2)if else语句,即双份支语句

if(条件表达式){
    当条件表达式成立时所执行的代码1
}else{
    当条件表达式不成立时所执行的代码2
}

if else语句是当条件成立时执行代码1,否则执行else后面大括号内的代码2

代码1和代码2必然且只能有一个被执行,即2选一

案例

(3) if else if 多分支语句

多分支语句是利用多个不同的条件选择不同的路径执行,最后返回不同的结果,是多选一,最多只能执行一个

格式

if(条件表达式1){
    代码1
}else if(条件表达式2){
    代码2
}else if(条件表达式3){
    代码3
}else{
 上面条件都不成立时执行代码4
}

执行路径是:若条件1成立则执行代码1,后离开该语句,后面的就不执行了

若条件1不成立,条件2成立,则执行代码2,后离开该语句

若条件1不成立,条件2也不成立,条件3成立,则执行代码3,后离开该语句

若条件1,条件2,条件3都不成立,则执行代码4,后离开该语句

即从第一个条件开始,哪一个条件满足,就执行哪一个{}里面的代码,前面的满足了,后面的就不在执行了,所有条件都不满足时则执行else里的代码,若干个{}必然会有一个执行

注意:

else if可以有任意多个

else if之间有空格,else后面不能有括号

if else if多分支语句的本质是多选一,只能有一条语句被执行

案例:

// 判断一个数是奇数还是偶数,数字%2取余,偶数返回值为0,即为false,返回值不为0,则为奇数即为true
var n=2;
if(n%2){
  console.log('奇数')
  }else{
  console.log('偶数')
}
//判断一个数字是不是在某个范围
// 判断n是不是在15和20之间  15 <= n <=20
//先比较15<=n 返回结果要么是true,要么是false,再和20比较,一定是小于20的,即返回true
var n=17;
if(15 <= n <=20){
  console.log('这样写总是会被执行')
}
// 正确写法是
if(15<=n&&n<=20){
    console.log('这是逻辑运算符,做了一个交集判断,两个两件必须同时满足')
}
作业:用户输入一个年份,判断是否是闰年,是则输出是闰年
提示:
判断闰年的条件
普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年、2020年等就是闰年)。
世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年(如1900年不是闰年,2000年是闰年)。
判断条件:年份可以被4整除且整百年份不能被100整除或者整百年份可以被400整除
条件1%&&条件2||条件3
if(year%4===0 && year%100!==0 || year%400===0){
    console.log('我是闰年')
}
猜数字 0到10之间的数字7

成绩   判断学生成绩是否及格

(4) 三元表达式

格式:条件?表达式1:表达式2;

三元表达式就是一个简化了的if else语句

执行路径:条件成立时执行表达式1,条件不成立执行表达式2. (表达式都是有返回值的)

var a=39;

var age=39;
var result=age>20?'你已经中年了':'你还年轻着呢';
switch语句 switch 开关 转换 case 选项

switch语句也是多分支语句,也是多选一

  • 格式:

switch (表达式) {   //在实际开发中我们常用变量作为表达式
	case value1:
		执行语句1;
		break;
   case value2:
    执行语句2;
    break;
   case value3:
    执行语句3;
    break;
    ....
   default:
    	所有的case值都不满足的时候执行最后的语句;
}
  • 执行路径:根据switch后表达式的返回值与case后的值相匹配,如果匹配上就执行该case后面的语句,遇见break则退出该switch语句,如果和所有case后的值都没有匹配上,就执行default后的语句

    在执行case后的执行语句后遇到break后会退出该switch语句,如果没有遇到break则会不管下一个case是不是满足就继续执行下一个case后的语句,直到遇到break为止,或者到switch结束

    • break的穿透,是从第一个满足条件的case开始进行穿透,直到遇到break为止,或者到switch结束

      var n=7;
      switch(n){
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
          console.log('31天');
          break;
        case 4:
        case 6:
        case 9:
        case 11:
          console.log('30天');
          break;
        case 2:
          console.log('28天');
          break;
      }
  • switch语句的表达式匹配的是case后面特定的值,且必须是全等才能匹配上,switch语句,只能判断准确的某一个字面量值,不能判断范围

  • default可以写也可以不写,作用类似于if语句里的else,当所有条件都不成立的时候,执行default

  • switch语句与if语句的区别,使用时如何选择(用自己的语言再总结一下)

    • 一般情况下他两是可以相互替换的

    • switch语句通常处理case值比较明确的情况,if else语句更加灵活,常用于范围判断

    • switch语句是先进行条件判断后直接执行到匹配的程序语句里去。效率更高,if else语句会依次判断所有的条件,直到条件成立,效率低

    • 当分支比较少时,if else语句的执行效率比switch语句高

    • 当分支比较多时,switch语句的执行效率比较高,而且结构更清晰

//判断学生成绩及格与否
var score=+prompt('请输入学生成绩');
switch(parseInt(score/10)){
    case 10;
    case 9;
    case 8;
    case 7;
    case 6;
      alert('学生成绩合格');
      break;
  default:
      alert('学生成绩不合格');
      break; 
}

循环结构

分支:根据条件来决定是不是执行某一段代码

循环:根据条件来决定一段代码重复执行几次

  • 循环成立的条件:

    • 初始值,作为循环的开始

    • 条件判断:决定要不要继续循环

    • 重复执行的代码

    • 改变初始值,为了让循环结束

while循环

变量初始化就是用var 声明一个变量,这个变量通常用作于计数器

条件表达式就是用来判断循环是继续执行还是终止的条件

操作表达式 就是每一次循环最后执行的代码,经常是对计数器进行更新操作(递增或者递减)

循环体 被重复执行的代码就是循环体

格式: while…时候

var n=1;//声明一个变量,这个变量通常用作于计数器
while(条件表达式){
	循环体
	n=n+1;
}

wile循环是在当条件成立的前提下执行循环体的代码,直到条件表达式是不成立时结束循环

循环执行的是{}里的语句

注意:while循环依然要有初始化变量(计数器)和操作表达式(更新计数器)

var num=1;
while(num<=100){
	console.log(num)
	num++
}
如果没有更新变量,会造成死循环

案例:

计算1至100的和

var i=1;
var sum=0;
while(i<=100){
 sum += i;
 i++
}
console.log(sum)
// 案例
// 6的阶乘 6*5*4*3*2*1
// 第一步 准备一个变量存错阶乘的结果
var and=1;
// 循环的初试变量
var n=6;
while (n>=1) {
  // 重复执行的循环体
  // and=and*n; 把每一个数字乘到and上
  and *= n;
  // 更新变量
  n--;
}
console.log('6的阶乘的结果为:',and);
用户输入人数及每一一个人的成绩
var sduentNum=prompt("请输入学生人数");//学生人数
var sum=0; //成绩总和
var i=1;

while(i<=sduentNum){
	sum += prompt("请输入学生"+i+的成绩);
	i++;
}
console.log("学生的平均成绩为"+sum/sduentNum);
//猜数字
var real=7;
var num=+prompt('请输入0到10之间的数字');//取正转换数据类型
while(num !== 7){
  if(num<real){
		alert('小了')
  }else{
    alert('大了')
  }
  num=prompt('请输入0到10之间的数字')-0;
}
alert('您猜对了')
do while循环

do while是while循环的变体

格式:

do{
	循环体
}while(条件表达式)

do while循环是不管条件是否成立先执行一次循环体的代码再判断条件表达式真假,如果为真则继续下一次循环,直到条件为假时结束循环

var n=10;
do{
	console.log('我执行了');
	n++;
}while(n<10)
//此只会执行一次

do while循环至少执行一次循环体的代码

do while 循环依然要有初始化变量(计数器)和操作表达式(更新计数器)

案例:计算1至100的和

var i=1,sum=0;
do{
 sum += i;
 i++;
}while(i<=100)
cosole.log(sum)
for循环

for循环,应用最多,是循环的语法糖,用起来方便,写起来方便,看起来不方便

  • 作用:可以重复执行一段代码,通常和计数有关

格式: 书写规范:()左右要有空格,后面的闭合花括号要于for对齐

for (变量初始化;条件表达式;操作表达式) {//变量初始化;条件表达式;操作表达式这些所要用到的一次写完
  循环体
}
//标准语法
var i=0;
for(;i<10;){
	重复执行的代码
	i++
}

变量初始化就是用var 声明一个变量,这个变量通常用作于计数器

条件表达式就是用来判断循环是继续执行还是终止的条件

操作表达式 就是每一次循环最后执行的代码,经常是对计数器进行更新操作(递增或者递减)

循环体 被重复执行的代码就是循环体

案例:打印多次。。

案例:打印100以内所有3的倍数

通过循环找到100以内所有的数字,判断每一个数字,如果是3的倍数,打印,不是则不打印

for(var i=1;i<=100;i++){
	if(!(i%3)){
		console.log('我是3的倍数:',i)
	}
}

案例1:求1至100的和。

var sum=0;  存储和的变量,和的初始值是0
for (var i=1; i<=100; i++) {
	// sum=sum+i;
  sum += i;
}
cosole.log(sum)

案例2:求1至100的偶数和 奇数和

求100以内所有能被3整除的数字的和pa

案例:

/* 
水仙花数字
三次自幂数(取值范围100~999)
即一个三位数字的每一位数的三次方之和,如果和这个数字一样就是三次自幂数
例子:153  即1*1*1+5*5*5+3*3*3即1+125+27=153


*/
for(var i=100;i<=999;i++){
    // 拆数字
    var a=parseInt(i/100);
    var b=parseInt(i%100/10);
    var c=i%10;
    if(a**3+b**3+c**3===i){
        console.log(i+'是三次自幂数')
    }
}
/* 
153是三次自幂数
370是三次自幂数
371是三次自幂数
407是三次自幂数 
*/

嵌套的for循环 ,双for循环

双层for循环,外层每执行一次,内层for循环全部执行

//吃9个馒头,每个馒头三口吃完
for(var i=1;i<=9;i++){
    console.log("这是我吃的第"+i+"馒头")
    for(var j=1;j<=3;j++){
        console.log('    第'+i+"个包子我吃的第"+j+"口");
    }
    console.log('老板再来一个');
}
打印正方形的星,即每一行星的个数都以一样

// 打印三角型,第一行一个*,第二行两个***,第三行三个*...以此类
// 外层循环控制行数,内层循环控制每一行有多少个*
for(var i=1;i<=9;i++){
    for(var j=1;j<=i;j++){
        document.write("* ");
    }
    //输出一行的*后另起一行
    document.write('<br>')
}
在页面中打印和在控制台打印

打印9*9乘法表
var str='';
for(var i=1;i<=9;i++){
    for(var j=1;j<=i;j++){
    str=str+"*";//打印星
    str+= i+"X"+j+"="+i*j;  //99乘法表
    
        document.write("* ");
        
    }
    //输出一行的*后另起一行
    document.write('<br>')
    str+="\n";//换行
}
//打印梯型
// 打印三角型,第一行一个*,第二行两个***,第三行三个*...以此类
// 外层循环控制行数,内层循环控制每一行有多少个*
for(var i=1;i<=9;i++){
    for(var j=1;j<=i;j++){
        // 打印梯型 前三行的不输出*,直接退出本次循环,继续下一次循环
        if(i<=3) continue;
        document.write("* ");
    }
    //输出一行的*后另起一行
    document.write('<br>')
}


// 打印倒三角型,第一行9个*,第二行8个***,第三行7个*...以此类
// 外层循环控制行数,内层循环控制每一行有多少个*
for(var i=1;i<=9;i++){
   //从1到9是9个,从2到9是8个,从3到9是7个...
    for(var j=i;j<=9;j++){
        document.write("* ");
    }
    //输出一行的*后另起一行
    document.write('<br>')
/}
9gfor(var i=1;i<=9;i++){
//第一行有9个空格一个*,第二行有8个空格2个*,第三行有7个空格3个*....
    for(var j=i;j<=9;j++){
        document.write("&nbsp;");
    }
    for(var k=1;k<=i;k++){
        document.write("* ");
    }
    //输出一行的*后另起一行
    document.write('<br>')
}
循环控制语句

continue break,使用在循环里的关键字

  • Continue

在执行循环时遇到continue(继续),continue后的代码不在执行,直接就退出本次循环,直接跳到i++的位置,继续执行剩余次数的循环

  • break

在执行循环时遇到break,直接结束当前循环,break后的代码不再执行,也不在继续以后的循环

  • 标记语法

    自己命名一个标记,可以控制遇见break时跳到哪里,直接在循环开始的地方做一个标记格式是 名字:

当准备跳出的时候语法为 break 名字;直接跳转到标记代表的循环结束位置

here:
for(var i=1;i<=9;i++){
  
    for(var j=1;j<i;j++){
        document.write("&nbsp;");
    }
   
    for(var j=i;j<=9;j++){
        document.write("* ");
       if(i=2) break here;
    }
  
    //输出一行的*后另起一行
    document.write('<br>')
}

数组

数组是js的一种数据类型,也是复杂数据类型 Array ,一个盒子,存储一堆数据,不是按照键值对存储的,是按照索引存储的(序号),基本数据类型都是单一的值,值和值之间没有没有任何联系,使用基本数据类型,我们创建的变量都是独立的,不能成为一个整体

数组:数组是按次序排列的一组值,每个值都有序号(从0开始的),整个数组用[]表示

[1,2,3,"55",true]

[]是数组的标致,里面的每个值是数组的元素,数组的元素可以是任意类型的值,数组中可以放另外的数组

  • 创建数组的方式

    var arr3=[1,3,'你安静点'];  //数组可以在创建时直接添加一些数组元素
    var arr2=[]; //用数组字面的方式创建一个空的数组,并赋值给arr2,也先定义后赋值
    
    数组里的数值一定要用逗号隔开
    //使用内置构造函数创建数组,js给我们提供了一个内置的构造函数Array
    var arr=new Array(); 创建一个空数组并赋值给变量arr
    
    	不传递参数的时候:创建一个空数组
    	传递一个正值,这个正值就是数组的长度
    	传递两个及两个以上参数的时候,传递进去的参数则是数组的内的元素

    (扩展不讲:数组不能使用点语法来操作,因为数组的下标是数字,点语法不能直接后接数字)

  • 数组的索引(下标)就是用来访问数组元素的序号

    数组的索引是从0开始的 0 1 2 3 4

  • 获取数组元素:格式 (多案例,比如循环遍历出数组中的元素,比较大小)

数组名[index] ;// index是数组的索引

console.log(arr3[index])

案例:

使用for循环遍历一个数组,遍历就是依次访问数组里的每一个数据
var Arr=['a','b','c','d','f'];

for(var i=0;i<Arr.length;i++){
    //i是for 循环的计数器,可以作为数组的索引,Arr[i]则是对应索引位置上的数据
    console.log(Arr[i])
}
  • 数组的长度,即数组中元素的个数

    数组的长度可以通数组的length属性获取,数组的length属性是一个可读写的属性

    读取格式

数组名.length ;		最终会返回数组中元素的个数

可以通过length属性来修改数组的长度

newArr.length=数值; 
//数值大于原数组的长度可以给数组扩容,多出来的就用空位补齐,读取空位时返回undefined
//数值小于原数组的长度,会原数组中多出来的元素删除
//清空一个数组就可以使用:  数组.length=0;
  • 替换数组中的元素,新增数组中元素

    索引也是一个可以读写的属性:

    - 读:读到指定索引位置的数据如果有就返回,没有就返回undefined
    - 写:
var newArr=[1,3,6,8];
newArr[1]=2; //指定的索引位置有数据,就是替换原数据
console.log(newArr[1]); //2  
newArr[5]=9;  //没有这个索引位置,就是新增数组中的元素,这个数字超出了数组的原长度,那么中间位置的空位补齐
console.log(newArr); //[1, 2, 6, 8, empty, 9]

数组循环组合多案例,筛选数组中元素,颠倒数组中的元素等。。。

案例:

求数组中元素的和及平均值
// 将数组中的每一个数据获取出来求和 
//和除以数据的个数得到平均值
var num=[1,3,4,7,8,9,6,5,2];
var add=0;
var average;
for(var i=0;i<num.length;i++){
//   add=add+num[i];
add+=num[i];
}
average=add/num.length;
// 求数组中的最大值
var num=[1,3,4,7,8,9,6,5,2,24];
// 假设元素中的第一位是最大值赋值给一个变量max,然后依次和数组中每一个数组元素相比较
//如果元素大于这个最大值,则就用这个较大的值更新max
var max=num[0];
for(var i=1;i<num.length;i++){
    if (num[i]>max) {
        max=num[i];
    }
}
console.log('数组中的最大值为',max)
将数组转换为字符串,并使用符号-分隔
var num=[null,'66',4,'undefined',8,9,6,5,2,24];
var str='';//定义一个新的变量来存储拼接后的字符串
for(var i=0;i<num.length;i++){
   
    // str += num[i]; //str为字符串,所以加号执行的是字符串拼接操作
    str += num[i]+'-';//拼接的每一个数据之间使用-分隔
}
console.log('11111',str)
创建一个数组,里面存放1-100的整数

var num=[];
for(var i=0;i<100;i++){
    num[i]=i+1;
}
console.log("数组为",num)
//筛选数组中的数据,将小余10的元素选出来放入一个新的数组
var arr=[12,4,5,3,66,3,232,22,1]
var newArr=[];//声明一个新的数组,存放选出来的元素
// var index=0;
for(var i=0;i<arr.length;i++){
    if(arr[i]<10){
        // newArr[index]=arr[i];
        // index++;
        newArr[newArr.length]=arr[i]
    }
}
console.log('>>',newArr)
颠倒数组中元素的顺序
var arr=[12,4,5,3,66,3,232,22,1]
var newArr=[];
for(var i=arr.length-1;i>=0;i--){
    newArr[newArr.length]=arr[i]
}
console.log('>>',newArr)

案例:冒泡排序,即给乱序的数组排列顺序,从小到大排列

目的不是为了上班用的,而是为了锻炼逻辑思维

口诀:双层for循环,一层减一次,里层减外层,变量相交换

前提知识:学会交换数组里的两个数据的位置, 遍历数组

var arr=[12,500,4,5,2,66,3,232,22,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 middle=arr[i];
            arr[i]=arr[i+1];
            arr[i+1]=middle;
        }
    }
}
console.log('>>',arr)

扩展不讲:数组的本质也是一个对象,还可当做对象使用,使用对象的.语法存储数据。。

  • 算法:就是观察执行过程,找到规则,转为代码

函数

  • 函数封装了一段可以被重复执行的代码,使代码可以重复使用,大大简化了代码的结构

    在js里函数就像是一个箱子,用来承载一段代码,当你要执行这段代码的时候只要呼唤这个箱子就可以了

    步骤:1:把代码装箱子里 2:使用箱子里的代码

定义函数

  • 声明式函数

    使用function关键字 声明函数,function要全部小写

    function 函数名() {  //空格必须要有分隔关键字和函数名
     	//函数体
    
    函数名要遵循变量的命名规则和命名规范
    函数是执行某些操作,函数名最好使用可以表示动作的名字 getName。。。

    这种方式声明的函数有名字,所以这样的函数是命名函数

  • 赋值式函数

    var 变量名= function() {
    	函数体
    }

    这是将一个匿名函数赋值给一个变量。这个匿名函数又被称为函数表达式,因为赋值符号(=)右边只能放表达式

  • 函数的重复声明

    如果同一个函数被多次声明,后面的声明就会覆盖前面的声明

    function fn(){
        console.log(1)
    }
    fn(); // 2
    function fn(){
        console.log(2)
    }
    fn(); //2

    由于函数名的提升,前一次的声明在任何时候都是无效的

调用函数

函数在声明后不调用是不会自己执行的,需要调用才可以执行;

定义函数的方式不一样,但是调用函数的方式是一样的,使用圆括号运算符

函数名();

注意:函数名 和 函数名() 是不一样的,单写一个函数名是一个变量,表示这个函数(函数对象),函数名()是要调用执行这个函数,会得到函数调用后的返回值

  • 使用函数封装代码的优点:可以一次书写,多次使用,代码简介

  • 两种声明函数的方式,调用函数的方法一样,但是调用的时机不一样

    • 声明式函数,可以在声明之前调用,也可以在声明之后调用

    • 赋值式函数,只能在声明后调用,声明之前不能调用否则会报错

参数

函数内某些值不能固定,我们可以在声明函数时使用参数来代表这些不能确定的值,在调用函数时通过参数传入不同的值进去。可以使函数通过参数来执行不同的代码

function 函数名(参数1,参数2...) {
	函数体
}
  • 函数可以有多个参数,中间使用,分隔

  • 函数的参数分为两种

    • 在函数声明时定义的参数是函数的形参(形式上的参数)

      • 形参像是一个只能在函数内部使用的不用声明的变量,用来接收实参

      • 形参的值由函数调用时传进来的实参来决定

      • 起名遵循变量的命名规则和规范

    • 在函数调用时传入的参数是函数的实参(实际参数)

      • 实参就是一个准确的值,就是为了给函数的形参进行赋值的

      • 函数调用时实际参与执行的是实参

      • 实参可以是任意的数据类型,也可以是一个对象,当我们的参数过多时,可以讲参数封装到一份对象里面,然后通过对象传递

      • 实参也可以是一个函数,传入函数后可以调用

        function hi(){
         cosnole.log('hi');
        }
        function fun(a){
        	a();    //这等同于调用了hi函数,若是hi函数需要参数的话,也可以在这传参
        }
        fun(hi)

        案例展示

  • 函数的 参数不是必须的

  • 实惨本应该是和形参从左往右一一对应的,但是实际调用函数时无论我们传入多少个实参,程序都不会报错(案例展示)

    • 当传入的实参与形参一样多时,从左向右一一对应

    • 当实参个数大于形参个数即实参多 只取形参的个数

      前面的按照顺序一一对应,多出的来实参在函数内部没有形参接收,所以多出来的实参不能直接使用

    • 当实参小于形参即实参少

      前面的按照顺序一一对应,但是多出来的形参因为没有实参赋值会采用默认值undefined

      (js中省略的参数默认值undefined,结果为NaN)

      在调用函数时我们没办法只省略前面的参数而传入后面的参数。如果非要只省略前面的参数只能显示的传入undefined

      function f(a, b) {
        return a;
      }
    
      f( , 1) // 直接省略第一个参数会报错
      f(undefined, 1) // undefined
    @param {参数类型} 参数名 - 对参数的描述信息
    @return {函数返回值的类型} 函数返回值的描述
    作用:对函数的参数,返回值进行解释

函数的arguments(参数)

案例:使用函数计算任意个数字的和,即无论传入多少个实参求和,我们的形参就没办法提前预测会传入多少个实参了,所以有一个新的知识点 arguments

  • 在js函数中允许传入任意多个参数,那具体传入了多少参数,可以使用arguments对象来读取

  • arguments是函数的内置对象,包含了函数运行时所有的参数即实参

    (arguments是在函数内部天生自带的变量,表示所有实参的集合是一个伪数组)

  • 每个函数都有arguments对象

  • arguments是一个类似于数组的伪数组,他有数组的一些特性但他不是真正意义上的数组

    • 具有数组的length属性

    • 按照索引的方式存储数据. ”序号“从0开始,依次加1,这序号叫做下标

      • 我们可以按照遍历数组的方式遍历arguments

      • 我们可以依靠索引来操作arguments里面的某一个数据

        • 读:arguments[索引]; 表示获取对应索引位置上的数据

        • 写:arguments[索引]=你要设置的值 ; 表示把arguments里面对应索引位置的数据改变

          写入时:如果你写的索引是arguments里面没有的一个索引,那么就是添加,如果有这个索引,那么就是修改对应索引位置上的值

    • 数组特有的一些方法在arguments上不能使用(push,forEach等)·

function f() {
  return arguments.length;
}

f(1, 2, 3) // 3
f(1) // 1
f() // 0


function newf(){
	for(var i=0;i<arguments.length;i++){
		console.log(arguments[i]);
	}
}

newf(1,2,3);
newf(4,5,6,7);

return语句

引入案例:在一个函数内的计算结果打印到控制台或者alert出来,或者documen.write到页面上,我们要不断的修改代码,我们可以利用函数把结果返回到函数外面,在函外面怎么处理都可以,就不需要一次次的修改函数内的代码了

在函数内我们可以使用return返回特定的值

function 函数名(参数1,参数2...) {
  函数体
	return 返回值1;
}
  • js在遇到return是就会返回return后面那个表达式的值并终止函数,即使后面还有其他代码也不再执行

  • return只能返回一个值,如果逗号隔开多个值,只能返回最后一个值。 return 表达式;

  • 如果想让返回多个值,可以利用数组

  • 谁调用就将返回值返回给谁。函数名()=返回值。常用变量来接收函数的返回结果 var a=函数名();

  • return语句不是函数必须的,但函数都是有返回值,如果有return则返回return后的表达式的值,如果没有return则返回undefined

  • 注意返回值如果是一个变量的话,返回的是这个变量所代表的值,而不是这个变量(在函数内定义的变量只能在函数内使用,外部获取不到)

function tt (){
  var ee=1;
  return ee; //return 作为返回值关键字来使用,返回的是retun 后的表达式
}
console.log(tt());
console.log(ee);
function tt (){
var ee=1;
return; //return进行打断函数的操作,return后面的语句不再执行了 此时测返回值是默认值undefined
console.log("6666");
}
console.log(tt());
  • return 返回值可以是任意类型的值,也可以是函数(闭包知识点)

    function fn3(){
    	//在函数内部再声明一个函数
    	function fn4(){
    		console.log(‘我是fn4’)
    	}
    	//将fn4函数作为返回值返回
    	return fn4;
    }
    var a=fn3();
    console.log(a);
    a();
    
    等同于
    fn3()();
  • break,continue(继续),return的区别

    break:结束当前循环体,不在执行后面的循环(主要针对循环。for循环,while循环…)

    continue:结束本次循环,继续执行下一次的循环 (主要针对循环。for循环,while循环..)

    return只能出现在函数中

在函数内的循环中是退出循环,并且返回return语句中的值. (自己再操作确定一下)

在函数中是退出当前函数并返回return语句中的值

打断循环用break,打断函数用return

  • 立即执行函数,函数定义完,立即被调用,这种函数就是立即执行函数(下有详细的)

  • 立即执行函数往往只会执行一次

函数内可以调用其他函数

第一等公民

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。

案例:多封装几个函数。 求最大值的函数 冒泡排序的函数。。。

作用域

作用域是指变量(变量名,函数名)存在的范围

es5中只有两个作用域:全局作用域和局部作用域

  • 全局作用域:一个打开的网页就是一个全局作用域;(整个script标签或者一个单独的js文件)

    在全局作用域定义的变量就是全局变量,在全局内任何地方都可以使用

    全局作用域在页面打开时创建,在页面关闭时销毁

    在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用,在全局作用域中创建的变量都会作为window对象的属性保存,创建的函数都会作为windoe对象的方法保存

  • 局部作用域:函数内部就是局部作用域 又称为函数作用域(私有作用域,每一个函数就是一个私有作用域)

    • 在调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁,每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的

    es6中有新增了块级作用域,现在Es5先不涉及

function f(){
	//局部作用域
	var p=1; //局部变量,只能在函数内使用
	a=10; //全局变量 如果一个变量在局部作用域内没有声明直接赋值了,那这个变量则是全局变量
}
console.log(p)

在局部作用域(函数内)内定义的变量就是局部变量,只有函数内可以使用,外部不能读取,但在函数作用可以访问到函数作用域的变量

当函数内和全局有相同的变量时(作用域链),在函数作用域内要访问全局变量,可以使用window对象。 Window.变量

函数的形参也可以看作是在函数作用内声明了局部变量

特例:如果一个变量在局部作用域内没有声明直接赋值了,那这个变量则是全局变量

全局变量和局部变量的区别

  • 全局变量在全局都可使用,只有在浏览器关闭的时候才会销毁,比较占内存资源

  • 局部变量只能在函数内使用。当其所在代码块执行时会被初始化,当代码块执行完毕就会被销毁,比较节约内存资源

(不讲)块级作用域是es6新增的,块级作用域是{}. 比如For{} if{},在外面不能使用块级作用域内的变量

作用域的上下级,你的函数写在那个作用域下,你就是谁的子级作用域

作用域上下级的关系的作用是为了确定变量的使用范围

三个机制:

  • 变量定义机制

    • 有var关键字 var 变量

    • 声明式函数 function fn(){}

    一个变量(函数)定义在哪一个作用域,只能在当前的作用域或者下级作用域内使用,上一级作用域不能使用

  • 变量使用机制

    • 当需要使用一个变量(函数)会首先在自己作用内查找,如果有,就直接使用,如果没有去上一级作用域查找,如果有就直接使用,如果还没有就继续向上一级作用域找直到全局作用域都没有,那就会报错

  • 变量赋值机制

    • 一定要有赋值符号

    • 当你需要给一个变量(函数名)赋值的时候,会首先在自己的作用域查找,如果有直接赋值,如果没有去上一级作用域查找,有就赋值还没有就再去上一级作用域查找直到全局作用域都没有,就把这个变量定义为全局变量,再进行赋值

      function fn(){
          // var num=100;  //声明了私有变量num,只能在fn内使用,fn外部不能使用
          num=100; //不能声明,只是赋值
          console.log('****',num)
      }
      fn(); //这个函数执行的时候会定义一个全局变量num
      console.log(num)
      //最终顺序  100 100
      function fn(){
      
          var a
          function fun() {
              a=200;  //给fn的私有变量a赋值
          }
          console.log(a) //undefined
          fun() //这个函数执行完,才给fn私有a赋值
          console.log(a)
      }
      // console.log(a) //报错
      fn()
      // console.log(a) //报错
      // 最终的顺序为:报错  undefined  200 报错

作用域链

如果函数内还有函数就在这外部函数的作用域内又生成一个作用域

内部函数可以访问外部函数中的变量

作用域链的案例展示:

<script>
	var a=7;
	function getNum() { //外部函数
		var a=8;
		function getNum2() { //内部函数 可访问外部函数里的变量
			console.log(a);
		}
		getNum2();
	}
	getNum(); //8

</script>

作用域链:在局部作用域获取一个变量,系统会先在当前作用域查找var声明语句,如果找到了就直接使用,找不到则继续向上一级作用域查找,直到找到全局作用域中(给变量赋值的时候若还是没有找到则自动在全局作用域中声明该变量。)我们把这种链式的结构称为作用域链

案例:多案例练习

<script>
	var a=100;
	function fn(a){//已经声明变量 var=a 没有赋值,所以
	//形参就相当于在函数内部定义的私有变量
		console.log(a)
  }
  fn(); //输出undefined

</script>
<script>
 var a=100;
	function fn(a){
		a=200;
		console.log(a)
  }
  fn()
  console.log(a)
  //输出顺序为200 100
</script>
var a=100;
    function fn(a){
        console.log(a);
		a=200;
		console.log(a)
  }
  console.log(a) //100 只会输出以个100,因为fn函数没有调用
var a=100;
  fn();
    function fn(a){
        console.log(a);//unde
		a=200;
		console.log(a)//200
  }
  console.log(a) //100
  
  //输出顺序为undefined 200 100
  • 函数的作用域是在函数定义时就已经确定了

闭包

  • 闭包就是能够用读取其他函数内部变量的函数,由于在js中,只有函数内部的子函数才能读取内部变量,因此可以将闭包理解成定义在函数内部的函数

    function f1() {
      var n = 999;
      function f2() {
        console.log(n);
      }
      return f2;
    }
    
    var result = f1();
    result(); // 999
    
    
    //等同于
    f1()();
    //---------------------f2函数就是闭包函数与有没有return没有关系
    function f1() {
      var n = 999;
      function f2() {
        return n
      }
      window.fn3=f2; //将闭包函数f2赋值给了全局对象window,这样就可以在函数f1外部获取f1内的局部变量了
    }
    f1();//调用f1,目的是给windown.fn3赋值
    
    console.log(window.fn3())
    console.log(window)
  • 闭包的最大的特点就是它可以记住诞生环境,比如f2记住了它的诞生环境f1,所以可以从f2可以得到f1的内部变亮,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁

  • 闭包的最大的两个用处有两个,一个是可以读取外层函数内部的变量(在函数的外部访问函数内部的私有变量),另一个就是让这些变量始终保存在内存中,即闭包可以使它的诞生环境一直存在

  • 闭包消耗内存很大,所以不要滥用闭包,否则会造成网页的性能问题,在ie浏览器中可能导致内存泄漏,解决方法就是,在退出函数前,将不使用的变量全部删除

  • 程序运行需要内存,只要程序提出要求,操作系统或者运行时就必须供给内存

    内存泄漏,对于持续运行的服务进程,必须及时的释放用不到的 内存,否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃

    用不到的内存,没有及时释放,就叫做内存泄漏

  • 闭包会在父函数外部改变父函数内部的变量,所以如果你把父函数当做对象(object)使用,把闭包当做它的公用方法,把内部变量当做它的私有属性,这时一定会要小心,不要随便来改变父函数内部的变量的值

闭包:

  • 形成闭包的条件

    1:一个不会被销毁的函数执行空间

    2:函数内部 直接或 间接 的返回一个函数

    3:内部函数操作(访问,赋值)着外部的函数的变量

  • 当三个条件都满足的时候

    我们管内部函数叫做外部函数的 闭包函数

  • 闭包的作用

    1 保护变量私有化

    定义在函数内部的变量就是私有变量

    2 在函数外部访问函数内部的私有变量,利用闭包函数访问

  • 闭包的特点

    1:保护变量私有化

    优点:不去污染全局

    缺点:外部不能访问,需要闭包函数

    2:在函数外部访问函数内部的私有变量

    优点:不局限于私有变量

    缺点:外部访问需要闭包函数

    3:变量的生命周期

    优点:变量的生命周期被延长

    缺点:一个不会被销毁的函数空间

    内存泄漏

    闭包慎用

  • 闭包销毁

    形成闭包的条件:不销毁的空间;返回函数;内部函数引用外部函数的变量

    当想销毁闭包的时候,只要这个不销毁的空间不存在了,闭包就没了

    • 不销毁空间

      返回复杂数据类型

      外部有变量接收

    • 销毁空间

      外部不再有变量接收

      外部接收的变量重新赋值

       function fn(){
            var num=100;
            //fn 函数内返回一个a函数 
            return function a(){
                //访问外部函数 fn 的私有变量 num
                //并把num 的值返回
                return num;
            }
        }
        //res 是fn的闭包函数
        var res=fn();
        //想要销毁闭包:res 存储的不再是fn函数内部返回的函数了,fn的执行空间就销毁了
        res=100;

递归函数

函数的一种应用方式

递:一层一层的进去

归:一层一层的回来

把一个事情分成若干事件来做

例子:5的阶乘 5 * 4 * 3 * 2 * 1

5 * 4的阶乘

4 * 3的阶乘

3 * 2的阶乘

2 * 1的阶乘

1的阶乘就是1

本质:一个函数自己调用自己,当达到设置的终点的时候,再归回来,归使用return

看递归:看浏览器控制它,call stack 执行栈,就是看在执行哪一些代码

注意:写递归先写停

function fn1(n){
  //写递归先写停
  if(!n){
     	return;
     }
  --n;
  fn1(n) 
}
fn(5);
// 使用递归实现数值的阶乘  5的阶乘
function fn2(n){
  //n 就是你要求的阶乘
  if(n==1){
    //终点位置出现
    return 1;  
  }
  //当n不是1的时候,我要递进去
  return n*fn2(n-1)
}
fn2(5)

预解析

JS代码是由浏览器的js解析器(引擎)解析执行的,js引擎在运行js代码是分为两步。分别是预解析和代码执行

1:预解析:

在当前作用域下,js代码执行之前,浏览器会把当前作用域内所有的var和function声明的变量提升到当前作用域的最前面(全局作用域,函数作用域),它会在所有代码执行之前就被创建(所以我们可以在函数声明前来调用函数,使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用)

  • 函数内部的变量也会发生变量提升

预解析分为:预解析又叫做变量,函数的提升

(1)变量预解析 将作用域内所有变量声明提升到当前作用域的最前面 ,变量的赋值不会提升

(2)函数预解析 将所有采用funchtion命令声明的整个函数提升到代码的最前面,但不会调用函数

注意:匿名函数没有function声明,所以不会提升

代码实现: 正常的代码实现,

 var box = 123;
        var fn1 = function () {
            box = 456;
            console.log(1111, box);
            return;
            var box = 789;
            console.log(222, box);
        }
        fn1()
        console.log(333, box)
 /* 
        
          var 关键字 声明的变量可以变量提生   es5

             推荐:先声明后使用
             
          let 关键字声明的变量不会变量   es6
        
        */
  /*      预解析
        var box;
         var fn1
*/



        代码执行:

        box = 123;
        fn1 = function () {
            预解析:
             var box   提前 局部变量 box
            代码执行:
                box = 456;      给局部变量赋值
                console.log(1111, box);   111,456
                return;
                box = 789;    //提前声明var box
                console.log(222, box);
        }
        fn1()
        console.log(333, box)   123