fn1.call.call.call(fn2) 结果是什么?包会

90 阅读3分钟

1. 题目

function fn1() {
    console.log(1)
}

function fn2() {
    console.log(2)
}

fn1.call.call.call(fn2)

2. 分析

要了解这个输出结果,我们要先了解下this的指向call的实现,下面我一步一步的介绍。

2.1 this指向

this指向当前的调用者。

例子

getValue函数是obj调用的,此时,this为obj对象,所有this.name为qy.



let obj = {
    name: 'qy',
    getValue(){
      console.log(this.name)
    }
}




obj.getValue() // qy
obj['getValue']() // qy this指向的也是obj

2.2 call的原理与实现

概念

call 方法是 JavaScript 中 Function 原型链上的一个方法,用于调用一个函数,并在调用时显式地指定 this 的值和传入的参数。

2.2.1 格式

javascript
Copy code
function.call(thisArg, arg1, arg2, ...)
  • thisArg:在函数运行时使用的 this 值。
  • arg1, arg2, ...:传递给函数的参数列表。

2.2.2 特点

  1. 显式设置 thiscall 方法允许你在调用函数时显式地设置 this 值,使得函数可以在不同的上下文中执行。
  2. 参数逐一传递:与 apply 方法不同,call 方法需要将参数逐个传递给函数。
  3. 无返回值的限制call 方法返回被调用函数的返回值,不限制函数类型。
  4. 改变函数上下文:可用于继承、借用方法等场景。

2.2.3 使用

  1. 基本使用
function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

greet.call(person, 'Hello', '!'); // 输出: Hello, Alice!

在这个例子中,greet 函数的 this 被设置为 person 对象,并传入了两个参数 'Hello''!'

  1. 继承与借用方法

function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Student(name, age, grade) {
  Person.call(this, name, age); // 借用 Person 构造函数
  this.grade = grade;
}

const student = new Student('Bob', 20, 'A');
console.log(student.name); // 输出: Bob
console.log(student.age);  // 输出: 20
console.log(student.grade); // 输出: A

在这个例子中,Student 构造函数借用了 Person 构造函数,以初始化 nameage 属性。

  1. 借用数组方法
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

const array = Array.prototype.slice.call(arrayLike);
console.log(array); // 输出: ['a', 'b', 'c']

在这个例子中,call 方法将 Array.prototype.slice 方法应用到类数组对象 arrayLike 上,将其转换为真正的数组。

2.2.4 实现call

Function.prototype.call = function(context, ...args) {
    var context = context || window //保证唯一性
    const fn = Symbol('fn')
    context[fn] = this
    const result = context[fn](...args)
    delete context[fn]
    return result
}

2.2.5 总结

  • call 方法是 JavaScript 中的一个强大工具,允许显式地设置 this 值。
  • 它可以传递任意数量的参数。
  • 主要用于改变函数执行上下文和借用其他对象的方法

2.3 原型

Function.prototype是一个函数.


console.log(typeof Function.prototype) // function

3. 讲解

要了解fn1.call.call.call(fn2) 我们先看下fn1.call(fn2)

我们先看下的结果。

function fn1() {
    console.log(1)
}

function fn2() {
    console.log(2)
}

fn1.call(fn2) // 1

在调用fn1.call的时候,this指向的是fn1,因此,源码中context[fn](...args)等价于fn1(..args).

我们再看下fn1.call.call.call(fn2),调用多个call的是很好,其实call只是Function里面的一个方法,我们只需要看fn1.call.call(fn2)的结果

fn1.call.call.call.call === Function.prototype.call // true

fn1.call.call === Function.prototype.call // true

这个时候this的指向是call函数,即为Function.prototype.call, 但是contextfn2函数。实现的call中,context[fn](...args)等价于fn2(...args),并绑定call函数。


const context = fn2() {
}

context[Symbol] = call

会继续的调用call, context[Symbol](...args)输出了2.

function fn1() {
    console.log(1)
}

function fn2() {
    console.log(2)
}

fn1.call.call.call(fn2) // 2

4. 扩展

大家可以思考下哈

function fn2() {
    console.log(2)
}

Function.call(fn2) // ?
Function.call.call.call(fn2)// ?