函数

105 阅读6分钟

函数的作用:功能的封装,直接调用,代码复用率提高

构建对象的模板(构造函数)

函数实际上是对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法,由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

1.函数声明

//第一种方式
function 函数名(形参列表){
    //函数体
}
//第二种方式  匿名函数
var 函数名 = function(形参列表){
    //函数体
}

2.函数内部属性

只有在函数内部才能访问的属性。this也可以在函数外部进行使用。

arguments

ECMAScript函数的参数与大多数其他语言中的函数的参数有所不同,ECMAScript函数不介意传递参数的个数以及参数类型,这是因为函数的参数在函数内部是使用一个类数组对象来表示的。这个类数组对象就是arguments。

arguments是一个类数组对象,包含着传入函数中的所有参数。arguments主要用途是保存函数参数,但是这个对象还有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

  1. 伪数组 并不是真正意义上的数组
  2. 具有数组的length属性
  3. 按照索引的方式进行存储
  4. 没有真正数组的一些方法 pop()  push()等
function add(a, b) {
    console.log(arguments[0],arguments[1],arguments[2],arguments[3]);
    console.log(a+b);
}
console.log(add.length);//结果是2  表示函数希望接受的命名参数的个数,即形参的个数。

add(1);
// 结果是
// 1 undefined undefined undefined
// NaN

add(1,2);
// 结果是
// 1 2 undefined undefined
// 3

add(1,2,3);
// 结果是
// 1 2 3 undefined
// 3
callee

callee 属性是 arguments 对象的一个成员,仅当相关函数正在执行时才可用。

// 实现匿名的递归函数
var sum = function (n) {
  if (1 == n) {
      return 1;
  } else {
      return n + arguments.callee(n - 1);  //6+5+4+3+2+1
  }
}
console.log(sum(6));//输出结果:21
this

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在事件中,this 表示接收事件的元素。
  • 在显示函数绑定时,我们可以自己决定this的指向
var person = {
    firstName"LeBron",
    lastName"James",
    id8888,
    fullNamefunction () {
        return this.firstName + " " + this.lastName;
    }
};
console.log(person.fullName());  //LeBron James

方法中的 this

在对象方法中, this 指向调用它所在方法的对象。

在上面一个实例中,this 表示 person 对象。

fullName 方法所属的对象就是 person。

单独使用 this

单独使用 this,则它指向全局对象。

在浏览器中,window 就是该全局对象为 [object Window]:

在node中,指向的是一个{}

var x = this;
console.log(x);  //{}

函数中使用 this(默认)

在函数中,函数的所属者默认绑定到 this 上。

在浏览器中,window 就是该全局对象为 [object Window]:

在node中,指向的就是global对象

事件中的 this

在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素

显式函数绑定

在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。

当我们使用 person2 作为参数来调用 person1.fullName 方法时, this 将指向 person2, 即便它是 person1 的方法:

var person1 = {
      fullNamefunction () {
      return this.firstName + " " + this.lastName;
    }
  }
  var person2 = {
    firstName"Bob",
    lastName"muu",
  }
  var name = person1.fullName.call(person2);  // 返回 "Bob muu"
  console.log(name);

3.函数调用

函数声明好之后并不会直接运行,需要进行调用才能运行。

调用函数的方式不仅限于()执行,还有其他几种方式

  • 函数名(实参列表);
  • 函数名.call(执行环境对象,实参列表);
  • 函数名.apply(执行环境对象,实参列表数组);
  • 函数名.bind(执行环境对象)(实参列表);
call(执行环境对象,实参列表);

调用call方法,第一个参数就是要把b添加到哪个环境中,简单来说,this就会指向那个对象。

var obj = {
    name'zhangsan',
    sayNamefunction () {
        console.log(this.name);
    }
}
var b = obj.sayName;
b.call(obj);
console.log(b);  //zhangsan

在使用call调用的时候,还可以传递多个参数

var obj = {
    name'zhangsan',
    sayNamefunction (a,b) {
        console.log(this.name);
        console.log(a , b);
    }
}
var b = obj.sayName;
b.call(obj,4,5);
console.log(b);  //zhangsan 4 5
apply(执行环境对象,实参列表数组);

apply方法和call方法有些相似,它也可以改变this的指向

var obj = {
    name: 'zhangsan',
    sayName: function () {
        console.log(this.name);
    }
}
var b = obj.sayName;
b.apply(obj);
console.log(b);  //zhangsan

同样apply也可以有多个参数,但是不同的是,第二个参数必须是一个数组

var obj = {
    name'zhangsan',
    sayNamefunction (a,b) {
        console.log(this.name);
        console.log(a , b);
    }
}
var b = obj.sayName;
b.apply(obj,[1,2,3]);
console.log(b);  //zhangsan 1 2

注意:如果call和apply的第一个参数是null,那么this在node环境下指向的是global对象,在HTML中指向的是window对象

bind(执行环境对象)(实参列表)
var obj = {
    name'zhangsan',
    sayNamefunction () {
        console.log(this.name);
    }
}
var b = obj.sayName;
b.bind(obj);  // 代码没有被打印,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。
var c = b.bind(obj);
console.log(c);  //发现c是[Function: bound sayName]函数
//执行c后
c();  //打印出来才是zhangsan

同样bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的

总结

call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。

4.函数的应用

函数本质上是一种对象,可以将其当做普通对象来使用

  • 回调函数

回调,就是回头调用的意思。主函数的事先做完,回头再调用传进来的那个函数。

function a(callback) {
    callback();
    console.log('我是主函数');
  }
  //定义回调函数
  function b() {
    // 模仿延时操作
    setTimeout(() => {
      console.log('我是回调函数');
    }, 3000);
  }
  //调用主函数,将函数B传进去 
  a(b);

回调函数的作用:回调函数都用在耗时操作上面:因为主函数不用等待回调函数执行完,可以接着执行自己的代码。比如ajax请求,比如处理文件等

  • 作为返回值

在作用域链中,就使用到了函数作为返回值的用法

var a = 10
function fn() {
  var b = 20
  function foo() {
    console.log(a + b) //30
  }
  return foo
}
var x = fn(), // 执行fn() 返回的是foo
b = 200
x() //执行x,就是执行foo函数