LINUX.ORG.RU

Стрелочные функции - undefined this

 


0

1

В веб-приложении на React есть примерно такой код.

const myObj = {
    foo: () => {
        console.log(this)
    }
}

class MyForm extends React.Component {
    
    constructor(props) {
        super(props);
        this.state = {
            ...
        }
        this.handleSubmit = this.handleSubmit.bind(this)
    }

    async handleSubmit(event) {
        ...
        console.log(this) // объект класса MyForm
        myObj.foo() // undefined, а ожидаю получить тот же самый объект
        ...
    }
}

Почему так? Как правильно в таких случаях делать?

Если посмотреть в отладчике браузера скомпилированный JS, то получится

const myObj = {
  foo: () => {
    console.log(undefined);
  }
};
★★★

М-м-м не уверен, что правильно всё понял. У стрелочных функций нет своего this. Или пиши обычную функцию, или через bind делай. Ну а вообще, почему у тебя классовый компонент, давно уже как юзают функциональные.

CryNet ★★★★★
()

Стрелочная функция берёт this области, где она объявлена. Поскольку твой const myObj объявлен в глобальной области, его this равен undefined.

Ты можешь заменить стрелочную функцию на обычную.

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

KivApple ★★★★★
()
Ответ на: комментарий от KivApple

Стрелочная функция берёт this области, где она объявлена.

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

In arrow functions, this retains the value of the enclosing lexical context's this.

Ты можешь заменить стрелочную функцию на обычную.

У обычной this будет смотреть на myObj.

damix9 ★★★
() автор топика
Ответ на: комментарий от damix9

Потому что у него есть state

У функционального компонента тоже есть.

И такие и такие юзают

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

CryNet ★★★★★
()
Последнее исправление: CryNet (всего исправлений: 2)

KivApple, CryNet, javascript, а как принято в таких случаях делать?

const myObj = {
    foo() {
        console.log(this)
    }
}
...
myObj.foo.call(this)

или просто

const myObj = {
    foo: (form) => {
        console.log(form)
    }
}
myObj.foo(this)

Надо попросить другой объект что-то сделать с экземпляром класса MyForm.

damix9 ★★★
() автор топика
Ответ на: комментарий от damix9

В приведенных тобой кейсах вообще никак не отражена необходимость в существовании твоего myObj. Все твои примеры упираются в то, что тебе нужна просто функция.

javascript
()
Ответ на: комментарий от damix9

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

По сабжу – не понятно, что именно тебе нужно сделать. В любом случае код выглядит крайне странно, и не важно, сделаешь ты .bind() или передашь элемент формы (не дай бог ещё инстанцию класса), все равно ты сходу что-то делаешь не так.

Princesska ★★★★
()
Ответ на: комментарий от damix9

const myObj = { foo: (form) => { console.log(form) } } myObj.foo(this)

Негры ждут!!

Oldboy
()
Ответ на: комментарий от Princesska

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

CryNet:

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

Прочитал. ИМХО это как-то хуже читается, чем классы.

все равно ты сходу что-то делаешь не так.

Конечно не так - генерирую весь HTML скриптом в браузере.

javascript:

В приведенных тобой кейсах вообще никак не отражена необходимость в существовании твоего myObj. Все твои примеры упираются в то, что тебе нужна просто функция.

Ну да. Сделал так.

В MyForm

    async handleSubmit(event) {
        ...
        console.log(this) // объект класса MyForm
        this.props.onSubmit(this.state); // тот же объект
        ...
    }
В другом компоненте
    foo = (formState) => {
        ...
        console.log(this.formRef)
        ...
    }

    render() {
        return (
            <MyForm ref={(f) => { this.formRef = f }} onSubmit={this.foo} />
        )
    }

damix9 ★★★
() автор топика
Ответ на: комментарий от damix9

Прочитал. ИМХО это как-то хуже читается, чем классы

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

CryNet ★★★★★
()

Захватывай context this.

const that=this
function inner() {
  // do smth with thi..that
}

Либо передавай его через bind:

const fn1 = this.methodName.bind(this)
// в твоем случае
const fn2 = myObj.foo.bind(this)

Но то что ты придумал - быдлокод. Та функция должна быть методом класса

tz4678_2
()
Ответ на: комментарий от tz4678_2

Но то что ты придумал - быдлокод.

То что в первом сообщении или это тоже?

Та функция должна быть методом класса

Так она и есть.

Либо передавай его через bind

KivApple и CryNet пишут, что так не будет работать. И действительно

const myObj = {
    foo: () => {
        console.log(this)
    }
}

class MyForm {
    bar() {
        console.log(this); // объект класса Myform
        const fn2 = myObj.foo.bind(this);
        fn2() // глобальный объект
    }
} 

damix9 ★★★
() автор топика
Ответ на: комментарий от damix9

Может дело в тебе? У меня вот все работает:

o = { foo() { console.log(this) } }
{foo: ƒ}
o.foo()
VM233:1 {foo: ƒ}
undefined
class Foo { foo() { o.foo.bind(this)() } }
undefined
new Foo().foo()
VM233:1 Foo {}
tz4678_2
()
Ответ на: комментарий от tz4678_2

То так или иначе, то что ты делаешь - лютейший быдлокод! Если хочешь как джавобои завести хелперы, то нужно класс объявлять со статическими методами:

class Utils { static title(s) { return s.split(' ').map(a => a.length ? a[0].toUpperCase() + a.slice(1) : a).join(' ') } }
undefined
Utils.title('arch linux')
'Arch Linux'
tz4678_2
()
Ответ на: комментарий от damix9

делай правильно как я советую, а не как какие-то быдлокодеры!

стрелочные функции захватывают контекст this!

conts GovnoObject = {
  // такую дрисню никогда не пиши, тут всегда будет this, который объявляен в этом скопе
  foo: () => {}
  // надо всегда так
  foo() {}
}

Захват контекста используют иначе:

methodName() {
  setTimeout(() => {
    // вот тут this будет всегда экземпляром класса
  }, 1000)
}
tz4678_2
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.