Смысл этого в JS в различных сценариях

программист JavaScript

Автор: Дмитрий Павлютин Переводчик: Front-end Xiaozhi Источник: дмитрипавлутин

Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статьяGitHub GitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.

Все говорили, что нет проекта для написания резюме, поэтому я помог вам найти проект, и это было с бонусом.【Учебник по строительству】.

1. Тайна этого

Много раз в JSthisНашим новичкам легко запутаться.thisФункция очень мощная, но требуется некоторое усилие, чтобы понять ее медленно.

Для Java, PHP или других стандартных языковthisПредставляет экземпляр текущего объекта в методе класса. в большинстве случаев,thisЕго нельзя использовать вне метода, чтобы сравнение было менее запутанным.

В J ситуация иная:thisУказывает текущий контекст выполнения функции.Вызовы функций в JS в основном включают следующие методы:

  • вызов функции:alert('Hello World!')

  • Вызов метода:console.log('Hello World!')

  • Конструктор:new RegExp('\\d')

  • Неявный вызов:alert.call(undefined, 'Hello World!')

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

Кроме того, строгий режим влияет на контекст выполнения.

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

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

Прежде чем мы начнем, знайте несколько терминов:

Вызывающая функция выполняет код, создавший тело функции, или просто вызывает функцию. Например, вызов функции parseInt — это parseInt('15').

  • вызов функции: выполнить код, составляющий тело функции: например,parseIntВызов функцииparseInt('15').

  • контекст вызова:Ссылаться наthisЗначение внутри тела функции. Например,map.set('key', 'value')Контекст вызоваmap.

  • объем функции: это набор переменных, объектов и функций, доступных в теле функции.

2. Вызов функции

Когда выражение представляет собой функцию, за которой следует(, некоторые аргументы, разделенные запятыми, и, вызов функции выполняется, например.parseInt('18').

Выражение вызова функции не может быть вызовом атрибута, напримерobj.myFunc(), который должен создать вызов метода. Другой пример[1,5].join(',')Не вызов функции, а вызов метода,Это различие нужно помнить, оно очень важно..

Простой пример вызова функции:

function hello(name) {
  return 'Hello ' + name + '!';
}
// 函数调用
const message = hello('World');
console.log(message); // => 'Hello World!'

hello('World')это вызов функции:helloВыражение эквивалентно функции, за которой следует пара круглых скобок и'World'параметр.

Более продвинутым примером может бытьIIFE(сразу же называется функциональным выражением)

const message = (function(name) {
   return 'Hello ' + name + '!';
})('World');
console.log(message) // => 'Hello World!'

IIFEТакже вызов функции: первая пара скобок(function(name) {...})это выражение, результатом которого является объект функции, за которым следует пара круглых скобок, аргументы которых“World”.

2.1 this в вызовах функций

это глобальный объект в вызовах функций

Локальные объекты определяются средой выполнения. В браузереthisдаwindowобъект.

При вызове функции контекстом выполнения является глобальный объект.

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

function sum(a, b) {
   console.log(this === window); // => true
   this.myNumber = 20; // 将'myNumber'属性添加到全局对象
   return a + b;
}
// sum() is invoked as a function
// sum() 中的 `this` 是一个全局对象(window)
sum(15, 16);     // => 31
window.myNumber; // => 20

вызовsum(15,16), JS автоматическиthisУстановите глобальный объект, который в браузереwindow.

когдаthisИспользуется вне области действия любой функции (самая верхняя область: глобальный контекст выполнения),thisвыражатьwindowобъект

console.log(this === window); // => true
this.myString = 'Hello World!';
console.log(window.myString); // => 'Hello World!'

<!-- In an html file -->
<script type="text/javascript">
   console.log(this === window); // => true
</script>

2.2 Как это выглядит при вызове функции в строгом режиме

thisв вызове функции в строгом режимеundefined

Строгий режим был введен в ECMAScript 5.1 и обеспечивает лучшую безопасность и более строгую проверку ошибок.

Чтобы включить строгий режим, заголовок функции пишетuse strictВот и все.

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

Пример вызова функции в строгом режиме:

function multiply(a, b) {
  'use strict'; // 启用严格模式
  console.log(this === undefined); // => true
  return a * b;
}
multiply(2, 5); // => 10

когдаmultiply(2,5)При вызове как функцииthisдаundefined.

Строгий режим действует не только в текущей области видимости, но и во внутренних областях видимости (для всех функций, объявленных внутри):

function execute() {
   'use strict'; // 开启严格模式  
   function concat(str1, str2) {
     // 严格模式仍然有效  
     console.log(this === undefined); // => true
     return str1 + str2;
   }
   // concat() 在严格模式下作为函数调用
   // this in concat() is undefined
   concat('Hello', ' World!'); // => "Hello World!"
}
execute();

'use strict'вставляется в начало выполнения, включая строгий режим в своей области. потому что функцияconcatобъявляется в области выполнения, поэтому он наследует строгий режим.

Один файл JS может содержать строгие и нестрогие режимы. Следовательно, в одном скрипте для одного и того же типа вызова может быть разное контекстуальное поведение:

function nonStrictSum(a, b) {
  // 非严格模式
  console.log(this === window); // => true
  return a + b;
}
function strictSum(a, b) {
  'use strict';
  // 启用严格模式
  console.log(this === undefined); // => true
  return a + b;
}

nonStrictSum(5, 6); // => 11
strictSum(8, 12); // => 20

2.3 Подводные камни:thisкогда внутри функции

Распространенная ошибка вызовов функций состоит в том, что они думают, чтоthisСитуация во внутренней функции такая же, как и во внешней функции.

Правильно говоря, контекст внутренней функции зависит только от типа ее вызова, а не от контекста внешней функции.

кthisустановить желаемое значение, которое можно.call()или.apply()Измените контекст внутренней функции или используйте.bind()Создайте функцию привязки.

В следующем примере вычисляется сумма двух чисел:

const numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       console.log(this === numbers); // => false
       return this.numberA + this.numberB;
     }
     return calculate();
   }
};
numbers.sum(); // => NaN 

sum()это вызов метода объекта, поэтомуsumКонтекст в томnumbersобъект.calculateфункция находится вsumопределено в , вы можете захотетьcalculate()серединаthisтакже значитnumberобъект.

calculate()является вызовом функции (а не вызовом метода), он будетthisкак глобальный объектwindow(в нестрогом режиме). даже если внешняя функцияsumпоставить контекст какnumberобъект, которыйcalculateВнутри эффекта нет.

sum()Результат вызова -NaN, не ожидаемый результат5 + 10 = 15, все потому что он не правильно называетсяcalculate.

Для решения этой проблемы,calculateКонтекст в функции должен быть таким же, какsumкак в так, чтобы вы могли получить доступnumberAа такжеnumberBАтрибуты.

Одно из решений - позвонитьcalculator.call(this)ВручнуюcalculateИзмените контекст на нужный контекст.

const numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       console.log(this === numbers); // => true
       return this.numberA + this.numberB;
     }
     // 使用 .call() 方法修改上下文
     return calculate.call(this);
   }
};
numbers.sum(); // => 15

call(this)Выполнить как обычноcalculateфункция, ноcallизменит контекст на значение, указанное в качестве первого параметра.

Сейчасthis.numberA + this.numberBэквивалентноnumbers.numberA + numbers.numberB. Функция возвращает ожидаемый результат5 + 10 = 15.

Другой - использовать функции стрелок

const numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     const calculate = () => {
       console.log(this === numbers); // => true
       return this.numberA + this.numberB;
     }
     return calculate();
   }
};
numbers.sum(); // => 15

3. Вызов метода

Методы — это функции, хранящиеся в свойствах объекта. Например

const myObject = {
  // helloFunction 是一个方法
  helloFunction: function() {
    return 'Hello World!';
  }
};
const message = myObject.helloFunction();

helloFunctionдаmyObjectМетод , чтобы вызвать этот метод, вы можете назвать его так:myObject.helloFunction.

Когда выражение выполняется в форме доступа к свойству, выполняется вызов метода, который эквивалентен функции, за которой следует (, набор аргументов, разделенных запятыми, и ).

Используя предыдущий пример,myObject.helloFunction()является объектомmyObjectтот, что наhelloFunctionвызов метода.[1, 2].join(',')или/\s/.test('beautiful world')Также считается вызовом метода.

Важно различать вызовы функций и вызовы методов, поскольку они относятся к разным типам. Основное отличие состоит в том, что для вызова метода требуется форма доступа к свойству для вызова функции (obj.myFunc()илиobj['myFunc']()), при этом вызов функции не требует (myFunc()).

['Hello', 'World'].join(', '); // 方法调用
({ ten: function() { return 10; } }).ten(); // 方法调用
const obj = {};
obj.myFunction = function() {
  return new Date().toString();
};
obj.myFunction(); // 方法调用

const otherFunction = obj.myFunction;
otherFunction();     // 函数调用
parseFloat('16.60'); // 函数调用
isNaN(0);            // 函数调用

Понимание разницы между вызовом функции и вызовом метода помогает правильно определить контекст.

3.1 Как это выглядит при вызове метода?

При вызове методаthisЯвляется ли объект с этим методом

При вызове метода объектаthisстановится самим объектом.

Создайте объект, у которого есть метод для увеличения числа

const calc = {
  num: 0,
  increment: function() {
    console.log(this === calc); // => true
    this.num += 1;
    return this.num;
  }
};
// method invocation. this is calc
calc.increment(); // => 1
calc.increment(); // => 2

передачаcalc.increment()СделатьincrementКонтекст функции становитсяcalcобъект. так что используйтеthis.numувеличиватьnumсвойство действительно.

Давайте посмотрим на другой пример. Объекты JS наследуют метод от прототипа, когда унаследованный метод вызывается для объекта, контекст вызова по-прежнему является самим объектом

const myDog = Object.create({
  sayName: function() {
     console.log(this === myDog); // => true
     return this.name;
  }
});
myDog.name = 'Milo';
// 方法调用 this 指向 myDog
myDog.sayName(); // => 'Milo'

Object.create()создать новый объектmyDog, и установите его прототип в соответствии с первым аргументом.myDogнаследование объектовsayNameметод.

воплощать в жизньmyDog. sayname()час,myDogявляется вызывающим контекстом.

в ЕС6classВ грамматике контекст вызова метода также является самим экземпляром.

class Planet {
  constructor(name) {
    this.name = name;    
  }
  getName() {
    console.log(this === earth); // => true
    return this.name;
  }
}
var earth = new Planet('Earth');
// method invocation. the context is earth
earth.getName(); // => 'Earth'
 

3.2 Ошибка: отделение метода от его объекта

Методы могут быть извлечены из объекта в отдельную переменнуюconst alone = myObj.myMethod. Когда метод вызывается один, то же самое, что и исходный объектalone()разделение, можно подумать, что текущийthisэто объект, который определяет методmyObject.

Если метод вызывается без объекта, то происходит вызов функции, в этот моментthisуказывает на глобальный объектwindowСтрогий режимundefined.

Следующий пример определяетAnimalКонструктор и создайте его экземпляр:myCat. потомsetTimout()печать через 1 секундуmyCatинформация об объекте

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;  
  this.logInfo = function() {
    console.log(this === myCat); // => false
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  }
}
const myCat = new Animal('Cat', 4);
// The undefined has undefined legs 
setTimeout(myCat.logInfo, 1000); 

ты можешь подуматьsetTimoutпередачаmyCat.loginfo()когда он должен печатать оmyCatинформация об объекте.

К сожалению, методы отсоединяются от объектов при передаче в качестве параметров.setTimout(myCat.logInfo)Следующие случаи эквивалентны:

setTimout(myCat.logInfo);
// 等价于
const extractedLogInfo = myCat.logInfo;
setTimout(extractedLogInfo);

будут разделеныlogInfoПри вызове как функцииthisявляется глобальнымwindow, поэтому информация об объекте печатается неправильно.

можно использовать функцию.bind()Метод привязан к объекту, и его можно решитьthisуказал на проблему.

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;  
  this.logInfo = function() {
    console.log(this === myCat); // => true
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  };
}
const myCat = new Animal('Cat', 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo.bind(myCat), 1000);

myCat.logInfo.bind(myCat)Возвращает новую функцию, которая выполняется так же, какlogInfoточно так же, но на этот разthisнаправлениеmyCat, даже в вызовах функций.

Другое решение - поставитьlogInfo()Метод определяется как стрелочная функция:

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;  
  this.logInfo = () => {
    console.log(this === myCat); // => true
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  };
}
const myCat = new Animal('Cat', 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo, 1000);

4. Вызов конструктора

когдаnewКлючевое слово вызывается сразу после объекта функции (, набора параметров, разделенных запятыми, и ), который выполняет вызов конструктора, напримерnew RegExp('\\d').

объявилCountryфункцию и вызовите ее как конструктор:

function Country(name, traveled) {
   this.name = name ? name : 'United Kingdom';
   this.traveled = Boolean(traveled); 
}
Country.prototype.travel = function() {
  this.traveled = true;
};
// 构造函数调用
const france = new Country('France', false);
// 构造函数调用
const unitedKingdom = new Country;

france.travel(); // Travel to France

new Country('France', false)даCountryВызов конструктора функции. Результатом его выполнения являетсяnameсобственность'France'новый объект. Если этот конструктор вызывается без аргументов, круглые скобки можно опустить:new Country.

Начиная с ES6, JS позволяет использоватьclassключевые слова для определения конструкторов

class City {
  constructor(name, traveled) {
    this.name = name;
    this.traveled = false;
  }
  travel() {
    this.traveled = true;
  }
}
// Constructor invocation
const paris = new City('Paris', false);
paris.travel();

new City('Paris')это вызов конструктора. Этот объект инициализируется специальным методом в этом классеconstructorобрабатывать. в,thisУказывает на вновь созданный объект.

Конструктор создает новый пустой объект, который наследует свойства прототипа конструктора. Роль конструктора заключается в инициализации объекта. Как вы, вероятно, уже знаете, в этом типе вызова контекст указывает на только что созданный экземпляр.

при доступе к свойствуmyObject.myFunctionесть фронтnewКогда ключевое слово, JS выполнит вызов конструктора вместо исходного вызова метода.

Напримерnew myObject.myFunction(): Это эквивалентно извлечению метода с доступом к свойствам.extractedFunction = myObject.myFunction, а затем используйте его как конструктор для создания нового объекта:new extractedFunction().

4.1 это в конструкторах

В вызове конструктора это указывает на вновь созданный объект

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

Взгляните на контекст в примере ниже

function Foo () {
  console.log(this instanceof Foo); // => true
  this.property = 'Default Value';
}
// Constructor invocation
const fooInstance = new Foo();
fooInstance.property; // => 'Default Value'

new Foo()Выполняется вызов конструктора, где контекстfooInstance. существуетFooВнутренний объект инициализации:this.propertyприсваивается значение по умолчанию.

Такая же ситуация с использованиемclassСинтаксис (от ES6 итого) также происходит, единственное отличие в том, что инициализация находится вconstructorв методе:

class Bar {
  constructor() {
    console.log(this instanceof Bar); // => true
    this.property = 'Default Value';
  }
}
// Constructor invocation
const barInstance = new Bar();
barInstance.property; // => 'Default Value'

4.2. Подводный камень: забыть использовать новые

Некоторые функции JS создают новые объекты не только при вызове как конструкторы, но и при вызове как функции, напримерRegExp:

var reg1 = new RegExp('\\w+');
var reg2 = RegExp('\\w+');

reg1 instanceof RegExp;      // => true
reg2 instanceof RegExp;      // => true
reg1.source === reg2.source; // => true

при исполненииnew RegExp('\\w+')а такжеRegExp('\\w+')Когда JS создает эквивалент объекта регулярного выражения.

Существует потенциальная проблема с использованием вызовов функций для создания объектов (за исключением фабричного шаблона), поскольку некоторые конструкторы могут игнорироватьnewЛогика инициализации объекта при использовании ключевого слова.

Следующий пример иллюстрирует проблему:

function Vehicle(type, wheelsCount) {
  this.type = type;
  this.wheelsCount = wheelsCount;
  return this;
}
// 忘记使用 new 
const car = Vehicle('Car', 4);
car.type;       // => 'Car'
car.wheelsCount // => 4
car === window  // => true

Vehicleявляется набором объекта контекстаtypeа такжеwheelsCountфункция собственности.

при исполненииVehicle('Car', 4), возвращает объектCar, который имеет правильные свойства:Car.typeдляCarа такжеCar.wheelsCountдля4, вы можете подумать, что это удобно для создания и инициализации новых объектов.

Однако при вызове функцииthisдаwindowобъект, поэтомуVehicle('Car',4)существуетwindowЗадайте свойства объектов. По-видимому, это ошибка, она не создает новый объект.

Когда вы хотите вызвать конструктор, убедитесь, что вы используетеnewоператор:

function Vehicle(type, wheelsCount) {
  if (!(this instanceof Vehicle)) {
    throw Error('Error: Incorrect invocation');
  }
  this.type = type;
  this.wheelsCount = wheelsCount;
  return this;
}
// Constructor invocation
const car = new Vehicle('Car', 4);
car.type               // => 'Car'
car.wheelsCount        // => 4
car instanceof Vehicle // => true

// Function invocation. Throws an error.
const brokenCar = Vehicle('Broken Car', 3);

new Vehicle('Car',4)Работает нормально: новый объект создается и инициализируется, потому что он используется в вызове конструктораnewключевые слова.

Добавлена ​​проверка конструктораthis instanceof Vehicleчтобы убедиться, что контекст выполнения имеет правильный тип объекта. еслиthisнетVehicle, то будет выдано сообщение об ошибке. Таким образом, если вы выполнитеVehicle('Broken Car', 3)(нетnew), получаем исключение:Error: Incorrect invocation.

5. Неявный вызов

использоватьmyFun.call()илиmyFun.apply()Когда метод вызывает функцию, выполняется неявный вызов.

Функции в JS являются объектами первого класса, что означает, что функции являются объектами, а объекты имеют типFunction. Из списка методов функционального объекта,.call()а также.apply()Используется для вызова функций с настраиваемым контекстом.

  • метод.call(thisArg[, arg1[, arg2[, ...]]])первый параметр, который будет принятthisArgкак контекст во время вызова,arg1, arg2, ...Они передаются в качестве аргументов вызываемой функции.

  • метод.apply(thisArg, [args])первый параметр, который будет принятthisArgв качестве контекста во время вызова и принимает другой массивоподобный объект[arg1, arg2, ...] Передается в качестве аргумента вызываемой функции.

Ниже приведен пример неявного вызова

function increment(number) {
  return ++number;  
}
increment.call(undefined, 10);    // => 11
increment.apply(undefined, [10]); // => 11

increment.call()а такжеincrement.apply()использовать параметры10Вызывается эта функция автоинкремента.

Разница между ними.call()Получение набора параметров, например.myFunction.call(thisValue, 'value1', 'value2'). а также.apply()Набор принимаемых аргументов должен быть массивоподобным объектом, например.myFunction.apply(thisValue, ['value1', 'value2']).

5.1 this в неявных вызовах

В неявном вызове .call() или .apply() это первый параметр.

Очевидно, что в неявном вызовеthisпередается в качестве первого параметра.call()или.apply().

var rabbit = { name: 'White Rabbit' };
function concatName(string) {
  console.log(this === rabbit); // => true
  return string + this.name;
}
concatName.call(rabbit, 'Hello ');  // => 'Hello White Rabbit'
concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'

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

function Runner(name) {
  console.log(this instanceof Rabbit); // => true
  this.name = name;  
}
function Rabbit(name, countLegs) {
  console.log(this instanceof Rabbit); // => true
  Runner.call(this, name);
  this.countLegs = countLegs;
}
const myRabbit = new Rabbit('White Rabbit', 4);
myRabbit; // { name: 'White Rabbit', countLegs: 4 }

RabbitсерединаRunner.call(this, name)Неявно вызывает функцию родительского класса для инициализации объекта.

6. Функции связывания

Связанная функция — это функция, связанная с объектом. обычно используется.bind()Методы создаются из примитивных функций. Исходные и связанные функции используют один и тот же код и область действия, но выполняются в разных контекстах.

методmyFunc.bind(thisArg[, arg1[, arg2[, ...]]])принимает первый параметрthisArgкак контекст, в котором выполняется связанная функция, и принимает необязательный набор параметровarg1, arg2, ...Как параметр модифицированной функции. Он возвращает привязкуthisArgновая функция.

function multiply(number) {
  'use strict';
  return this * number;
}
const double = multiply.bind(2);

double(3);  // => 6
double(10); // => 20

bind(2)возвращает новый объект функцииdouble,doubleсвязать номера2.multiplyа такжеdoubleимеют тот же код и область действия.

а также.apply()а также.call()вместо этого он не вызывает функцию немедленно,.bind()Метод просто возвращает новую функцию, которая вызывается после, только что已经предварительно установлен.

6.1 this в связанных функциях

При вызове связанной функцииthisда.bind()первый параметр .

.bind()В результате создается новая функция, которая при вызове передает контекст как.bind()первый параметр . Это мощная техника, которая позволяет нам создать определениеthisфункция стоимости.

Давайте посмотрим, как установить, как связать функциюthis

const numbers = {
  array: [3, 5, 10],
  getNumbers: function() {
    return this.array;    
  }
};
const boundGetNumbers = numbers.getNumbers.bind(numbers);
boundGetNumbers(); // => [3, 5, 10]
// Extract method from object
const simpleGetNumbers = numbers.getNumbers;
simpleGetNumbers(); // => undefined (严格模式下报错)

numbers.getNumbers.bind(numbers)обратная привязкаnumbersобъектboundGetNumbersфункция.boundGetNumbers()когда звонятthisдаnumberобъект и может вернуть правильный объект массива.

функция может бытьnumbers.getNumbersизвлечь в переменнуюsimpleGetNumbersбез привязки. в последующих вызовах функцийsimpleGetNumbers()изthisдаwindow(в строгом режиме этоundefined),нетnumberобъект. В этом случае,simpleGetNumbers()не возвращает массив правильно.

6.2 Жесткая привязка контекста

.bind()Создайте постоянную контекстную ссылку и всегда сохраняйте ее. Связанная функция не может пройти.call()или.apply()чтобы изменить его контекст, даже повторное связывание не имеет никакого эффекта.

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

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

function getThis() {
  'use strict';
  return this;
}
const one = getThis.bind(1);
// 绑定函数调用
one(); // => 1
// 使用带有.apply()和.call()的绑定函数
one.call(2);  // => 1
one.apply(2); // => 1
// 再次绑定
one.bind(2)(); // => 1
// 以构造函数的形式调用绑定函数
new one(); // => Object

Толькоnew one()Изменен контекст связанной функции, другими способами вызоваthisвсегда равен 1.

7. Стрелочные функции

Стрелочные функции используются для объявления функций в более короткой форме и лексической привязки контекста. это можно использовать так

const hello = (name) => {
  return 'Hello ' + name;
};
hello('World'); // => 'Hello World'
// Keep only even numbers
[1, 2, 5, 6].filter(item => item % 2 === 0); // => [2, 6]

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

Стрелочные функции анонимны, что означаетnameсвойство представляет собой пустую строку''. Таким образом, у него нет лексических имен функций (имена функций полезны для рекурсии, разделения обработчиков событий)

Также, в отличие от штатных функций, не обеспечиваетargumentsобъект. Однако это работает в ES6 черезrest parametersФиксированный:

const sumArguments = (...args) => {
   console.log(typeof arguments); // => 'undefined'
   return args.reduce((result, item) => result + item);
};
sumArguments.name      // => ''
sumArguments(5, 5, 6); // => 16

7.1 this в стрелочных функциях

это определяет объемлющий контекст функции стрелки

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

Следующий пример иллюстрирует эту контекстно-прозрачную функцию:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  log() {
    console.log(this === myPoint); // => true
    setTimeout(()=> {
      console.log(this === myPoint);      // => true
      console.log(this.x + ':' + this.y); // => '95:165'
    }, 1000);
  }
}
const myPoint = new Point(95, 165);
myPoint.log();

setTimeoutиспользовать сlog()метод в том же контексте (myPointобъект) для вызова функции стрелки. Как видите, стрелочная функция наследует контекст от определяющей ее функции.

Если вы попробуете обычную функцию в этом примере, она создаст свой собственный контекст (windowили в строгом режимеundefined). Таким образом, чтобы тот же самый код правильно использовал выражения функций, вам нужно вручную связать контекст:setTimeout(function(){…}.bind(this)). Это многословно, использование стрелочных функций — более чистое и короткое решение.

Если функция стрелки определена в самой верхней области (вне любой функции), контекстом всегда является глобальный объект (окно в браузерах):

onst getContext = () => {
   console.log(this === window); // => true
   return this;
};
console.log(getContext() === window); // => true

Стрелочные функции раз и навсегда привязаны к лексическому контексту. Даже если контекст изменен,thisЕго также нельзя изменить:

const numbers = [1, 2];
(function() {  
  const get = () => {
    console.log(this === numbers); // => true
    return this;
  };
  console.log(this === numbers); // => true
  get(); // => [1, 2]
  // Use arrow function with .apply() and .call()
  get.call([0]);  // => [1, 2]
  get.apply([0]); // => [1, 2]
  // Bind
  get.bind([0])(); // => [1, 2]
}).call(numbers);

Вызовите функцию стрелки в любом случаеgetЭто всегда сохраняет лексический контекстnumbers. Неявные вызовы с другими контекстами (черезget.call([0])илиget.apply([0])) или перепривязать (через.bind()) не будет работать.

Стрелочные функции нельзя использовать в качестве конструкторов. вызовите его как конструктор (new get()) выдаст ошибку:TypeError: get is not a constructor.

7.2 Подводный камень: определение методов со стрелочными функциями

Вы можете использовать стрелочные функции для объявления методов объекта. Определения стрелочных функций намного короче, чем функциональные выражения:(param) => {...} instead of function(param) {..}.

Давайте посмотрим на пример, используя стрелочную функцию, определенную в классе Period.format()метод:

function Period (hours, minutes) {  
  this.hours = hours;
  this.minutes = minutes;
}
Period.prototype.format = () => {
  console.log(this === window); // => true
  return this.hours + ' hours and ' + this.minutes + ' minutes';
};
const walkPeriod = new Period(2, 30);  
walkPeriod.format(); // => 'undefined hours and undefined minutes'

из-заformatявляется стрелочной функцией и определяется в глобальном контексте (самая верхняя область видимости), поэтомуthisнаправлениеwindowобъект.

хотяformatвызывается как метод объекта, такого какwalkPeriod.format(),windowЕще контекст этого звонка. Причина этого в том, что стрелочные функции имеют статический контекст, который не меняется в зависимости от того, как они вызываются.

Метод возвращает'undefined hours和undefined minutes', который не тот результат, который мы хотим.

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

function Period (hours, minutes) {  
  this.hours = hours;
  this.minutes = minutes;
}
Period.prototype.format = function() {
  console.log(this === walkPeriod); // => true
  return this.hours + ' hours and ' + this.minutes + ' minutes';
};
const walkPeriod = new Period(2, 30);  
walkPeriod.format(); // => '2 hours and 30 minutes'

walkPeriod.format()это вызов метода для объекта, контекст которогоwalkPeriodобъект.this.hoursравный2,this.minutesравный30, поэтому этот метод возвращает правильный результат:'2 hours and 30 minutes'.

оригинал:Поездка на рис avlutin.com/gentle-ex довольно…

Ошибки, которые могут существовать после развертывания кода, не могут быть известны в режиме реального времени.Чтобы решить эти ошибки впоследствии, много времени тратится на отладку журнала.Кстати, я рекомендую всем полезный инструмент мониторинга ошибок.Fundebug.

Суммировать

для пары вызовов функцийthisСамое большое влияние, отныне не спрашивайте себя:

откуда это?

но видеть

Как называется функция?

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

Где определена эта стрелочная функция, что это?

это обработкаthisКогда правильные идеи, они могут избавить вас от головной боли.

общаться с

Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.