今天必须把箭头函数讲明白

380 阅读6分钟

箭头函数是 ES6 引入的一种新的函数声明方式。它的语法更简洁,但是有一些特性与普通的 function 声明的函数是不同的。

  1. 更简洁的语法:箭头函数的语法更为简洁。例如,一个普通的函数可以写成 function (x) { return x * x },而使用箭头函数,可以简写成 x => x * x
  2. 没有自己的 this:箭头函数不会创建自己的 this 值。在箭头函数内部,this 是词法层面上的;也就是说,它是由上下文决定的,而不是由函数调用方式决定的。这与普通函数不同,普通函数的 this 值是由它的调用方式决定的。
  3. 不能作为构造函数:你不能使用 new 关键字来调用箭头函数。如果你尝试这样做,JavaScript 引擎会抛出错误。
  4. 没有 arguments 对象:在普通函数中,arguments 是一个类数组对象,包含了函数被调用时的参数列表。但在箭头函数中,arguments 对象并不存在。如果你需要使用所有参数,可以使用剩余参数(...args)的方式。
  5. 没有 prototype 属性:由于箭头函数不能作为构造函数,所以它也没有 prototype 属性。
  6. 隐式返回:如果箭头函数的函数体只有一行并且没有大括号,那么这一行的结果会被隐式返回。也就是说,你不需要(也不能)在这种箭头函数中使用 return 关键字。

没有自己的 this 值

在 JavaScript 中,函数有它们自己的上下文,也就是它们的 "this"。这个 "this" 指向的是调用这个函数的对象。让我们来看一个例子:

let myObject = { 
    value"Hello"sayHellofunction() { 
        console.log(this.value); 
    } 
}; 
myObject.sayHello();  // 输出 "Hello"

在这个例子中,sayHello 函数的 "this" 就是 myObject,因为是 myObject 调用了这个函数。

所以,当我们在函数内部打印 this.value 时,它打印的是 myObjectvalue 属性。

然而,当我们在函数中创建一个新的函数时,新的函数会有它自己的 "this":

let myObject = {
  value: "Hello",
  sayHello: function() {
    console.log(this.value);  // 输出 "Hello"
    function innerFunction() {
      console.log(this.value);
    }
    innerFunction();  // 输出 "undefined"
  }
};

myObject.sayHello();

在这个例子中,innerFunction 的 "this" 不是 myObject,而是全局对象(在浏览器中就是 window)。因为 window 上没有 value 属性,所以 this.valueundefined

然而,箭头函数不会创建它自己的 "this",它会从外部函数(或者全局作用域)继承 "this":

let myObject = {
  value: "Hello",
  sayHello: function() {
    console.log(this.value);  // 输出 "Hello"
    let innerFunction = () => {
      console.log(this.value);  // 输出 "Hello"
    }
    innerFunction();
  }
};

myObject.sayHello();

在这个例子中,innerFunction 是一个箭头函数,所以它没有自己的 "this",而是从 sayHello 函数中继承了 "this"。因此,当我们在箭头函数内部打印 this.value 时,它打印的是 myObjectvalue 属性。

没有 arguments 对象

首先,我们需要先搞懂,什么是类数组对象?

类数组对象,就是其结构和数组类似,即具有一个索引(例如,0, 1, 2, 3等)和长度(length)属性,但并不具备数组的内置方法(如 push, pop, forEach等)。JavaScript 中的一些内置对象,例如 argumentsNodeList,就是类数组对象。

比如说,我们创建一个对象,赋予它索引和长度属性:

javascriptCopy code
let arrayLike = {
  0: 'Hello',
  1: 'World',
  length: 2
};

这个 arrayLike 就是一个类数组对象,它有索引和长度,但它并不是一个真正的数组,因为它没有数组的方法,例如 pushpop

关于 arguments 对象和箭头函数,让我再详细解释一下。arguments 对象是一个特殊的对象,它在函数内部可用,包含了传递给函数的所有参数。然而,箭头函数并不创建自己的 arguments 对象。如果在箭头函数中访问 arguments,会引用其外部的(箭头函数外层的最近的非箭头函数)arguments 对象。

让我们来看这个例子:

function outerFunction(arg1, arg2) {
  const innerFunction = () => {
    console.log(arguments[0]); // 这里的 arguments 是 outerFunction 的 arguments
    console.log(arguments[1]); // 这里的 arguments 是 outerFunction 的 arguments
  };
  innerFunction('innerArg1', 'innerArg2');
}

outerFunction('outerArg1', 'outerArg2'); 
// 输出 "outerArg1",然后输出 "outerArg2"

在这个例子中,innerFunction 是一个箭头函数,它没有自己的 arguments 对象。所以,当我们在 innerFunction 中访问 arguments,我们实际上访问的是 outerFunctionarguments 对象。

在JavaScript中,arguments对象是一个类数组对象,它包含了函数被调用时的参数列表。在普通函数中,你可以通过arguments对象来访问函数的参数,即使你在函数定义的时候没有明确地声明这些参数。例如:

function myFunction() {
  console.log(arguments[0]);
  console.log(arguments[1]);
}

myFunction('Hello', 'World');  // 输出 "Hello",然后输出 "World"

在上述例子中,尽管myFunction没有定义任何参数,但我们仍然可以通过arguments对象来访问传给函数的参数。

然而,箭头函数并不绑定自己的arguments对象。如果你在箭头函数内部尝试访问arguments,你实际上会得到箭头函数外部(即包裹箭头函数的最近的非箭头函数)的arguments对象。例如:

function myFunction() {
  let arrowFunction = () => {
    console.log(arguments[0]);
    console.log(arguments[1]);
  }
  arrowFunction('Goodbye', 'World');
}

myFunction('Hello', 'World');  // 输出 "Hello",然后输出 "World"

在上述例子中,我们在箭头函数内部尝试访问arguments,但我们得到的是myFunctionarguments对象,而不是arrowFunction的参数。

如果你需要在箭头函数内部访问函数的参数,你需要在箭头函数定义的时候显式地声明这些参数:

let arrowFunction = (arg1, arg2) => {
  console.log(arg1);
  console.log(arg2);
}

arrowFunction('Hello', 'World');  // 输出 "Hello",然后输出 "World"

这就是箭头函数没有自己的arguments对象的含义。

没有 prototype 属性

在 JavaScript 中,每个函数都有一个 prototype 属性,这个 prototype 属性是一个对象,它包含一个 constructor 属性,该属性指向函数自身。这个 prototype 对象主要用于实现继承和共享属性。

当我们使用 new 关键字创建一个新的对象实例时,这个新对象会从它的构造函数(也就是我们调用 new 的那个函数)的 prototype 对象中继承属性和方法。

然而,箭头函数是一个例外。箭头函数并没有 prototype 属性,也就是说,你不能使用箭头函数作为构造函数来通过 new 关键字创建新的对象实例。这是因为箭头函数主要被设计为匿名函数,用于执行简单的操作,而不是定义新的类型。

下面是一个示例,展示了普通函数和箭头函数在这方面的区别:

function regularFunction() {}
const arrowFunction = () => {};

console.log(regularFunction.prototype); // 输出 { constructor: f }
console.log(arrowFunction.prototype); // 输出 undefined

在这个例子中,你可以看到,对于普通函数 regularFunction,它的 prototype 属性是一个对象,

而对于箭头函数 arrowFunction,它的 prototype 属性是 undefined。这说明了箭头函数没有 prototype 属性,也就不能作为构造函数使用。