【js面试题】JavaScript 中箭头函数与普通函数的深度剖析

231 阅读5分钟

在 JavaScript 编程的世界里,函数是极为重要的组成部分。而随着 ES6 的出现,箭头函数成为了 JavaScript 函数家族中的新成员。它与传统的普通函数有着诸多的不同之处,这些差异深刻地影响着我们编写代码的方式以及代码的执行逻辑。本文将对 JavaScript 中的箭头函数与普通函数进行全面而深入的剖析,帮助大家更好地理解和运用这两种函数类型。

一、语法结构对比

(一)普通函数的语法

普通函数的定义使用 function 关键字,其基本语法结构如下:

function functionName(parameters) {
    // 函数体
    return value;
}
function add(a, b) {
    return a + b;
}

这里 function 关键字明确标识了这是一个函数定义,add 是函数名,(a, b) 是参数列表,花括号内的部分是函数体,通过 return 语句返回函数的计算结果。

(二)箭头函数的语法

箭头函数则采用了更为简洁的箭头 => 语法。如果函数体只有一行且是返回值表达式,那么可以省略花括号和 return 关键字,其语法形式为:

(parameters) => expression

const add = (a, b) => a + b;

在这个例子中,(a, b) 是参数列表,=> 后面直接跟着返回值表达式 a + b。如果函数体有多行代码,则需要使用花括号包裹,并显式使用 return 关键字(如果有返回值),语法如下:

(parameters) => {
    // 多行函数体
    return value;
}
const multiplyAndLog = (a, b) => {
    const result = a * b;
    console.log(result);
    return result;
}

二、this 指向的差异

(一)普通函数的 this 指向

普通函数的 this 指向在函数被调用时确定,它取决于函数的调用方式。在全局环境下直接调用函数时,非严格模式下 this 指向全局对象(在浏览器中是 window),严格模式下 this 为 undefined。例如:

function globalFunction() {
    console.log(this);
}
globalFunction(); // 在非严格模式下输出 window

function strictFunction() {
    'use strict';
    console.log(this);
}
strictFunction(); // 输出 undefined

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

const myObject = {
    name: 'Object',
    myMethod: function() {
        console.log(this.name);
    }
};
myObject.myMethod(); // 输出 'Object'

如果函数被作为构造函数使用,通过 new 关键字创建对象实例,那么 this 指向新创建的对象实例。例如:

function Person(name) {
    this.name = name;
    this.sayHello = function() {
        console.log('Hello, my name is'+ this.name);
    };
}
const person = new Person('John');
person.sayHello(); // 输出 'Hello, my name is John'

(二)箭头函数的 this 指向 箭头函数没有自己的 this,它的 this 是在定义时继承自外层作用域的 this。例如:

const outerObject = {
    name: 'Outer',
    regularFunction: function() {
        const arrowFunction = () => {
            console.log(this.name);
        };
        arrowFunction();
    }
};
outerObject.regularFunction(); // 输出 'Outer'

在这个例子中,箭头函数 arrowFunction 内部的 this 继承自外层函数 regularFunction 的 this,也就是 outerObject。无论箭头函数在何处被调用,其 this 始终保持与定义时外层作用域的 this 一致。这种特性使得在处理回调函数等场景中,箭头函数能够避免 this 指向的混淆。例如:

const button = document.getElementById('myButton');
button.addEventListener('click', function() {
    // 这里的 this 指向 button 元素
    console.log(this);
    const innerArrowFunction = () => {
        // 箭头函数的 this 继承自外层函数,也就是 button 元素
        console.log(this);
    };
    innerArrowFunction();
});

三、arguments 对象的使用

(一)普通函数的 arguments

普通函数内部有一个内置的 arguments 对象,它包含了函数被调用时传入的所有参数。例如:

function sumAll() {
    let sum = 0;
    for (let i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}
console.log(sumAll(1, 2, 3, 4)); // 输出 10

这里 arguments 可以获取到传入的所有参数值,即使函数定义时没有指定具体的参数名。 (二)箭头函数与 arguments

箭头函数没有自己的 arguments 对象。如果在箭头函数中需要访问参数列表,可以使用剩余参数语法(...)。例如:

const sumAllArrow = (...args) => {
    let sum = 0;
    for (let i = 0; i < args.length; i++) {
        sum += args[i];
    }
    return sum;
}
console.log(sumAllArrow(1, 2, 3, 4)); // 输出 10

四、构造函数的适用性

(一)普通函数作为构造函数

普通函数可以通过 new 关键字作为构造函数来创建对象实例。在构造函数内部,this 指向新创建的对象实例,可以用来初始化对象的属性和方法。例如:

function Person(name) {
    this.name = name;
    this.sayHello = function() {
        console.log('Hello, my name is'+ this.name);
    };
}
const person = new Person('John');
person.sayHello(); // 输出 'Hello, my name is John'

(二)箭头函数不能作为构造函数 箭头函数不能被用作构造函数,因为它没有自己的 this,无法进行对象实例的初始化操作。如果尝试使用 new 关键字调用箭头函数,会抛出错误。例如:

const ArrowPerson = (name) => {
    this.name = name;
    this.sayHello = () => {
        console.log('Hello, my name is'+ this.name);
    };
}
const arrowPerson = new ArrowPerson('Alice'); // 抛出错误

总结

在 JavaScript 中,箭头函数和普通函数在语法结构、this 指向、arguments 对象使用以及构造函数适用性等方面都存在明显的差异。 普通函数具有较为复杂的 this 指向规则,其在不同的调用方式下 this 会指向不同的对象,同时拥有内置的 arguments 对象方便获取所有传入参数,并且可以作为构造函数创建对象实例。而箭头函数以简洁的语法著称,其 this 指向在定义时确定并继承自外层作用域,没有自己的 arguments 对象,不能作为构造函数使用。在实际编程中,我们需要根据具体的需求和场景来选择使用箭头函数还是普通函数。如果需要动态确定 this 指向、使用 arguments 对象或者创建对象实例,那么普通函数是合适的选择;而如果希望保持 this 与外层作用域一致,且函数逻辑较为简单,箭头函数则能提供更简洁清晰的代码表达方式。深入理解这两种函数的特性,有助于我们编写更加高效、可读和可维护的 JavaScript 代码,提升我们的编程能力和代码质量。