JS 화살표 기능에 대한 자세한 설명

青灯夜游
풀어 주다: 2019-11-26 16:18:57
앞으로
3465명이 탐색했습니다.

JS에서 화살표 함수는 일반 함수처럼 다양한 방식으로 사용될 수 있습니다. 그러나 일반적으로 콜백 함수와 같이 익명 함수 표현식이 필요할 때 사용됩니다.

JS 화살표 기능에 대한 자세한 설명

다음 예에서는 특히 map(), filter(), reduce()에 대한 콜백 함수인 화살표 함수의 예를 보여줍니다. > , sort() 및 기타 배열 메서드. map(), filter(), reduce(), sort()等数组方法。

const scores = [ 1, 28, 66, 666];
const maxScore = Math.max(...scores);

scores.map(score => +(score / maxScore).toFixed(2));
로그인 후 복사

乍一看,箭头函数似乎可以按常规函数来定义与使用,但事实并非如此。出于箭头函数的简洁性,它与常规函数有所不同,换一种看法,箭头函数也许可以把箭头函数看作是异常的 JS 函数。

【相关课程推荐:JavaScript视频教程

虽然箭头函数的语法非常简单,但这不是本文的重点。本文主要讲讲箭头函数与常规函数行为的差异,以及咱们如果利用这些差异来更好使用箭头函数。

 ● 无论在严格模式还是非严格模式下,箭头函数都不能具有重复的命名参数。

 ● 箭头函数没有arguments绑定。但是,它们可以访问最接近的非箭头父函数的arguments对象。

 ● 箭头函数永远不能用作构造函数,自然的不能使用new关键字调用它们,因此,对于箭头函数不存在prototype属性。

 ● 在函数的整个生命周期中,箭头函数内部的值保持不变,并且总是与接近的非箭头父函数中的值绑定。

命名函数参数

JS中的函数通常用命名参数定义。命名参数用于根据位置将参数映射到函数作用域中的局部变量。

来看看下面的函数:

function logParams (first, second, third) {
  console.log(first, second, third);
}

// first => 'Hello'
// second => 'World'
// third => '!!!'
logParams('Hello', 'World', '!!!'); // "Hello"  "World"  "!!!"

// first => { o: 3 }
// second => [ 1, 2, 3 ]
// third => undefined
logParams({ o: 3 }, [ 1, 2, 3 ]); // {o: 3}  [1, 2, 3]
로그인 후 복사

logParams()函数由三个命名参数定义: firstsecondthird。如果命名参数多于传递给函数的参数,则其余参数undefined

对于命名参数,JS函数在非严格模式下表现出奇怪的行为。在非严格模式下,JS函数允许有重复命名参数,来看看示例:

function logParams (first, second, first) {
  console.log(first, second);
}

// first => 'Hello'
// second => 'World'
// first => '!!!'
logParams('Hello', 'World', '!!!'); // "!!!"  "World"

// first => { o: 3 }
// second => [ 1, 2, 3 ]
// first => undefined
logParams({ o: 3 }, [ 1, 2, 3 ]); // undefined  [1, 2, 3]
로그인 후 복사

咱们可以看到,first参数重复了,因此,它被映射到传递给函数调用的第三个参数的值,覆盖了第一个参数,这不是一个让人喜欢的行为。

// 由于参数重复,严格模式会报错
function logParams (first, second, first) {
  "use strict";
  console.log(first, second);
}
로그인 후 복사

箭头函数如何处理重复的参数

关于箭头函数:

与常规函数不同,无论在严格模式还是非严格模式下,箭头函数都不允许重复参数,重复的参数将引发语法错误。

// 只要你敢写成重复的参数,我就敢死给你看
const logParams = (first, second, first) => {
  console.log(first, second);
}
로그인 후 복사

函数重载

函数重载是定义函数的能力,这样就可以根据不同的参数数量来调用对应的函数, JS 中可以利用绑定方式来实现这一功能。

来看个简单的重载函数,计算传入参数的平均值:

function average() {
  const length = arguments.length;

  if (length == 0) return 0;

  // 将参数转换为数组
  const numbers = Array.prototype.slice.call(arguments);

  const sumReduceFn = function (a, b) { return a + Number(b) };
  // 返回数组元素的总和除以数组的长度
  return numbers.reduce(sumReduceFn, 0) / length;
}
로그인 후 복사

这样函数可以用任意数量的参数调用,从0到函数可以接受的最大参数数量应该是255

average(); // 0
average('3o', 4, 5); // NaN
average('1', 2, '3', 4, '5', 6, 7, 8, 9, 10); // 5.5
average(1.75, 2.25, 3.5, 4.125, 5.875); // 3.5
로그인 후 복사

现在尝试使用剪头函数语法复制average()函数,一般咱们会觉得,这没啥难的,无法就这样:

const average = () => {
  const length = arguments.length;

  if (length == 0) return 0;

  const numbers = Array.prototype.slice.call(arguments);
  const sumReduceFn = function (a, b) { return a + Number(b) };

  return numbers.reduce(sumReduceFn, 0) / length;
}
로그인 후 복사

现在测试这个函数时,咱们会发现它会抛出一个引用错误,arguments 未定义。

咱们做错了啥

对于箭头函数:

与常规函数不同,arguments不存在于箭头函数中。但是,可以访问非箭头父函数的arguments对象。

基于这种理解,可以将average()函数修改为一个常规函数,该函数将返回立即调用的嵌套箭头函数执行的结果,该嵌套箭头函数就能够访问父函数的arguments

function average() {
  return (() => {
    const length = arguments.length;

    if (length == 0) return 0;

    const numbers = Array.prototype.slice.call(arguments);
    const sumReduceFn = function (a, b) { return a + Number(b) };

    return numbers.reduce(sumReduceFn, 0) / length;
  })();
}
로그인 후 복사

这样就可以解决了arguments对象没有定义的问题,但这种狗屎做法显然很多余了。

做点不一样的

对于上面问题是否存在替代方法呢,可以使用 es6 的 rest 参数。

使用ES6 rest 参数,咱们可以得到一个数组,该数组保存了传递给该函数的所有的参数。rest语法适用于所有类型的函数,无论是常规函数还是箭头函数。

const average = (...args) => {
  if (args.length == 0) return 0;
  const sumReduceFn = function (a, b) { return a + Number(b) };

  return args.reduce(sumReduceFn, 0) / args.length;
}
로그인 후 복사

对于使用rest参数需要注意一些事项:

 ● rest参数与函数内部的arguments对象不同。rest参数是一个实际的函数参数,而arguments

function baseConvert (num, fromRadix = 10, toRadix = 10) {
  if (arguments.length < 3) {
    // swap variables using array destructuring
    [toRadix, fromRadix] = [fromRadix, toRadix];
  }
  return parseInt(num, fromRadix).toString(toRadix);
}
로그인 후 복사
로그인 후 복사

얼핏 보면 화살표 함수를 일반 함수처럼 정의해서 사용할 수 있을 것 같지만 그렇지 않습니다. 화살표 함수는 단순성 때문에 일반 함수와는 다릅니다. 다른 관점에서 보면 화살표 함수는 비정상적인 JS 함수로 간주될 수도 있습니다. 🎜🎜【관련 강좌 추천: JavaScript 동영상 튜토리얼]🎜🎜화살표 기능이 있지만 구문은 매우 간단하지만 이것이 이 기사의 초점은 아닙니다. 이 기사에서는 주로 화살표 함수와 일반 함수의 동작 차이점과 이러한 차이점을 사용하여 화살표 함수를 더 잘 사용할 수 있는 방법에 대해 설명합니다. 🎜🎜● 엄격 모드이든 비엄격 모드이든 화살표 함수는 중복된 명명된 매개 변수를 가질 수 없습니다. 🎜🎜● 화살표 함수에는 인수 바인딩이 없습니다. 그러나 화살표가 아닌 가장 가까운 상위 함수의 arguments 객체에 액세스할 수 있습니다. 🎜🎜● 화살표 함수는 생성자로 사용할 수 없으며 당연히 new 키워드를 사용하여 호출할 수 없습니다. 따라서 화살표 함수에는 prototype 속성이 없습니다. 🎜🎜● 화살표 함수 내부의 값은 함수 수명 내내 변경되지 않고 항상 가장 가까운 비화살표 상위 함수의 값에 바인딩됩니다. 🎜🎜🎜이름이 지정된 함수 매개변수🎜🎜🎜JS의 함수는 일반적으로 이름이 지정된 매개변수로 정의됩니다. 명명된 매개변수는 매개변수를 함수 범위의 지역 변수에 위치적으로 매핑하는 데 사용됩니다. 🎜🎜다음 함수를 살펴보겠습니다. 🎜
// num => 123, fromRadix => 10, toRadix => 10
console.log(baseConvert(123)); // "123"

// num => 255, fromRadix => 10, toRadix => 2
console.log(baseConvert(255, 2)); // "11111111"

// num => 'ff', fromRadix => 16, toRadix => 8
console.log(baseConvert('ff', 16, 8)); // "377"
로그인 후 복사
로그인 후 복사
🎜 logParams() 함수는 세 개의 명명된 매개변수인 first, second로 정의됩니다. 그리고 세 번째. 함수에 전달된 것보다 더 많은 명명된 인수가 있는 경우 나머지 인수는 정의되지 않음입니다. 🎜🎜JS 함수는 명명된 매개변수를 사용하는 비엄격 모드에서 이상한 동작을 나타냅니다. 비엄격 모드에서 JS 함수는 이름이 지정된 매개변수의 반복을 허용합니다. 예를 살펴보겠습니다. 🎜
const baseConvert = (num, ...args) => {
  // 解构`args`数组和
  // 设置`fromRadix`和`toRadix`局部变量
  let [fromRadix = 10, toRadix = 10] = args;

  if (args.length < 2) {
    // 使用数组解构交换变量
    [toRadix, fromRadix] = [fromRadix, toRadix];
  }

  return parseInt(num, fromRadix).toString(toRadix);
}
로그인 후 복사
로그인 후 복사
🎜 첫 번째 매개변수가 반복되어 전달된 매개변수에 매핑되는 것을 볼 수 있습니다. 세 번째 매개변수의 값은 첫 번째 매개변수를 덮어쓰는데 이는 바람직한 동작이 아닙니다. 🎜
function Square (length = 10) {
  this.length = parseInt(length) || 10;

  this.getArea = function() {
    return Math.pow(this.length, 2);
  }

  this.getPerimeter = function() {
    return 4 * this.length;
  }
}

const square = new Square();

console.log(square.length); // 10
console.log(square.getArea()); // 100
console.log(square.getPerimeter()); // 40

console.log(typeof square); // "object"
console.log(square instanceof Square); // true
로그인 후 복사
로그인 후 복사
🎜🎜화살표 함수가 반복 인수를 처리하는 방법🎜🎜🎜🎜화살표 함수 정보: 🎜🎜🎜은 엄격 모드 여부에 관계없이 일반 함수와 다릅니다. 엄격 모드에서 화살표 함수는 반복되는 매개변수를 허용하지 않습니다. 반복되는 매개변수는 구문 오류를 발생시킵니다. 🎜
function Square (length = 10) {
  this.length = parseInt(length) || 10;
}

Square.prototype.getArea = function() {
  return Math.pow(this.length, 2);
}

Square.prototype.getPerimeter = function() {
  return 4 * this.length;
}

const square = new Square();

console.log(square.length); // 10
console.log(square.getArea()); // 100
console.log(square.getPerimeter()); // 40

console.log(typeof square); // "object"
console.log(square instanceof Square); // true
로그인 후 복사
로그인 후 복사
🎜🎜함수 오버로딩🎜🎜🎜함수 오버로딩은 함수를 정의하여 매개변수 수에 따라 해당 함수를 호출할 수 있도록 하는 기능입니다. 이 기능을 달성하기 위해 JS에서 바인딩을 사용할 수 있습니다. 🎜🎜전달된 매개변수의 평균을 계산하는 간단한 오버로드된 함수를 살펴보겠습니다. 🎜
const Square = (length = 10) => {
  this.length = parseInt(length) || 10;
}

// throws an error
const square = new Square(5);

// throws an error
Square.prototype.getArea = function() {
  return Math.pow(this.length, 2);
}

console.log(Square.prototype); // undefined
로그인 후 복사
로그인 후 복사
🎜이렇게 하면 0부터 함수가 허용할 수 있는 최대 매개변수 수까지 원하는 수의 매개변수를 사용하여 함수를 호출할 수 있습니다. 255 code>여야 합니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">function processFormData (evt) {   evt.preventDefault();   const form = this.closest('form');   const data = new FormData(form);   const { action: url, method } = form; } button.addEventListener('click', processFormData, false);</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div>🎜이제 head-cut 함수 구문을 사용하여 <code>average() 함수를 복사해 보세요. 일반적으로 우리는 이것이 어렵지 않다고 생각하고 이것만 할 수는 없습니다. 🎜
this.closest('form');
로그인 후 복사
로그인 후 복사
🎜이제 이 함수를 테스트하면 참조 오류가 발생하고 인수가 정의되지 않음을 알 수 있습니다. 🎜🎜🎜우리가 무엇을 잘못했나요🎜🎜🎜화살표 함수의 경우: 🎜🎜일반 함수와 달리 인수가 존재하지 않습니다. 화살표 기능에서. 그러나 화살표가 아닌 상위 함수의 인수 객체에 액세스하는 것은 가능합니다. 🎜🎜이러한 이해를 바탕으로 average() 함수를 일반 함수로 수정하면 즉시 호출된 중첩 화살표 함수의 결과를 반환하고 중첩 화살표 함수에서 액세스할 수 있게 됩니다. 부모 함수의 인수입니다. 🎜
const processFormData = (evt) => {
  evt.preventDefault();

  const form = this.closest('form');
  const data = new FormData(form);
  const { action: url, method } = form;
}

button.addEventListener('click', processFormData, false);
로그인 후 복사
로그인 후 복사
🎜이렇게 하면 arguments 개체가 정의되지 않은 문제를 해결할 수 있지만, 이 헛소리 접근 방식은 분명히 중복됩니다. 🎜🎜🎜뭔가 다르게 해보세요🎜🎜🎜위 문제에 대한 대안이 있나요? es6의 rest 매개변수를 사용할 수 있나요? . 🎜🎜ES6 rest 매개변수를 사용하면 함수에 전달된 모든 매개변수를 저장하는 배열을 얻을 수 있습니다. rest 구문은 일반 함수든 화살표 함수든 모든 유형의 함수에 작동합니다. 🎜
button.addEventListener('click', processFormData.bind(button), false);
로그인 후 복사
로그인 후 복사
🎜rest 매개변수를 사용할 때 주의해야 할 사항이 있습니다: 🎜🎜● rest 매개변수는 인수와 다릅니다. > 함수 내부의 객체. rest 매개변수는 실제 함수 매개변수인 반면, arguments 개체는 함수 범위에 바인딩된 내부 개체입니다. 🎜

 ● 一个函数只能有一个rest参数,而且它必须位于最后一个参数。这意味着函数可以包含命名参数和rest参数的组合。

 ● rest 参数与命名参数一起使用时,它不包含所有传入的参数。但是,当它是惟一的函数参数时,表示函数参数。另一方面,函数的arguments对象总是捕获所有函数的参数。

 ● rest参数指向包含所有捕获函数参数的数组对象,而arguments对象指向包含所有函数参数的类数组对象。

接着考虑另一个简单的重载函数,该函数将数字根据传入的进制转换为另一个类的进制数。 可以使用一到三个参数调用该函数。 但是,当使用两个或更少的参数调用它时,它会交换第二个和第三个函数参数。如下所示:

function baseConvert (num, fromRadix = 10, toRadix = 10) {
  if (arguments.length < 3) {
    // swap variables using array destructuring
    [toRadix, fromRadix] = [fromRadix, toRadix];
  }
  return parseInt(num, fromRadix).toString(toRadix);
}
로그인 후 복사
로그인 후 복사

调用 baseConvert 方法:

// num => 123, fromRadix => 10, toRadix => 10
console.log(baseConvert(123)); // "123"

// num => 255, fromRadix => 10, toRadix => 2
console.log(baseConvert(255, 2)); // "11111111"

// num => 'ff', fromRadix => 16, toRadix => 8
console.log(baseConvert('ff', 16, 8)); // "377"
로그인 후 복사
로그인 후 복사

使用箭头函数来重写上面的方法:

const baseConvert = (num, ...args) => {
  // 解构`args`数组和
  // 设置`fromRadix`和`toRadix`局部变量
  let [fromRadix = 10, toRadix = 10] = args;

  if (args.length < 2) {
    // 使用数组解构交换变量
    [toRadix, fromRadix] = [fromRadix, toRadix];
  }

  return parseInt(num, fromRadix).toString(toRadix);
}
로그인 후 복사
로그인 후 복사

构造函数

可以使用new关键字调用常规JS函数,该函数作为类构造函数用于创建新的实例对象。

function Square (length = 10) {
  this.length = parseInt(length) || 10;

  this.getArea = function() {
    return Math.pow(this.length, 2);
  }

  this.getPerimeter = function() {
    return 4 * this.length;
  }
}

const square = new Square();

console.log(square.length); // 10
console.log(square.getArea()); // 100
console.log(square.getPerimeter()); // 40

console.log(typeof square); // "object"
console.log(square instanceof Square); // true
로그인 후 복사
로그인 후 복사

当使用new关键字调用常规JS函数时,将调用函数内部[[Construct]]方法来创建一个新的实例对象并分配内存。之后,函数体将正常执行,并将this映射到新创建的实例对象。最后,函数隐式地返回 this(新创建的实例对象),只是在函数定义中指定了一个不同的返回值。

此外,所有常规JS函数都有一个prototype属性。函数的prototype属性是一个对象,它包含函数创建的所有实例对象在用作构造函数时共享的属性和方法。

以下是对前面的Square函数的一个小修改,这次它从函数的原型上的方法,而不是构造函数本身。

function Square (length = 10) {
  this.length = parseInt(length) || 10;
}

Square.prototype.getArea = function() {
  return Math.pow(this.length, 2);
}

Square.prototype.getPerimeter = function() {
  return 4 * this.length;
}

const square = new Square();

console.log(square.length); // 10
console.log(square.getArea()); // 100
console.log(square.getPerimeter()); // 40

console.log(typeof square); // "object"
console.log(square instanceof Square); // true
로그인 후 복사
로그인 후 복사

如下所知,一切仍然按预期工作。 事实上,这里有一个小秘密:ES6 类在后台执行类似于上面代码片段的操作 - 类(class)只是个语法糖。

那么箭头函数呢

它们是否也与常规JS函数共享此行为?答案是否定的。关于箭头函数:

与常规函数不同,箭头函数永远不能使用new关键字调用,因为它们没有[[Construct]]方法。 因此,箭头函数也不存在prototype属性。

箭头函数不能用作构造函数,无法使用new关键字调用它们,如果这样做了会抛出一个错误,表明该函数不是构造函数。

因此,对于箭头函数,不存在可以作为构造函数调用的函数内部的new.target等绑定,相反,它们使用最接近的非箭头父函数的new.target值。

此外,由于无法使用new关键字调用箭头函数,因此实际上不需要它们具有原型。 因此,箭头函数不存在prototype属性。

由于箭头函数的prototypeundefined,尝试使用属性和方法来扩充它,或者访问它上面的属性,都会引发错误。

const Square = (length = 10) => {
  this.length = parseInt(length) || 10;
}

// throws an error
const square = new Square(5);

// throws an error
Square.prototype.getArea = function() {
  return Math.pow(this.length, 2);
}

console.log(Square.prototype); // undefined
로그인 후 복사
로그인 후 복사

this 是啥

JS函数的每次调用都与调用上下文相关联,这取决于函数是如何调用的,或者在哪里调用的。

函数内部this值依赖于函数在调用时的调用上下文,这通常会让开发人员不得不问自己一个问题:this值是啥。

下面是对不同类型的函数调用this指向一些总结:

 ● 使用new关键字调用:this指向由函数的内部[[Construct]]方法创建的新实例对象。this(新创建的实例对象)通常在默认情况下返回,除了在函数定义中显式指定了不同的返回值。

 ● 不使用new关键字直接调用:在非严格模式下,this指向window对象(浏览器中)。然而,在严格模式下,this值为undefined;因此,试图访问或设置此属性将引发错误。

 ● 间接使用绑定对象调用Function.prototype对象提供了三种方法,可以在调用函数时将函数绑定到任意对象,即:call()apply()bind()。 使用这些方法调用函数时,this指向指定的绑定对象。

 ● 作为对象方法调用this指向调用函数(方法)的对象,无论该方法是被定义为对象的自己的属性还是从对象的原型链中解析。

 ● 作为事件处理程序调用:对于用作DOM事件侦听器的常规函数,this指向触发事件的目标对象、DOM元素、documentwindow

再来看个函数,该函数将用作单击事件侦听器,例如,表单提交按钮:

function processFormData (evt) {
  evt.preventDefault();

  const form = this.closest('form');

  const data = new FormData(form);
  const { action: url, method } = form;
}

button.addEventListener('click', processFormData, false);
로그인 후 복사
로그인 후 복사

与前面看到的一样,事件侦听器函数中的 this值是触发单击事件的DOM元素,在本例中是button

因此,可以使用以下命令指向submit按钮的父表单

this.closest('form');
로그인 후 복사
로그인 후 복사

如果将函数更改为箭头函数语法,会发生什么?

const processFormData = (evt) => {
  evt.preventDefault();

  const form = this.closest('form');
  const data = new FormData(form);
  const { action: url, method } = form;
}

button.addEventListener('click', processFormData, false);
로그인 후 복사
로그인 후 복사

如果现在尝试此操作,咱们就得到一个错误。从表面上看,this 的值并不是各位想要的。由于某种原因,它不再指向button元素,而是指向window对象。

如何修复this指向

利用上面提到的 Function.prototype.bind() 强制将this值绑定到button元素:

button.addEventListener('click', processFormData.bind(button), false);
로그인 후 복사
로그인 후 복사

但这似乎不是各位想要的解决办法。this仍然指向window对象。这是箭头函数特有的问题吗?这是否意味着箭头函数不能用于依赖于this的事件处理?

为什么会搞错

关于箭头函数的最后一件事:

与常规函数不同,箭头函数没有this的绑定。 this的值将解析为最接近的非箭头父函数或全局对象的值。

这解释了为什么事件侦听器箭头函数中的this值指向window 对象(全局对象)。 由于它没有嵌套在父函数中,因此它使用来自最近的父作用域的this值,该作用域是全局作用域。

但是,这并不能解释为什么不能使用bind()将事件侦听器箭头函数绑定到button元素。对此有一个解释:

与常规函数不同,内部箭头函数的this值保持不变,并且无论调用上下文如何,都不能在其整个生命周期中更改。

箭头函数的这种行为使得JS引擎可以优化它们,因为可以事先确定函数绑定。

考虑一个稍微不同的场景,其中事件处理程序是使用对象方法中的常规函数定义的,并且还取决于同一对象的另一个方法:

({
  _sortByFileSize: function (filelist) {
    const files = Array.from(filelist).sort(function (a, b) {
      return a.size - b.size;
    });

    return files.map(function (file) {
      return file.name;
    });
  },

  init: function (input) {
    input.addEventListener('change', function (evt) {
      const files = evt.target.files;
      console.log(this._sortByFileSize(files));
    }, false);
  }

}).init(document.getElementById('file-input'));
로그인 후 복사

上面是一个一次性的对象,该对象带有_sortByFileSize()方法和init()方法,并立即调init方法。init()方法接受一个input元素,并为input元素设置一个更改事件处理程序,该事件处理程序按文件大小对上传的文件进行排序,并打印在浏览器的控制台。

如果测试这段代码,会发现,当选择要上载的文件时,文件列表不会被排序并打印到控制台;相反,会控制台上抛出一个错误,问题就出在这一行:

console.log(this._sortByFileSize(files));
로그인 후 복사

在事件监听器函数内部,this 指向 input 元素 因此this._sortByFileSizeundefined

要解决此问题,需要将事件侦听器中的this绑定到包含方法的外部对象,以便可以调用this._sortByFileSize()。 在这里,可以使用bind(),如下所示:

init: function (input) {
  input.addEventListener('change', (function (evt) {
    const files = evt.target.files;
    console.log(this._sortByFileSize(files));
  }).bind(this), false);
}
로그인 후 복사
로그인 후 복사

现在一切正常。这里不使用bind(),可以简单地用一个箭头函数替换事件侦听器函数。箭头函数将使用父init()方法中的this的值:

init: function (input) {
  input.addEventListener('change', (function (evt) {
    const files = evt.target.files;
    console.log(this._sortByFileSize(files));
  }).bind(this), false);
}
로그인 후 복사
로그인 후 복사

再考虑一个场景,假设有一个简单的计时器函数,可以将其作为构造函数调用来创建以秒为单位的倒计时计时器。使用setInterval()进行倒计时,直到持续时间过期或间隔被清除为止,如下所示:

function Timer (seconds = 60) {
  this.seconds = parseInt(seconds) || 60;
  console.log(this.seconds);

  this.interval = setInterval(function () {
    console.log(--this.seconds);

    if (this.seconds == 0) {
      this.interval && clearInterval(this.interval);
    }
  }, 1000);
}

const timer = new Timer(30);
로그인 후 복사

如果运行这段代码,会看到倒计时计时器似乎被打破了,在控制台上一直打印 NaN

这里的问题是,在传递给setInterval()的回调函数中,this指向全局window对象,而不是Timer()函数作用域内新创建的实例对象。因此,this.secondsthis.interval  都是undefined的。

与之前一样,要修复这个问题,可以使用bind()setInterval()回调函数中的this值绑定到新创建的实例对象,如下所示

function Timer (seconds = 60) {
  this.seconds = parseInt(seconds) || 60;
  console.log(this.seconds);

  this.interval = setInterval((function () {
    console.log(--this.seconds);

    if (this.seconds == 0) {
      this.interval && clearInterval(this.interval);
    }
  }).bind(this), 1000);
}
로그인 후 복사

或者,更好的方法是,可以用一个箭头函数替换setInterval()回调函数,这样它就可以使用最近的非箭头父函数的this值:

function Timer (seconds = 60) {
  this.seconds = parseInt(seconds) || 60;
  console.log(this.seconds);

  this.interval = setInterval(() => {
    console.log(--this.seconds);

    if (this.seconds == 0) {
      this.interval && clearInterval(this.interval);
    }
  }, 1000);
}
로그인 후 복사

现在理解了箭头函数如何处理this关键字,还需要注意箭头函数对于需要保留this值的情况并不理想 - 例如,在定义需要引用的对象方法时 使用需要引用目标对象的方法来扩展对象或扩充函数的原型。

不存在的绑定

在本文中,已经看到了一些绑定,这些绑定可以在常规JS函数中使用,但是不存在用于箭头函数的绑定。相反,箭头函数从最近的非箭头父函数派生此类绑定的值。

总之,下面是箭头函数中不存在绑定的列表:

 ● arguments:调用时传递给函数的参数列表

 ● new.target:使用new关键字作为构造函数调用的函数的引用

 ● super:对函数所属对象原型的引用,前提是该对象被定义为一个简洁的对象方法

 ● this:对函数的调用上下文对象的引用

原文:https://s0dev0to.icopy.site/bnevilleoneill/anomalies-in-javascript-arrow-functions-2afh

为了保证的可读性,本文采用意译而非直译。

本文来自 js教程 栏目,欢迎学习!

위 내용은 JS 화살표 기능에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿