js笔记(三)--===、||、!、头等函数、回调函数、立即执行函数、debug调试

51 阅读10分钟

1. === 和 == 的区别

  1. 普通的相等性检查 == 存在一个问题,它不能区分出 0 和 false,或者空字符串和 false这类运算:
  • 这是因为在比较不同类型的值时,处于判断符号 == 两侧的值会先被转化为数字;
  • 空字符串和 false 也是如此,转化后它们都为数字 0;

image.png 2. 如果我们需要区分 0 和 false,该怎么办?

  • 严格相等运算符 === 在进行比较时不会做任何的类型转换;
  • 换句话说,如果 a 和 b 属于不同的数据类型,那么 a === b 不会做任何的类型转换而立刻返回 false;
  1. 同样的,“不相等”符号 != 类似,“严格不相等”表示为 !==。

  2. 严格相等的运算符虽然写起来稍微长一些,但是它能够很清楚地显示代码意图,降低你犯错的可能性。

  3. 运算符的优先级, 在MDN上给出了所有运算符的优先级(不用去记)

2. 逻辑或 || 的本质

image.png

3.逻辑与 & 的本质

image.png

4.!(非)

  1. 逻辑非运算符接受一个参数,并按如下运算:
  • 步骤一:将操作数转化为布尔类型:true/false;
  • 步骤二:返回相反的值;
  1. 两个非运算 !! 有时候用来将某个值转化为布尔类型:
  • 也就是,第一个非运算将该值转化为布尔类型并取反,第二个非运算再次取反。
  • 最后我们就得到了一个任意值到布尔值的转化。
 <script>

    var message = "Hello World"
    console.log(Boolean(message))//true
    console.log(!!message)//true


    var obj = null
    console.log(Boolean(obj))//false
    console.log(!!obj)//false


  </script>

5.switch语句

  1. switch是分支结构的一种语句:
  • 它是通过判断表达式的结果(或者变量)是否等于case语句的常量,来执行相应的分支体的;
  1. 与if语句不同的是,switch语句只能做值的相等判断(使用全等运算符 ===),而if语句可以做值的范围判断;
  2. switch的语法:
  • switch 语句有至少一个 case 代码块和一个可选的 default 代码块。

image.png

6. for循环

image.png

7. 认识函数

  1. 当我们在谈函数时, 到底在谈些什么?
  • 函数其实就是某段代码的封装,这段代码帮助我们完成某一个功能;
  • 默认情况下JavaScript引擎或者浏览器会给我们提供一些已经实现好的函数;
  • 我们也可以编写属于自己的函数;
  1. 函数使用的步骤

函数的使用包含两个步骤:

  • 声明函数 —— 封装 独立的功能
  • 调用函数 —— 享受 封装 的成果

声明函数,在JavaScript中也可以称为定义函数:

  • 声明函数的过程是对某些功能的封装过程;
  • 在之后的开发中,我们会根据自己的需求定义很多自己的函数;

调用函数,也可以称为函数调用:

  • 调用函数是让已存在的函数为我们所用;
  • 这些函数可以是刚刚自己封装好的某个功能函数;
  • 当然, 我们也可以去使用默认提供的或者其他三方库定义好的函数;

函数的作用:

  • 在开发程序时,使用函数可以提高编写的效率以及代码的重用;

8.arguments参数

  1. 事实上在函数有一个特别的对象:arguments对象
  • 默认情况下,arguments对象是所有(非箭头)函数中都可用的局部变量;
  • 该对象中存放着所有的调用者传入的参数,从0位置开始,依次存放;
  • arguments变量的类型是一个object类型( array-like ),不是一个数组,但是和数组的用法看起来很相似;
  • 如果调用者传入的参数多余函数接收的参数,可以通过arguments去获取所有的参数;
  1. 因为这里涉及到数组、对象等概念,目前大家了解有这么一个参数即可。
  • 后续我们会对其专门进行学习,包括和数组之间的转化;

9.局部变量和外部变量

  1. 在JavaScript(ES5之前)中没有块级作用域的概念,但是函数可以定义自己的作用域。
  • 作用域(Scope)表示一些标识符的作用有效范围(所以也有被翻译为有效范围的);
  • 函数的作用域表示在函数内部定义的变量,只有在函数内部可以被访问到;
  1. 外部变量和局部变量的概念:
  • 定义在函数内部的变量,被称之为局部变量(Local Variables)。
  • 定义在函数外部的变量,被称之为外部变量(Outer Variables)。
  1. 什么是全局变量?
  • 在函数之外声明的变量(在script中声明的),称之为全局变量。
  • 全局变量在任何函数中都是可见的。
  • 通过var声明的全局变量会在window对象上添加一个属性(了解);
  1. 在函数中,访问变量的顺序是什么呢?
  • 优先访问自己函数中的变量,没有找到时,在外部中访问。
  1. 关于块级作用域、作用域链、变量提升、AO、VO、GO等概念我们后续将进行学习。

10.函数表达式

image.png 函数声明 vs 函数表达式

  1. 在开发中,函数的声明和函数表达式有什么区别,以及如何选择呢?
  2. 首先,语法不同:
  • 函数声明:在主代码流中声明为单独的语句的函数。
  • 函数表达式:在一个表达式中或另一个语法结构中创建的函数。
  1. 其次,JavaScript创建函数的时机是不同的:
  • 函数表达式是在代码执行到达时被创建,并且仅从那一刻起可用。
  • 在函数声明被定义之前,它就可以被调用。
    • ✓ 这是内部算法的原故;
    • ✓ 当 JavaScript 准备 运行脚本时,首先会在脚本中寻找全局函数声明,并创建这些函数;
  1. 开发中如何选择呢?
  • 当我们需要声明一个函数时,首先考虑函数声明语法。
  • 它能够为组织代码提供更多的灵活性,因为我们可以在声明这些函数之前调用这些函数。

 <script>
    // 函数的声明(声明语句)
    foo()
    function foo() {
      console.log("foo函数被执行了~")//执行了
    }
    // 函数的表达式
    // console.log(message) // undefined
    // var message = "why"
    // console.log(bar)
    bar()
    var bar = function() {
      console.log("bar函数被执行了~")//报错
    }
  </script>

11.JavaScript头等函数

  1. 头等函数(first-class function;第一级函数)是指在程序设计语言中,函数被当作头等公民。
  • 这意味着,函数可以作为别的函数的参数函数的返回值赋值给变量或存储在数据结构中;
  • 有人主张也应包括支持匿名函数(待会儿会讲到);
  1. 通常我们对作为头等公民的编程方式,称之为函数式编程
  • JavaScript就是符合函数式编程的语言,这个也是JavaScript的一大特点;
  1. 比如:函数可以在变量和变量之间相互进行赋值;

image.png 4. 一个函数是否可以作为头等函数,满足一下条件

  <script>

    // 函数作为一等(头等)公民
    // 1.函数可以被赋值给变量(函数表达式写法)
    var foo1 = function() {
      console.log("foo1函数被执行~")
    }
    // foo1()

    // 2.让函数在变量之间来回传递
    // var foo2 = foo1
    // foo2()


    // 3.函数可以另外一个函数的参数
    // function bar(fn) {
    //   console.log("fn:", fn)
    //   fn()
    // }
    // bar(foo1)


    // 4.函数作为另外一个函数的返回值
    // function sayHello() {
    //   function hi() {
    //     console.log("hi kobe")
    //   }
    //   return hi
    // }

    // var fn = sayHello()
    // fn()


    // 5.将函数存储在另外一个数据结构中
    var obj = {
      name: "why",
      eating: function() {
        console.log("eating")
      }
    }
    obj.eating()
    function bar1() {
      console.log("bar1函数被执行~")
    }
    function bar2() {
      console.log("bar2函数被执行~")
    }
    function bar3() {
      console.log("bar3函数被执行~")
    }
    // 事件总线的封装
    var fns = [bar1, bar2, bar3]

    // 函数式编程: 使用函数来作为头等公民使用函数, 这种编程方式(范式).
    // JavaScript支持函数式编程.

  </script>

12.回调函数(Callback Function)

  1. 既然函数可以作为一个值相互赋值,那么也可以传递给另外一个函数。 2.foo这种函数我们也可以称之为高阶函数(Higher-order function);
  2. 高阶函数必须至少满足两个条件之一:
  • 接受一个或多个函数作为输入;
  • 输出一个函数;
  1. 匿名(anonymous)函数的理解:
  • 如果在传入一个函数时,我们没有指定这个函数的名词或者通过函数表达式指定函数对应的变量,那么这个函数称之为匿名 函数。
 <script>

    // 1.函数回调的概念理解
    // function foo(fn) {
    //   // 通过fn去调用bar函数的过程, 称之为函数的回调
    //   fn()
    // }
    // function bar() {
    //   console.log("bar函数被执行了~")
    // }
    // foo(bar)


    // 2.函数回调的案例
    // function request(url, callback) {
    //   console.log("根据URL向服务器发送网络请求")
    //   console.log("需要花费比较长的时间拿到对应的结果")
    //   var list = ["javascript", "javascript学习", "JavaScript高级编程"]
    //   callback(list)
    // }

    // function handleResult(res) {
    //   console.log("在handleResult中拿到结果:", res)
    // }
    // request("url", handleResult)


    // 3.函数回调的案例重构
    function request(url, callback) {
      console.log("根据URL向服务器发送网络请求")
      console.log("需要花费比较长的时间拿到对应的结果")
      var list = ["javascript", "javascript学习", "JavaScript高级编程"]
      callback(list)
    }

    // 传入的函数是没有名字, 匿名函数
    request("url", function(res) {
      console.log("在handleResult中拿到结果:", res)
    })

  </script>

13.立即执行函数

  1. 什么是立即执行函数?
  • 专业名字:Immediately-Invoked Function Expression(IIFE 立即调用函数表达式)
  • 表达的含义是一个函数定义完后被立即执行
    • 第一部分是定义了一个匿名函数,这个函数有自己独立的作用域。
    • 第二部分是后面的(),表示这个函数被执行了

image.png

 <script>

    // 1.普通函数的使用过程
    // function foo() {
    //   console.log("foo函数被执行~")
    // }
    // foo()
    // foo(); // ()[]{}

    // 2.定义函数, 定义完这个函数之后, 会要求这个函数立即被执行
    // {} 代码块/对象类型
    // () 控制优先级(2+3)*5/函数的调用/函数的参数
    // [] 定义数组/从数组-对象中取值/对象的计算属性
    // 立即执行函数(常用的写法)
    (function() { 
      console.log("立即执行函数被调用~")
    })()

    // 3.立即执行函数的参数和返回值
    var result = (function(name) {
      console.log("函数立刻被执行~", name)
      return "Hello World"
    })("why")
    console.log(result)

  </script>

  1. 这个东西有什么用?
  • 会创建一个独立的执行上下文环境,可以避免外界访问或修改内部的变量,也避免了对内部变量的修改
 // 应用场景一: 防止全局变量的命名冲突
    
    // 立即执行函数和普通的代码有什么区别?
    // 在立即执行函数中定义的变量是有自己的作用域的
    (function() {
      var message = "Hello World"
      // console.log(message)
    })()
    // console.log(message)
    // var message = "Hello World"
    // console.log(message)

//应用二
  // 使用立即执行函数
    var btnEls = document.querySelectorAll(".btn")
    for (var i = 0; i < btnEls.length; i++) {
      var btn = btnEls[i];
      (function(m) {
        btn.onclick = function() {
          console.log(`按钮${m+1}发生了点击`)
        }
      })(i)
    }
  1. 立即执行函数的其他写法

image.png

 <script>
    // 1.常见的写法
    // (function() {
    //   console.log("立即执行函数被调用~")
    // })()


    // 2.错误的写法
    // () -> 优先级的()
    // function foo() {
    // }()

    // 3.其他写法
    // 匿名函数
    (function(fn) {
      console.log("立即执行函数被调用")
    }());

    // +(正号)-(符号)!(取反) - 了解
    +function foo() {}()

  </script>

14.代码风格

<script>

    // 1.foo和()之间不需要有空格
    // 2.多参数,后面加上一个空格
    // 3.()和{之间有一个空格
    // 4.{和其他函数定义在同一行中
    function foo(m, n) {

    }

    foo(20, 30)

    if (true) {
    } else {

    }

    for (var i = 0; i < 10; i++) {

    }

    // 模板字符串(可以换行)
    var message = `
       哈哈哈哈哈${100}
    `

    // 图里面建议:
    function sum(num1, num2) {
      return num1 + num2
    }

  </script>

15.Chrome的debug调试技巧

image.png 或者在源代码中添加debugger image.png

  1. 添加变量,对变量进行监听

image.png 2. 断点的数量

image.png 3. 移除断点

image.png 4. 作用域

scope里面查看作用域,以及当前的作用域里面的变量,以及变量的值。 image.png 5. 函数调用栈

image.png 6. 各项操作

image.png

  • 恢复,只恢某一个断点的执行,连续点击会一次恢复断点。
  • 执行下一行代码
  • 进入函数内部,查看对应函数一步一步的执行过程,会进入异步函数
  • 立即跳出函数
  • 也是进入函数内部,不会进入异步函数