写给 Javascript 初级学者的第 3 份笔记持续更新中~~~

168 阅读4分钟

函数的作用域:

  • 概念: 就是变量可以起效果的范围
  • 目的: 为了提高程序的可靠性更重要的是减少命名的冲突

全局作用域 && 局部作用域:

全局作用域:(直接在script内 或者 在一个单独的JS文件内书写的代码)
在此作用域创建的变量,我们叫做全局变量,在当前script 标签内的哪里都能使用
<script>
   var num =100
   //假设中间间隔500行代码
   console.log(num); //此时依然可以使用 num;JS中,全局作用域只有一个 提前给我们准备好的 对象(一种数据格式);这个对象叫做: window 
我们创建的全局变量,会被自动添加到 window 对象中
  var abc = "我是一个全局变量 ABC " //创建一个全局变量 abc
  console.log(window);
</script>
局部作用域:(在JS中,只有函数能够创建局部作用域所以也可以叫做: 函数作用域)
在此作用域创建的变量,只能在当前作用域使用,超出这个作用域(也就是在函数外部) 是不能使用的
<script>
   function fn(){
    var sum1 = "我是函数在 fn 内创建的变量,我是局部变量,所以只能在当前函数内使用"
    console.log(sum1);//此时控制台会打印sum1的结果
   }
    fn() 
    console.log(sum1); //因为 sum是在函数内创建的,只能在函数内调用,超出函数的范围 就不可以使用的
</script>

作用域链:

  • 概念: 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取哪个值,这种结构我们称之为作用域链;

访问规则:

  • 在访问一个变量的时候,如果当前作用域内没有,会去自己的父级作用域内查找,如果找到了就直接使用,如果没有找到就继续向上层查找;直到查找到 最顶层的全局作用域,如果找到了就直接使用,如果没有找到,报错会提示变量不存在(未定义)
访问规则案例1:
<script>
  function fn1(){
    var num = 100
    function fn2(){
      console.log(num);//100
      /**
       * 控制台打印的值为 100
       * 1.先在当前作用域内,也就是fn2 函数内部开始查找变量num,然后发现,当前这个作用域内 没有这个变量,所以会去自己的父级的作用域内查找(也就是fn1 这个函数内部)
       * 2.来到自己父级内部查找,此时找到了一个变量 num 它的值为 100,然后直接使用这个变量 并且停止查找
      */
    }
    fn2()
   }
   fn1()

</script>
访问规则案例2:
<script>
  function fn1(){
    function fn2(){
      console.log(num);
      /**
       *  num 找不到,所以控制台出现报错
       * 1.先在当前作用域内查找,也就是 fn2 内部,发现没有,去自己的父级查找,也就是 fn1 内部
       * 2.来到了 fn1 内部查找,发现没有,去自己的父级查找,也就是 全局作用域
       * 3.来到了全局作用域内查找,发现还是没有,然后停止查找,返回一个 num 未定义的报错
       * 
       * 4.虽然 fn2 作用域内的子级作用域内 (fn3函数内部) 有一个变量叫做 num 但是根据 作用域链的访问规则
       * 我们并不会去这个fn3的作用域内查找,因为作用域只会逐层向上查找,并不会向下查找
      */
      function fn3(){
        var num = 666
      }
      fn2()
    }
    fn2()
   }
   fn1()
</script>
访问规则案例3:
<script>
  var num = 999
   function fn1(){
    function fn2(){
      console.log(num);
      /**
       * 控制台打印的值为 999
       * 1.先在当前作用域内,也就是fn2 函数内部开始查找变量num,然后发现,当前这个作用域内 没有这个变量,所以会去自己的父级的作用域内查找(也就是fn1 这个函数内部)
       * 2.来到自己父级内部查找,发现并没有一个叫做 num 的变量,然后继续向上层查找,也就是去全局作用域内查找
       * 3.来到全局作用域内查找的时候,发现了一个叫做 num 的变量,值为999,然后停止查找,直接使用该变量
      */
    }
    fn2()
   }
   fn1()
</script>

赋值规则:

  • 在给变量赋值的时候,首先会去当前作用域查找变量,如果有直接赋值,并停止查找;如果没有,会去自己的父级查找,在父级找到直接重新赋值然后停止查找.如果没有继续向自己的父级查找,直到找到全局作用域;在全局作用域内,找到了就直接重新赋值修改它的值,如果没有找到,那么会在全局作用域创建一个变量,并赋值;
赋值规则案例1:
<script>
      function fn1(){
          function fn2(){
            num = 100
          }
          fn2()
        }
        fn1()
        console.log(num);//100
</script>
赋值规则案例2:
<script>
function fn1(){
          var num = 999
          function fn2(){
            num = 100
            /**
             * 在当前作用域内查找 num 发现没有,会去自己的父级作用域内查找,也就是 fn1 函数内部
             * 在fn1 函数内部发现一个变量 num 然后值为 999 我们会对这个变量做一个重新赋值的操作
             * 也就是 将他的值 重新修改为 100
            */
          }
          fn2()
        }
        fn1()
        console.log(num);//未定义
 </script>

递归函数:

  • 概念: 本质上还是一个函数.当一个函数在函数的内部,调用了自身,那么就算是一个所谓的 递归函数(只不过有点小缺陷)
简单的递归函数:
 <script>
  function fn(n){
   fn()
 }
 </script>

写一个简单的递归:

案例分析递归函数:
 <script>
  function fn(n){
    if(n===1){
      //说明此时想要计算1的阶乘,那么我直接将1 的阶乘的结果 return 出去
      return 1
    }
  return n * fn(n - 1)
   }
   var sum = fn(4)
   console.log(sum);//24
 </script>
 步骤:
 /**
    * 第一次调用 传递的参数为: 4
    *   形参 n===4
    *   函数体 1.if分支   2.return 递归调用
    *     此时if分支语句不会执行,开始执行return 递归调用
    *     return n*fn(n - 1)  return 4 * fn(4-1) ===24
    * 
    *   计算fn(3) 
    *   传递的形参 n ===3
    *   此时分支语句不会执行,开始执行return 递归调用
    *      return n*fn(n - 1)  return 3 * fn(3-1) ===6
    *   
    *  计算fn(2) 
    *   传递的形参 n ===2
    *   此时分支语句不会执行,开始执行return 递归调用
    *      return n*fn(n - 1)  return 2 * fn(1) ===2
    * 
    *  计算fn(1) 
    *   传递的形参 n ===1
    *   此时分支语句判断成功,所以 return 1  并且中断函数的递归
   */

简单了解对象:

  • 概念: 是JS中的一种数据格式,对象在JS中的数据类型数据为: 引用数据类型(也叫复杂数据类型)
  • 储存对象的语法: 语法1: var obj = {键值对 key:value}
语法的使用:
 在下边这个对象中:
                 a 为key,100 为对应的 value
      另一种叫法: a 为键 ,100 为对应的值
      另一种叫法: a 为属性名,100为对应的属性值
 <script>
 var obj = {
    //key : value
       a:100,
       b:200,
       c:300,
       q:666
   }
   console.log(obj)
 </script>

创建对象:(分为两种方式)

1.字面量方式创建对象:

 语法:var obj = {键值对}
 <script>
   var obj = {
    a:100,
    b:200,
    c:"300",
    d:"abc",
    e:undefined,
    w:null,
    r:function(){
      console.log("我是obj 对象 内 r 这个属性的属性值,我是一个函数")
    }
   }
   console.log(obj)
 </script>

2.内置构造函数创建:

   语法1: var obj1 = new Object() //创建空对象
   语法2: var obj1 = new Object({a :1,b:2})//创建一个具有属性或者说具有键值对的对象
   注意:  new Object 的 O 是大写的,不是小写;
 <script>
   var obj1 = new Object({
    a:100,
    b:200,
    c:"300",
    d:"abc",
    e:undefined,
    w:null,
   })
   console.log(obj1)
 </script>

对象内对于 键(key) 的要求:

1.推荐使用符合变量命名规则与规范的名字 ->(一般公司推荐使用这一种,后两种不常见)
2.对象的 key 也可以使用 纯数字 来当做键名(属性名/key)
3.可以使用任何特殊符号(但是需要使用引号包裹)
4. 对象内部有多个 键值对,那么需要用 逗号 间隔开

对象数据类型的操作(增删改查)两种语法:

  • 就是对内部的属性进行操作

点语法:

点语法增删改查的使用:
<script>
   var obj = {
      a: 11,
      b:"qwe",
      c:true
    }
    console.log("原始对象:",obj);
    
 //1.点语法 -- 新增: 向对象内部新增一个属性
    obj.q = "QF001"
    obj.f = 111
    console.log("最新的对象:",obj);
    
 //2.点语法 -- 删除  删除对象内部的某一个属性;需要借用一个关键字来实现: delete
     delete obj.a
     console.log("删除属性后的 obj:",obj);
     
 //3.点语法 -- 修改:  修改对象内部某一个属性对应的属性值
    obj.c = false
    console.log("修改属性后的对象:",obj);
    
 //4.点语法 -- 查询: 获取到对象内部 某一个属性对应的属性值
    console.log(obj.a);
    console.log(obj.b);
</script>

中括号语法(数组语法):

中括号的增删改查的使用:
<script>
    var obj = {
      a: 11,
      b:"qwe",
      c:true
    }
    console.log("原始对象:",obj);
    
  //1. 中括号语法--- 查询
     console.log(obj["a"]); //1
     console.log(obj["c"]); //true
     
  //2.中括号语法 --- 新增
     obj["r"] = 12345
     obj["w"] = 789
     console.log("新增后的obj:",obj);
     
  //3.中括号语法---修改
     obj["b"] = "QF001"
     obj["c"] = false
     console.log("修改后的obj:",obj);
     
 //4.中括号语法--- 删除
     delete obj["a"]
     console.log("删除属性后的obj:",obj);
 
</script>

两者的差异:

  • 相同: 点语法与中括号语法,作用相同;
  • 不同点: 特殊情况下我们需要使用中括号语法:
1.对象的属性名,有纯数字或者特殊符号,这个时候,就只能使用中括号语法
<script>
   var obj = {
    100:"我的属性名是 纯数字 100",
    '@':'我的属性名是 特殊符号 @',
    '!':'我的属性名是 特殊符号 !'
   }
   console.log(obj[100]);
   console.log(obj["100"]);
   console.log(obj["!"]);// 特殊符号需要使用引号进行包裹
   console.log(obj["@"]);
</script>
2.如果涉及变量相关的时候,也需要使用中括号
<script>
  var obj1 = {
    a:1,
    b:2,
    name:"QF001"
  }
  var myName = "name"
  console.log(obj1.myName);//undefined
  console.log(obj1[myName]);//QF001
  // 报错输出结果是 undefined
  //原因:因为对象的 点语法,会将 点 后边的字符 当成一个字符串去使用,而不是当做变量;
  
  //实际输出的正确结果: QF001
  //中括号语法,内部书写的字符,如果不加引号,会把它当做变量去使用,所以找到实际的值之后,myName 这个变量对应的值为 "name"
</script>

for in 循环遍历对象:

     for...in: 一个循环语句
     对象: 一种数据格式
     遍历:一般我们会说 " 遍历对象" /'遍历数组'
     "遍历对象" 想办法拿到对象内部所有的 属性名与属性值
     语法: for(var i in 要遍历的对象){循环要执行的代码}
案例:
<script>
   var obj = {
      a:1,
      q:"qwe",
      t:true,
      u:undefined
    }
    for(var i in obj){
    // console.log(1);
    // console.log(i); //a q t u 这四个 是 对象 obj 的所有属性名/键名/key
    //需求: 打印对象所有的属性值
    console.log(obj); //此时打印的是完整的对象,所以不合适
    console.log(obj.i); //undefined 点语法会将后边的字符当做字符串来使用,而不是当做变量,如果想当变量来使用,那么需要使用中括号语法
    console.log(obj[i]); //打印结果为: 属性值
   }
</script>