day4

139 阅读12分钟

1.while循环

1.循环结构语句 - while
    + 循环: 重复的执行某一段代码

  循环三要素
    + 开始: 初始变量, 初始数字
    + 结束: 判断条件, 结束数字
    + 步长: 每次间隔多少进行数数

  while 语法
    + while (条件) { 重复执行的代码 }
    + 意义:
      => 首先进行条件判断
      => 如果条件为 false, 直接结束整个循环
      => 如果条件为 true, 那么执行一遍 {} 内的代码
      => 然后再次进行条件判断
      => 如果条件为 false, 直接结束整个循环
      => 如果条件为 true, 那么执行一遍 {} 内的代码
      => 然后再次进行条件判断
      => ...
  
  Note:使用while深度遍历,一般属性名需要相同!!!

1.1 while循环小案例

Note:下述案例都用了临时变量temp,用临时变量时定义时最好加 _ 下划线!!!!!
1.斐波那契数列
    var a=1,b=1,s=a+b,str=a+"+"+b;
    var _temp;
    while(a+b<100){
        _temp=a;
        a=b;
        b+=_temp;
        s+=b;
        str+="+"+b;
    }
    str+="="+s;
    console.log(str)    //1+1+2+3+5+8+13+21+34+55+89=232
2.卢卡斯数列
    var a=1,b=3,s=a+b,str=a+"+"+b;
    var _temp;
    while(a+b<100){
        _temp=a;
        a=b;
        b+=_temp;
        s+=b;
        str+="+"+b;
    }
    str+="="+s;
    console.log(str)  //1+3+4+7+11+18+29+47+76=196

微信图片_20220603101643.png

1.2 while循环小案例

1.99乘法口诀表
    var table=document.getElementById("table");
    var str="";
    var i=1;
    while(i<10){
        str+="<tr>";
        var j=1;
        while(j<=i){
            str+=`<td>${i}*${j}=${i*j}</td>`;
            j++;
        }
       str+="</tr>";
        i++;
    }
   
   table.innerHTML=str;
2.100以内所有质数,素数
    var i=2;
    while(i<100){
        var j=2;
        var bool=true;  //设置的小开关
        while(j<i){
           if(i%j===0){
                bool=false;//只要能够被2~当前数-1的值整除,标志为这个数不是素数
           }
           j++;
        }
        if(bool){
            console.log(i);
        }
        i++;
    }
 3.最大公约数
    var a=12;
    var b=45;
    var max=a>b?a:b
    var min=a>b?b:a
    var str
    for(var i=min;i>=0;i--){
        if(a%i===0 && b%i===0){
        str=i
        break
        }
    }
    console.log(str)    //3
 4.最小公倍数
    var a=12;
    var b=45;
    var max=a>b?a:b
    var min=a<b?a:b
    var str
    for(var i=max;i<=a*b;i+=max){
    if(i%min===0){
        str=i
        break
    }
    }
    console.log(str)   //180

2.break和continue和标记语法 作用

流程控制语句
    1. break
    2. continue
    3. 标记语法

  break
    + 是一个使用在循环内的关键字
    + 当循环内执行到 break 关键字的时候, 会直接结束整个循环

  continue
    + 是一个使用在循环内的关键字
    + 当循环内执行到 continue 关键字的时候, 会结束循环的本次, 去到下一次(只是结束这次的循环)

  标记语法(label)
    + 给循环起一个名字
    + 书写在循环的上面一行, 写 名字:
    + 当你使用 breakcontinue 关键字的时候
      => 可以书写 break 名字
      => 或者书写 continue 名字
      => 表示打断哪一个循环或者结束哪一个循环的本次
    + 用在循环嵌套内

2.1补充

1.Note:对于while使用continue,i++必须考虑在continue之前,否则会死循环!!!!!
    例:var i=0;
       while(i<10){
          i++;
          if(i===5) continue;
          console.log(i)
      }
2.Note:当循环增加label(标签,标记) ,可以使用break跳出这个laebl循环
    例:var i=0;
xietian:while(i<10){
         var j=0;
         while(j<10){
            if(j===5) break xietian;
             j++;
             console.log(i,j)
         }
         i++;
         }
3.Note:continue跳出label时,会不执行当前循环和外层循环后面的语句!!!!!
    例:   var i=0;
  xietian:while(i<10){
          i++;
           var j=0;
           while(j<10){
               if(j===5) continue xietian;
               j++;
               console.log(i,j)
               } 
         console.log("aaa")
        }

3.do...while循环

 循环结构语句 - do ... while
    + 和 while 循环差不多的语句

  语法:
    do {
      重复执行的代码
    } while (条件)
  意义:
    + 不管条件是否满足, 首先先执行一遍 {} 内的代码
    + 开始进行条件判断
    + 如果为 false, 直接结束循环
    + 如果为 true, 那么执行一遍 {} 内的代码
    + 再次进行条件判断
    + 如果为 false, 直接结束循环
    + 如果为 true, 那么执行一遍 {} 内的代码
    + 再次进行条件判断
    + ...

  区别:
    + 当初始变量在条件以内的时候, 两个循环没有区别
    + 当初始变量在条件以外的时候
      => while 循环一次都不会执行
      => do ... while 循环会执行一次

4.for循环

 循环结构语句 - for
    + 循环结构语句 的一种

  语法:
    for (初始变量; 条件判断; 修改初始值) { 重复执行的代码 }

4.1补充

上述提到:对于while使用continue,i++必须考虑在continue之前,否则会死循环
如果使用continue时,for语句可以更完美处理这个i++的问题,不会造成死循环
例:for(var i=0;i<100;i++){
   if(i===10) continue;
     console.log(i)
       }

5.对象

对象数据类型
    + 是 JS 内的一个复杂数据类型 - Object
    + 对象:
      => 含义1: 一类事物的某一个具体个例
      => 含义2: JS 内的一种数据类型
    + 对象数据类型
      => 私人: 是一个 "盒子", 是一个承载数据的 "盒子"
      => 是一个无序的数据集合
      => 对象是一个 键值对 的集合

  创建对象数据类型
    + 字面量形式
      => 创建一个空对象: var obj = {}
      => 创建带有数据的对象: var obj = { 键值对, 键值对, ... }
        -> 键值对     键: 值
    + 内置构造函数形式
      => 创建一个空对象: var obj = new Object()

1.对象的键,必须是字符串或者Symbol类型,其他任何类型都会隐式转换为字符串 !!!!!!!!
2.所有引用类型尽量使用JSON.stringify转换为字符串查看!!!!!!!!!

5.1对象数据类型的基本操作

对象数据类型的基本操作(CRUD 增删改查)
    + JS 的语法内, 对于对象的操作给出了两套语法
      => 点语法
      => 数组关联语法
      
1.点语法    var obj = {} //空对象
    + 增: 向对象内插入一条数据
      => 语法: 对象名.键名 = 值
    + 删: 删除对象内的某一个数据
      => 语法: delete 对象名.键名
    + 改: 修改对象内的某一个数据
      => 语法: 对象名.键名 = 值
      => 对象内的 key 不允许重复, 原先有就是修改, 原先没有就是增加
    + 查: 获取对象内某一个 key 所保存的值
      => 语法: 对象名.键名
      => 如果对象内有该键名, 那么就是该键名对应的值
      => 如果对象内没有该键名, 那么就是 undefined
 
 2.数组关联语法    
  增:
    => 语法: 对象名['键名'] = 值
  删:
    => 语法: delete 对象名['键名']
  改:
    => 语法: 对象名['键名'] = 值
    => 原先有就是修改, 原先没有就是增加
  查:
    => 语法: 对象名['键名']
3.定义对象的方法
     obj.fn=function(){
                  }     

5.1的补充

1.对象内的 key 可以是什么 ? 什么都行
    1. 推荐使用 满足变量命名规则和规范 的名字
    2. 可以使用纯数字, 如果你使用的是纯数字, 会排列在对象的最前面
    3. 如果使用其他内容, 需要单独使用引号包裹 key
2.两种操作语法的区别
    1. 如果你操作的是 满足变量命名规则和规范 的 key
      => 两个语法一模一样
    2. 如果你操作的是 纯数字 或者 其他符号 的 key
      => 只能使用数组关联语法, 点语法会报错
    3. 涉及变量相关的时候
      => 只能使用数组关联语法
       
 例:var obj = {
  name: 'Jack',
  a$2: 100,
  user_email: '11111@qq.com',
  100: 200,
  1: 'abc',
  'c-z': 135,
  'a#c': 'hello world'
}
console.log(obj)
操作语法的区别举例:!!!!!!!!!!!!!!!!!!!!!!!!!很重要的 一定要注意
 1.JS 内, 点后面如果是数字, 那么要求前面也必须是数字(.后面为数字他就会把整个当成数值类型) 
    console.log(obj.100) // 报错
    console.log(obj[100])
 2.- 在 JS 内单独使用的时候, 就只能表示 减号
   这个的意义是 obj.c 减去 z 变量
   console.log(obj.c-z) // 报错 因为没有z变量
   console.log(obj['c-z']) 
3.涉及变量相关的时候
     var a = 'name'
  3.1当你使用点语法的时候, a 只能是表示一个对象内的准确的键名, 和 a 变量不可能产生任何关系
    console.log(obj.a)          //undefind       上述代码没有叫a的名字 
  3.2当你使用数组关联语法, 并且中括号内直接书写的字符串的时候, 只能是准确的表示对象内的某一个键名和 a 变量没有任何关系
    console.log(obj['a'])      //undefind        
  3.3当你使用数组关联语法, 并且中括号内书写的是一个变量的时候会把该变量的值解析出来, 放在中括号内
    obj[a] 等价于 obj['name']
    console.log(obj[a])  

5.2遍历对象

  回忆: 遍历 arguments
    => 为什么使用 for 循环遍历 arguments
    => 因为 arguments 内数据的排列是按照索引排列(索引是一组有规律的数字)
    => 又因为循环可以提供一组有规律的数字
    => 我们使用循环控制变量充当索引去遍历

  现在: 遍历 对象
    => 问题: 对象内的 key 没有规律
    => 使用 for in 循环的来遍历对象
    => 语法:
      for (var 变量 in 对象名) {
        重复执行的代码
        // 随着循环, 变量 分别表示对象内的每一个 key键
        // 随着循环, 对象名[变量] 分别表示对象内的每一个 value值
      }
      
例:var obj = {
   name: 'Jack',
   age: 18,
   gender: '男',
   scroe: 100
 }
 console.log(obj)
// 开始遍历
for (var k in obj) {
  // 这个循环会根据 obj 内有多少键值对, 执行多少次
  // 随着循环, k 变量分别是对象内的每一个 key, 是一个字符串类型
  // 因为是一个变量 k 表示的 键名
  // 所以只能使用数组关联语法
  console.log('我执行了', ' ---- ', k, ' ---- ', obj[k])
  // 当 k === 'name' 的时候, obj[k] 等价于 obj['name']
  // 当 k === 'age' 的时候, obj[k] 等价于 obj['age']
  // 当 k === 'gender' 的时候, obj[k] 等价于 obj['gender']
  // 当 k === 'score' 的时候, obj[k] 等价于 obj['score']
}  

5.2遍历对象的补充

 Note:遍历顺序                  !!!!!!!!!!
    1.先遍历 数字属性,从小到大
    2.其次按照属性的添加顺序遍历
    3.不遍历Symbol属性  (目前不知)
 例: var obj={};
    obj.b=2;
    obj.a=3;
    obj["10"]=10;
    obj["1"]=1;

    for(var prop in obj){
        console.log(prop,obj[prop]); //顺序1 1
       }                             //   10 10
                                     //    b 2
                                     //    a 3
       obj["a"]=1;                  //数组关联语法的 增
       console.log(obj["a"]);       // 1
  
   记住就行:对象的遍历并不是顺序
// 对象是松散结构,互相之间没有关系,所以对象不能做排序,key是唯一
// 在对象中直接通过key获取值,这种查找速度是最快的

微信图片_20220604141705.png

6.深浅拷贝

1. 赋值 把 对象的地址 复制一份给到另一个变量

2. 浅拷贝(只能拷贝一层 第二层就不算拷贝了 因为:第一层的可以单独修改 第二层会共同修改也就是公用一个存储空间)
// 创建一个新的对象数据类型
// 把 原始对象进行遍历, 依次添加到新对象内

例:
var o2 = {}
// 遍历原始对象
for (var k in o1) {
  // 当 k === 'info' 的时候
  // o1[k] 存储的就是一个对象地址
  // 把 o1['info'] 的对象地址, 赋值给了 o2['info']
  // 从此以后, o1['info'] 和 o2['info'] 操作的是一个对象空间
  o2[k] = o1[k]
}
console.log(o1, o2)
// 修改一级数据
o1.age = 20
console.log(o1, o2)//一个改了 一个没改
// 修改二级数据
o1.info.height = 200
console.log(o1, o2)//两个都改了

3. 深拷贝(你是你 我是我 我改了 你不改)
// 不管对象多少层级关系, 每一次都是完全独立的
// 遍历原始对象
//   判断原始对象如果某一个 key 存储的不是复杂数据类型, 直接拷贝
//   判断原始对象如果某一个 key 存储的是复杂数据类型, 在继续遍历

例:
// 递归实现深拷贝(target目标对象  origin原始对象)
function diffCopy(target, origin) {
  for (var k in origin) {
    // 判断数据类型
    if (Object.prototype.toString.call(origin[k]) === '[object Object]') {
      // 表示 origin[k] 是一个对象数据类型  ([object object] 是一个字符串)
      // 让 target[k] 也是一个对象数据类型
      target[k] = {}
      // 把 origin[k] 这个对象内的成员深拷贝一份到 target[k] 这个对象内
      diffCopy(target[k], origin[k])
    } else {
      // 表示非对象数据类型, 基本数据类型
      // 当 k === 'name'
      // target['name'] = origin['name']
      // 因为 target 内没有 name 这个成员
      // target['name'] = xxxx 就是在增加
      // 因为 origin 内有 name 这个成员 并且在 等于号 右边, 所以是访问
      target[k] = origin[k]
    }
  }
}

// 将来使用的时候
var o2 = {}
// 把 o1 对象内的内容深拷贝一份拷贝到 o2 内
diffCopy(o2, o1)
console.log('拷贝结束')
console.log(o1, o2)
// 修改一级数据
o1.name = 'Rose'
console.log(o1, o2)//一个改了 一个没改
// 修改二级数据
o1.info.height = 220
console.log(o1, o2)//一个改了 一个没改
// 修改三级数据
o1.info.address.city = '上海'
console.log(o1, o2)//一个改了 一个没改
// 修改四级数据
o1.info.address.hobby.ball = '足球'
console.log(o1, o2)//一个改了 一个没改

7.判断数据类型

小问题: 判断一个数据是 对象数据类型
// 目前: 我们只学习过一个 typeof 和 isNaN
// 只是: 准确的判断每一个数据类型
// 语法: Object.prototype.toString.call(你要判断的数据类型)
// 返回值: '[object 数据类型]'

8.函数

 认识函数数据类型 - Function
    + 复杂数据类型
    + 私人:
      => 是一个 "盒子", 一个承载一段代码的 "盒子"

  函数必然经历两个阶段
    1. 函数定义阶段: 把代码装进 "盒子" 的过程
    2. 函数调用阶段: 把 "盒子" 内的代码执行的过程

  函数定义阶段
    1. 声明式函数(命名式函数)
      => 语法: function 函数名() { 代码段 }
        -> function     定义函数的关键字
        -> 函数名        就是一个变量名
        -> ()           必须写, 放置参数(欠着, 已还)
        -> {}           承载代码段, 你要装进 "盒子" 的代码
    2. 赋值式函数(函数表达式/匿名函数)
      => 语法: var 变量名 = function () { 代码段 }
         (其中形参和实参关系不大 有实参按照实参来 没有实参默认用形参)
         
  函数调用阶段
    + 目的: 把 函数 体内的代码执行一遍
    + 语法: 函数名()
      => 函数名    表示你要调用的是哪一个函数
      => ()       调用(把函数体内的代码执行), 参数(欠着, 已还)

  函数调用上的区别
    + 声明式函数可以先调用, 也可以后调用
    + 赋值式函数只能后调用, 先调用会报错 xxx is not a function
 
例:
1. 函数定义阶段
1-1. 声明式函数
function fn() {
  console.log('我是声明式函数 fn')
}

1-2. 赋值式函数
var fun = function () {
  console.log('我是赋值式函数 fun')
}

2. 函数调用阶段
2-1. 我想执行 fn 函数体内的代码
fn()
2-2. 我想执行 fun 函数体内的代码
fun()

8.1函数补充

1.如果把命名函数写在语句块中就不会预赋值
例:console.log(a);     //undefind
    var a=1;
   {
       function a(){
       
        }
    }
    
   console.log(a);
   if(a==2){           //此处写undefind结果也一样
      function a(){

        }
     }
     console.log(a);  //undefind


   if(!a){             
       function a(){

        }
    }
    console.log(a); //结果为函数: ƒ a(){ }

自执行函数(匿名函数) 认识即可
    (function(){
    })();
    
    ~function(){
    }();
    
    +function(){
    }();
    
    -function(){
    }();

9.预解析

预解析
    + 在所有代码执行以前, 对代码进行通读并解释
  解析的内容
    1. var 定义的变量
      => 告诉浏览器, 我要定义一个 xxx 变量, 但是暂时不赋值
    2. 声明式函数
      => 注意: 函数表达式不进行预解析
      => 告诉浏览器, 我定义了一个叫做 xxx 变量(函数名), 并且赋值为一个函数
      
预解析的重名问题
    + 当你定义函数名和变量名的时候, 如果重名, 以函数为准, 只是在预解析阶段
    + 不要把变量和函数名重名(属于出错的一个问题  体现了变量语义化的方便 习惯问题)

9.1命名函数的预解析和预赋值

1.块语句中同名函数变量,将会把最后一个同名函数上面赋值的变量赋值在外面的变量  !!!!!
例:var a=1;
    if(a===1){
        a=2;
        function a(){    //最后一个同名函数上面赋值的变量 赋值在外面的变量
                         //最后一个 a=2 赋值到外面
        }
        a=3;
    }
    console.log(a);   //2

10.作用域

作用域
    + 指一个变量的使用范围

作用域的分类
    1. 全局作用域
      => 一个 html 文件打开, 就是一个全局作用域
      => 叫做 window
    2. 私有作用域(函数作用域)
      => **只有函数决定私有作用域**

作用域的父子关系
   + 写在哪一个作用域内的函数, 就是哪一个作用域的子级作用域
  
例:
// 此处在全局定义了一个叫做 f1 的函数
function f1() {
  // 此处是一个 f1 私有作用域
  // 是全局下的私有作用域
  // f1 的父级作用域就是 window

  // 此处是在 f1 私有作用域内定义了 f2 函数
  function f2() {
    // 此处是一个 f2 私有作用域
    // 是 f1 内的私有作用域
    // f2 的父级作用域就是 f1
  }
}

// 此处在全局定义了一个叫做 f3 的函数
function f3() {
  // 此时是一个 f3 私有作用域
  // f3 的父级作用域就是 window
}

10.1作用域的三个规则

作用域的三个规则    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    + 在作用域内一共提供了三个规则
    + 三个规则在所有作用域内通用
    + 熟读并背诵全文

  变量定义规则
    + 定义在哪一个作用域下的变量
    + 就是哪一个作用域的私有变量
    + 只能在该作用域及其后代作用域内使用
    + 不能再父级作用域内使用

  变量访问规则
    + 当你需要访问一个变量的值的时候
    + 首先在自己作用域内查找, 如果有, 直接使用, 停止查找
    + 如果没有, 去到父级作用域查找, 如果有直接使用, 停止查找
    + 如果还没有, 在去到父级作用域
    + 直到 全局作用域(window) 都没有
    + **那么直接报错 xxx is not defined**

  变量赋值规则
    + 当你需要给一个变量进行赋值操作的时候
    + 首先在自己作用域内查找, 如果有, 直接赋值, 停止查找
    + 如果没有, 去到父级作用域查找, 如果有直接赋值, 停止查找
    + 如果还没有, 就再去父级作用域查找
    + 以此类推, 直到全局作用域(window) 都没有
    + **那么把这个变量定义为全局变量, 再进行赋值**

10.2变量赋值补充

 1.函数内使用var定义的变量都是局部变量,一般会在当前函数执行完成后销毁
 2.函数外是无法调用到函数内的局部变量
 3.每个函数是一个独立的作用域,我们可以向上调用到父级作用域的变量,但是如果当前函数的变量与父级作用域同名时,就不能调用到父级作用域的变量
 4.父级作用域是无法调用到子级作用域的变量的
例:var a=1;
   var b=2;
   var c=10;

   function fn(){
      console.log(a)//1
      console.log(b);//2
      var c=4;
      function fn1(){
      console.log(a);//1
      console.log(b);//undefined (下面有个b=3) 函数的变量与父级作用域同名时,就不能调用到父级作用域的变量
      var b=3;
      console.log(b);//3
      console.log(c);//4
        }
        fn1();
        console.log(b);//2  父级作用域是无法调用到子级作用域的变量的
       }
    fn();
    console.log(c);//10
    
5.参数也是局部变量,参数属于当前函数内的局部变量 var
例:var a=1;
    function fn(a){
        console.log(a);  //3 因为3传参给了a a也是局部变量传进来的
        // var a=2;
    }
    fn(3);
    console.log(a)     //1

    var a=3;
    function fn(b){
        var a=b;       //4会传参进入b
    }
    fn(4);
    console.log(a);   //3  父级作用域是无法调用到子级作用域的变量的


    var a=3;
    function fn(a){
        // 参数也是局部变量,参数相当于var a
        a=7;
    }
    fn(5);
    console.log(a);//3
    
    //综合体:
    a(4)
    {
        a=4;
        function a(){
            console.log("b")
        }
        a=5;
        function a(){
            console.log("c")
        }
        a=6;                  //块语句中同名函数变量,将会把最后一个同名函数上面赋值的变量(这里是5)赋值在外面的变量
    }
    // var a=3;               //因为有下面函数a 此处var删除也不会报错
    function a(a){           //(a)里面的a是参数 参数就是局部变量 所以下面打印 7
        a=7
        console.log(a)//7
    }
    
    console.log(a)//5        //对应上述块语句 所以此处加上上述a=6;结果还是 5