函数

    面试指南

    函数声明与函数表达式

    对于函数声明解释器会首先读取,并使其在执行任何代码之前可用;对于函数表达式,则必须等到解释器执行到它所在的代码行,才会被真正解析。

    例如下面例子,函数表达式test2必须声明在其调用之前才可用

    1. const factorial = (function f(num){
    2. if(num <= 1){
    3. return 1;
    4. }else{
    5. return num * f(num -1);
    6. }
    7. });
    8. console.log(factorial(3)); // 6

    内置函数

    • push()

    let a = [].push('test'); 输出a的值为1而不是['test'],因为push()返回的是数组的长度。

    1. let a = [];
    2. a.push('test');
    3. console.log(a); //['test']

    arguments对象

    系统内置的arguments对象,可以用于获取函数参数、参数长度等

    面试:递归调用实现一个阶乘函数

    1. function sum(num){
    2. if(num <= 1){
    3. //获取参数长度
    4. console.log('arguments.length: ', arguments.length);
    5. return 1;
    6. }else{
    7. return num * arguments.callee(num-1);
    8. }
    9. }
    10. console.log(sum(3));
    • apply使用情况
    1. function box(num1,num2){
    2. return num1+num2;
    3. }
    4. function sum(num1,num2){
    5. return box.apply(this,[num1,num2]);
    6. //或者下面写法arguments可以当数组传递
    7. //return box.apply(this,arguments);
    8. }
    9. console.log(sum(10,10)); //输出结果: 20
    • call的使用示例
    1. function box(num1,num2){
    2. return num1+num2;
    3. }
    4. function sum2(num1,num2){
    5. }
    6. console.log(sum(10,10)); //输出结果: 20

    总结两种情况区别: apply传递参数是按照数组传递,call是一个一个传递

    引用传递

    • 示例一:js代码按值传递

    如果按引用传递,那么函数里面的num会变成类似全局变量,最后输出60

    • 示例二:php代码传递一个参数:

    php中的引用传递,会改变外部的num值,最后num也会输出60。

    1. function box(&$num){
    2. //加上&符号将num变成全局变量
    3. $num+=10;
    4. return $num;
    5. }
    6. $num = 50;
    7. echo box($num); // 60
    8. echo $num; // 60
    • 示例三:js代码传递一个对象
    1. function box(obj){ // 按对象传递
    2. obj.num+=10;
    3. return obj.num;
    4. }
    5. var obj = { num: 50 };
    6. console.log(box(obj)); // 60
    7. console.log(obj.num); // 60

    匿名函数与闭包

    匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数,由于闭包作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。

    匿名函数的自我执行

    1. (function(num){
    2. return num;
    3. })(1) //1

    函数里放一个匿名函数将会产生闭包

    1. 使用局部变量实现累加功能。
    2. 定义函数test1,返回一个匿名函数形成一个闭包
    3. test1赋给test2,此时test2会初始化变量a,值为test1返回的匿名函数
    4. 执行test2()
    1. {
    2. function test1(){
    3. var a = 1;
    4. return function(){
    5. // a++;
    6. // return a;
    7. // 或以下写法
    8. return ++a;
    9. }
    10. }
    11. var test2 = test1();
    12. console.log(test2()); // 2
    13. console.log(test2()); // 3
    14. console.log(test2()); // 4
    15. //不能这样写,这样外层函数每次也会执行,从而age每次都会初始化
    16. console.log(test1()()); // 2
    17. console.log(test1()()); // 2
    18. }

    闭包中使用this对象将会导致的一些问题

    1. var box={
    2. getThis:function(){
    3. return this;
    4. }
    5. }
    6. console.log(box.getThis()); // { getThis: [Function: getThis] }

    闭包中的this将返回全局对象,浏览器中window对象,Node.jsglobal对象,可以使用对象冒充或者赋值来解决闭包中this全局对象问题。

    对象冒充

    1. var box={
    2. user: 'zs',
    3. getThis:function(){
    4. return function(){
    5. return this;
    6. };
    7. }
    8. }
    9. console.log(box.getThis().call(box)); // { user: 'zs', getThis: [Function: getThis] }

    赋值

    1. var box={
    2. user: 'zs',
    3. getThis:function(){
    4. var that = this; // 此时的this指的是box对象
    5. return function(){
    6. return that.user;
    7. };
    8. }
    9. }
    10. console.log(box.getThis()());

    一个例子看懂循环和闭包之间的关系

    下例,循环中的每个迭代器在运行时都会给自己捕获一个i的副本,但是根据作用域的工作原理,尽管循环中的五个函数分别是在各个迭代器中分别定义的,但是它们都会被封闭在一个共享的全局作用域中,实际上只有一个i,结果每次都会输出6

    1. for(var i=1; i <= 5; i++){
    2. setTimeout(function(){
    3. console.log(i);
    4. })
    5. }

    解决上面的问题,在每个循环迭代中都需要一个闭包作用域,下面示例,循环中的每个迭代器都会生成一个新的作用域。

    1. for(var i=1; i <= 5; i++){
    2. (function(j){
    3. setTimeout(function(){
    4. console.log(j);
    5. })
    6. })(i)
    7. }

    也可以使用let解决,let声明,可以用来劫持块作用域,并且在这个块作用域中声明一个变量。

    1. for(let i=1; i <= 5; i++){
    2. setTimeout(function(){
    3. console.log(i);
    4. })