简体   繁体   中英

What’s the point of exporting something from a JavaScript file when it is not “ready” yet?

Here's an example: https://middy.js.org/docs/intro/getting-started


import middy from '@middy/core'
import middleware1 from 'sample-middleware1'
import middleware2 from 'sample-middleware2'
import middleware3 from 'sample-middleware3'

const lambdaHandler = (event, context) => {
  /* your business logic */
}

export const handler = middy(lambdaHandler)

handler
  .use(middleware1())
  .use(middleware2())
  .use(middleware3())

Why export handler first, and then further configure it in the same file in which it's been defined in?

  1. Is there ever a good reason to use this pattern?
  2. Also does Node normalize the exports, by which I mean ignoring where export statements are located and when someone imports a package, Node somehow ensures all the exports occur after everything else?

Oddly enough they use different patterns in different examples. Here's another one:


// import core
import middy from '@middy/core' // esm Node v14+
//const middy = require('@middy/core') // commonjs Node v12+

// import some middlewares
import jsonBodyParser from '@middy/http-json-body-parser'
import httpErrorHandler from '@middy/http-error-handler'
import validator from '@middy/validator'

// This is your common handler, in no way different than what you are used to doing every day in AWS Lambda
const lambdaHandler = async (event, context) => {
 // we don't need to deserialize the body ourself as a middleware will be used to do that
 const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body

 // do stuff with this data
 // ...

 const response = { result: 'success', message: 'payment processed correctly'}
 return {statusCode: 200, body: JSON.stringify(response)}
}

// Notice that in the handler you only added base business logic (no deserialization,
// validation or error handler), we will add the rest with middlewares

const eventSchema = {
 type: 'object',
 properties: {
   body: {
     type: 'object',
     properties: {
       creditCardNumber: { type: 'string', minLength: 12, maxLength: 19, pattern: '\\d+' },
       expiryMonth: { type: 'integer', minimum: 1, maximum: 12 },
       expiryYear: { type: 'integer', minimum: 2017, maximum: 2027 },
       cvc: { type: 'string', minLength: 3, maxLength: 4, pattern: '\\d+' },
       nameOnCard: { type: 'string' },
       amount: { type: 'number' }
     },
     required: ['creditCardNumber'] // Insert here all required event properties
   }
 }
}

// Let's "middyfy" our handler, then we will be able to attach middlewares to it
const handler = middy()
  .use(jsonBodyParser()) // parses the request body when it's a JSON and converts it to an object
  .use(validator({eventSchema})) // validates the input
  .use(httpErrorHandler()) // handles common http errors and returns proper responses
  .handler(lambdaHandler)

Wherever the export statement appears for an identifier in a given file has no effect at all on anything, including on how/when other modules interact with it. There is no difference between

export const handler = middy(lambdaHandler)

handler
  .use(middleware1())
  .use(middleware2())
  .use(middleware3())

and

const handler = middy(lambdaHandler)

handler
  .use(middleware1())
  .use(middleware2())
  .use(middleware3())

export { handler };

except that the second takes a but more code (and so some prefer the first approach).

It's a bit similar to var . No matter where var <someVarName> appears in a given block, all the engine cares about is that someVarName was declared in the block, not where the line with var is. Similarly, with exports, all the engine cares about is whether something was exported, not where the export keyword happens to be.

Node somehow ensures all the exports occur after everything else?

Yes, mostly. After importing, a module script will always run all of its (top-level) code to the end before any other module starts running its (top-level) code. If there is

console.log('1');
// 5000 lines of code
export const foo = 'foo';
// 500 lines of code
console.log('2');

Any other modules that use foo will only do so after both 1 and 2 have been logged.

(The only exception to ensuring that everything has been exported properly before other modules use them is circular dependencies, which mess things up - with circular dependencies, if a module imports another, and that other module imports the first, one of those won't be initialized when the other starts running its top-level code - but the location of the export keyword still has no impact)

The export declaration is used when creating JavaScript modules to export live bindings to functions, objects, or primitive values. MDN:export

Live bindings is a concept introduced in ES modules. It means that when the exporting module changes a value, the change will be visible from the importer side. SO

The way I think of it, is like making a reference to an object, then editing the original object. Any changes will be reflected when calling the reference:

const a = {foo:'foo'}

const c = a; // Imagine this is the export and import in another file

a.foo = 'bar';

console.log(c.foo) // 'bar' - the change is reflected in the import

The thing here is that export is not like return which ends execution. The code after the export still runs. JSBin example.

The exported object can be considered global wherever imported, so if our importing script mutates the objects further, then these changes will also be reflected in the global object.

It could be useful to think of it as a singleton .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM