廖雪峰JS学习总结-函数篇

5,764 阅读7分钟

最近在刷廖雪峰的JS教程,把里面的自己不太清楚的东西在刷一遍。
教程网址:www.liaoxuefeng.com/wiki/001434…

函数的定义和调用:

  1. 如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined
  2. JavaScript允许传入任意参数,即使函数内部不需要这些参数,也不影响调用。
  3. JavaScript还有一个免费赠送的关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array
  4. 利用arguments即使函数不定义任何参数,还是可以拿到参数的值。
    function abs() {
     if (arguments.length === 0) {
         return 0;
     }
     var x = arguments[0];
     return x >= 0 ? x : -x;
    }
    abs(); // 0
    abs(10); // 10
    abs(-9); // 9
  5. ES6引入了新的rest参数,他可以获得额外的rest参数,rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。(如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined))
    function foo(a, b, ...rest) {
     console.log('a = ' + a);
     console.log('b = ' + b);
     console.log(rest);
    }
    foo(1, 2, 3, 4, 5);
    // 结果:
    // a = 1
    // b = 2
    // Array [ 3, 4, 5 ]
    foo(1);
    // 结果:
    // a = 1
    // b = undefined
    // Array []
  6. return语句必须在一行上,如果必须为多行返回,需要在retrun后紧跟 {,正确的多行写法如下:
    function foo() {
     return { // 这里不会自动加分号,因为{表示语句尚未结束
         name: 'foo'
     };
    }

变量作用域:

  1. 如果用var在函数体内声明变量,该变量作用域为整个函数;因为这个原因,在不同函数内部用var申明的同名变量互相独立,互不影响。
  2. 由于函数可以相互嵌套,内部函数可以访问外部函数定义的变量,但是如果内部和外部都定义了同名变量怎么办了?JS函数查找变量会从自身定义函数开始,也就是从’内’向’外’,顺着作用域链查找,如果内部函数已经定义了,则会’屏蔽’掉外部函数变量。
  3. JS函数的var定义有个特点,它会先扫描整个函数体的语句,把所有var申明的变量“提升”到函数顶部,所以定义变量时,我们应该严格遵守在函数内部先声明的规则。
  4. JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性上了。
  5. 为了防止造成命名冲突,比较好的方法是把自己的所有变量和函数,都绑定到一个全局变量中:
    // 唯一的全局变量MYAPP:
    var MYAPP = {};
    // 其他变量:
    MYAPP.name = 'myapp';
    MYAPP.version = 1.0;
    // 其他函数:
    MYAPP.foo = function () {
     return 'foo';
    };
  6. 在for循环是无法定义具有局部作用域的的变量的。在ES6中可以使用let替代var可以申明一个块级作用域的变量。
  7. 对于常量的定义,以前都是使用全部大写来规定,现在ES6总可以使用关键字const来定义常量,constlet都具有块级作用域,并且const定义以后无法修改。
  8. 补充说明一下letconst的特点,不存在变量提升、会造成暂时性死区、不允许重复定义。

方法:

在一个对象中绑定函数,称为这个对象的方法。这里要介绍一个很坑爹的概念,就是this的指向问题。

那么对于不同的调用,这个this的指向分别是什么了??
答:this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。
在《javascript语言精髓》中大概概括了4种调用方式:

1. 方法调用模式
2. 函数调用模式
3. 构造器调用模式
4. apply/call调用模式

特别补充:

  1. 在构造器调用时,如果加入了return并且return了一个对象,this会指向这个return的对象。
  2. 严格模式下在函数内部定义的函数,this指向undefined(在非strict模式下,它指向全局对象window

那么有没有办法去控制this的指向了?
有的,可以使用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。另一个与apply()类似的方法是call(),唯一区别是:

  • apply()把参数打包成Array再传入;
  • call()把参数按顺序传入。
    另外ES5还加入了一个bind()方法,它会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

利用apply(),我们还可以动态改变函数的行为。
JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。

高阶函数:

  1. 一个函数接收另一个函数作为参数,这种函数就称之为高阶函数。
  2. forEach方法,是最基本的方法,就是遍历与循环,默认有3个传参:分别是遍历的数组内容item、数组索引index、和当前遍历数组Array。另外,除去第一个必须的回调函数参数,还可以接受一个上下文参数(改变回调函数的this指向);并且forEach不会遍历空元素。
  3. map方法,基本用法与forEach一致,但是不同的,它会返回一个新的数组,所以在callback需要有return值,如果没有,会返回undefined。(从字面理解,map就是映射的意思)
  4. filter方法,用法和map很相似,从字面理解,就是过滤、筛选的意思。但是函数的callback需要返回布尔值truefalse,并且返回值只需要为弱等==即可。
  5. some 方法,对数组中每一项运行指定函数,如果该函数对任一项返回true,则返回true。(一旦遇到true,就会中断循环,返回true,类似于||判断)
  6. every方法,对数组中的每一项运行给定函数,如果该函数对每一项返回true,则返回true。(一旦遇到false,就会中断循环,返回false,类似于&&判断)
  7. sort方法,默认把所有元素先转换为String再进行ASCII码排序,所以这个很坑爹。要想正确的排序,需要添加一个函数。sort方法,可以接受一个回调函数,默认有两个传参:分别是比较的数组项。

闭包(还要在研究一下,以后补充吧):

「闭包」,是指那些能够访问独立(自由)变量的函数(变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。特性:

  1. 函数嵌套函数
  2. 函数内部可以引用外部的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

箭头函数:

  1. 箭头函数使得表达更加简洁。
  2. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  3. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  4. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
  5. 不可以使用yield命令,因此箭头函数不能用作Generator函数。

Generator:

  1. Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。
  2. next法返回值的value属性,是 Generator 函数向外输出数据;next方法还可以接受参数,向 Generator 函数体内输入数据。
  3. Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。

有错误希望指出,一定会及时修改!