zoukankan      html  css  js  c++  java
  • 《JavaScript 高级程序设计》学习总结七(1)

    引言:函数表达式是JavaScript 中一个既强大又容易令人困惑的特性。这一章我们着重对JavaScript 的函数进行总结学习,内容包括:JS的函数特性,以及闭包、this 对象等。不得不说,《JavaScript高级程序设计第三版》这本书中最让我觉得纠结的就是这一章节了,前面或者是后面的章节学习不是没有难点,但是无论是这一章的“this” 还是“JavaScript 的块级作用域(注意我这里说的是JavaScript的)”以及闭包等等知识在学习的时候都很容易让人糊涂,比如一个“this”的作用,不同的应用场景,this 的指向又有所不同,在学习this的时候我总是会感觉 this 的运用有些莫名其妙,所以这一章的总结,我想我会尽量去进行这些知识点的举例,通过更多的例子来进行说明、搞懂这些知识点。

    函数声明提升:

    关于函数声明,它的一个重要的特征就是“函数声明提升” 意思是在执行代码前会先读取函数声明,这就意味着可以把函数声明放在调用它的语句后面。比如:

    sayHi();

    function sayHi(){

      alert("Hi");

    }

    这个例子不会报错,因为在代码执行前会先读取函数声明。(这里不得不说的是,JavaScript 中对于var 声明的变量也有“变量提升”——函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部)。这其实也归功于JavaScript 的解析机制:遇到 script 标签的话 js 就进行预解析,将变量 var 和 function 声明提升,但不会执行 function,然后就进入上下文执行,上下文执行还是执行预解析同样操作,直到没有 var 和 function,就开始执行上下文。需要注意都是函数声明提升直接把整个函数提到执行环境的最顶端

    第二种创建函数的方式是使用函数表达式。函数表达式有几种不同的语法形式。下面是最常见的一 种形式。

    var functionName = function(arg0, arg1, arg2){

    //函数体

    };

    这种形式看起来好像是常规的变量赋值语句,即创建一个函数并将它赋值给变量 functionName。 这种情况下创建的函数叫做匿名函数(anonymous function),因为 function 关键字后面没有标识符。 (匿名函数有时候也叫拉姆达函数。)匿名函数的 name 属性是空字符串。

    函数表达式与其他表达式一样,在使用前必须先赋值。以下代码会导致错误。

    sayHi();//错误:函数还不存在

    var sayHi = function(){

    alert("Hi!");

    };

    函数声明与函数表达式区别:

    理解函数提升的关键,就是理解函数声明与函数表达式之间的区别。例如,执行以下代码的结果可 能会让人意想不到。

    //不要这样做!

    if(condition){

    function sayHi(){

    alert("Hi!");

    } }

    else { function sayHi(){ alert("Yo!"); } }

    一个定义。实际上,这在 ECMAScript 中属于无效语法,JavaScript 引擎会尝试修正错误,将其转换为合 理的状态。但问题是浏览器尝试修正错误的做法并不一致。大多数浏览器会返回第二个声明,忽略 condition;Firefox 会在 condition 为 true 时返回第一个声明。因此这种使用方式很危险,不应该 出现在你的代码中。不过,如果是使用函数表达式,那就没有什么问题了。 

     1 //可以这样做
     2 var sayHi;
     3 if(condition){
     4  sayHi = function(){
     5  alert("Hi!");
     6  };
     7 } else {
     8  sayHi = function(){
     9  alert("Yo!");
    10  };
    11 } 

    函数递归:

    所谓递归函数,通俗理解就是函数调用自己本身。比如:

    1 function factorial(num){
    2  if (num <= 1){
    3  return 1;
    4  } else {
    5  return num * factorial(num-1);
    6  }
    7 } 

    这是一个经典的递归阶乘函数。虽然这个函数表面看来没什么问题,但下面的代码却可能导致它出错。

    var anotherFactorial = factorial;

    factorial = null;

    alert(anotherFactorial(4)); //出错!

    以上代码先把 factorial()函数保存在变量 anotherFactorial 中,然后将 factorial 变量设 置为 null,结果指向原始函数的引用只剩下一个。但在接下来调用 anotherFactorial()时,由于必 须执行 factorial(),而 factorial 已经不再是函数,所以就会导致错误。在这种情况下,使用 arguments.callee 可以解决这个问题。 我们知道,arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数 的递归调用,例如:

    1 function factorial(num){
    2  if (num <= 1){
    3  return 1;
    4  } else {
    5  return num * arguments.callee(num-1);
    6  }
    7 } 

    加粗的代码显示,通过使用 arguments.callee 代替函数名,可以确保无论怎样调用函数都不会 出问题。因此,在编写递归函数时,使用 arguments.callee 总比使用函数名更保险。 但在严格模式下,不能通过脚本访问 arguments.callee,访问这个属性会导致错误。不过,可 以使用命名函数表达式来达成相同的结果。例如:

    1 var factorial = (function f(num){
    2  if (num <= 1){
    3  return 1;
    4  } else {
    5  return num * f(num-1);
    6  }
    7 }); 

    以上代码创建了一个名为 f()的命名函数表达式,然后将它赋值给变量 factorial。即便把函数 赋值给了另一个变量,函数的名字 f 仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和 非严格模式下都行得通。

    -------------------------------------------------------------------------------------本章节完-------------------------------------------------------------------------------

    下章节预告:闭包与this。

    (因为闭包与this常常被拿出来问,同时也是javascript中让人模糊的知识点,所以我打算好好在下一章节对其进行总结,这样一来,也比较方便于知识点的阅读)

     

  • 相关阅读:
    【学习笔记 2】单调队列 & 单调栈
    【学习笔记 1】快速幂
    题解P1151
    题解 P6161【[Cnoi2020]高维】
    不知道叫啥的题目1
    神秘题目1
    5.30 模拟赛赛后总结
    矩阵乘法加速图上问题专题总结
    点分治&点分树 复习
    5.26赛后总结
  • 原文地址:https://www.cnblogs.com/wxhhts/p/9486566.html
Copyright © 2011-2022 走看看