【JavaScript核心】调用函数的四种方式

本文详细解释了JavaScript函数的定义、变量提升特性,以及四种不同的函数调用方式(普通调用、方法调用、构造函数调用和上下文调用),着重讲解了this关键字的行为,包括其在不同情况下的指向。同时介绍了call、apply和bind方法如何控制函数的调用上下文。
摘要由CSDN通过智能技术生成

前言

函数定义从 function 关键字开始,构成函数主体的 JavaScript 代码在定义之时并不会执行,只有调用该函数时,它们才会执行。

关于变量提升:var 只有变量声明提前,变量的初始化代码仍然在原来的位置;然而 function 则会使函数名称和函数体均提前。

函数调用方式共有四种:

  • 作为函数调用
  • 作为方法调用
  • 作为构造函数调用(new 调用)
  • 上下文调用(callapplybind

在 ECMAScript 2015(ES6)之前,函数内部的 this 指向是由该函数的调用方式决定的。

作为函数调用

对于普通的函数调用,如果该函数没有指定返回值,返回值就是 undefined;如果该函数指定了返回值(有 return),返回值就是 return 之后的表达式的值;如果 return 语句没有值,则返回 undefined

如果函数是声明在 window 对象中全局函数,非严格模式下的调用上下文(this 的值)是全局对象,严格模式 thisundefined

基本模式:

function foo() {
  return this;
}
foo(); // 返回 window 对象

作为方法调用

方法指该 JavaScript 函数是一个对象的属性。函数本身就是一个属性访问表达式,这意味着该函数被当做一个方法,而不是一个普通函数来调用。其返回值的处理方式,和普通函数调用完全一致。

调用上下文为当前对象,即 this 指向当前对象。

基本模式:

var calculator = {
  value1: 1,
  value2: 1,
  add: function() {
    // this 指代当前对象
    this.result = this.value1 + this.value2;
  }
};

calculator.add(); // 调用 add 方法结算结果
calculator.result; // => 2

// 方括号(属性访问表达式)进行属性访问操作
calculator["add"]();

和变量不同,关键字 this 没有作用域的限制,嵌套的函数不会从调用它的函数中继承 this。如果嵌套函数作为方法调用,其 this 的值指向调用它的对象。如果嵌套函数作为函数调用,其 this 值不是全局对象(非严格模式下)就是 undefined(严格模式下)。

因此若想访问外部函数的 this 值,那么就需要将 this 保存在变量中(变量具有作用域)。

示例:

var o = {
  m: function() {
    let self = this;            // 将this 的值进行保存
    console.log(this === o);    // true
    f();                        // 调用嵌套函数f()
        
    function f() {
      console.log(this === o);  // false:this 的值为全局对象或 undefined
      console.log(self === o);  // true:self 指向外部函数 this 的值
    }
  }
}

作为构造函数调用

如果函数或者方法调用之前带有关键字 new,它就构成构造函数调用。

基本模式:

let person = new People();

构造函数调用会创建一个新的空对象,并初始化这个新创建的对象,将这个对象用作其调用上下文。因此构造函数中 this 关键字指向这个新创建的对象。

如下示例代码,尽管构造函数看起来像一个方法调用,但它依然会使用 new 出来的新对象作为调用上下文。也就是说,在表达式 new o.m() 中,调用上下文并不是 o

let o = {
  m: function() {
    return 1;
  }
};
console.log(new o.m() === 1); // false

构造函数通常不使用 return 关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值。然而如果构造函数显式地使用 return 语句返回一个对象,那么调用表达式的值就是这个对象。如果构造函数使用 return 语句但是没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为返回结果。

::: tip
特别提醒一下,new 调用时的返回值,如果没有显式返回对象或者函数(包含 FunctoinArrayDateRegExgError),才是返回生成的新对象
虽然实际使用时不会显示返回,但面试官可能会问到。
:::

上下文调用

上下文调用方式有三种,callapplybind,这是一种很强大的调用方式。

call 和 apply

JavaScript 中的函数也是对象,函数对象也可以包含方法。其中 call()apply() 就是预定义的函数方法。

这两个方法可用于间接地调用函数,它们都允许显式指定调用所需的 this 值。也就是说,任何函数都可以作为任何对象的方法来调用,使得关键字 this 指向该对象,哪怕这个函数不是该对象的方法。

两个方法的第一个参数是要调用函数的母对象,它是调用上下文。

基本模式:

// call()
// obj: 这个对象将代替 func 里 this 对象
// param1 ~ paramN: 这是一个参数列表
func.call(obj, 'param1', 'param2')
// apply() 
// 该方法能接收两个参数
// obj: 这个对象将代替 func 里 this 对象
// args: 这是一个数组,它将作为参数传给 func(args --> arguments)
func.apply(obj, ['param1', 'param2'])

注意:

  • callapply 区别在于第二个参数:apply 传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而 call 从第二个参数开始都是参数。
  • 在严格模式下,在调用函数时第一个参数会成为 this 的值,即使该参数不是一个对象。
  • 在非严格模下,如果第一个参数的值是 nullundefined,它将使用全局对象替代。

bind

bind 方式一般人用的比较少,但有的时候具有一些举足轻重的作用。

不同的是,callapply 是立刻执行了这个函数,并且执行过程中绑定了 this 的值;bind 并没有立刻执行这个函数,而是创建了一个新的函数,新函数绑定了 this 的值,如果要执行还得在后面加个 ()

基本模式:

// bind() 
// 该方法能接收两个参数
// obj: 这个对象将代替 func 里 this 对象
// param1 ~ paramN: 这是一个参数列表
func.bind(obj, 'param1', 'param2')()

bind 函数在对象中:

let obj = {
  name: "西瓜",
  drink: (function () {
    console.log(this.name);
    console.log(obj.name);
  }).bind({name: "橙汁"})
}
obj.drink();

// "橙汁"
// "西瓜"

bind 函数在定时器中:

let obj = {
  name: "西瓜",
  drink: function () {
    setTimeout((function () {
      console.log(this.name);
    }).bind(this), 100)
  }
};
obj.drink();

// "西瓜"

call、apply、bind 小结

通过 callapplybind 方法把对象绑定到 this 上,叫做显式绑定。对于被调用的函数来说,叫做间接调用。

  • callapplybind 三者的第一个参数都是 this 要指向的对象。
  • bind 只是返回函数,还未调用,所以如果要执行还得在后面加个 ()callapply 是立即执行函数。
  • 三者后面都可以带参数
    • call 后面的参数用逗号隔开:func.call(obj, value1, value2);
    • apply 后面的参数以数组的形式传入:func.apply(obj, [value1, value2]);
    • bind 可以在指定对象的时候传参(同 call),以逗号隔开;也可以在执行的时候传参,写到后面的括号中:func.bind(obj,value1,value2)();func.bind(obj)(value1,value2);

参考资料

MDN - 函数

(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值