JS的ECMAScript1

141 阅读1小时+

1、JS的组成(面试题)

ECMAScript 和 DOM 和 BOM 三者 共同组成
1.ES:定义了JS的语法规范,描述了语言的基本语法和数据类型
      简单来说: Js怎么写,需要根据ES的规定
2.DOM(文档对象模型)
      *有一套成熟的操作DOM节点的API,通过DOM可以操作页面中的元素(其实就是标签)
            比如:增加一个 Drv
                 删除一个span
                 修改h1标签的内容
3.BON(浏览器对象模型)
      * 有一套成熟的操作BOM的API
            比如:弹出框
                浏览器跳转
                获取浏览器相关信息
                获取浏览器尺寸

2、JS的变量

JS的变量是什么?
     变量是计算机中存储数据的一个标识符,通过这个标识符可以找到内存中存储的数据
     大白话版:我有一个东西需要暂时存储起来,比如说一个数字100 存储到某一个变量中,
                 当我后续需要使用的时候可以通过这个变量找到这个数字100
     变量就相当于是一个容器,内部可以存储任意类型的值,使用变量的时候,用的是内部存储的数据

使用变量需要用两步
      1.定义变量
           语法:var 变量名
      2.赋值变量
           变量名 = 值
           备注:JS中一个等号,叫做赋值号
     打印数据:console.log(要打印的值);可以将我们想要打印的值打印在浏览器的控制面板 
    //  1.定义变量
    var a //定义一个变量,变量名为a
    var b //定义一个变量,变量名为b
    var c //定义一个变量,变量名为c
     
    // 2.赋值变量(暂时只使用数字作为值)
    a = 100
    b = 200
    c = 500
     
    // 3.打印变量
    console.log(a) //100
    console.log(b) //200
    console.log(c) //500

    // 4.定义变量与赋值变量的简化写法
    var q = 666; //定义一个变量,变量名为q,并且给这个变量赋值为666
    var w = 888; //定义一个变量,变量名为w,并且给这个变量赋值为888

    console.log(q);//666
    console.log(w);//888
案例:交换两个变量的值
    var a = 100;
    var b = 200;

    console.log(a);//100
    console.log(b);//200
    var c = a;
    var a = b;
    var b = c; 

    console.log(a);//200
    console.log(b);//100

3、变量的命名规则和规范(面试题)

1.规则(必须遵守的,如果不遵守会报错)
      * 在书写变量名的时候,必须由数字字母下划线$符号(英文的)并且不能以数字开头
      * 书写时 区分大小写
      * 书写变量名时。不能使用关键字或者保留字作为变量名
      *   * 关键字:在js中县有特殊含义的字母组合
      *         var if for function
      *   * 保留字:在js中目前没有特殊含义,但在将来的某个版本可能会用到,所以此时也不能使用
          *         在ES6还没出现之前具有的保留字
          *               let const promise
2.规范(建议遵守,如果不遵守也不会报错)
     * 变量名具有含义,符合见名知意
           尽量不要用字母作为变量名,除非是练习
     * 多个单词的组合命名时,使用驼峰命名法(多个单词拼接时,后一个单词的首字母大写)
           eg:bigbox -> bigBox
           
     // 符合命名规则
     var a = 100
     var a1 = 100
     var a1_ = 100
     var a1_$ = 100

     // 不符合命名规则
     // var 6qwer = 100 //这种写法变量名开头是数字,不符合变量命名的规则,所以会报错

     // 变量名区分大小写
     var A = 100;
     var a = 200;

     console.log(A);//A = 100
     console.log(a);//a = 200

4、JS的数据类型

JS 中变量可以存储 任意类型的值
JS 中数据类型的分类(基本数据类型)
①.Number 类型
 * 数字类型, 不区分 整数浮点数
 *  100 200 1.1 0 -6 -999都是数字
 ```js
     var a = 100
 ```
②.String 类型
 * 字符串类型:只要是引号包裹的内容,就是字符串类型(引号不区分单双)
 ```js
      var str1 = '你好'
      var str2 = "你好"
      var str3 = 'qwer'
      var str4 = '123' //String 类型
      var str5 = 123   //Number 类型
 ```
③.undefined 类型
 * undefined 类型:他是一个数据,表明未定义
 *            变量只定义,不赋值的时候,默认的值就是undefined 表明未定义
 ```js
     var un = undefined  
     var z 
     console.log(un)//定义一个变量,变量名为un,并给它赋值为undefined
     console.log(z)//定义一个变量,变量名为z,没有赋值操作,所以变量默认的值为undefined
 ```
④.Boolean 类型 (后期更多的 用在判断上)
 * 只有两个值 一个是true 一个是false
 *     * true:代表真,正确的
 *     * false:代表假,错误的
 ```js
     var boo1 = true; //定义一个变量,名为boo1,值为布尔值,true
     var boo3 = false;//定义一个变量,名为boo2,值为布尔值,false
     var boo3 = 'true' //因为true是在引号内的,所以boo3是一个字符串类型的
 ```
⑤.null 类型
 * 代表空
 *   * 本身是一种数据类型,只不过代表的是空(啥也没有)
 *   * nullundefined 不同!!
 *   * 使用场景:在创建对象的时候,如果开始不知道对象内部有什么,可以给一个null
 *               类似于在创建变量的时候,只定义,不赋值默认是一个 undefined
 ```js
     var nu = null; //定义一个变量,名为nu,值为null类型,代表空
     console.log(nu);
 ```
* *延申:一道面试题
JS 中数据类型有哪些?

5、JS 的数据类型检测

通过一个方法检测 数据类型的值是什么
 * 语法:typeof(要检测的数据类型的值)
 * typeof 的问题,不能正确检测出null 这个类型的实际类型,检测nuLl 的时候打印结果为object
 *      null代表的含义是空,也就是空对象,所以 typeof会把他当成一个对象去输出
  var str1 = '100'
  var str2 = 100
  var str3
  var str4 = true
  var str5 = false
  var str6 = null

  console.log(typeof(str1)) //string 类型
  console.log(typeof(str2)) //number 类型
  console.log(typeof(str3)) //undefined 类型
  console.log(typeof(str4)) //boolean 类型
  console.log(typeof(str5)) //boolean 类型
  console.log(typeof(str6)) //object
* 面试题:typeof 能正确检测所有数据类型的值吗?
 * 不能。因为他没有办法准确的检测出null的类型是什么
* JS如何正确检测null的类型呢? 欠着,以后会有专门的方法讲解

6、JS的数据类型转换

(1).转换为数字类型
* ①.number(数据)
 *     可以用于任何数据类型,将其转换为数字类型
 *        + 字符串转数字
 *             如果转换的字符串是纯数字组成的。那么直接转换为数字
 *             如果转换的字符串是空字符串或者空白字符串,那么直接转换为数字0
 *             其他类型的字符串(不是空---空自---纯数字字符串)会转换为NaN
 *                 NAN:表示的是一个number的数字,但是没有办法通过常规的数字表明它,所以只能使用NaN来代替(坏掉的数字)
 ```js
     console.log(typeof(Number('100')),Number('100'));//number 100 会将'100’转换为数字然后打印到控制台
     console.log(typeof(Number('')),Number(''));//number 0
     console.log(typeof(Number(' ')),Number(' '));//number 0
     console.log(typeof(Number('abc')),Number('abc'));//number NAN
 ```
 *        + 布尔值转数字
 *            true转换为1
 *            false转换为0
 ```js
     console.log(typeof(Number(true)),Number(true));//number 1
     console.log(typeof(Number(false)),Number(false));//number 0
 ```
 *        + undefined转数字
 *            会直接转换为NaN
 ```js
     console.log(typeof(Number(undefined)),Number(undefined));//number NAN
 ```
 *        + null转数字
 *            会直接转换为0
 ```js
      console.log(typeof(Number(null)),Number(null)) //number 0
 ```
* ②.parseInt(数据) 转换为整数
 * 会将数据转换为number类型,并且值是整数(直接忽略掉小数点以后的值,并不是四舍五入)
 * 在转换的时候,如果数据是绒数字字符串或者是数字开头的字符串,那么会转换为数字,否则就是转换为NaN
  ```js
      console.log(typeof(parseInt('100')),parseInt('100')) //number 100
      console.log(typeof(parseInt('100.123456789')),parseInt('100.123456789')) //number 100
      console.log(typeof(Number('100.123456789')),Number('100.123456789')) //number 100.123456789
      console.log(typeof(parseInt('abc')),parseInt('abc')) //number NAN
      console.log(typeof(parseInt('100abc')),parseInt('100abc'))//number 100
      console.log(typeof(parseInt('999abc')),parseInt('999abc'))//number 999
      console.log(typeof(parseInt('abc666')),parseInt('abc666'))//number NAN
      console.log(typeof(parseInt('')),parseInt(''))//number NAN
      console.log(typeof(parseInt(' ')),parseInt(' '))//number NaN
      console.log(typeof(parseInt(true)),parseInt(true))//number NAN
      console.log(typeof(parseInt(false)),parseInt(false))//number NaN
      console.log(typeof(parseInt(undefined)),parseInt(undefined))//number NAN
      console.log(typeof(parseInt(null)),parseInt(null))//number NaN
 ```
* ③.parseFloat(数据) 转换为小数
 * 转换结果与parseInt类似,但是在转换小数的时候会保留小数点后的数字
 ```js
     console.log(typeof(parseFloat('100.123456789')),parseFloat('100.123456789'))
     //number 100.123456789
     console.log(typeof(parseFloat('100')),parseFloat('100'))//number 100
     console.log(typeof(parseFloat('100abc')),parseFloat('100abc'))//number 100
     console.log(typeof(parseFloat('999abc')),parseFloat('999abc'))//number 999
     console.log(typeof(parseFloat('abc')),parseFloat('abc'))//number NaN
     console.log(typeof(parseFloat('abc666')),parseFloat('abc666'))//number NaN
     console.log(typeof(parseFloat('')),parseFloat(''))//number NaN
     console.log(typeof(parseFloat(' ')),parseFloat(' '))//number NaN
     console.log(typeof(parseFloat(true)),parseFloat(true))//number NaN
     console.log(typeof(parseFloat(false)),parseFloat(false))//number NaN
     console.log(typeof(parseFloat(undefined)),parseFloat(undefined))//number NaN
     console.log(typeof(parseFloat(null)),parseFloat(null))//number NaN
 ```
* ④.扩展:在实际开发中 我个人常用 数据 - 0 (暂时先不考虑原理)
```js
      console.log(typeof('100'),'100')//string 100
      console.log(typeof('100' - 0),'100' - 0)//number 100
      console.log(typeof('100abc' - 0),'100abc' - 0)//number NaN
      console.log(typeof('abc100' - 0),'abc100' - 0)//number NaN
 ```
(2).转换为字符串类型
转换为字符串类型
 * +. 变量.toString()方法
 *       注意:undefined 类型和 null不能使用toString方法(因为JS没有给我们提供,或者说Js不允许)
 ```js
      var str1 = 100
      console.log(typeof(str1.toString()),str1.toString()) //string 100
      var str2 = true
      console.log(typeof(str2.toString()),str2.toString()) //string true
      var str3 = false
      console.log(typeof(str3.toString()),str3.toString()) //string false
      var str4 = undefined
      console.log(typeof(str4.toString()),str4.toString()) 
      //这里会报错,因为undefined不能使用toString
      var str5 = null
      console.log(typeof(str5.toString()),str5.toString()) 
      //这里会报错,因为null不能使用toString*/
 ```
 * +. String(变量)
 *       什么类型都可以转换为字符串类型的,包括undefinednull类型
 ```js
      var str1 = 100
      console.log(typeof(String(str1)),String(str1))//string 100
      var str2 = true
      console.log(typeof(String(str2)),String(str2))//string true
      var str3 = false
      console.log(typeof(String(str3)),String(str3))//string false
      var str4 = undefined
      console.log(typeof(String(str4)),String(str4))//string undefined
      var str5 = null
      console.log(typeof(String(str5)),String(str5))//string null
 ```
 * +.扩展:开发时常用的一个方法 
 *       变量 + ''       (暂时不考虑原理)
 *     转换结果与string类似,写法上更简单一些,所以推荐使用
 ```js
      var str1 = 100
      console.log(typeof(str1 + ''),str1 + '')//string 100
      var str2 = true
      console.log(typeof(str2 + ''),str2 + '')//string true
      var str3 = false
      console.log(typeof(str3 + ''),str3 + '')//string false
      var str4 = undefined
      console.log(typeof(str4 + ''),str4 + '')//string undefined
      var str5 = null
      console.log(typeof(str5 + ''),str5 + '')//string null
 ```
(3).转换为布尔类型
BooLean(变量)  将其他数据类型,转换为布尔值(也就是转换为 true 或者 false)
①.数字转换布尔值,只有0会转换为false 其他的数字都会转换为true(非0即为真)
```js
    var num1 = 100
    var num2 = 0
    var num3 = -1
    console.log(num1,'---->',Boolean(num1))//100 --->true
    console.log(typeof(Boolean(num1)),Boolean(num1))//BooLean true
    console.log(num2,'---->',Boolean(num2))//0 --->false
    console.log(num3,'---->',Boolean(num3))//-1 --->true
```
②.字符串转换布尔值,只有空字符串会转换为 false其他的字符串都会转换为 true
```js
    var str1 = ''
    var str2 = ' '
    var str3 = 'abc'
    var str4 = '0'
    console.log(str1,'---->',Boolean(str1))//'' --->false
    console.log(str2,'---->',Boolean(str2))//' ' --->true
    console.log(str3,'---->',Boolean(str3))//'abc' --->true
    console.log(str4,'---->',Boolean(str4))//'0' --->true
```
③.undefined 和 null 转换为布尔值的时候就是false(重点,以后会常用!!!)
```js
    var und = undefined
    var nul = null
    console.log(und,'---->',Boolean(und))//und --->false
    console.log(nul,'---->',Boolean(nul))//nul --->false
``` 
#### **7、JS的运算符**
①.算术运算符
 * 加减乘除  取余(求余数)
 * + - * /   %
 * 注意:+ 运算符,相加时如果两边都是数字,那么会运算数字的相加操作
 *                相加时如果两边有一个不是数字的,比如出现一个字符串,那么运行的  不是在相加,而是拼接操作
 *    拼接操作只有+运算时会出现,其他符号不出现
 *    其他运算符在遇到两边有非数字类型时,会将其转换为数字(这一步就叫做隐式操作),然后在运算
 ```js
     console.log(1 + 1) //先将小括号内的运算完毕,然后打印在控制台
     console.log(3 + 4) //7
     console.log(5 + 0) //5
     console.log(5 + '0') 
     //数字5+字符串0  因为加号两边出现了一个非数字类型的字符串,所以会运行两个值的拼接 50
     
     console.log(1 - 1) //0
     console.log(9 - 4) //5
     console.log(5 - '0') //5
     // 两边出现了一个字符串类型的0,JS 会帮助我们将他的类型先转换为数字 然后在运算减法 
     // 也就是会计算  数字5 - 数字0 ---> 5
     
     console.log(1 * 1) //1
     console.log(3 * 4) // 12
     console.log(5 * '0')
     //两边出现了一个字符串类型的 0,JS会帮助我们将他的类型先转为数字,然后在运算减法
     //也就是会计算 数字5 * 数字0 ---> 0
     
     console.log(5 / 1) //5
     console.log(8 / 2) //4
     console.log(6 / '2')
     //两边出现了一个字符串类型的'2',JS会帮助我们将他的类型先转为数字,然后在运算除法
     // 也就是会计算 数字6 / 数字2 -->3
     
     console.log(5 % 1) //取余0
     console.log(10 %  3) //计算10除以3 取余1
     //两边出现了一个字符串类型的'2',JS会帮助我们将他的类型先转为数字,然后在运算除法
     // 也就是会计算 数字10 % 数字3 -->1
     
     console.log(5 / 0) //Infinity 表明 无穷的意思
     console.log(5 % 0) //NaN
     // 0.1 + 0.2  不等于 0.3
 ```
②.赋值运算符
 * = 一个赋值号
 * +=  相当于是“我自身”加上一个新值。就可以用这个 += 来优化,例子:a = a + 10 优化后:a += 10
 * -= *= /= %=   运算逻辑与+=是完全相同的
 ```js
      //+=
      var a = 1
      a = a + 10 //将变量a重新赋值为变量a + 10  a = a + 10   a = 1 + 10   a = 11
      console.log(a) //11
      
      var a = 1
      a += 10 // a += 10 ---> a=a+10--->a=1+10-->a=11
      console.log(a)//11
      
      // -=
      var a = 5
      a -= 5 //将变量a重新赋值为 变量a - 5 a= a - 5 -->a = 5 - 5-->a = 0
      console.log(a)//0
      
      // *=
      var a = 2
      a *= 2// a *= 2 --->a = a * 2 --->a = 2 * 2 --->a = 4
      console.log(a)//4
      
      // /=
      var a = 4
      a /= 4// a /= 4 ---> a = a / 4 --->a = 4 /4 --->a = 1
      console.log(a) //1
      
      // %=
      var a = 10
      a %= 3 //a %= 3 ---> a = a % 3 ---> a = 10 % 3 --->a = 1
      console.log(a)//1
 ```
③.比较运算符
比较运算符 比较运算符两边的值之后,返回一个布尔值,也就是返回一个true或者false
 * 判断
 *    > < >= <=
 * 判断相等
 *    == ===  在js中等号出现两次或者三次时代表等于的意思
 *
 * 判断不相等
 *    !=  !==
 *    共同点:判断符号两边的值是否不相等,不相等时返回true  相等时返回false
 *    不同点
 *         != 不会对比数据类型
 *         !-= 会对比数据类型
 * */ 
 ```js
     //  比较两边的值,然后返回一个布尔值,也就是返回一个true或者false
     console.log(3 > 4)
     //打印时先看3是否大于4,因为3小于4,不满足大于4的条件,所以返回的是 false
     console.log(3 < 4) //打印时先看4是否大于 3,因为4大于3,满足条件,所以返回的是true
     console.log(5 < 6) //因为5<6 满足条件,所以返回的是true
     console.log(6 < 5) //因为6<5不满足条件,所以返回的是false
     
     console.log(1 > 1)
     console.log(1>=1)//判断1是否大于或等于1 此时满足第二个条件也就是1等于1 所以会返回true
     
     console.log(1<1)
     console.log(1<=1) 
     //判断1是否小于或等丁于1 此时满足第二个条件也就是1等于1 所以会返回true
     
     console.log(1 == 1)//判断符号两边的值是否相等true
     console.log(99 === 99)//判断符号两边的值是否相等true
     
     console.log(1 == '1') //判断符号两边的值是否相等(不会对比数据类到) true
     console.log(1 === '1')//判断符号两边的值是否相等(这里会对比数据类型) false 因为数据类型不同!
     
     console.log(3 != 4)//7断符号两边是否不相等true
     console.log(9 !== 10)//7断符号两边是否不相等true
     
     console.log(3 != '3') 
     //判断符号两边是否不相等 且不会对比数据类型,所以这他认为3和"'3’是相等的 所以会返回false
     console.log(3 !== '3') 
     //判断符号两边是否不相等 且会对比数据类型,所以这他认为3和"'3’是不相等的 所以会返回true
 ```
* 两个等号和三个等号的区别(面试题)
 * == 在的js中判断时会做隐式转换,也就是说只会对比值是否相等,不会对比数据类型
 * === 在JS中也叫做全等,在判断的时候不会做隐式转换,也就是说,
         在对比时除了会对比值是否相等,还会判断数据类型是否相同
④.逻辑运算符
 * &&  逻辑与(相当于并且的意思)
 * ||  逻辑或(相当于或者的意思)
 * !  逻辑非(取反,反义词)
 * 
 * !逻辑与运算符(&&)的特性!!!
 *      符号左边的值 如果为真(也就是转为布尔值后为true),那么会返回符号右边的值
 *                  如果为假(也就是转为布尔值后为false),那么会将自身返回出去
 * !逻辑或运算符的特性!!!
 *      符号左边的值  如果为真,那么会将自身返回出去
                     如果为假。那么会将符号右的值返回出去
 * !逻辑非运算符的特性!!!
        语法:!值
        返回值:将值转换为布尔值之后,做一个取反的操作。也就是true改变falsefalse改变为 true
 * */ 
 ```js
     //  &&
     console.log(1 && 100) //根据逻辑与运算符的特性,返回的是100
     console.log(0 && 99) //根据逻辑与运算符的特性,返回的是0
     console.log(true && 'qwer') //qwer
     console.log(false && 'qwer') //false
     
     //  ||
     console.log(1 || 100) //根据逻辑或的特性,返回的是1
     console.log(0 || 99) //根据逻辑或的特性,返回的是99
     
     // !(布尔值中除了0都是true)
     console.log(!99) //!99 ---> !true --->false
     console.log(!0) //!0 ---> !false --->true
     console.log(!true) //false
 ```
⑤.自增自减运算符
 ++   --
 * 语法:(自增自减都是相同的语法,这里就拿自增为例)
 *  1.++值
 *  2.值++
 * 
 *    自增运算符的能力就是将自身的值+1
 *    自减运算符的能力就是将自身的值-1
 * 
 *    自增自减运算符写法不同,运行结果也不同
 *       1. ++值(++在前)
 *              如果++在前,那么会先将自身的值+1  然后参与其他的
 *       2. 值++(++在后)
 *              如果++在后,那么会先参与周围的运算,然后将自身的值+1
 * 
 * 自减运算符在前与在后的作用与自增运算符完全相同,只不过是-1
 ```js
     var a = 1
     var b = 1
     
     console.log(++a) 
     //如果++在前,那么会先将自身的值+1 然后打印到控制台
     console.log(b++) 
     //如果++在后,那么会先将目前的值打印到控制台(也就是1),然后将自身的值+1   也就是说这行运行完华后,变量b的值变为2了
     console.log(a,b) //2,2
 ```

8、JS的优先级

 * 运算符优先级从高到低
 *    自增自减运算符++ -- !
 *    算术运算符 先* / % 后+ -
 *    比较运算符中的 > >= < <=
 *    比较运算符中的 == != === !==
 *    逻辑运算符 先&& 后||
 *    赋值运算符

9、课后练习

 ```js
     //1. `var a = 3; var b = a++; a + b + a++`该代码输出的值为11
     var a = 3; 
     var b = a++; //a++先赋值后自增  所以b=3 a=4
     console.log(a + b + a++)// a + b + a++ = 4 + 3 + 4 =11
     //2. `console.log(parseInt(1 * 100 / 3 + ""))`该代码输出的值为33
     //100 / 3 + "" = "33.33333" = 33
     //3. `var a = 3; a++; a++; var b = a+2; ++a; console.log(a)` 该代码输出的值为6
     //b = 7  a = 6
     //4. `3 - "36" % 5 + "2" - 2`该代码输出的值为20
     //36 % 5 = 1    3 - 1 + "2" - 2 = 22 - 2 = 20
     // 5. `var a = 4; var num = 1 * (2 + 3) && a++ || 5 > 6 && 7 < 8 || !9;` num的值是 4
     // num = 4 || false || false = 4
     //6. 在不考虑四舍五入的情况下对一个数字进行保留两位小数(提醒: 使用 parseInt)
    ```JS
        var orign = 123.456789
        // code run here ...
        // 输出结果: 123.45
    
        var orign1 = 987.654321
        // code run here ...
        // 输出结果: 987.65
    ```
var orign = 123.456789
console.log(typeof (parseInt(orign)), parseInt(orign * 100) / 100)
 ```

10、JS的分支语句

分支语句:
 * 根据我们设置好的条件!!!  然后来决定执行那些代码
 * 
①.if 分支语句
     语法:
     *   if(条件) {满足条件的时候执行的代码,如果不满足,这里边的代码永远不执行}
     *    条件最终会返回一个布尔值,如果为真,那么会执行对应的代码
     *                            如果为假,那么不会执行
 ```js
      // if 分支语句的基本写法
      var a = 1
      // if(条件){要执行的代码}
      if (a === 1) {//如果a === 1这个条件成立,那么会执行打印1
          console.log(1)
      }
      if (a === 2) {//如果a === 2这个条件成立,那么会执行打印2
          console.log(1)
      }
      // if...else...的嵌套写法
      var a = 100
      if(a === 1) {
          console.log(1)
      } else if(a === 2) {
          console.log(2)
      } else {
          console.log('a 不等于 1或者2')
      }
   /**
     * 运行流程:
     *    首先判断α是否等于1,如果满足条件,直接打印1,并结束这个分支
     * 
     *    如果不满足,会进入下一个分支的判断,判断a是否等于2,如果满足条件,直接打印2,并结束这个分支
     * 
     *    最后结尾的else分支 只会在前边所有的条件都不满足的时候 才会执行,前边如果有一个条件成立,那么 else 就不会执行
     * */ 
 ```
if分支的课堂练习
    // 1. 判断一个正整数, 是不是偶数, 如果是偶数, 在控制台打印 "偶数", 否则在控制台打印 "不是偶数"
    // 偶数的特点:能够被⒉整除,也就是说一个数字取余 2 如果正好等于0,那么就是偶数
    var num = 12
    if(num % 2 === 0) {
      console.log('偶数')
    }else {
      console.log('不是偶数')
    }
    // 2.判断一个数字是否在某一个区间,例子:判断数字是否在10~20之间
    var a = 12
    if (10 <= a && a <= 20) {
      console.log('数字在10~20之间')
    } else {
      console.log('数字不在10~20之间')
    }
    console.log(10<=9<=20)
    console.log(10<=9) //false
    console.log(false<=20) //首先将false 转换为数字(也就是0),然后和20做比较
    console.log(true);//得到一个布尔值
    // 3.根据成绩(0~100)在控制台输出不同的内容
    var num = 62

    if(num >= 90) {
      console.log('优秀');
    }else if (num >= 80) {
      console.log('中等')
    }else if (num >= 70) {
      console.log('及格')
    }else if (num >= 60) {
      console.log('要努力')
    }else {
      console.log('你危险了')
    }
    // 4.判断一个年份是不是闰年  闰年的条件: 1. 4年一闰,百年不闰 2. 四百年补一闰

    var num = 2023
    if (num % 4 === 0 && num % 100 !== 0 || num % 400 === 0) {
      console.log('是闰年')
    } else {
      console.log('不是闰年')
    }
②.switch 分支语句
if 相同 ,也属于条件分支语句
 * if(要判断的变量) {
 *     想要执行的代码
 * }
 * 
 * switch (要判断的变量) {
 *    case 情况1:
 *        情况1要执行的代码
 *        break;
 *    case 情况2:
 *        情况2要执行的代码
 *        break;
 * }
 * 
 * 判断逻辑:判断的变量 是否 ===case上说明的情况
 * 
 * 注意:switch...case 在判断时,执行时全等,也就是 === 所以数据类型不同时,也不会正确执行
 ```js
 var a = 100
 switch (a) {
  case 1:
    console.log('如果我执行,说明 a === 1')
    break;
  case 10:
    console.log('如果我执行,说明 a === 10')
    break;
  case 100:
    console.log('如果我执行,说明 a === 100')
    break;
  default:
    console.log('上述情况都不满足的时候,我会执行')
 }
 ```
③.switch穿透语法
switch 在书写的时候 如果不写break 会出现穿透现象
 * 
 * 穿透现象: 
 *     找到第一个满足的case的时候,开始执行这个代码
 *     执行完毕如果没有break 会继续执行下一个case
 *     直到 遇到一个break 或者 分支语句全部执行完毕
 ```js
 var a = 100
 switch (a) {
  case 1:
    console.log('case 1 执行')
    // break;
  case 10:
    console.log('case 10 执行')
    // break;
  case 20:
    console.log('case 20 执行')
    // break;
  case 30:
    console.log('case 30 执行')
    // break;
  case 40:
    console.log('case 40 执行')
    // break;
  case 100:
    console.log('case 100 执行')
    break;
  default:
    console.log('上述情况都不满足的时候,我会执行')
 }
 ```
 ```js
 /**
 * 穿透语法
 *      根据月份,输出相应的天数 (2月按照28天)
 * */ 
 var month = 11
 switch (month){
  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('2月有28天')
    break
 }
 ```
switch课堂练习
    /**
     * 课堂练习
     *      根据成绩(0~100)冉控制台输出对应的成绩
     * */ 

     var num = 67
    //  求十位上的数字 (parseInt)
    // parseInt只能忽略掉小数点以后的所有内容
    // 如果我们想要只留下十位上的数字 那就应该想办法让这个数字多一个小数点

     switch (parseInt(num/10)) {
      case 10:
        console.log('完美')
        break
      case 9:
        console.log('优秀')
        break
      case 8:
        console.log('中等')
        break
      case 7:
        console.log('及格')
        break
      case 6:
        console.log('要努力')
        break
      case 5:
        console.log('你危险了')
        break
     }

11、三元表达式

/**
 * 三元表达式
 *     别名:三目表达式 三目运算符 三月 问号冒号表达式
 * 
 *     语法: 条件 ?条件为真时,执行的代码 : 条件为假时,执行的代码
 * 
 *      意义:对if分支语句做一个简化操作
 *
 *      注意:不管条件真还是假的代码!! 都只能写一行
*/
```js
    var a = 100
    a > 1 ? console.log('如果我输出了,说明 a 的值 大于 1'):console.log('如果我输出了,说明 a 的值小于 1')
    //  利用三元表达式给变量赋值
    var num = 1 //约定,如果num ===1那么代表为男性如果,num !== 1那么代表为女性
    var gender = num === 1 ?'男':'女'
    console.log(gender)
```

12、while循环语句

①.while循环语句
意义:帮助我们去执行 重复的代码
 * 
 *    什么是循环语句
 *          根据给出的某些条件,重复执行一段代码
 * 
 *     循环语句
 *        1.初始化
 *        2.条件判断
 *        3.要执行的代码
 *        4.改变自身(改变初始化的内容)
 * 
 *    while 循环
 *          语法: while (条件) {满足条件时执行的代码}
 ```js
     //  需求,想让这个代码执行1000次  console.log(1)
     var num = 0 //1.初始化
     while (num < 1000) { //2.小括号内的是 条件判断
         console.log(1)//3.要执行的代码
         num++  //4.改变自身
     }
 ```
②.while循环的课堂练习
 ```js
     // 1. 求 1 ~ 100 之间的 所有数字相加的和
     var num = 1//1.初始化
     var sum = 0//用于存储1~100之间所有数字相加的和
     while (num <= 100) {//条件判断
         // console.log(num)//执行代码
         sum = sum + num//sum += num
         num++//4.改变自身
     }
     console.log(sum)//5050
 ```

13、循环语句练习

```js
// 1. 用户输入年份和月份, 判断当月有几天
/**
 * 核心
 *    1.有用户输入年份和月份
 *    2.判断当月有几天 (2月的时候还需要判断当年是否为闰年)
 *      闰年的条件1:4年一润 百年不润
 *            条件2:四百年补一润
 * */ 
var year = prompt('请输入年份') - 0
var month = prompt('请输入月份') - 0//- 0 的目的将月份转换成数值
switch (month){
  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:
    // 如果输入的是2月需要先判断闰年
  year % 4 === 0 && year % 100 !== 0 || year % 400 === 0?console.log('因为',year,'是闰年,所以2月有29天'):console.log('因为',year,'不是闰年,所以2月有28天')
  break;
 }
```
```js
// 2. 求 1~100之间 所有3的倍数的和
/**
 * 核心
 *    1.找出1-100之间所有的数字
 *    2.找出1-100之间3的倍数的数字
 *    3.将所有3的倍数的数字求和
 * */ 
var num = 1//1.初始化1-100
var sum = 0
while(num <= 100) {//2.条件
  // 找出了所有3的倍数的数字
  if(num % 3 === 0) {//3的倍数的条件
    // console.log(num)//3.执行代码
    sum = sum + num
  }// sum = sum + (num % 3 ===0 ? num : 0)
  num++//4.改变自身
}
console.log('所有3的倍数的和为:'+sum)//1683
```

14、do...while循环

①.do...while循环
    是一个类似 while 的循环语句
 * 
 * while循环在开始的时候,会先判断条件是否成立 然后决定是否执行
 * do..while 循环在开始第一次的时候,不会判断条件,也就是说,不管条件成功与失败,第一次都会执行

 var num = 10
 do{
  console.log(num)
  num--
 }while(num < 5)
/**
 * do. . .whiLe循环的时候,第一次的时候不会管这个条件是否成立,他一定会执行一次
 * */  
 ②.do...while循环练习
 ```js
     /**
     * 需求:用户输入密码,密码正确,正常向下执行,密码错误,让用户重新输入密码
     * */ 
     
     /**
     * 流程:用户打开页面,输入一段密码
         由程序检测,如果密码正确,那么继续向下执行
         如果密码不正确,那么让用户重新输入密码
     * */  
     var password;
     do {
         password = prompt('请输入您的密码')
     }while(password !== '12345')
     //如果password != 12345 那么继续执行循环,如果密码正好是‘12345’那么停止循环
 ```

15、for 循环

for 循环
 *      也是 循环语句的一种,但是 语法上和另外两个不同!
 *      语法:for (1.初始化 2.条件 3.改变自身){4.循环要执行的代码}
 *      目前for循环的使用场景来看,要稍微多一点,但不代表可以完全替代 while 循环或者do...while循环
 ```js
     // 用for循环重构
     for(var n = 0; n < 3; n++) {
         console.log(n)
     }
 ```
 ②.for循环练习
 ```js
     /**
     * 1.求1~100之间的所有的和
     * */ 
     /**
     * 核心
     *    1.求出1~100之间所有的数字
     *    2.求出所有数字的和
     * */  
     var sum = 0
     for (var i = 1; i <= 100; i++) {
         sum += i
     }
     console.log(sum)
     
     //  2.求1~100之间的所有偶数的和
     /**
     * 核心
     *    1.先打印1-100内所有数字
     *    2.打印1-100内的偶数
     *    3.打印1-100内所有偶数的和
     * */ 
     var sum = 0
     for (var i = 1; i <= 100; i++) {
         if(i % 2 ===0 ){
             // console.log(i)
             sum += i
         }
     }
     console.log(sum)
     
     // 3.课后练习:求出1000-2022之间的所有闰年,输出在页面
     /**
     * 核心内容
     *      1.打印出1000-2022年的年份
     *      2.算出闰年
     *      3.打印出所有闰年
     * */
     for (var year = 1000; year <= 2022; year++){
         // console.log(year)
         if(year % 4 === 0 || year % 100 === 0 || year % 400 ===0){
             console.log(year,'是闰年')
         }
     }
     
     /**
     * 4.求 水仙花
     *        有一个三位数(100~999) 各个位置上的数字的三次方的和 正好等于自身
     *        那么说明这个数字就是水仙花数
     * */ 
     
     /**
     * 核心内容
     *      1.找出所有的三位数
     *      2.找出符合条件的数字
     *        条件:各个位置上的数字的三次方的和 正好等于自身
     * 
     *        2.1 先计算各个位置上的数字的值是什么,也就是想办法拿到各位 十位 百位上的数
     *        2.2 计算 三个位置上的数字 的三次方和
     *        2.3判断三次方的和是否等于自身
     * */  
     
     for(var i = 100; i <= 999; i++){
         // console.log(i)
         // 根据水仙花数的规则,找出符合条件的数字
         // 先计算各个位置上的数字的值是什么,也就是想办法拿到个位十位百位的值
         /**
         * 就拿123为例
         *  拿到百位数 先缩小100倍,然后用parseInt忽略掉小数点后的数字,就只留下百位上的数字了
         *  拿到十位数 先缩小10倍,然后取余10,得到的数字 通过perseInt取整后,就只留下十位上的数字了
         *  拿到个位数 直接取余10,就留下了十位数字了
         * */ 
         
         var baiW = parseInt(i / 100)
         var shiW = parseInt(i / 10 % 10)
         var geW = i % 10
         
         // 计算三个位置上的数字的三次方的和
         var sum = Math.pow(baiW,3)+Math.pow(shiW,3)+Math.pow(geW,3);// var sum = baiW * baiW *baiW + shiW * shiW * shiW + geW * geW * geW
         if(sum === i){
             console.log(i,'是一个水仙花数')
         }
     }

// JS求 3次方的简写 Math.pow(底数,指数)
 ```

16、流程控制语句

* 通过两个关键字,可以起到控制循环的一个作用,这就是流程控制
 * 
 *    1. break(结束掉整个循环,不管循环后边还有几轮)
 *      今天早上我买了五个包子,然后呢我吃了3包子,这个时候我吃饱了,我吃不下去了 此时我就结束掉了吃包子这件事,也就是说第四个和第五个包子我就不吃了
 ```js
     for(var i = 1; i <= 5; i++) {
         console.log('我吃了一个包子')
         if(i === 3) {
             break //当循环执行到i === 3的时候,此时这个if判断的条件成立,开始执行内部代码,也就是执行 break 结束整个循环
         }
     }
 ```
 *    2.continue(跳出当前这一轮循环,直接开始下一轮循环)
 *      今天早上我买了五个包子,然后呢我在吃第三个包子的时候,这个时候,第三个包子掉地上了,所以这个包子我不吃了 此时我开始吃第四个包子与第五个包子
 ```js
     for (var i = 1; i <= 5; i++) {
         if(i === 3) {
             console.log('第三个包子掉地上了,我不吃这个包子了')
             continue
         }
         console.log('我吃了一个包子')
     }
 ```

17、循环嵌套

* 注意:外层循环的变量与内层循环的变量不要相同
```js
for (var j = 0; j < 3; j++) {
    for (var i = 0; i < 3; i++) {
      console.log(i)
    }
    console.log('j的值为',j)
}
```
for循环课堂案例
    * 需求:在页面上打印9个*
    document.write('*') //这个方法能够将我们需要的内容直接输出在页面上,而不是控制台面板
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')

    // 需求:在页面上打印两行9个*
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('<br>')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')
    document.write('*')

    // 在页面输出一个9 * 9的方阵
    for(var j = 0; j < 9; j++) { //外层循环:控制有多少行
      for (var i = 0; i < 9; i++){//内层循环:控制一行有多少个*
        document.write('*')
      }
      document.write('<br>')
    }

    // 在页面输出一个三角形
    for(var j = 0; j < 9; j++) { //外层循环:控制有多少行
      for (var i = 0; i < j; i++){//内层循环:控制一行有多少个*
        document.write('*')
      }
      document.write('<br>')
    }

    //在页面输出九九乘法表
    for(var j = 1; j <= 9; j++) { //外层循环:控制有多少行
      for (var i = 1; i <= j; i++){//内层循环:控制一行有多少个*
        sum = i * j
        document.write(i , '*' , j , '=' , sum,'  ')
      }
      document.write('<br>')
    }

18、课后练习

```js
    //1. 用户输入一个整数 n,计算 n 的阶乘。即`n*(n-1)*(n-2)*……*3*2*1`
    var sum = 1 //初始化
    for (var n = parseInt(prompt('请输入n的值')); n >= 1; n--) { //条件
        // 计算这个数字的阶乘
        sum = sum * n //执行代码
    }
    console.log(sum)
    
    /* var num = prompt('请输入一个数字') - 0;
    var sum = 1
    for(var i = 1; i <= num; i++) {
        sum *= i
    }
    console.log(sum);*/
    
    //2. 苹果 3 元一个, 鸭梨 2 元一个, 桃子 1 元一个。现在想用 200 元正好买 100 个水果, 用 JS 列出所有购买方案
    /**
    * 核心内容
    *  条件:
    *    1.买三种水果一共花200元 3*苹果+2*鸭梨+1*桃子=200
    *    2.买三种水果一共买100个 苹果+鸭梨+桃子=100
    * */ 
    for(var a = 0; a <= 100; a++){
        for(var b = 0; b <= 100; b++) {
            for(var c = 0; c <= 100; c++) {
                if(a + b + c === 100 && 3*a + 2*b + c === 200){
                    console.log('购买方案为','苹果',a,'鸭梨',b,'桃子',c)
                }
            }
        }
    }
```
#### **19、函数的概念**
首先一定要明确,和数学中的函数完全是两回事
 * 
 *      在JS 中,函数可以理解为一段在程序(页面)中多次出现的代码段 封装起来的盒子
 * 
 *      简单来说,JS 的函数就是一个盒子,盒子里边装的是﹑在当前页面中多次出现的较为复杂的代码段

20、函数的使用

* 如果函数只定义不调用,那么没有任何意义
 1. 函数的定义(创建一个盒子)
     分为两种方式定义
 *       1.1 声明式定义
 *              语法:function fn (){}
 *                   function: 关键字 -> 表明后续的是一段函数
 *                   fn:       函数的名字 -> 将来函数调用的的候需要用到,函数名自定义
 *                   ():      内部填写参数 -> 欠着,后续详细讲解
 *                   {}:内部填写函数调用时要执行的代码段
 ```js
     function fn1(){
         console.log('我是fn1函数')
     }
     fn1()
 ```
 *       1.2 赋值式定义
 *             语法:var fn = function () {}
 ```js
     var fn2 = function () {
         console.log('我是fn1函数')
     }
     fn2()
 ```
 2.函数的使用(使用盒子内的代码)
     * 不管是声明式还是赋值式定义的函数,调用方式都是一样的
     * 调用语法:函数名() / 变量名()

21、声明式与赋值式的区别

 *      1.写法不同
 *      2.调用上略有不同
 *          声明式定义的函数,可以在 函数定义前 去调用
 ```js
     fn1()
     console.log('我是fn1函数定义前的 输出~~~')
     function fn1(){
         console.log('我是fn1函数')
     }
     fn1()
 ```
 *          赋值式定义函数,不能在函数定义前 去调用
 ```js
     //fn2()
     console.log(fn2) //undefined
     console.log('我是fn2函数定义前的 输出~~~')
     var fn2 = function () {
         console.log('我是fn2函数')
     }
     // fn2()
     console.log(fn2) //打印了fn2这个变量内部存储的值
     console.log(fn2()) //打印了fn2这个函数的执行结果,一般默认是undefined  
 ```
 * 
 *    赋值式定义函数不能在函数定义前调用的原因
 *        赋值式定义,其实就是声明一个变量,然后给他赋值为一个函数
 *         
 *        再JS中,如果再定义变量之前使用变量的话。那么变量的值为undefined (变量提升 面试可能会问)
 * 
 *      函数的执行结果,一般默认都是undefined,除非手动更改

22、函数的参数

①.函数的参数
*      函数的参数如何书写?
*          书写在function后的小括号内
*      参数的作用
*          如果一个函数没有书写参数,那么这个函数的功能相对来说比较单一
*          如果书写了参数,能够使我们这个函数的使用更加灵活
*      参数的书写,分为两个
*          1.function 后的小括号内 书写的参数我们叫做"形参"
        !!! 形参的作用:书写之后,相当于在函数内部创建了一个变量,变量实际的值由"实参”传递
*          2.函数名后的小括号内 书写的参数我们叫做“实参”
        !!! 实参的作用:将自身的值,按照一一对应的关系,传递给形参
```js
    // 一个需求,需要封装一个函数,这个函数内需要计算出1+2的值,并输出在页面上
    function fn() {
        var sum = 1 + 2
        console.log(sum)
    }
    fn()
    
    // 新需求:封装一个函数,这个函数内需要计算5 + 8 的值,并输出在页面上
    function fn1() {
        var sum = 5 + 8
        console.log(sum)
    }
    fn1()
    
    // 新需求:封装一个函数,这个函数内需要计算100 + 200的值,并输出在页面上
    function fn2(a, b) {
        var sum = a + b
        console.log(sum) //300
    }
    fn2(100,200)
    // fn2('我是第一个实参','我是第二个实参')
    
    // 新需求:计算300 + 400的值
    fn2(300,400)
    
    // 新需求:计算365+ 1的值
    fn2(365,1)
```
②.函数参数的注意事项
函数的参数
 * 形参 和 实参  两个的数量,要一一对应
 *    1. 形参的数量如果大于实参
 *         如果形参的数量大于实参的数量,那么会将实参按照顺序一一传递给 对应的形参 多出来的形参,相当于变量只定义没赋值,所以他们的值﹑是undefined
 *    2. 实参的数量如果大于形参
 *         如果实参的数量大于形参的数量,那么会将实参按照顺序一一传递给 对应的形参多出来的实参,无法在函数内部通过参数的方式调用
 ```js
     function fn(a , b , c , d) {
         console.log(a , b , c , d)//1 2 undefined undefined
     }
     fn(1,2)
     
     function fn(a , b) {
         console.log(a , b) //100 200
     }
     fn(100,200,300,400)
     /*
     *函数参数的默认值
     *
     * *函数再创建形参的时候,默认给一个值,将来在调用函数的时候,
     * *如果没有传递那么这个形参的值也不会是 undefined 而是给的默认值
     * 
     * *如果传递了对应的值,那么形参的值是实参传递进来的值,否则按照默认值来运行
     * */
     function fn(a = 100 , b = '我是形参b' , c) {
         console.log(a , b , c) //100 '我是形参b' undefined
     }
     fn()
 ```

23、函数的返回值

* 函数的返回值(函数的执行结果)
 * 
 *      在函数内部创建(定义)的变量,只能在函数内部使用,后续学习作用域的时候会详细讲解
 * 
 *      我们如果想在函数外部得到函数内部的某一个值,或者运算结果,我们可以通过return这个关键字来帮我们完成
 ```js
     //  需求:封装一个函数,这个函数内需要计算100 + 200的值
     function fn(a , b) {
         // 书写返回值
         return a + b
     }
     // 创建变量 接收函数的返回值
     var num = fn(100,200)
     console.log('num的值',num) //300
     
     // 如何书写返回值  如何接收返回值
     function test() {
         return 50 - 10
     }
     var sum = test()
     console.log(sum) //40
 ```

24、课堂练习

 ```js
 // 1.需求: 封装一个函数,这个函数能够计算一个区间内所有数字相加的和
/**
 * 1.封装一个函数
 * 
 * 2.需要参数吗?
 * 
 * 3.需要几个参数?(需要两个参数)
 * 
 * 4.需要返回值吗? 可写可不写,出于学习的目的,我们写上返回值
 * */ 
 function fn(start, end) {
     // 计算从start 到 end 之间的所有数字的和
     var sum = 0
     for(var i = start; i <= end; i++) {
         sum += i
     }
     return sum 
 }
 var num = fn(1, 100)
 console.log('num的值',num) //目前猜测应该为5050
 ```
 ```js
 // 2.需求:封装一个函数 判断 一个数字!!! 是否为水仙花数;是水仙花数,返回一个true,否则返回false
/***
 * 1.封装一个函数
 * 2.需要参数吗?
 * 3.需要几个参数?
 * 4.需要返回值吗?
 **/ 
/**
 * 153 是一个水仙花数·
 * 370 是一个水仙花数'
 * 371 是一个水仙花数·
 * 407 是个水仙花数
 * */ 
// var num = prompt('请输入一个三位数') - 0
function fn(i) {
  var baiW = parseInt(i / 100)
  var shiW = parseInt(i / 10 % 10)
  var geW = i % 10
  var sum = Math.pow(baiW,3)+Math.pow(shiW,3)+Math.pow(geW,3);
  if(sum === i){
    // console.log(i,'是一个水仙花数')
    return true
  }else {
    // console.log(i,'不是一个水仙花数')
    return false
  }
}
var boo = fn(153)
console.log(boo)
 ```

25、return的注意事项

return 的注意事项
 * return 具有中断函数的能力
 * 所以一般来说我们将它放在函数的尾部

26、函数的课后练习

```js
// 1. 封装一个函数, 判断一个数字是否为 水仙花数
// 什么是水仙花数, 一个四位数字, 各个位置的四次方和!!! 四次方和!!! 四次方和  如果等于自身, 那么就是水仙花数1234
/**
* *核心
*     1.封装一个函数*
*     2.需要参数吗?需要
*     3.需要几个?1个(默认是4位数字1000~9999)
*     4.需要返回值吗?
*     出于这个学习的目的我们这里写上返回值 同时我们约定,如果这个数字是水仙花数,返回一个 true否则_返回一个false
*/

function fn(i) {
// 计算 参数i 接收到四位数字,是否为水仙花数
  var qianW = parseInt(i/1000)
  var baiW = parseInt(i/100)%10
  var shiW = parseInt(i/10)%10
  var geW = i%10
  // console.log(i)
  // ** ES6 以后新推出的一个语法
  // var sum = qianW ** 4   
  var sum = Math.pow(qianW,4)+Math.pow(baiW,4)+Math.pow(shiW,4)+Math.pow(geW,4)
  if(sum === i){
    // console.log(i,'是一个水仙花数')
    return (i+'是一个水仙花数')
  }else {
    // console.log(i,'不是一个水仙花数')
    return (i+'不是一个水仙花数')
  }
}
var bool = fn(8569)
console.log(bool)

/**
 * 2. 封装一个函数, 对一个四位数字加密
    加密规则:
            1. 每一位上的数字 +5    然后使用 10的余数替代
            2. 一三交换位置, 二四交换位置
    举例:
        输入 1234
        1. 每一位上的数字 +5 ===> 6789
        2. 使用 10 的余数代替 ===> 6789
        3. 一三  二四  交换位置 ===> 8967

        输入 5655
        1. 每一位上的数字  +5 ===> 0100
        2. 使用 10 的余数代替 ===> 0100
        3. 一三  二四  交换位置 ===> 0001   (这里需要打印0001, 不能打印1)
 * */ 
function fn2(i) {
  // 新需求:判断参数是否为4位数
  if(i >= 1000 && i <= 9999){
    var qianW = parseInt(i/1000)
    var baiW = parseInt(i/100)%10
    var shiW = parseInt(i/10)%10
    var geW = i%10
    // var sum = qianW * 1000 + baiW * 100 + shiW * 10 + geW
    // console.log(sum)
    // 置换
    qianW = (qianW+5)%10
    baiW = (baiW+5)%10
    shiW = (shiW+5)%10
    geW = (geW+5)%10
    // return shiW * 1000 + geW * 100 + qianW * 10 + baiW
    // var sum = shiW * 1000 + geW * 100 + qianW * 10 + baiW
    // console.log(sum)
    // return String(shiW) + String(geW) + String(qianW) + String(baiW)
    return ''+ shiW + geW + qianW + baiW
  }else {
    console.log('传入的数字,不是四位数字')
  }
}
var sum = fn2(5655)
console.log('加密后的数字为:',sum)

// 3. 封装一个函数, 求两个数字的最大公约数
/**
 * 核心
 *    1.封装一个函数
 *    2.需要参数吗?需要
 *    3.需要几个参数? 需要两个参数
 *    4.需要返回值吗? 需要 将最大的公约数,返回 出去
 * 
 * 思考:
 *      如何计算 最大公约数
 *      约数:整数X 除以 y(y!==0)  ,余数%为0,  此时我们说y是x的约数
 * */ 
function fn3(a,b) {
  // for (var i = a; i >= 1; i--){//假设i是a和b的最大公约数
  //   if(a%i === 0 && b%i === 0){//如果能被a和b同时整除,那么i就是a和b的最大公约数
  //     return i
  //   }
  // }
  // 寻找两个数中较小的值
  var min = a > b ? b : a //如果a>b条件成立,说b的值小,所以返回b,否则,说明a的值小。那么返回a
  // 2.找约数
  for (var i = min; i >= 1; i--) {
    // 假设min的值为8,那么i的值可能是8 7 6 5 4 3 2 1
    if(a % i === 0 && b % i === 0){
      return i
    }
  }
}
var sum = fn3(8,12) //声明一个变量等于这个函数的结果
console.log('a和b的最大公约数是:',sum)//输出最大公约数

// 4.封装一个函数, 求两个数字的最小公倍数
function fn4(a,b){
  /**
   * 一个数学等式:
   *          两数的乘积===两数的最大公约数*两数的最小公倍数
   *           a * b === a和b的最大公约数 * α和b的最小公倍数
   *根据数学等式,做一个变换
   *            a* b / α和的最大公约数=== a和动b的最小公倍数
   * */ 
  /*var i = fn3(a, b) //这里的α和b其实就是fn2函数的两个形参,得到的值,就是α和b的最大公约数
  var num2 = a * b / i //计算最小公倍数
  return num2;//将最小公倍数返回*/
  return a * b / fn3(a,b)
}
var zx = fn4(8,12)
console.log('a和b的最小公倍数是:',zx)
```

27、函数的预解析

预解析的一个表现就是 声明式函数再定义前可以被调用
     * 
     * 预解析是什么?
     *   JS 在执行代码的时候, 会有一个 所谓的 解析阶段
     *      解析阶段, 做了一件事, 就是 函数提升, 就是将 声明式 函数的定义, 提升到当前 作用域的最顶端
     * 
     *  作用域的最顶端:
     *    暂时理解为 当前页面的最开始的位置
     ```js
         fn()
         function fn() {
             console.log('我是 fn 函数, 我被调用了')
         }
     ```
一道面试题: 函数的预解析是什么?
正常书写的 代码
*      fn()
*      function fn() {
*          console.log('我是 fn 函数, 我被调用了')
*      }
* 
*  浏览器会对我们的 JS 代码, 做一个 预解析, 预解析的时候, 会将函数提升到 当前作用域的最顶端, (暂时理解为 当前页面最开始的位置)
* 
*      fn()    ->  这行代码是函数调用, 所以不需要提升
*      function fn() {                ->      这是一个声明式定义的函数, 所以需要提升
*          console.log('我是 fn 函数, 我被调用了')
*      }
* 
*  预解析之后的代码长什么样(执行顺序)?
*      function fn() {
*          console.log('我是 fn 函数, 我被调用了')
*      }
* 
*      fn()  // 所以此时调用的时候, 因为 fn 函数已经定义完成了, 所以这里能够正常执行函数

28、作用域

什么是 作用域?          (这是一道面试题)
     * 就是变量可以起作用的范围
     * 
     * 作用域分为两个  (这是一道面试题)
     *  1. 全局作用域(直接在 script 内书写的代码)
     *      再此作用域创建的变量, 我们叫做全局变量, 在当前 script 标签内的哪里都能使用
     *          在 JS 中, 全局作用域中有一个 提前给我们准备好的 对象(一种数据格式, 后续会详细的讲解)
     *          这个 对象叫做 window
     *       我们创建的全局变量, 会被自动添加到 window 对象中
     * 
     * 2. 局部作用域(在 JS 中, 只有函数能够创建局部作用域)
     *      在此作用域创建的变量, 只能在当前作用域使用, 超出这个作用域(也就是在函数外边)去使用, 就会找不到变量
     ```js
     var num = 100
     // 假设 间隔 500 行
     console.log(num)
     
     function fn() {
        var sum = '我是在函数 fn 内部创建的变量, 我是局部变量, 所以我只能在当前函数内使用'
        var abc123 = '我是在 fn 函数内部创建的局部变量'
        console.log(sum)
    }
    fn()
    // console.log(sum) // 这里因为超出了这个变量的使用区间, 所以会 报错

    var abc = '我是一个全局变量 abc'    // 创建一个全局变量 abc
    console.log(window)
     ```

29、作用域链

*  作用域链    (这是一个纯概念性的东西, 面试也可能会问)
     * 作用域链就是在访问一个变量的时候, 如果当前作用域内没有
     * 
     * 会去自己的父级作用域, 也就是上一层作用域内查找, 如果找到就直接使用, 如果没有找到继续向上层查找
     * 
     * 直到查找到 最顶层的全局作用域, 如果找到了直接使用, 如果没找到 报错提示变量不存在(未定义)
     * 
     * 我们将这个一层一层向上查找的规律, 叫做作用域链
     ```js
    var num = 999
    function fn1() {
        var num = 100
        function fn2() {
            console.log(num)
            /**
             *  打印的值 为 100
             *      1. 先在当前作用域内, 也就是 fn2 函数内部开始查找变量 num, 然后发现 当前作用域内 没有这个变量
             *          所以会去自己的父级的作用域内查找(也就是 fn1 这个函数内部)
             *      2. 来到了自己父级内部查找, 此时找到了一个变量 num 他的值 为 100, 然后直接使用这个变量 并停止查找
            */
        }
        fn2()
    }
    fn1()

    var num = 999
    function fn1() {
        function fn2() {
            console.log(num)
            /**
             *  打印的值 为 999
             *      1. 先在当前作用域内, 也就是 fn2 函数内部开始查找变量 num, 然后发现 当前作用域内 没有这个变量
             *          所以会去自己的父级的作用域内查找(也就是 fn1 这个函数内部)
             *      2. 来到了自己父级内部查找, 发现并没有一个叫做 num 的变量, 然后继续向上层查找, 也就是 全局作用域 内
             * 
             *      3. 来到全局作用域内查找的时候 发现了一个叫做 num 的变量, 值为 999, 然后停止查找, 直接使用该变量
            */
        }
        fn2()
    }
    fn1()


    function fn1() {
        function fn2() {
            console.log(num)
            /**
             *  num 找不到, 所以会报错
             * 
             *      1. 先在当前作用域内查找, 也就是 fn2 内部, 发现没有, 去自己的父级查找, 也就是 fn1 内部
             *      2. 来到了 fn1 内部查找, 发现没有, 去自己的父级查找, 也就是 全局作用域
             *      3. 来到了全局作用域内查找, 发现还是没有, 然后停止查找, 返回一个 num 未定义的报错
             * 
             *      4. 虽然 fn2 作用域内的子级作用域内(fn3函数内部) 有一个变量叫做 num 但是根据 作用域链的访问规则
             *          我们并不会去这个 作用域内查找, 因为 作用域只会逐层向上查找, 并不会向下查找
            */
            function fn3() {
                var num = 666
            }
            fn3()
        }
        fn2()
    }
    fn1()
     ```
* 作用域链的赋值规则
     * 在给变量赋值的时候, 首先会去当前作用域查找, 如果有直接赋值, 并停止查找
     * 
     * 如果没有, 会去自己的父级查找, 在父级找到直接修改值然后停止查找, 如果没有继续向自己的父级查找, 直到找到全局作用域
     * 
     * 在全局作用域内, 找到直接赋值修改他的值, 如果没有找到, 那么会在全局作用域创建一个变量, 并赋值
     ```js
     function fn1() {
        function fn2() {
            num = 100
        }
        fn2()
    }
    fn1()
    console.log(num)    // 100

    function fn1() {
        var num = 999
        function fn2() {
            num = 100
            /**
             *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
             * 
             *  在 fn1 函数内部发现一个变量 num 然后值为 999    我们会对这个变量做一个重新赋值的操作
             * 
             *  也就是将他的值 重新修改为 100
            */
        }
        fn2()
        console.log(num)    // 100
    }
    fn1()
    console.log(num)    // 未定义


    var num = 666
    function fn1() {
        var num = 999
        function fn2() {
            num = 100
            /**
             *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
             * 
             *  在 fn1 函数内部发现一个变量 num 然后值为 999    我们会对这个变量做一个重新赋值的操作
             * 
             *  也就是将他的值 重新修改为 100
            */
        }
        fn2()
    }
    fn1()
    console.log(num)    // 666

    var num = 666
    function fn1() {
        function fn2() {
            num = 100
            /**
             *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
             * 
             *  在 fn1 函数内部, 发现没有 这个变量, 继续去自己的父级作用域查找, 也就是 全局作用域
             * 
             *  在全局作用域发现了一个变量 叫做 num, 他的值是 666, 我们将这个变量重新赋值为 100
            */
        }
        fn2()
    }
    fn1()
    console.log(num)    // 100
     ```

30、递归函数

     递归函数
     * 
     * 本质上还是一个函数
     * 
     * 当一个函数在函数的内部, 调用了自身, 那么就算是一个 所谓的 递归函数(只不过有点小缺陷)
     ```js
     function fn(n) {
        /**
         *  计算 4 的阶乘
         *      4 的阶乘: 4 * 3的阶乘
        */
        // return 4 * fn(3)
        return n * fn(n - 1)
    }
    var sum = fn(4)
    console.log(sum)    // 此时打印的值 为 4 的阶乘
    
    function fn(n) {
        if (n === 1) {
            // 说明此时想要计算 1 的阶乘, 那么我直接将 1 的阶乘的结果 return 出去
            return 1
        }
        return n * fn(n - 1)
    }
    var sum = fn(4)
    console.log(sum)    // 24

    var sum1 = fn(10)
    console.log(sum1) //3608800
     ```

19、函数的概念

首先一定要明确,和数学中的函数完全是两回事
 * 
 *      在JS 中,函数可以理解为一段在程序(页面)中多次出现的代码段 封装起来的盒子
 * 
 *      简单来说,JS 的函数就是一个盒子,盒子里边装的是﹑在当前页面中多次出现的较为复杂的代码段

20、函数的使用

* 如果函数只定义不调用,那么没有任何意义
 1. 函数的定义(创建一个盒子)
     分为两种方式定义
 *       1.1 声明式定义
 *              语法:function fn (){}
 *                   function: 关键字 -> 表明后续的是一段函数
 *                   fn:       函数的名字 -> 将来函数调用的的候需要用到,函数名自定义
 *                   ():      内部填写参数 -> 欠着,后续详细讲解
 *                   {}:内部填写函数调用时要执行的代码段
 ```js
     function fn1(){
         console.log('我是fn1函数')
     }
     fn1()
 ```
 *       1.2 赋值式定义
 *             语法:var fn = function () {}
 ```js
     var fn2 = function () {
         console.log('我是fn1函数')
     }
     fn2()
 ```
 2.函数的使用(使用盒子内的代码)
     * 不管是声明式还是赋值式定义的函数,调用方式都是一样的
     * 调用语法:函数名() / 变量名()

21、声明式与赋值式的区别

 *      1.写法不同
 *      2.调用上略有不同
 *          声明式定义的函数,可以在 函数定义前 去调用
 ```js
     fn1()
     console.log('我是fn1函数定义前的 输出~~~')
     function fn1(){
         console.log('我是fn1函数')
     }
     fn1()
 ```
 *          赋值式定义函数,不能在函数定义前 去调用
 ```js
     //fn2()
     console.log(fn2) //undefined
     console.log('我是fn2函数定义前的 输出~~~')
     var fn2 = function () {
         console.log('我是fn2函数')
     }
     // fn2()
     console.log(fn2) //打印了fn2这个变量内部存储的值
     console.log(fn2()) //打印了fn2这个函数的执行结果,一般默认是undefined  
 ```
 * 
 *    赋值式定义函数不能在函数定义前调用的原因
 *        赋值式定义,其实就是声明一个变量,然后给他赋值为一个函数
 *         
 *        再JS中,如果再定义变量之前使用变量的话。那么变量的值为undefined (变量提升 面试可能会问)
 * 
 *      函数的执行结果,一般默认都是undefined,除非手动更改

22、函数的参数

①.函数的参数
*      函数的参数如何书写?
*          书写在function后的小括号内
*      参数的作用
*          如果一个函数没有书写参数,那么这个函数的功能相对来说比较单一
*          如果书写了参数,能够使我们这个函数的使用更加灵活
*      参数的书写,分为两个
*          1.function 后的小括号内 书写的参数我们叫做"形参"
        !!! 形参的作用:书写之后,相当于在函数内部创建了一个变量,变量实际的值由"实参”传递
*          2.函数名后的小括号内 书写的参数我们叫做“实参”
        !!! 实参的作用:将自身的值,按照一一对应的关系,传递给形参
```js
    // 一个需求,需要封装一个函数,这个函数内需要计算出1+2的值,并输出在页面上
    function fn() {
        var sum = 1 + 2
        console.log(sum)
    }
    fn()
    
    // 新需求:封装一个函数,这个函数内需要计算5 + 8 的值,并输出在页面上
    function fn1() {
        var sum = 5 + 8
        console.log(sum)
    }
    fn1()
    
    // 新需求:封装一个函数,这个函数内需要计算100 + 200的值,并输出在页面上
    function fn2(a, b) {
        var sum = a + b
        console.log(sum) //300
    }
    fn2(100,200)
    // fn2('我是第一个实参','我是第二个实参')
    
    // 新需求:计算300 + 400的值
    fn2(300,400)
    
    // 新需求:计算365+ 1的值
    fn2(365,1)
```
②.函数参数的注意事项
函数的参数
 * 形参 和 实参  两个的数量,要一一对应
 *    1. 形参的数量如果大于实参
 *         如果形参的数量大于实参的数量,那么会将实参按照顺序一一传递给 对应的形参 多出来的形参,相当于变量只定义没赋值,所以他们的值﹑是undefined
 *    2. 实参的数量如果大于形参
 *         如果实参的数量大于形参的数量,那么会将实参按照顺序一一传递给 对应的形参多出来的实参,无法在函数内部通过参数的方式调用
 ```js
     function fn(a , b , c , d) {
         console.log(a , b , c , d)//1 2 undefined undefined
     }
     fn(1,2)
     
     function fn(a , b) {
         console.log(a , b) //100 200
     }
     fn(100,200,300,400)
     /*
     *函数参数的默认值
     *
     * *函数再创建形参的时候,默认给一个值,将来在调用函数的时候,
     * *如果没有传递那么这个形参的值也不会是 undefined 而是给的默认值
     * 
     * *如果传递了对应的值,那么形参的值是实参传递进来的值,否则按照默认值来运行
     * */
     function fn(a = 100 , b = '我是形参b' , c) {
         console.log(a , b , c) //100 '我是形参b' undefined
     }
     fn()
 ```

23、函数的返回值

* 函数的返回值(函数的执行结果)
 * 
 *      在函数内部创建(定义)的变量,只能在函数内部使用,后续学习作用域的时候会详细讲解
 * 
 *      我们如果想在函数外部得到函数内部的某一个值,或者运算结果,我们可以通过return这个关键字来帮我们完成
 ```js
     //  需求:封装一个函数,这个函数内需要计算100 + 200的值
     function fn(a , b) {
         // 书写返回值
         return a + b
     }
     // 创建变量 接收函数的返回值
     var num = fn(100,200)
     console.log('num的值',num) //300
     
     // 如何书写返回值  如何接收返回值
     function test() {
         return 50 - 10
     }
     var sum = test()
     console.log(sum) //40
 ```

24、课堂练习

 ```js
 // 1.需求: 封装一个函数,这个函数能够计算一个区间内所有数字相加的和
/**
 * 1.封装一个函数
 * 
 * 2.需要参数吗?
 * 
 * 3.需要几个参数?(需要两个参数)
 * 
 * 4.需要返回值吗? 可写可不写,出于学习的目的,我们写上返回值
 * */ 
 function fn(start, end) {
     // 计算从start 到 end 之间的所有数字的和
     var sum = 0
     for(var i = start; i <= end; i++) {
         sum += i
     }
     return sum 
 }
 var num = fn(1, 100)
 console.log('num的值',num) //目前猜测应该为5050
 ```
 ```js
 // 2.需求:封装一个函数 判断 一个数字!!! 是否为水仙花数;是水仙花数,返回一个true,否则返回false
/***
 * 1.封装一个函数
 * 2.需要参数吗?
 * 3.需要几个参数?
 * 4.需要返回值吗?
 **/ 
/**
 * 153 是一个水仙花数·
 * 370 是一个水仙花数'
 * 371 是一个水仙花数·
 * 407 是个水仙花数
 * */ 
// var num = prompt('请输入一个三位数') - 0
function fn(i) {
  var baiW = parseInt(i / 100)
  var shiW = parseInt(i / 10 % 10)
  var geW = i % 10
  var sum = Math.pow(baiW,3)+Math.pow(shiW,3)+Math.pow(geW,3);
  if(sum === i){
    // console.log(i,'是一个水仙花数')
    return true
  }else {
    // console.log(i,'不是一个水仙花数')
    return false
  }
}
var boo = fn(153)
console.log(boo)
 ```

25、return的注意事项

return 的注意事项
 * return 具有中断函数的能力
 * 所以一般来说我们将它放在函数的尾部

26、函数的课后练习

```js
// 1. 封装一个函数, 判断一个数字是否为 水仙花数
// 什么是水仙花数, 一个四位数字, 各个位置的四次方和!!! 四次方和!!! 四次方和  如果等于自身, 那么就是水仙花数1234
/**
* *核心
*     1.封装一个函数*
*     2.需要参数吗?需要
*     3.需要几个?1个(默认是4位数字1000~9999)
*     4.需要返回值吗?
*     出于这个学习的目的我们这里写上返回值 同时我们约定,如果这个数字是水仙花数,返回一个 true否则_返回一个false
*/

function fn(i) {
// 计算 参数i 接收到四位数字,是否为水仙花数
  var qianW = parseInt(i/1000)
  var baiW = parseInt(i/100)%10
  var shiW = parseInt(i/10)%10
  var geW = i%10
  // console.log(i)
  // ** ES6 以后新推出的一个语法
  // var sum = qianW ** 4   
  var sum = Math.pow(qianW,4)+Math.pow(baiW,4)+Math.pow(shiW,4)+Math.pow(geW,4)
  if(sum === i){
    // console.log(i,'是一个水仙花数')
    return (i+'是一个水仙花数')
  }else {
    // console.log(i,'不是一个水仙花数')
    return (i+'不是一个水仙花数')
  }
}
var bool = fn(8569)
console.log(bool)

/**
 * 2. 封装一个函数, 对一个四位数字加密
    加密规则:
            1. 每一位上的数字 +5    然后使用 10的余数替代
            2. 一三交换位置, 二四交换位置
    举例:
        输入 1234
        1. 每一位上的数字 +5 ===> 6789
        2. 使用 10 的余数代替 ===> 6789
        3. 一三  二四  交换位置 ===> 8967

        输入 5655
        1. 每一位上的数字  +5 ===> 0100
        2. 使用 10 的余数代替 ===> 0100
        3. 一三  二四  交换位置 ===> 0001   (这里需要打印0001, 不能打印1)
 * */ 
function fn2(i) {
  // 新需求:判断参数是否为4位数
  if(i >= 1000 && i <= 9999){
    var qianW = parseInt(i/1000)
    var baiW = parseInt(i/100)%10
    var shiW = parseInt(i/10)%10
    var geW = i%10
    // var sum = qianW * 1000 + baiW * 100 + shiW * 10 + geW
    // console.log(sum)
    // 置换
    qianW = (qianW+5)%10
    baiW = (baiW+5)%10
    shiW = (shiW+5)%10
    geW = (geW+5)%10
    // return shiW * 1000 + geW * 100 + qianW * 10 + baiW
    // var sum = shiW * 1000 + geW * 100 + qianW * 10 + baiW
    // console.log(sum)
    // return String(shiW) + String(geW) + String(qianW) + String(baiW)
    return ''+ shiW + geW + qianW + baiW
  }else {
    console.log('传入的数字,不是四位数字')
  }
}
var sum = fn2(5655)
console.log('加密后的数字为:',sum)

// 3. 封装一个函数, 求两个数字的最大公约数
/**
 * 核心
 *    1.封装一个函数
 *    2.需要参数吗?需要
 *    3.需要几个参数? 需要两个参数
 *    4.需要返回值吗? 需要 将最大的公约数,返回 出去
 * 
 * 思考:
 *      如何计算 最大公约数
 *      约数:整数X 除以 y(y!==0)  ,余数%为0,  此时我们说y是x的约数
 * */ 
function fn3(a,b) {
  // for (var i = a; i >= 1; i--){//假设i是a和b的最大公约数
  //   if(a%i === 0 && b%i === 0){//如果能被a和b同时整除,那么i就是a和b的最大公约数
  //     return i
  //   }
  // }
  // 寻找两个数中较小的值
  var min = a > b ? b : a //如果a>b条件成立,说b的值小,所以返回b,否则,说明a的值小。那么返回a
  // 2.找约数
  for (var i = min; i >= 1; i--) {
    // 假设min的值为8,那么i的值可能是8 7 6 5 4 3 2 1
    if(a % i === 0 && b % i === 0){
      return i
    }
  }
}
var sum = fn3(8,12) //声明一个变量等于这个函数的结果
console.log('a和b的最大公约数是:',sum)//输出最大公约数

// 4.封装一个函数, 求两个数字的最小公倍数
function fn4(a,b){
  /**
   * 一个数学等式:
   *          两数的乘积===两数的最大公约数*两数的最小公倍数
   *           a * b === a和b的最大公约数 * α和b的最小公倍数
   *根据数学等式,做一个变换
   *            a* b / α和的最大公约数=== a和动b的最小公倍数
   * */ 
  /*var i = fn3(a, b) //这里的α和b其实就是fn2函数的两个形参,得到的值,就是α和b的最大公约数
  var num2 = a * b / i //计算最小公倍数
  return num2;//将最小公倍数返回*/
  return a * b / fn3(a,b)
}
var zx = fn4(8,12)
console.log('a和b的最小公倍数是:',zx)
```

27、函数的预解析

预解析的一个表现就是 声明式函数再定义前可以被调用
     * 
     * 预解析是什么?
     *   JS 在执行代码的时候, 会有一个 所谓的 解析阶段
     *      解析阶段, 做了一件事, 就是 函数提升, 就是将 声明式 函数的定义, 提升到当前 作用域的最顶端
     * 
     *  作用域的最顶端:
     *    暂时理解为 当前页面的最开始的位置
     ```js
         fn()
         function fn() {
             console.log('我是 fn 函数, 我被调用了')
         }
     ```
一道面试题: 函数的预解析是什么?
正常书写的 代码
*      fn()
*      function fn() {
*          console.log('我是 fn 函数, 我被调用了')
*      }
* 
*  浏览器会对我们的 JS 代码, 做一个 预解析, 预解析的时候, 会将函数提升到 当前作用域的最顶端, (暂时理解为 当前页面最开始的位置)
* 
*      fn()    ->  这行代码是函数调用, 所以不需要提升
*      function fn() {                ->      这是一个声明式定义的函数, 所以需要提升
*          console.log('我是 fn 函数, 我被调用了')
*      }
* 
*  预解析之后的代码长什么样(执行顺序)?
*      function fn() {
*          console.log('我是 fn 函数, 我被调用了')
*      }
* 
*      fn()  // 所以此时调用的时候, 因为 fn 函数已经定义完成了, 所以这里能够正常执行函数

28、作用域

什么是 作用域?          (这是一道面试题)
     * 就是变量可以起作用的范围
     * 
     * 作用域分为两个  (这是一道面试题)
     *  1. 全局作用域(直接在 script 内书写的代码)
     *      再此作用域创建的变量, 我们叫做全局变量, 在当前 script 标签内的哪里都能使用
     *          在 JS 中, 全局作用域中有一个 提前给我们准备好的 对象(一种数据格式, 后续会详细的讲解)
     *          这个 对象叫做 window
     *       我们创建的全局变量, 会被自动添加到 window 对象中
     * 
     * 2. 局部作用域(在 JS 中, 只有函数能够创建局部作用域)
     *      在此作用域创建的变量, 只能在当前作用域使用, 超出这个作用域(也就是在函数外边)去使用, 就会找不到变量
     ```js
     var num = 100
     // 假设 间隔 500 行
     console.log(num)
     
     function fn() {
        var sum = '我是在函数 fn 内部创建的变量, 我是局部变量, 所以我只能在当前函数内使用'
        var abc123 = '我是在 fn 函数内部创建的局部变量'
        console.log(sum)
    }
    fn()
    // console.log(sum) // 这里因为超出了这个变量的使用区间, 所以会 报错

    var abc = '我是一个全局变量 abc'    // 创建一个全局变量 abc
    console.log(window)
     ```

29、作用域链

*  作用域链    (这是一个纯概念性的东西, 面试也可能会问)
     * 作用域链就是在访问一个变量的时候, 如果当前作用域内没有
     * 
     * 会去自己的父级作用域, 也就是上一层作用域内查找, 如果找到就直接使用, 如果没有找到继续向上层查找
     * 
     * 直到查找到 最顶层的全局作用域, 如果找到了直接使用, 如果没找到 报错提示变量不存在(未定义)
     * 
     * 我们将这个一层一层向上查找的规律, 叫做作用域链
     ```js
    var num = 999
    function fn1() {
        var num = 100
        function fn2() {
            console.log(num)
            /**
             *  打印的值 为 100
             *      1. 先在当前作用域内, 也就是 fn2 函数内部开始查找变量 num, 然后发现 当前作用域内 没有这个变量
             *          所以会去自己的父级的作用域内查找(也就是 fn1 这个函数内部)
             *      2. 来到了自己父级内部查找, 此时找到了一个变量 num 他的值 为 100, 然后直接使用这个变量 并停止查找
            */
        }
        fn2()
    }
    fn1()

    var num = 999
    function fn1() {
        function fn2() {
            console.log(num)
            /**
             *  打印的值 为 999
             *      1. 先在当前作用域内, 也就是 fn2 函数内部开始查找变量 num, 然后发现 当前作用域内 没有这个变量
             *          所以会去自己的父级的作用域内查找(也就是 fn1 这个函数内部)
             *      2. 来到了自己父级内部查找, 发现并没有一个叫做 num 的变量, 然后继续向上层查找, 也就是 全局作用域 内
             * 
             *      3. 来到全局作用域内查找的时候 发现了一个叫做 num 的变量, 值为 999, 然后停止查找, 直接使用该变量
            */
        }
        fn2()
    }
    fn1()


    function fn1() {
        function fn2() {
            console.log(num)
            /**
             *  num 找不到, 所以会报错
             * 
             *      1. 先在当前作用域内查找, 也就是 fn2 内部, 发现没有, 去自己的父级查找, 也就是 fn1 内部
             *      2. 来到了 fn1 内部查找, 发现没有, 去自己的父级查找, 也就是 全局作用域
             *      3. 来到了全局作用域内查找, 发现还是没有, 然后停止查找, 返回一个 num 未定义的报错
             * 
             *      4. 虽然 fn2 作用域内的子级作用域内(fn3函数内部) 有一个变量叫做 num 但是根据 作用域链的访问规则
             *          我们并不会去这个 作用域内查找, 因为 作用域只会逐层向上查找, 并不会向下查找
            */
            function fn3() {
                var num = 666
            }
            fn3()
        }
        fn2()
    }
    fn1()
     ```
* 作用域链的赋值规则
     * 在给变量赋值的时候, 首先会去当前作用域查找, 如果有直接赋值, 并停止查找
     * 
     * 如果没有, 会去自己的父级查找, 在父级找到直接修改值然后停止查找, 如果没有继续向自己的父级查找, 直到找到全局作用域
     * 
     * 在全局作用域内, 找到直接赋值修改他的值, 如果没有找到, 那么会在全局作用域创建一个变量, 并赋值
     ```js
     function fn1() {
        function fn2() {
            num = 100
        }
        fn2()
    }
    fn1()
    console.log(num)    // 100

    function fn1() {
        var num = 999
        function fn2() {
            num = 100
            /**
             *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
             * 
             *  在 fn1 函数内部发现一个变量 num 然后值为 999    我们会对这个变量做一个重新赋值的操作
             * 
             *  也就是将他的值 重新修改为 100
            */
        }
        fn2()
        console.log(num)    // 100
    }
    fn1()
    console.log(num)    // 未定义


    var num = 666
    function fn1() {
        var num = 999
        function fn2() {
            num = 100
            /**
             *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
             * 
             *  在 fn1 函数内部发现一个变量 num 然后值为 999    我们会对这个变量做一个重新赋值的操作
             * 
             *  也就是将他的值 重新修改为 100
            */
        }
        fn2()
    }
    fn1()
    console.log(num)    // 666

    var num = 666
    function fn1() {
        function fn2() {
            num = 100
            /**
             *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
             * 
             *  在 fn1 函数内部, 发现没有 这个变量, 继续去自己的父级作用域查找, 也就是 全局作用域
             * 
             *  在全局作用域发现了一个变量 叫做 num, 他的值是 666, 我们将这个变量重新赋值为 100
            */
        }
        fn2()
    }
    fn1()
    console.log(num)    // 100
     ```

30、递归函数

     递归函数
     * 
     * 本质上还是一个函数
     * 
     * 当一个函数在函数的内部, 调用了自身, 那么就算是一个 所谓的 递归函数(只不过有点小缺陷)
     ```js
     function fn(n) {
        /**
         *  计算 4 的阶乘
         *      4 的阶乘: 4 * 3的阶乘
        */
        // return 4 * fn(3)
        return n * fn(n - 1)
    }
    var sum = fn(4)
    console.log(sum)    // 此时打印的值 为 4 的阶乘
    
    function fn(n) {
        if (n === 1) {
            // 说明此时想要计算 1 的阶乘, 那么我直接将 1 的阶乘的结果 return 出去
            return 1
        }
        return n * fn(n - 1)
    }
    var sum = fn(4)
    console.log(sum)    // 24

    var sum1 = fn(10)
    console.log(sum1) //3608800
     ```

31、认识对象

①什么是对象
Js中的一种数据格式   对象在JS中的数据类型数据为:引用数据类型(也有喜欢叫复杂数类型)
②如何向变量中存储一个叫做对象的数据呢?
语法1:var obj = {键值对}
键值对 -> key:value
如果对象内部有多个键值对,那么需要使用逗号隔开
```js
    var obj = { a:100, b:200, c:300, q:999}
    console.log(obj)
```
③什么是键值对(拿对象obj 为例子)
在这个对象中,akey,100为对应value
    另外一种叫法:a为键,100为对应的值
    另外一种叫法:a为属性名,100为对应的属性值
    bkey,200为对应的value
    b为键,200为对应的值
    b为属性名,200为对应的属性值

32、建对象

创建对象 分为两种方式:
    *1.字面量的形式(使用频率比较高)
        语法:var obj = {键值对}
    *2.内置构造函数的创建
        语法1var obj1 = new Object() //创建空对象
        语法2var obj1 = new Object({a: 1, b: 2}) //创建一个具有属性或者说具有键值对的对象
            注意: new Object  的О是大写的,不是小写
*面试官:JS创建对象的方式有哪些?
目前暂时是 两种
    1.字面量的方式
    ```js
        var obj = { 
            a:1, 
            b:2, 
            c:0, 
            d:'abc', 
            e:true,  
            q:undefined, 
            w:null, 
            r:function () {console.log('我是 obj对象内r这个属性的属性值,我是一个函数')}
        }
        console.log(obj)
    ```
    2.内置构造函数的方式 
    ```js
        var obj1 = new Object({ a: 1, q: 777, w: 666, e:'qwer'})
        console.log(obj1)
    ```

33、象的操作(增删改查)

换句话说,就是对内部的属性的操作
    分为两种方式
        1.点语法
        2.中括号语法(数组语法)
*一般来说,大部分场景使用点语法更简单,有一些特殊场景只能使用中括号语法
 一般大部分情况下,点语法与中括号语法,作用相同,怎么选择都可以
 特殊情况下我们需要使用中括号语法
     1,对象的属性名,有纯数字或者特殊符号,这个时候,就只能使用中括号语法
     2.如果涉及变量相关的时候,也需要使用中括号
```js
    //特殊情况1
    var obj = {
      100: '我的属性名 是纯数字100',
      '!': '我的属性名是特殊符号 !',
      '@': '我的属性名是特殊符号 @'
    }
    //此时不能使用点语法,可以使用中括号语法
    console.log(obj.100)//会报错
    console.log(obj[100])
    console.log(obj['!'])//有问题,需要将特殊符号用引号包裹
    console.log(obj['@'])
    
    //特殊情况2
    var obj = {a: 1, b: 2, name: 'QF001'}
    var myName = 'name'
    console.log(obj.myName) //undefined
    原因:obj.myName
    我们猜想他这个myName是一个变量,所以实际的应该是 obj.'name',
    所以应该打印 obj 的name属性,但实际的输出结果是undefined
    因为对象的点语法,会将点后边的字符当成一个字符串去使用,而不会当成变量
    拿obj.myName举例
    他会将 myName 当成一个字符串去对象中查找,有没有一个叫做myName 的属性名
    找完之后发现对象中没有这个属性名,所以打印的值为undefined
    console.log(obj[myName])//QF001
    原因:中括号语法,内部书写的字符,如果不加引号,会把它当成变量去使用,所以找到实际的值之后。myName这个变量对应的值为name
    所以obj[myName]相当于写了obj['name']
    所以会去对象obj中找一个叫做name的属性名,找到之后打印在页面
```

34、for...in遍历对象

for...in 一个循环语句
    对象:一种数据格式
    遍历:一般我们会说'遍历对象'/'遍历数组'
        '遍历对象'想办法拿到对象内部所有的属性名与属性值
   for (var i in 要遍历的对象) { 循环要执行的代码}
*对象关于key的要求 或者说对于属性名的要求 或者说对于键值对的键的要求
1.推荐使用符合变量命名规则与规范的名字
2.对象的 key 也可以使用 纯数字 来当作键名(属性名/key)
3.可以使用任何特殊符号(但是需要使用引号包裹)
!注意:一般只推荐使用第一种,二三了解即可
    var obj = {a: 1, q: 'qwe', t: true, u: undefined}
     for (var i in obj) {
         //console.log(i) //a q t u 这四个是对象obj的所有 属性名/键名/ key
         // console.log(obj.i) 
         //undefined 点语法会将后边的字符当成字符串来使用,而不是当成变量,如果相当变量来使用,那么应该用中括号语法
         console.log(obj[i]) //该对象的所有属性值
    }

35、认识数组数据类型

数组 是一种数据类型,他也是属于引用数据类型(复杂数据类型)
根据字面意思来说,存放数字的一个组合,但这样说有点片面了
更完善的说法:数组是存放一些数据的集合
换句话说:我们把数据放在一个盒子中,这个盒子就叫做数组,注意!数组内的数据是有顺序的
    //  创建一个变量 并在内部存储一个 数组
    var arr = [1, 2, 3, 'q', 'w', 'e', true, false, undefined]
    console.log(arr)

36、创建数组

创建数组
    分为两种方式    
        1.字面量的方式
            语法:var arr = [1, 2, 3, 'q', 'w', 'e']     
        ```js
            var arr = [1, 2, 3, 'q', 'w', 'e']
            console.log(arr)
        ```
        2.内置构造函数的方式
            语法1var arr = new Array() //创建一个空数组     
        ```js
            var arr1 = new Array()
            console.log(arr1)
        ```
            语法2var arr = new Array(5) //创建一个有长度的数组
        ```js
            var arr2 = new Array(5)
            console.log(arr2)
        ```
            语法3var arr = new Array(1, 2, 3) //创建一个有内容的数组
        ```js
            var arr3 = new Array(1, 2, 3)
            console.log(arr3)
        ```

37、数组的length

数组的 length 属性
 *    length 翻译过来就是 长度的意思
 *        代表 这个数组内,有多少个成员
 *    语法 数组名.length
    var arr1 = [1, 2, 3, 4, 5]
    var arr2 = ['q', 'w', 'e', 'r']
    var arr3 = new Array(1, 2, 3, 4, 5)
    // console.log(arr1)
    // console.log(arr2)
    console.log(arr1.length)
    console.log(arr2.length)
    console.log(arr3.length)

38、数据的索引

索引 也有人叫做 下标
     就是指一个数据,在这个数组内排列在第几个位置上
     注意:在JS中,索引(下标) 是从0开始计算的
         如果想要获取到数组指定位置的值,可以通过下标来获取
     语法:数组名[下标] -> 能够获取到这个数组中对应下标的成员具体的值
    var arr = ['b', 'a', 'c', 1, 2, 3]
    //  console.log(arr)
    console.log(arr[0]) //b
    console.log(arr[3]) //1

39、遍历数组

遍历数组
    想办法 拿到 数组的每一个成员
    想拿到数组的所有成员,需要先想办法拿到数组的所有下标
    规律:所有数组的下标都是从0开始的,然后到 数组.length - 1结束
    var arr = ['b', 'a', 'c', 1, 2, 3]
    //          0    1    2   3  4  5
    var arr1 = ['b', 'a', 'c', 1, 2]
    //          0    1    2   3  4
    // console.log(arr)
    // console.log(arr1)
    // 需求,就是根据arr这个数组,拿到他的所有下标
    for (var i = 0; i < arr.length; i++) {
      // console.log(i) // 0 1 2 3 4 5 输出所有下标
      // 需求:拿到数组所有的值,输出在控制台
      console.log(arr[i]) //输出所有下标对应的值
    }

40、课堂案例

    /**
     * 1.求数组内所有成员的和
     * 核心内容
     *      1.拿到所有的成员
     *      2.计算所有的和
     * */ 
    var arr = [9, 5, 6, 11, 8, 4, 3]
    var sum = 0 //初始化
    for(var i = 0; i < arr.length; i++) {
      // console.log(arr[i]) // 9 5 6 11 8 4 3
      sum += arr[i]
    }
    console.log('数组内所有成员的和为',sum)
    /**
     * 2.求数组内最大的数字
     * 核心内容
     *      1.拿到所有的成员
     *      2.找出最大值
     * */ 
     var arr = [9, 5, 6, 11, 8, 4, 3]
     var max = arr[0] //假设数组下标为0 的值是最大的,然后存储在max中
     for(var i = 0; i < arr.length; i++) {
         // console.log(arr[i]) // 9 5 6 11 8 4 3
         if(max < arr[i]) {//如果我假设的最大值小于数组中的某个值,那么会执行这个分支语句
             max = arr[i]
         }
     }
     console.log('数组内最大的数字',max)

41、课后练习

1. 封装函数, 把数组进行放大十倍的操作
```javascript
function map(arr) {
    // code run here ...
}
// 将来使用的时候
var arr = [100, 200, 300];
var res = map(arr);
// res: [ 1000, 2000, 3000 ]
```
- 要求: 函数的返回值需要是一个新数组
/**
 * 核心内容
 *    1.封装一个函数
 *    2.需要参数吗?需要
 *    3.需要几个参数? 1个数组
 *    4.需要返回值吗? 需要,返回一个放大十倍后的新数组
 * */
```js
    var arr = [100, 200, 300]
    function fn(fnArr) { //fnArr是形参,arr是实参
        var newFnArr = [] //声明一个新数组newFnArr,将放大十倍的数组放进去
        for(var i = 0; i < fnArr.length; i++) {
            // console.log(fnArr[i])
            newFnArr.push(fnArr[i] * 10)
        }
        return newFnArr
    }
    var newArr = fn(arr) //接收fn返回的值
    console.log(newArr)
```
2. 使用 JS 生成一个数组
- 要求: 数组内存储的数据是从 2 开始 的所有 偶数, 要求数组的长度为 30
```js
var arr = [100, 200, 300];
arr.push(500);
```
```js
    //方式一
    var arr = []
    for (var i = 2; i <= 60; i++) {
      // console.log(arr[i])
      if (i % 2 === 0) {
        arr.push(i);
      }
    }
    console.log(arr)
    //方式2
    var arr2 = new Array(30)
    for (var i = 0; i < arr2.length; i++) {
      arr2[i] = (i + 1) * 2
    }
    console.log(arr2)
```
3、 利用作业 2 生成的数组, 每 5 个数字求一个平均值, 放在新数组内
 *例子: [2, 4, 6, 8, 10, ..., 60] -> [6, 16, 26, 36, 46, 56]
 ```js
    var arr2 = []
    var sum = 0;//累加器
    var ciShu = 0;//计数器
    var avg = 0; //平均值
    for (var j = 0; j < arr.length ;j++) {
      // console.log(arr[j])
      ciShu++ //1 2 3 4 5每累加一次,那么计数器自增1,用来记录累加的次数
      sum += arr[j] //2+4+6+8+10累加器每次去和数组的值相加
      if (ciShu === 0) {
        var avg = sum / ciShu
        arr2.push(avg)
        sum = 0 //累加器清0
        ciShu = 0 //计时器清0
      }
    }
    console.log(arr2)
 ```

42、冒泡排序

属于数组排序的算法之一
 *    其实就是通过一种算法,将一个乱序的数组,调整为指定顺序的数组(从大到小/从小到大)
 * 
 * 什么是算法?
 *    解决某一个问题最简单的方式/最高效的方式
 * 
 * 从1-10万,这组数字中,少了一个数字,要求我们找出来
 *    常规写法:后一位 - 前一位 如果差值 === 2 那么就找出来了
 * 
 *    将1-10万分为两组 1~5万 5万零一~10万 然后去找这两组数字中那一组的数量不够万,找到之后将这组再次一分为二     
    var arr = [9, 3, 6, 2, 4, 1, 8, 5, 7]
    //         0  1  2  3  4  5  6  7  8
    console.log('原始数组:', arr)
    for(var k = arr.length - 2; k >= 0; k--) {
      console.log('k的值为', k)
      for (var  i = 0; i <= k; i++) {
        console.log(arr[i] , arr[i+1])
        if(arr[i] > arr[i+1]) {//相邻的两个数比较,前面的值>后面的值,那么前边的值往后挪 (从小到大排列)
          var temp = arr[i+1] 
          arr[i+1] = arr[i] 
          arr[i] = temp
        }
      }
    }
    console.log('冒泡排序后的数组:', arr)

43、选择排序

    var arr = [9, 3, 6, 2, 4, 1, 8, 5, 7]
    //         0  1  2  3  4  5  6  7  8
    console.log('原始数组:', arr)
    /**
     * *             第几次循环   假设谁是最小值   和谁交换  内层循环从几开始
     * 
     * *   k === 0       1              0            0              1
     * *   k === 1       2              1            1              2
     * *   k === 2       3              2            2              3
     * */ 
    for(var k = 0; k < arr.length; k++) {
      var minIndex = k //假设当前最小值的下标 为k
      for (var i = k + 1; i <= arr.length; i++){
        // console.log(arr[i])
        if (arr[minIndex] > arr[i]) { //如果当前分支执行,说明在数组中找到了一个比假设的最小值要小的元素
          minIndex = i
        }
      }
      var temp = arr[k]
      arr[k] = arr[minIndex]
      arr[minIndex] = temp
    }
    console.log('选择排序后的数组' ,arr)

44、数据类型之间的区别

数据类型分为两种
    1.基本数据类型(简单数据类型)
    2.引用数据类型(复杂数据类型)
!1.存储
    变量的数据存储的地方是 内存中,内存分为两个 栈内存,堆内存
    ** 基本数据类型存储在 栈内存中,比如:string number undefined null boolean
    ** 复杂数据类型,将数据本体存放在堆内存中,比如对象或者数组或者函数,然后将指向该内存的地址,
            存放在数组名或者对象名或者函数名中,数组/对象/函数 名 存放在 栈内存中
    ```js
        var num = 100
        var str = 'abc'
        var obj = {
            a: 1,
            b: 2
        } 
        var arr = [1, 2, 3, 4]
    ```
面试官:数据类型之间有什么区别?
基本数据类型有哪些(string number undefined null boolean),然后他们存储的地方是栈内存中
引用数据类型有哪些(对象或者数组或者函数)。然后他们数据本体存放的地方是堆内存中。然后变量名在储的位置是栈内存中
**!!!基本数据类型内部存储的是值;引用数据类型内部存储的是地址**
!2.赋值
 * 基本数据类型:赋值之后,两个变量之间没有任何关系,相当于将我自己的某一个东西,复制一份给你,然后你的就是你的,我的就是我的
 *   例子:我有一张考试卷,然后我复制一份给你,然后你在卷子上书写答案,并不会影响到我自己原本的这张卷子
 * 复杂数据类型:赋值以后,因为变量内部存储的是指向堆内存的地址 所以在赋值的时候,其实是将这个地址给到了另外一个变量 那么相当于这两个变量存储的是同一个钥匙 所以操作其中一个变量的时候,会影响另外一个变量
 *   例子:我房间有一个开门的钥匙,我将我的钥匙复制一份,给到你,那么此时我们两个共同拥有了一个房间的钥匙
 *       此时如果我对房间的布局做了修改,那么你进入房间的时候你能看到布局的修改 
 *       此时如果你将房间的所有东西全都偷走,那么我进入房间的时候能看到房间所有东西都被偷走了
 ```js
     var num1 = 100
     var num2 = num1
     var num2 = 666
     console.log(num1) //100
     console.log(num2) //666
     var obj1 = {
         name: 'QF001',
         age: 18
     }
     var obj2 = obj1;//这一步相当于将变量obj1内部存储的"钥匙", 给到了变量obj2,那么此时 obj2和 obj1相当于操作的是一个内存空间
     console.log(obj2) //{name: 'QF001', age: 18}
     obj2.name = 'QF666'
     console.log(obj2) //{name: 'QF006', age: 18}
     console.log(obj1) //{name: 'QF006', age: 18}
     /***
     * 首先创建了一个对象obj1 数据本体为:{name: 'QF0o1', age: 18} 然后变量名存储的是指向堆内存的地址,我们假设为XF0O1
     * 
     * 然后创建了一个对象obj2内部存储的是和 obj1一样的地址
     * 
     * 此时相当于obj2和 obj1共同保管一个内存空间
     * 
     * 换句话说:操作obj2会影响obj1 操作obj1也会影响obj2
     * */
 ```
!3.比较
 基本数据类型:就是值的比较
 引用数据类型:比较的时候 比较的是 存储地址
 ```js
     var obj1 = {name: 'QF001', age: 18}
     var obj2 = {name: 'QF001', age: 18}
     obj2.name = 'QF666'
     console.log(obj1.name) // QF001
     console.log(obj1 === obj2) //false
     /**
     *引用数据类型在对比的时候,对比的是地址,而这两个对象的地址完全不同,所以返回的结果是false
     * */  
     var num1 = 100
     var num2 = 200
     console.log(num1 === num2) //false
     var arr1 = [1, 2, 3]
     var arr2 = arr1
     console.log(arr1 === arr2) //true
     //引用数据类型在对比的时候,对比的是地址 因为他们两个的地址完全相同,所以返回的结果是true
 ```
!4.传参
 基本数据类型:将值拷贝一份传递给形参,在函数内修改不会影响外界
 引用数据类型:将存储地址赋值给形参,在函数内修改会影响外界
 ```js
     function fn(num) {num = 'QF001'}
     var str = 'abc'
     fn(str) 
     console.log(str) //abc
     function fn1(o) {o.name = 'qwer'}
     var obj = {name: 'ABC'}
     fn1(obj)
     console.log(obj.name) //qwer
 ```
一道面试题:
    var obj = {name: 'Jack'}
    function fn() {
      obj.name = 'Rose'
      obj = {}
      obj.name = 'Jerry'
      console.log(obj.name) //Jerry
    }
    fn()
    console.log(obj.name) //Jerry
    //解析:
    /**
     * *  代码从上往下开始执行
     * *      创建一个变量 叫做 obj, 数据本体 {name: Jack}   假设内部存储的地址为 XF001
     * *      定义一个 fn 函数, 但是没调用, 所以直接向下走
     * *      调用函数, 开始执行函数内部代码, 函数的代码是
     *        obj.name = 'Rose'
     *        obj = {}
     *        obj.name = 'Jerry' 
     *        执行顺序还是从上往下
     * * 
     * *          开始执行函数内代码
     * *              obj.name = 'Rose'
     * *                 会先在当前作用域内查找有没有定义 obj 这个对象, 所以会向父级作用域查找
     * *                     在父级作用域, 找到了 全局变量 obj
     * *                     所以obj.name = 'Rose'相当于 修改了 全局变量 obj 的 name 属性
     * *              obj = {}
     * *                     会先在当前作用域内查找有没有定义 obj 这个对象, 所以会向父级作用域查找
     * *                     在父级作用域, 找到了全局变量 obj
     * *                     所以 obj = {} 相当于 修改 全局变量 obj 内部存储的地址, 此时这个变量为一个空对象了
     * *                     然后内部存储的地址 更改了 XF002, 与原本的地址没有任何联系了
     * *             obj.name = 'Jerry'
     * *                   会先在当前作用域内查找有没有定义 obj 这个对象, 因为没找到, 所以会向父级作用域查找
     * *                   在父级作用域, 找到了全局变量 obj
     * *                   然后给这个对象内部 添加一个 name 属性, 值 Jerry
     * *            console.log(obj.name) 
     * *                   根据分析我们此时可以得知, 我现在打印的对象应该是 全局对象 obj, 此时他的值已经被修改了
     * *                   所以此时打印的值 应该是 Jerry
     * *                  此时函数执行完毕, 代码继续向下执行
     * *      console.log(obj.name) 此时打印的是 当前作用域的变量 obj   因为现在 在全局作用域
     * *          所以会打印全局作用域的变量 obj,     因为在打印之前函数 fn执行时 已经修改过 obj 对象的内容
     * *          所以 此时 打印的时候 obj.name   应该是 Jerry
     * */

45、数组的常用方法

⑴.push
 * 语法:数组.push(数据)
 * 作用:向数组末尾添加数据
 * 返回值:追加数据后,数组最新的长度(length)
 ```js
     var arr = [1, 2, 3]
     console.log('原始数组:', arr)
     
     var len = arr.push(500)
     console.log(len) //4
     console.log(arr) // [1, 2, 3, 500]
 ```
⑵.pop
 * 语法:数组.pop()
 * 作用:删除数组最后一条数据
 * 返回值:被删除的数据(被删除的数组中的数据)
 ```js
     var arr = [1, 2, 3]
     console.log('原始数组:', arr)
     
     var po = arr.pop()
     console.log(po) //3 被删除的数组的值
     console.log(arr) //[1, 2]
 ```
⑶.unshift
 * 语法:数组.unshift(数据)
 * 作用:向数组开头添加数据
 * 返回值:添加数据后,数组最新的长度(length)
 ```js
     var arr = [1, 2, 3]
     console.log('原始数组:', arr)
     
     var len = arr.unshift(666)
     console.log(len) //4
     console.log(arr) //[666, 1, 2, 3]
 ```
⑷.shift
 * 语法:数组.shift()
 * 作用:删除数组第一条数据
 * 返回值:被删除的数据(被删除的数组中的数据)
 ```js
     var arr = [1, 2, 3]
     console.log('原始数组:', arr)
     
     var st = arr.shift()
     console.log(st) //1 被删除的数组的值
     console.log(arr) //[2, 3]
 ```
⑸.reverse
 * 语法:数组.reverse()
 * 作用:反转数组
 * 返回值:反转后的数组
 ```js
     var arr = [1, 2, 3]
     console.log('原始数组:', arr)
     
     var newArr = arr.reverse()
     console.log('newArr', newArr) //[3, 2, 1]
     console.log('arr', arr) //[3, 2, 1]
 ```
⑹.sort
 * 语法1:数组.sort()
 *      * 作用:会将数据转化为字符串后,一位一位的对比
 * 语法2:数组.sort(function(a, b) {return a - b})
 *      * 作用:会按照数字大小的升序排列
 * 语法3:数组.sort(function(a, b) {return b - a})
 *      * 作用:会按照数字大小的降序排列
 * 返回值:排序后的数组
 ```js
     var arr = [100, 101, 200, 10001, 100002, 1000003, 10]
     console.log('原始数组:', arr)
     
     var newArr = arr.sort()
     console.log('newArr', newArr)//[10, 100, 1000003, 100002, 10001, 101, 200]
     console.log('arr', arr)//[10, 100, 1000003, 100002, 10001, 101, 200]
     
     var newArr = arr.sort(function (a, b) {return a - b})
     console.log('newArr', newArr)//[10, 100, 101, 200, 10001, 100002, 1000003]
     console.log('arr', arr)//[10, 100, 101, 200, 10001, 100002, 1000003]
     
     var arr = [100, 101, 200, 10, '999', '123abc', 'qwe']
     console.log('原始数组:', arr)
     
     var newArr = arr.sort(function (a, b) {return b - a})
     console.log('newArr', newArr)//['999', 200, 101, 100, 10, '123abc', 'qwe']
     console.log('arr', arr) //['999', 200, 101, 100, 10, '123abc', 'qwe']
 ```
⑺.splice
 * 语法1:数组.splice(开始索引(下标),多少个(数组中的数据))
 *      * 作用:截取数组部分内容
 * 语法2:数组.splice(开始索引,多少个,插入的数据1,插入的数据2,插入的数据3...)
 *      * 作用:截取数组部分内容,并插入新的数据
 * 返回值:截取出来的部分内容 组成的数组
 ```js
     var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        //      0  1  2  3  4  5  6  7  8
     console.log('原始数组:', arr)
     
     //语法1
     var newArr = arr.splice(3, 4) //从下标3开始截取,截取4个
     console.log('newArr', newArr)//[4, 5, 6, 7]
     console.log('arr', arr) //[1, 2, 3, 8, 9]
     
     // 语法2
     var newArr = arr.splice(3, 4, '我是新插入进来的数据1', '我是新插入进来的数据2') 
     //从下标3开始截取,截取4个,在截取的数据之后插入新的数据
     console.log('newArr', newArr)//[4, 5, 6, 7]
     console.log('arr', arr) //[1, 2, 3, '我是新插入进来的数据1', 8, 9]
 ```
* 数组的方法能够改变原数组的就只有上边说的7个
⑻.slice
 * 语法:数组.slice(开始索引(下标), 结束索引(下标))
 * 参数:
 *     + 包前不包后:包含开始索引位置的数据,不包含结束索引位置的数据
 *                 (包含前边下标截取的数据,不包含后边下标截取的数据)
 *     + 不写开始索引,默认是0;不写结束索引,默认是数组的length (数组下标个数的和)
 *     + 参数支持写负数,表示倒数第几个,其实就是length + 负数
 * 作用:截取数组部份内容
 * 返回值:截取出来的部分内容组成的新数组
 ```js
     var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        //      0  1  2  3  4  5  6  7  8
     console.log('原始数组:', arr)
     
     var newArr = arr.slice(3, 4) //从下标3开始截取,截取到下标4
     console.log('newArr', newArr)//[4]
     console.log('arr', arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
     
     var newArr = arr.slice(5) 
     //不写结束索引,相当于写了数组.length, 所以这里相当于写了slice(5, arr.length)
     console.log('newArr', newArr)//[6, 7, 8, 9]
     console.log('arr', arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
     
     var newArr = arr.slice() 
     //此时开始索引和结束索引都没写,开始索引相当于写了0,结束索引相当于写了数组.length, 所以arr.slice()相当于写了arr.slice(0, 9)
     console.log('newArr', newArr)//[1, 2, 3, 4, 5, 6, 7, 8, 9]
     console.log('arr', arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
     
     var newArr = arr.slice(3, -2) 
     // 写负数的时候,就相当于写了length + 负数 ----> 相当于写了9 - 2 = 7
     console.log('newArr', newArr)//[4, 5, 6, 7]
     console.log('arr', arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
 ```
* *面试题:数组中有两个方法,splice 与slice,你能描述一下他们两个的区别吗?
 * 1.参数含义不同,然后介绍一下 参数哪里不同
 *             参数不同: 数组.splice(开始索引(下标),多少个(数组中的数据))
 *                      数组.slice(开始索引(下标), 结束索引(下标))
 * 2.splice 会改变原数组,而slice不会
⑼.concat
 * 语法:原始数组.concat(数组1,数组2..., 数据1, 数据2, 数据3...)
 * 作用:进行数据拼接,把数组...数据之类的小括号里的内容,拼接在原始数组里
 * 返回值:拼接好的数组
 ```js
     var arr= [1, 2, 3]
     console.log('原始数组:', arr)
     
     var newArr = arr.concat([4, 5, 6], [20, 30], ['a', 'b', 'c'], 'qwer')
     console.log('newArr',newArr)// [1, 2, 3, 4, 5, 6, 20, 30, 'a', 'b', 'c', 'qwer']
     console.log('arr', arr)
 ```
⑽.join
 * 语法:数组.join('连接符')
 * 作用:使用"连接符",把数组内的每一个数据连接成一个字符串(不写连接符,默认使用的是逗号)
 * 返回值:连接好的字符串
 ```js
     var arr= [1, 2, 3]
     console.log('原始数组:', arr)
     
     var newArr = arr.join()//不传递连接符,默认使用的是逗号连接
     console.log(newArr) //1,2,3
     
     var newArr = arr.join('!')//不传递连接符,默认使用的是逗号连接
     console.log(newArr)//1!2!3
 ```
⑾.indexOf
 * 语法1:数组.indexOf(要检查的数据)
 *     * 作用:从前到后(从左到右)检查该数据第一次在该数组内出现的索引(下标)
 * 语法2:数组.indexOf(要检查的数据,开始索引)
 *     * 作用:在开始索引的位置按照从左到右的顺序,检查该数据第一次在该数组内出现的索引
 * 返回值:找到数据的情况下,会将数据第一次出现的下标(索引)返回
 *        没找到的情况下,会直接返回一个 -1
 * 
 * 备注:开始索引不写的时候 默认为0
 ```js
    var arr = [1, 1, 2, 2, 3, 3, 0, 4, 0]
    //         0  1  2  3  4  5  6  7  8
     
    var num = arr.indexOf(100) 
    //此时要检查的数据是数字100, 但是数组中并没有出现过数字100 所以返回值应该是-1
    console.log(num) //-1
     
    var num = arr.indexOf(0) 
    //此时要检查的数据是数字0,数字0按照从左到右的顺序,第一次出现的下标为6,所以返回值应该是 6
     console.log(num) //6
     
    var num = arr.indexOf(1) 
    //此时要检查的数据是数字1,数字0按照从左到右的顺序,第一次出现的下标为0,所以返回值应该是 0
     console.log(num) //0
     
    var num = arr.indexOf(1, 3) 
    //此时要检查的数据是数字1,但是是从下标3的位置开始按照从左往右的顺序查找
    console.log(num) //-1
 ```
⑿.lastIndexOf
 * 语法:数组.lastIndexOf(要检查的数据)
 *     * 作用:从后向前(从右向左)检查该数据第一次在该数组内出现的索引(下标)
 * 语法2:数组.lastIndexOf(要检查的数据,开始索引)
 *     * 作用:在开始索引的位置按照从右到左的顺序,检查该数据第一次在该数组内出现的索引
 * 返回值:找到数据的情况下,返回第一次出现的下标(索引)
 *         没找到的情况下,会直接返回一个 -1
 ```js
     var arr = [1, 1, 2, 2, 3, 3, 0, 4, 0]
     //         0  1  2  3  4  5  6  7  8
     
     var num = arr.lastIndexOf(3) 
     //此时按照从右向左的顺序查找,发现第一次出现的位置是下标为5的位置 所以num为5
     console.log(num) //5
     
     var num = arr.lastIndexOf(3, 2) 
     //此时在下标2的位置按照从右向左的顺序查找,但是 此时在数组中后续的位置并没有出现数字3,所以应该返回-1
     console.log(num) //-1
 ```