Skip to content

JavaScript this 与函数面试题

1. 请解释 JavaScript 中 this 的指向规则

Details

在 JavaScript 中,this 的指向是在函数调用时确定的,而不是在函数定义时确定的。以下是 this 指向的主要规则:

1. 全局上下文

在全局上下文中,this 指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。

javascript
console.log(this); // 在浏览器中输出 window 对象

2. 函数调用

在普通函数调用中,this 指向全局对象。

javascript
function foo() {
  console.log(this); // 在浏览器中输出 window 对象
}
foo();

3. 方法调用

当函数作为对象的方法调用时,this 指向调用该方法的对象。

javascript
const obj = {
  name: "John",
  greet: function() {
    console.log(this.name); // 输出: John
  }
};
obj.greet();

4. 构造函数调用

当函数作为构造函数使用 new 关键字调用时,this 指向新创建的对象。

javascript
function Person(name) {
  this.name = name;
  console.log(this); // 输出新创建的 Person 对象
}
const person = new Person("John");

5. apply、call 和 bind 方法

使用 applycallbind 方法可以显式地指定 this 的指向。

javascript
function greet() {
  console.log(this.name);
}

const obj1 = { name: "John" };
const obj2 = { name: "Jane" };

greet.call(obj1); // 输出: John
greet.apply(obj2); // 输出: Jane
const greetJohn = greet.bind(obj1);
greetJohn(); // 输出: John

6. 箭头函数

箭头函数没有自己的 this,它会继承外层作用域的 this

javascript
const obj = {
  name: "John",
  greet: function() {
    setTimeout(() => {
      console.log(this.name); // 输出: John,继承 greet 方法中的 this
    }, 1000);
  }
};
obj.greet();

2. 请解释 JavaScript 中的 call()、apply() 和 bind() 方法的区别和用途

Details

call()apply()bind() 都是 JavaScript 中用于改变函数 this 指向的方法。

call() 方法

call() 方法调用一个函数,使用一个指定的 this 值和单独提供的参数列表。

语法

javascript
function.call(thisArg, arg1, arg2, ...)

示例

javascript
function greet(greeting, punctuation) {
  console.log(greeting + " " + this.name + punctuation);
}

const person = { name: "John" };
greet.call(person, "Hello", "!"); // 输出: Hello John!

apply() 方法

apply() 方法调用一个函数,使用一个指定的 this 值和一个数组(或类数组对象)作为参数。

语法

javascript
function.apply(thisArg, [argsArray])

示例

javascript
function greet(greeting, punctuation) {
  console.log(greeting + " " + this.name + punctuation);
}

const person = { name: "John" };
greet.apply(person, ["Hello", "!"]); // 输出: Hello John!

bind() 方法

bind() 方法创建一个新的函数,当调用时,将其 this 关键字设置为提供的值,并在调用新函数时,将给定的参数列表作为原始函数的参数。

语法

javascript
function.bind(thisArg, arg1, arg2, ...)

示例

javascript
function greet(greeting, punctuation) {
  console.log(greeting + " " + this.name + punctuation);
}

const person = { name: "John" };
const greetPerson = greet.bind(person, "Hello");
greetPerson("!"); // 输出: Hello John!

区别和用途

方法区别用途
call()接收单独的参数列表适用于参数数量已知的情况
apply()接收参数数组适用于参数数量未知或已存在于数组中的情况
bind()返回一个新函数,不立即执行适用于需要稍后调用函数的情况,或需要固定某些参数的情况

示例:使用 apply() 求数组最大值

javascript
const numbers = [1, 2, 3, 4, 5];
const max = Math.max.apply(null, numbers);
console.log(max); // 输出: 5

示例:使用 bind() 创建偏函数

javascript
function multiply(a, b) {
  return a * b;
}

const double = multiply.bind(null, 2);
console.log(double(5)); // 输出: 10

3. 请解释 JavaScript 中的函数柯里化(Currying)

Details

函数柯里化是一种将接受多个参数的函数转换为一系列接受单个参数的函数的技术。

柯里化的定义

函数柯里化是指将一个接受 n 个参数的函数,转换为 n 个接受单个参数的函数的过程。

柯里化的示例

javascript
// 普通函数
function add(a, b, c) {
  return a + b + c;
}

// 柯里化函数
function curryAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    }
  }
}

// 使用柯里化函数
const result = curryAdd(1)(2)(3);
console.log(result); // 输出: 6

柯里化的实现

可以编写一个通用的柯里化函数来处理任意函数:

javascript
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...nextArgs) {
        return curried.apply(this, [...args, ...nextArgs]);
      }
    }
  }
}

// 使用通用柯里化函数
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)(3)); // 输出: 6
console.log(curriedAdd(1, 2)(3)); // 输出: 6
console.log(curriedAdd(1)(2, 3)); // 输出: 6

柯里化的优点

  1. 参数复用:可以固定某些参数,创建新的函数。
  2. 函数组合:柯里化函数更容易与其他函数组合使用。
  3. 延迟执行:可以分步骤传递参数,延迟函数的执行。
  4. 代码可读性:使代码更加模块化和可读性更高。

柯里化的应用场景

  1. 配置参数:固定配置参数,创建特定功能的函数。
  2. 函数式编程:在函数式编程中,柯里化是一种常见的技术。
  3. 事件处理:在事件处理中,可以固定某些参数。
  4. API 设计:设计更灵活的 API。

示例:使用柯里化创建特定的日志函数

javascript
function log(level, message) {
  console.log(`[${level}] ${message}`);
}

const curryLog = curry(log);
const infoLog = curryLog('INFO');
const errorLog = curryLog('ERROR');

infoLog('This is an info message'); // 输出: [INFO] This is an info message
errorLog('This is an error message'); // 输出: [ERROR] This is an error message