Поглед към JavaScript-константата „this“ и как да я използвате с TypeScript
Функциите са малки блокове код, които приемат вход и връщат изход или имат странични ефекти, което означава, че if
променя променливи извън функцията. Имаме нужда от функции за организиране на кода в малки блокове, които могат да се използват многократно. Без функции, ако искаме да изпълним отново част от кода, трябва да го копираме на различни места. Функциите са критични за всяка програма на TypeScript. В тази част от поредицата продължаваме да разглеждаме различни части от функциите на TypeScript, включително как да се справяме с обекта this
с TypeScript и функциите за претоварване.
Този обект
Ако this
е посочено в обикновената функция, декларирана с ключовата дума function
, обектът this
не е зададен вътре във функция със стрелка към функцията, която има this
вътре. Ако функция със стрелка е вътре в конструктор, тогава this
се задава на новия обект. Ако не е вътре в обект, тогава this
вътре във функцията стрелка е undefined
в строг режим. this
ще бъде зададено на основния обект, ако функцията стрелка е вътре в обект. Винаги обаче получаваме window
обекта, ако се позоваваме на this
във функция със стрелка. Например следният код няма да се компилира и изпълни, тъй като TypeScript не позволява на нашия код да има глобалната променлива като стойност за this
:
const fn = () => this console.log(fn());
Получаваме обекта window
, който се регистрира, когато console.log
се изпълнява. По същия начин, ако изпълним това:
let obj = {} obj.f = () => { return this; }; console.log(obj.f());
Получаваме същото, което получавахме преди. Това е в контраст с традиционните функции, декларирани с ключовата дума function
. Нека да видим какво се случва, ако заменим функциите по-горе с традиционни функции в кода по-горе, както в следния код:
const fn = function() { return this }; console.log(fn);
Сега, ако имаме включен флаг noImplicitThis
в нашия tsconfig.json
, получаваме следната грешка от компилатора на TypeScript: 'this' implicitly has type ‘any’ because it does not have a type annotation.(2683)
Ние сме по-близо до това да накараме нашия код да работи, но той все още няма да се компилира. За да коригираме тази грешка, трябва да поставим фалшив параметър, this: void
, в сигнатурата на функцията, както в кода по-долу:
const fn = function(this: void) { return this; }; console.log(fn);
С горния код правим променливата this
неизползваема. Ако искаме да го използваме за нещо, можем да добавим явен тип за this
вместо void
, за да го накараме да направи нещо. Например, ако искаме да направим обект конструктор, можем да напишем нещо като следния код:
В кода по-горе създадохме интерфейс, наречен Person
, за да добавим тип данни към обекта this
във функцията greet
в обекта person
. Когато извикаме функцията greet
, параметърът this
се игнорира, тъй като сме го поставили като първи параметър. TypeScript разглежда само типа this
в параметъра и няма да очаква от нас да извикаме функцията greet
чрез предаване на аргумент за this
. След като дефинираме обекта person
, можем да присвоим стойности на свойствата name
и age
извън него. Вече изпълнихме изискванията, изброени в интерфейса, когато дефинирахме обекта person
, но трябва също така да му присвоим някаква стойност на значението, за да можем да използваме функцията greet
.
След това, когато изпълняваме, изпълняваме console.log
в последния ред на примера по-горе, получаваме Hi Jane. You’re 20 years old
. Успешно създадохме тип данни за this
, така че няма да има никаква неяснота относно стойността на this
. Това помага на разработчиците да разберат какво има this
в кода, тъй като това е един от най-объркващите аспекти на JavaScript. В обикновен JavaScript this
може да приема различни стойности в зависимост от това къде се намира ключовата дума this
в кода. В традиционните функции стойността на this
ще бъде самата функция. Стрелковите функции не променят стойността на this
, така че каквото и да е било отвън, е същото като каквото и да е вътре в стрелковата функция.
С TypeScript не можем да използваме this
и традиционните функции за създаване на класове. Например, ако напишем:
Тогава ще получим грешка Cannot find name ‘Person’. Did you mean ‘person’?(2552)
и кодът няма да се компилира. TypeScript не ни позволява да използваме традиционни функции като класове. За да използваме make класове, трябва да използваме синтаксиса class
.
„това“ параметри в обратните извиквания
За функциите за обратно извикване, използвани за слушатели на събития, функциите за обратно извикване, които предаваме, трябва да бъдат въведени в интерфейс. Например, за да зададем типа персонализирани компоненти за контрол на входа, можем да напишем нещо като Tthis:
interface InputElement { addKeyUpListener(onclick: (this: void, e: Event) => void): void; }
След това, когато хората, които използват контролата, могат да напишат нещо подобно, за да се изпълнява:
Какво ще стане, ако разработчиците, които използват библиотеката, се опитат да препратят към this
, както в следния код?:
В този случай компилаторът на TypeScript ще издаде грешка, тъй като this
е маркиран с типа void
в интерфейса InputElement
.
Функционални претоварвания
Претоварването на функция е създаване на функции с едно и също име, но с различни сигнатури. Това не е разрешено в JavaScript, тъй като функциите са обекти и не можем да предекларираме един и същ обект няколко пъти. Въпреки това, тъй като TypeScript е строго типизиран, което е обратното на динамичната природа на JavaScript, той трябва да намери начин да приспособи динамичните аспекти на JavaScript. За да направим това, TypeScript ни предоставя начин за претоварване на функции с различни сигнатури. За да претоварим функциите с TypeScript, просто трябва да напишем множество функционални подписи с едно и също име, преди да дефинираме действителната функция с това име. Например, можем да напишем нещо подобно:
function getPerson(person: { name: string, age: number }): { name: string, age: number }; function getPerson(person: { name: string }): { name: string }; function getPerson(person: any): any { return person; }
С кода по-горе имаме функция getPerson
, която може или да приеме обект със свойствата name
и age
, или само свойството name
. Върнатият тип, който е след двоеточието, може да бъде или обект със свойствата name
и age
, или обект само със свойството name
. Това е, което имаме в първите три реда на примера с код по-горе, където просто дефинираме подписите, които искаме за нашата getPerson
функция.
След това в действителната дефиниция на функция getPerson
имаме действителната дефиниция на функция. Ние отбелязваме типа на параметъра и връщаме като any
, което е ОК, защото вече сме дефинирали параметрите и типовете връщане, които getPerson
приема и съответно връща. Можем да извикаме getPerson
, както е в кода по-долу:
console.log(getPerson({ name: 'Jane', age: 20 })); console.log(getPerson({ name: 'Jane' }));
От console.log
твърденията по-горе получаваме:
{name: "Jane", age: 20} {name: "Jane"}
Ако се опитаме да го извикаме с аргумент, който не сме дефинирали в подписа като: console.log(getPerson({}))
, получаваме грешка No overload matches this call
.
Основният начин за работа с обекта this
в традиционните функции е да го предадете като първи параметър на функция и след това да му зададете тип, като дефинирате интерфейс за типа. TypeScript ще игнорира параметъра this
и ще го третира така, сякаш не съществува. Използва се само за задаване на типа данни this
. Можем да претоварваме функции в TypeScript, което не е разрешено в JavaScript. За да направим това, просто дефинираме различни подписи за функцията и им даваме едно и също име, след което дефинираме функцията със същото име, след като сме дефинирали подписите, които нашата функция ще приеме.