Експорт ES6 за замовчуванням із кількома функціями, що посилаються одна на одну


77

в es6 там ви можете визначити модуль таких функцій

export default {
    foo() { console.log('foo') }, 
    bar() { console.log('bar') },
    baz() { foo(); bar() }
}

вище, здається, є дійсним кодом, але якщо я зателефоную, baz()це видасть помилку:

ReferenceError: foo is not defined

Як телефонувати fooз іншої функції? в цьому випадкуbaz

Редагувати

Ось код, який насправді не працює. Я спростив код, тому це лише ядро, якщо потрібно

const tokenManager =  {
  revokeToken(headers) { 
    ... 
  },
  expireToken(headers) {
    ...
  },
  verifyToken(req, res, next) {
    jwt.verify(... => {
      if (err) {
        expireToken(req.headers)
      }
    })
  }
}

export default tokenManager 

і помилка в тому

expireToken(req.headers);
        ^
ReferenceError: expireToken is not defined

Редагувати 2

Я просто намагався додати tokenManagerраніше, expireTokenі це нарешті працює


1
Дивіться відповідь мого або @ pawel. Щоб виправити, замініть expireToken(req.headers)на tokenManager.expireToken(req.headers)або з this.expireToken(req.headers).
skozin

Відповіді:


155

export default {...}Конструкція просто ярлик для чого - щось на зразок цього:

const funcs = {
    foo() { console.log('foo') }, 
    bar() { console.log('bar') },
    baz() { foo(); bar() }
}

export default funcs

Це не повинно стати зараз очевидно , що немає foo, barабо bazфункції в рамках модуля. Але є об'єкт з іменем funcs(хоча насправді він не має імені), який містить ці функції як свої властивості і який стане експортом модуля за замовчуванням.

Отже, щоб виправити свій код, перепишіть його, не використовуючи ярлик, і звертайтесь до властивостей fooі barяк funcs:

const funcs = {
    foo() { console.log('foo') }, 
    bar() { console.log('bar') },
    baz() { funcs.foo(); funcs.bar() } // here is the fix
}

export default funcs

Іншим варіантом є використання thisключового слова для посилання на funcsоб'єкт без необхідності явного оголошення, як зазначив @pawel .

Ще один варіант (і той, який я взагалі віддаю перевагу) - це оголошення цих функцій в області модуля. Це дозволяє звернутися до них безпосередньо:

function foo() { console.log('foo') }
function bar() { console.log('bar') }
function baz() { foo(); bar() }

export default {foo, bar, baz}

І якщо вам потрібна зручність експорту за замовчуванням та можливість імпортувати елементи окремо, ви також можете експортувати всі функції окремо:

// util.js

export function foo() { console.log('foo') }
export function bar() { console.log('bar') }
export function baz() { foo(); bar() }

export default {foo, bar, baz}

// a.js, using default export

import util from './util'
util.foo()

// b.js, using named exports

import {bar} from './util'
bar()

Або, як запропонував @loganfsmyth, ви можете обійтися без експорту за замовчуванням і просто використовувати, import * as util from './util'щоб отримати всі іменовані експорти в одному об’єкті.


Я спробував це, але не зміг змусити це працювати. Я відредагував питання за допомогою реального коду. Ви бачите, що тут пішло не так?
chrs

1
@chrs, див. мій коментар до вашого запитання. Вам потрібно замінити expireTokenна tokenManager.expireToken.
skozin

@chrs Я був швидший на хвилину;) Але я не проти, мені подобається остання пропозиція тут ( export default { foo, bar, baz }) і, мабуть, я б сам її використав.
Павел

" якщо вам потрібна зручність експорту за замовчуванням та можливість імпортувати елементи окремо " - тоді вам не слід повторно експортувати літерал об'єкта за замовчуванням, а просто писати import * as util from './util'. Саме для цього було створено імпорт простору імен.
Бергі,

26

Однією з альтернатив є зміна модуля. Як правило, якщо ви експортуєте об'єкт із набором функцій, простіше експортувати купу названих функцій, наприклад

export function foo() { console.log('foo') }, 
export function bar() { console.log('bar') },
export function baz() { foo(); bar() }

У цьому випадку ви експортуєте всі функції з іменами, щоб ви могли це зробити

import * as fns from './foo';

щоб отримати об’єкт із властивостями для кожної функції замість імпорту, який ви використовували б у своєму першому прикладі:

import fns from './foo';

3
Це остаточно найкраще рішення. Використовуйте іменовані експорти замість об’єктів за замовчуванням.
Бергі,

12

tl; dr: baz() { this.foo(); this.bar() }

У ES2015 ця конструкція:

var obj = {
    foo() { console.log('foo') }
}

дорівнює цьому коду ES5:

var obj = {
    foo : function foo() { console.log('foo') }
}

exports.default = {} це як створення об’єкта, ваш експорт за замовчуванням перекладається в код ES5 таким чином:

exports['default'] = {
    foo: function foo() {
        console.log('foo');
    },
    bar: function bar() {
        console.log('bar');
    },
    baz: function baz() {
        foo();bar();
    }
};

тепер це очевидно (я сподіваюся), що bazнамагається викликати fooта barвизначено десь у зовнішній області, які не визначені. Але this.fooі this.barбуде вирішувати ключі, визначені в exports['default']об'єкті. Тож за замовчуванням експорт, що посилається на власні методи shold, виглядає так:

export default {
    foo() { console.log('foo') }, 
    bar() { console.log('bar') },
    baz() { this.foo(); this.bar() }
}

Див. Перекладений код babel repl .


Коли я роблю це, як у вашому останньому прикладі, я все ще отримую TypeError: Cannot read property 'foo' of undefined- чого мені не вистачає?
leonheess
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.