前端js复习 4-函数

我们的认知中函数是一段可以反复调用的代码块,函数还能接受输入的参数,不同的参数会返回不同的值,在js的世界了里有三种声明函数的方法。

  • function命令
  • 函数表达式
  • new Function构造函数(几乎不使用,对构造函数不理解的可以看js的设计模式,后续会有章节介绍)
  • 题外话,=> 箭头函数 es6

那我们来看几个列子


//函数声明

function test() {
   console.log(1)
}
test() //调用该函数 1

//函数表达式(匿名函数赋值给变量)

var test = function() {
    console.log(2)
};
test() //2

//如果不是匿名函数的表达式,该函数名只能在函数里有效,以便在函数体内部调用自身

var test = function test1() {
     console.log(typeof test1); // function
};
console.log(test1);// error : test1 is not defined

//new Funciton 构造函数(arg1,arg2,...,函数内容)
var test = new Function(a,console.log(a))
test(1) // 1

//箭头函数

var test = (x,y) =>  x + y ; 
test(1,3) // 4

函数额外知识点:

1.每个javascrit中函数有一个内置的对象arguments对象,它是一个类数组,我们可以用它达到函数重载的效果

function sum() {

     var  sum=0;

     for(var i = 0 ;i< arguments.length;i++) {

              sum+=arguments[i]

    }

    return sum

}

sum(1,2,3)  //6

2.函数的this指向个人理解:函数在执行时,会在函数体内部自动生成一个this指针。谁直接调用产生这个this指针的函数,this就指向谁。

 var a = 1

 function global(){

    var a = 2;

    console.log(this) //window

    console.log(this.a) //1

    console.log(a) //2

}

global() 等价于 window.global() //得出全局函数的this都是指向的window,调用可以省略window

//如果函数作为对象的属性被调用

var obj = {

    name: "师父",

    say: function() {

           console.log("大师兄," + this.name +  "被妖怪抓走了")

    },

    action: {

        name: "二师兄",

        me: "我老沙呀",

        say: function() {

            console.log("大师兄," + this.name +  "被妖怪抓走了");

            function whoSay() {

                    console.log(this) // window

                    console.log(this.me) //undefined

             }

             whoSay();
             // 因为是属于普通函数调用,但是并没有通过 obj.action.say 直接调用,
             //只是自执行,这个时候,whoSay就是一个全局函数,因此该函数的
            //this指向的就是window,在window上找不到me故所以是undefined
        }

  }

}

obj.say(); //大师兄,师父被妖怪抓走了
obj.action.say(); //大师兄,二师兄被妖怪抓走了

var elseObj=obj.say; 
elseObj(); // 注意此时的this指向为window  大师兄,被妖怪抓走了

3.我们可以利用call,apply调用函数,并改变了函数的this指向问题,他们区别传参不同

function Animal (name){
    this.name = name
    this.showName = function() {
        console.log(this.name)
   }
}

function Cat(name) {
     Animal.call(this,name) //this指代函数Cat
}

function Dog(name) {
     Animal.apply(this,[name]) // 也可以写成Animal.apply(this,[arguments]) 
}

var cat = new Cat('miaomiao');
console.log(cat.name) // miaomiao
console.log(cat.showName()) //miaomiao

var dog = new Dog('wangwang');
console.log(dog.name) //wangwang
console.log(dog.showName()) //wangwang
  1. 函数闭包 :在《JavaScript高级程序设计(第3版)》:闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
    作用:一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
    看个阮一峰老师最简单的闭包列子:
function f1(){
    var n=999;
    nAdd = function(){n+=1}
              function f2(){
                        return n
              }
      return f2;
 }

console.log(n) // 报错,想要拿到函数内部的n,那么用过一个函数返回

var result=f1();

result(); // 999

nAdd();

result(); // 1000 原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

好吧,这东西呢仔细琢磨琢磨就行,现在我们拿一个列子来加深印象

 function A(){
        var funs=[];
        for(var i=0;i<10;i++){
           funs[i]=function(){
               return i;
           }
        }
        return funs; 
    }
    var funs = A();//定义funs[0]-funs[9],10个函数
    console.log(funs[0]());//10
    console.log(funs[1]());//10
    console.log(funs[6]());//10

按理说我们脑袋反应的应该是0 ,1 ,6 怎么全是10呢?
因为执行环境和变量对象在运行函数时生成,在funs[0] () 的时候才产生真正的环境变量i,那么此时i是什么,看return的时候,显然是10,所以返回的都是10了

我们用闭包改一下,j 加一个立即执行函数正应对了我们刚提到的执行环境和变量对象在运行函数时生成,那么此时的环境变量已经变成了1,2,3....

 function A(){
        var funs=[];
        for(var i=0;i<10;i++){
           funs[i]=function B(num){
                   return function() {
                        console.log(num)
                  }
           }(i)
        }
        return funs; 
    }
  1. 函数递归:字面就是在函数里面调用自己,在满足递归条件时需要多次调用,比较著名的斐波那契数列fibonacci
    fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …],就是后一项等于强两项的和,看下代码实现
function fibonacci(n) {
         if(n  <= 1) {
               return 1
         }
         return fibonacci(n-1) + fibonacci(n-2)
}
// 我们可以用闭包递推来实现然这里也是使用了递归,
//但是闭包大大减少了递归的次数典型的用空间换时间的例子。
var fibonacci = (function(){
  var arr = [0,1,1];  
  return function(n) {
    var res = arr[n];
    if(res) {
        return res;
    } else {
        arr[n]=fiba(n-1)+fiba(n-2);
        return arr[n];
    }
  }   
}(n)
//递推法就是循环嘛
function fibonacci(n){
    var one = 1;
    var two = 1;
    for (var i = 3;i<=n; i++) {
        var three = one + two;
        one = two;
        two = three;
    }
    if(n==1 || n==2) {
        return one
    }
    return three
}

递推法的效率最高,其次是闭包,而递归法效率最低。
闭包效率虽然相对递归法高了不少,但是这种方法,如果使用不当会造成内存泄露,

递归算法一般用于解决三类问题:

  • 数据的定义是按递归定义的。(Fibonacci函数)
  • 问题解法按递归算法实现。(回溯)
  • 数据的结构形式是按递归定义的。(树的遍历,图的搜索)

但是递归算法解题的运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
如果使用循环不能解决,再考虑用递归

你可能感兴趣的:(前端js复习 4-函数)