«Серия ES6» полностью понимает функции стрелок (с упражнениями)

ECMAScript 6
«Серия ES6» полностью понимает функции стрелок (с упражнениями)

Чем больше вы знаете, тем больше вы не знаете
点赞Посмотри еще раз, аромат остался в руке, и слава

введение

Стрелочные функции — одно из самых интересных и популярных дополнений к ES6.

Эта статья будет разделена на три части:

Первая часть в основном знакомит с основным синтаксисом и использованием стрелочных функций, в том числеthis的指向Проблема будет освещена.

Вторая часть исследует, как работают стрелочные функции.自执行函数странное явление в.

В третьей части будет немного面试题目, чтобы помочь вам понять.

что такое стрелочная функция

Как следует из названия, стрелочные функции — это новый синтаксис для определения функций с использованием (=>), который немного отличается от традиционных функций ES5.

Вот функция, написанная на синтаксисе ES5:

function addTen(num){
  return num + 10;
}
addTen(5); // 15

С помощью стрелочных функций ES6 мы можем использовать стрелочные функции для представления:

var addTen = num => num + 10

addTen(5); // 15

Стрелочные функции намного короче! Из-за неявного возврата мы можем опустить фигурные скобки и оператор return.

Важно понимать, как ведут себя стрелочные функции по сравнению с обычными функциями ES5.

Особенности стрелочных функций

более короткий синтаксис

Основной синтаксис следующий:

(参数)=> { statements }

Далее разбираем различные формы написания стрелочных функций:

Когда нет параметров, используйте скобки, представляющие параметры

let f = ()=> 5;

f(); // 5

При наличии только одного параметра круглые скобки можно опустить.

let f = num => num + 5;

f(10); // 15

При наличии нескольких параметров определите несколько параметров в круглых скобках, разделенных запятыми.

let f = (a,b) => a + b;

f(1,2); // 3

Когда в блоке кода стрелочной функции имеется более одного оператора, его необходимо заключить в фигурные скобки и использовать оператор return.

// 没有大括号,默认返回表达式结果
let f1 = (a,b) => a + b
f1(1,2) // 3

// 有大括号,无return语句,没有返回值
let f2 = (a,b) => {a + b}
f2(1,2) // undefined

// 有大括号,有return语句,返回结果
let f3 = (a,b) => {return a + b}
f3(1,2) // 3

Поскольку фигурные скобки интерпретируются как блоки кода, если стрелочная функция возвращает объект напрямую, вы должны заключить объект в круглые скобки, иначе будет сообщено об ошибке.

//报错
let f1 = num => {num:num}

//不报错
let f2 = num => ({num:num})

нельзя вызывать с новым ключевым словом

Нет функционального метода arrow [[Construct]], его нельзя использовать в качестве конструктора.

let F = ()=>{};

// 报错 TypeError: F is not a constructor
let f = new F();

нет прототипа

Поскольку ее нельзя вызвать через ключевое слово new, нет необходимости создавать прототип, поэтому стрелочная функция не имеет свойства прототипа.

let F = ()=>{};
console.log(F.prototype) // undefined

Не может использоваться в качестве функции генератора

В стрелочных функциях нельзя использовать команду yield, поэтому стрелочные функции нельзя использовать в качестве функций-генераторов.

без аргументов, супер, new.target

В стрелочных функциях нет привязки аргументов, super и new.target, и эти значения определяются ближайшей нестрелочной функцией на периферии.

Взяв аргументы в качестве примера, см. следующий код:

let f = ()=>console.log(arguments);

//报错
f(); // arguments is not defined

Поскольку стрелочная функция f определена в глобальной среде, для f нельзя получить значение аргумента окружающей нестрелочной функции, поэтому здесь сообщается об ошибке.

Давайте посмотрим на другой пример:

function fn(){
    let f = ()=> console.log(arguments)
    f();
}
fn(1,2,3) // [1,2,3]

В приведенном выше коде аргументы внутри стрелочной функции f на самом деле являются аргументами переменной функции fn.

Если вы хотите получить список параметров переменной длины в стрелочной функции, вы можете использовать параметр rest в ES6 для его решения:

let f = (...args)=>console.log(args)

f(1,2,3,4,5) // [1,2,3,4,5]

нет этой привязки

Прежде чем понять проблему этого в стрелочных функциях, давайте вернемся к примеру в ES5:

var obj = {
  value:0,
  fn:function(){
    this.value ++
  }
}
obj.fn();
console.log(obj.value); // 1

Этот код прост, ожидается, что obj.value будет увеличиваться на 1 каждый раз, когда вызывается obj.fn().

Теперь давайте немного изменим код:

var obj = {
  value:0,
  fn:function(){
    var f = function(){
      this.value ++
    }
    f();
  }
}
obj.fn();
console.log(obj.value); // 0

Мы немного модифицировали код, добавили функцию f в метод obj.fn, а действие добавления 1 к obj.value поместили в функцию f. Но из-за ошибки в дизайне языка javascript this в функции f не совпадает с this в методе obj.fn, поэтому мы не можем получить obj.value.

Чтобы решить такие проблемы, в ES5 мы обычно присваиваем this во внешней функции временной переменной (обычно называемой that, _this, self), и если мы хотим использовать this внешней функции во внутренней функции, получаем через эту временную переменную. Измените код следующим образом:

var obj = {
  value:0,
  fn:function(){
    // 本人喜欢定义为 _this,也有很多人喜欢定义成 that 或 self
    var _this = this;
    var f = function(){
      _this.value ++
    }
    f();
  }
}
obj.fn();
console.log(obj.value); // 1

Из этого примера мы знаем, как решить внутреннюю функцию, чтобы получить this внешней функции в ES5.

Затем давайте посмотрим на разницу в этом указателе стрелочных функций по сравнению с функциями в ES5.

Посмотрите на некоторые определения изНачало работы с стандартами ES6

Объект this в теле стрелочной функции — это объект, в котором он определен, а не объект, в котором он используется.

Итак, как понимать эту фразу?

мы пытаемся использоватьbabelДавайте преобразуем следующий код в код ES5 и посмотрим, что он делает.

function fn(){
  let f = ()=>{
    console.log(this)
  }
}

Давайте посмотрим на преобразованные результаты прямо выше:

Что мы нашли? Это так же, как мы решали внутреннюю функцию, чтобы получить this внешней функции в ES5 раньше, определив временную переменную _this ~

Итак, куда делась функция стрелок?

Ответ заключается в том, что у стрелочных функций вообще нет this!

Итак, можно подытожить, переводя малопонятное определение на просторечие:

  • Если во внешнем слое стрелочной функции есть обычная функция, то this стрелочной функции — это this внешней обычной функции.
  • Если во внешнем слое стрелочной функции нет обычной функции, то this стрелочной функции является глобальной переменной.

Давайте используем несколько примеров для проверки правил, которые мы заключили:

let obj = {
    fn:function(){
        console.log('我是普通函数',this === obj)
        return ()=>{
            console.log('我是箭头函数',this === obj)
        }
    }
}

console.log(obj.fn()())

// 我是普通函数 true
// 我是箭头函数 true

Из приведенного выше примера мы можем видеть, что эта функция со стрелкой равна этой внешней функции.

Глядя на пример вложения многоуровневой стрелочной функции:

let obj = {
    fn:function(){
        console.log('我是普通函数',this === obj)
        return ()=>{
            console.log('第一个箭头函数',this === obj)
            return ()=>{
                console.log('第二个箭头函数',this === obj)
                return ()=>{
                    console.log('第三个箭头函数',this === obj)
                }
            }
        }
    }
}

console.log(obj.fn()()()())
// 我是普通函数 true
// 第一个箭头函数 true
// 第二个箭头函数 true
// 第三个箭头函数 true

В этом примере мы можем знать, что для стрелочных функций this стрелочной функции равно this первой обычной функции во внешнем слое, независимо от того, сколько слоев стрелочных функций вложены друг в друга.

Давайте рассмотрим пример без внешней функции:

let obj = {
    fn:()=>{
        console.log(this === window);
    }
}

console.log(obj.fn())

// true

Этот пример доказывает, что когда нет обычной функции вне стрелочной функции, this стрелочной функции равно глобальному объекту.

Следует отметить, что глобальный объект в среде браузера — это окно, а глобальный объект в среде узла — глобальный, который необходимо различать при проверке.

Стрелочные функции встречают вызов, применяют, связывают

Увидев это, я полагаю, вы уже знаете, что стрелочные функции вообще не имеют собственного this, так что же происходит, когда стрелочные функции сталкиваются с вызовом, применением и связыванием?

Мы знаем, что роль состоит в том, чтобы изменить вызов и применить его к функции, передавая параметры и выполнение функции, Эффект заключается в создании привязки этой новой функции и предустановленных параметров функции.

Однако, поскольку стрелочные функции вообще не имеют своего this, поэтому:

  • При использовании метода call или apply в стрелочной функции передаются только параметры и вызывается функция, а указатель this в стрелочной функции не изменяется;
  • При использовании метода привязки для стрелочной функции он возвращает только новую функцию с заданным параметром и не привязывает указатель this новой функции.

Давайте проверим:

window.name = 'window_name';

let f1 = function(){return this.name}
let f2 = ()=> this.name

let obj = {name:'obj_name'}

f1.call(obj) // obj_name
f2.call(obj) // window_name

f1.apply(obj) // obj_name
f2.apply(obj) // window_name

f1.bind(obj)() // obj_name
f2.bind(obj)() // window_name

В приведенном выше коде объявлена ​​нормальная функция f1 и стрелочная функция f2.

Указатель this обычной функции является динамически изменяемым, поэтому при использовании вызова, применения и привязки к f1 указатель this внутри f1 изменится.

Указатель this стрелочной функции определяется во время ее определения и никогда не изменится, поэтому при использовании вызова, применения, привязки к f2 параметр входящего контекста игнорируется.

самовыполняющаяся функция

До появления стрелочных функций ES6 самовыполняющиеся функции обычно писались так:

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

Или напишите так:

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

Конечно, стрелочные функции также могут использоваться как самовыполняющиеся функции, которые можно записать следующим образом:

(() => {
    console.log(1)
})()

Однако большинство людей не ожидают, что следующий способ написания сообщит об ошибке:

(() => {
    console.log(1)
}())

Итак, почему он получает ошибку?

Меня долго мучил этот вопрос, пока не прочиталСпецификация ECMAScript® 2015, из которого мы знаем, что стрелочная функция является разновидностью AssignmentExpression, а вызов функции принадлежит CallExpression. Спецификация требует, чтобы при CallExpression левое выражение должно быть MemberExpression или другим CallExpression, а стрелочная функция не принадлежит этим двум выражениям, поэтому При компиляции будет сообщено об ошибке.

Это принцип, подробности см.Спецификация ECMAScript® 2015

Вопрос о стрелочных функциях

В интервью исследование стрелочных функций в основном было сосредоточено на указании ключевого слова arguments и этом указании стрелочной функции.Следующие темы, от более мелкого к более глубокому, приведены для вашей справки.

Тема 1

function foo(n) {
  var f = () => arguments[0] + n;
  return f();
}

let res = foo(2);

console.log(res); // 问 输出结果
Ответы и анализ

Ответ: 4

У стрелочных функций нет собственных аргументов, поэтому аргументы в вопросе относятся к объекту arguments функции foo. Итак, arguments[0] равно 2, n равно 2, а результат равен 4.

тема вторая

function A() {
  this.foo = 1
}

A.prototype.bar = () => console.log(this.foo)

let a = new A()
a.bar() // 问 输出结果
Отвечать

Ответ: неопределенный

Arrow Эта функция не владеет, поэтому эта функция эквивалентна внешней стрелке стрелки не эта функция. Поскольку внешний слой не является обычной функцией стрелки функции, стрелки в этой функции эквивалентны глобальному объекту, поэтому вывод неопределен.

тема три

let res = (function pt() {
  return (() => this.x).bind({ x: 'inner' })();
}).call({ x: 'outer' });

console.log(res)  // 问 输出结果
Отвечать

Ответ: «внешний».

Эта задача немного сложнее, найдите выход res.

проанализируйте, как показано ниже:

  1. Найдите возвращаемое значение функции pt после ее вызова с помощью call.
  2. Это внутри функции pt преобразуется вызовом {x:'outer'}.
  3. В функции pt функция стрелки генерирует новую функцию через привязку и выполняет ее, а результатом выполнения является возвращаемое значение функции pt.
  4. Это в стрелочной функции не может быть привязано методом привязки.Это при выполнении стрелочной функции является this из внешней области видимости.
  5. Когда функция стрелки выполняется, this внешней области видимости равно {x:'outer'}, указанному методом вызова.
  6. Окончательный результат res является «внешним».

тема четвертая

window.name = 'window_name';

let obj1 = {
    name:'obj1_name',
    print:()=>console.log(this.name)
}

let obj2 = {name:'obj2_name'}

obj1.print()  // 问 输出结果
obj1.print.call(obj2)  // 问 输出结果
Отвечать

Ответ: 'имя_окна' 'имя_окна'

Стрелочные функции не имеют собственного this и не могут изменить this в стрелочных функциях через вызов, применение и привязку. Это стрелочной функции зависит от того, есть ли обычная функция во внешнем слое.Если есть обычная функция, это указывает на это в обычной функции.Если во внешнем слое нет обычной функции, это в стрелке функция является глобальным объектом.

В этом вопросе нет обычной функции за пределами функции стрелки, поэтому она указывает на глобальный объект, поэтому результатом является «имя_окна», «имя_окна».

пятая тема

let obj1 = {
    name:'obj1_name',
    print:function(){
        return ()=>console.log(this.name)
    }
}

let obj2 = {name:'obj2_name'}


obj1.print()() // 问 输出结果
obj1.print().call(obj2) // 问 输出结果
obj1.print.call(obj2)() // 问 输出结果
Отвечать

Ответ: 'obj1_name' 'obj1_name' 'obj2_name'

this стрелочной функции такое же, как this внешней обычной функции, и не имеет ничего общего с вызовом, применением и связыванием.

В этом вопросе obj1.print возвращает функцию стрелки, this в функции стрелки - это this при вызове obj1.print.

  1. obj1.print()(): на этот раз в obj1.print будет obj1, поэтому вывод будет obj1_name
  2. obj1.print().call(obj2): на этот раз в obj1.print будет obj1, поэтому вывод будет obj1_name
  3. obj1.print.call(obj2)(): на этот раз в obj1.print будет obj2, поэтому вывод будет obj2_name

Ссылаться на

напиши в конце

  • Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, добро пожаловать点赞а также关注
  • Эта статья была впервые опубликована одновременно сgithub, доступны наgithubНайдите больше отличных статей вWatch & Star ★
  • Для последующих статей см.:строить планы

Добро пожаловать в публичный аккаунт WeChat【前端小黑屋】, 1–3 высококачественные высококачественные статьи публикуются каждую неделю, чтобы помочь вам в продвижении вперед.