JavaScript函数理解

  本文参考自简书javaScript之函数详解

  这里从函数的构造函数开始。

  在js中,函数都是对象,它们都是Function构造函数的实例。因此,类似Java中的对象,函数名可以理解为指向该Function构造函数实例的指针。下面很多内容都将与该点相关。

  一个函数的定义方法有如下3种。

  1.即最原始的使用构造函数定义,也是我们基本不使用也不推荐使用的方法

1 var foo = new Function("a","console.log(a)");
2 foo(11);   // 11
3 var foo = new Function("a","b","console.log(a+b)");
4 foo(11,22); //33
5 foo(11);    //NaN

  看到,该构造函数的最后一项参数为函数体执行的部分,前面不论几项都是该函数的参数,不推荐的原因也很明显,太难写了!

  2.函数表达式写法,即对一个匿名函数赋予一个函数名

1 var foo = function() {
2     console.log("aa");
3 };
4 var bar = foo;
5 foo = undefined;
6 bar();
7  // foo();

3.普通的函数声明

 1 console.log(a);  // undefined
 2 foo3();  // 33
 3 // foo4();    //is not a function
 4 function foo3() {
 5     console.log(33)
 6 }
 7 var foo4 = function() {
 8     console.log(44)
 9 }
10 foo4();  //44
11 var a = 4;

  2、3两种方法在平时是最常用到的,因此不单独写了,代码中加了一部分测试语句。 

  再次申明本文的主题:函数名是指向函数对象的一个指针。

  首先看第一段代码。Js中,重复的申明一个变量并不会报错。第一次将一个函数赋予给foo,即将foo的地址指向第一个函数;当再次赋予第二个函数时,可以看到,第一个函数被覆盖了。这里可以简单的理解为,foo所保存的地址被改为了指向第二个函数的地址。因此也可以理解:js中的函数没有重载的概念。

  再看第二段代码,继续理解该概念。函数是一个对象,函数名为指向该对象的指针。因此我们可以将多个变量名指向一个函数对象;如第二段代码所示,将变量bar指向变量foo所指的函数变量,此时,foo和bar两个变量所保存的都为同一个函数对象的地址。此时将foo指向undefined,可以看到,函数对象并没有消失,bar可以正常使用。

  最后,说一下函数声明提升。我们都知道,在正常使用过程中,对于普通的函数声明语句而言,无论我们在哪里声明,我们可以在任意位置直接使用。这是因为在代码执行之前,解析器通过函数声明提升,将函数添加到执行环境中。在我参考的文章中,作者提出函数声明与函数表达式之间的区别在于解析器的函数声明提升对于函数声明是有效的,而不作用于函数表达式。这里我更倾向于认为函数表达式也能经过函数声明提升,只不过此时对象虽然已被创建,但是并没有将该对象赋值给函数名。当然结果是一样的,单纯的匿名函数的存在对于我们而言并没有什么意义。

  下面看下一个例子,该例子包括函数名是一个变量和函数声明提升的概念。

1  var getName = function(){
2      console.log(2);
3  }
4  function getName (){
5      console.log(1);
6  }
7  getName();  //2

  此时getName()打印出的值为2。根据函数声明提升,4-6行代码将首先被解析器执行并加入上下文中。此时上下文中保存的内容为变量getName和其指向的函数对象。随后在js执行时,getName再次被声明,此时单纯的声明变量并不会将变量之前的内容覆盖掉,所以此时并没有什么影响。之后第一行执行完后getName被赋予了新的函数对象。再次运行到4-6行时,由于其已被解析器执行过,将会跳过。最终打印出2。另外,该例子并不能说明之前我的猜测是错误的,虽然我刚看到这个例子时也怀疑了下。

  函数作为第一公民,它也可以作为另一个函数的参数或者返回值,不过这里我暂时并没有看到它太大的意义,先不谈,具体可以看开篇引用的文章。

  函数自带2个特殊的对象:arguments和this。

  arguments

  arguments的主要作用为保存传入该函数的参数。此外,该对象有一个callee的属性,该属性效果和函数变量名一致,指向用有该arguments的函数对象。不过在严格模式下访问该属性将出错,因此使用严格模式的同学可以忽略它。该属性主要作用于递归中,例子如下:

1 function factorial(num) {
2     if (num <= 1) {
3         return 1;
4     } else {
5         return num * factorial(num - 1)
      (return num * arguments.callee(num - 1))
6 } 7 }
1 var trueFactorial = factorial;
2 factorial = function() {
3     return 0;
4 };
5 console.log(trueFactorial(5)); // 120
6 console.log(factorial(5)); // 0

  this:

  this指向的是函数运行的环境对象,其基本情况与Java中一样。

  this引用的是函数据以执行的环境对象(当在网页的全局作用域中调用函数时,this对象引用的就是window)。示例见下:

 1 window.color = 'red';
 2 
 3 function sayColor() {
 4     console.log(this.color);
 5 };
 6 var foo = {
 7     color: 'blue',
 8     sayColor: function() {
 9         console.log(this.color);
10     }
11 }
12 sayColor();   //red
13 foo.sayColor();   //blue
14 console.log(this);  //window

  基础的this相对比较简单。但与Java中不同的一点是,js中的this是可以被指定的。参考资料见JavaScript秘密花园,或见顶部的参考。示例如下  

//this 可变的
function bar2(color) {
    this.color = color;
    console.log(this)
}
var bar = new bar2('blue');
sayColor.call(bar);   //blue

  call(Object,arg1,arg2,...)和apply(Object,arguments)方法可以将函数内部的this显示的指定为传入的第一个参数object。它们的唯一区别在于传入参数的方式不同,call方法必须显式的将参数一个个传入,而apply方法第二个参数为一个参数数组。

  bind(Object)的作用同样是将函数内部的this赋值为Object,但和call、bind不同的是,call和bind是将该函数改变this后直接执行了,而bind返回的是一个函数对象,它可以赋值给一个变量,并在适当的时候执行。

  js的this的晚绑定特训,简单看例子:

1 var test = someObject.methodTest; 
2 test();

  test就像一个普通的函数被调用;因此,函数内的this将不再被指向到someObject对象。

  js中基础的函数就讲到这了,以上内容参考自文内的两个链接的文章,这里记录下自己所学。

  end

猜你喜欢

转载自www.cnblogs.com/ykqfrost/p/easyFunction.html
今日推荐