IIFE Parentheses #21

Closed
jcutrell opened this Issue Nov 8, 2012 · 8 comments

Projects

None yet

8 participants

@jcutrell
jcutrell commented Nov 8, 2012

Could this:

(function() {
  console.log('Welcome to the Internet. Please follow me.');
})();

Be this? :

// Crockford's preference - parens on the inside
(function() {
  console.log('Welcome to the Internet. Please follow me.');
}());
@reissbaker
Contributor

For modules we've decided on this:

!function() {
  console.log('Welcome to the Internet. Please follow me.');
}();

...But I have no particular preference on where parens should go for IIFEs that aren't also modules. Usually they're not very useful -- if it needs to be hidden, make it its own module -- but in the cases where they are it doesn't really matter to me. Both seem equally readable.

Is there enough of a difference to make this worth codifying?

@hshoff
Member
hshoff commented Nov 8, 2012

If you don't care about the return value of the IIFE, it could also be any of the following:

!function(){}();  // => true
~function(){}(); // => -1
+function(){}(); // => NaN
-function(){}();  // => NaN

Let's explore this a bit more.

// module1.js
(function() {
  console.log('module1');
})();

// module2.js
(function() {
  console.log('module2');
})();
// production.min.js
(function(){console.log('module1');})();(function(){console.log('module2');})();
// => module1
// => module2

// crockford production.min.js
(function(){console.log('module1');}());(function(){console.log('module2');}());
// => module1
// => module2

Both work the same. It starts to get interesting when one of the modules is missing a trailing semicolon:

// problem
(function(){console.log('module1');})()(function(){console.log('module2');})();
// => module1
// => TypeError: undefined is not a function

// crockford problem
(function(){console.log('module1');}())(function(){console.log('module2');}());
// => module1
// => module2
// => TypeError: undefined is not a function

With a missing semicolon, each set of parens is trying to immediately-invoke the preceding expression. That would be the return value of the preceding IIFE.

(function(){ return undefined; })()();
// => TypeError: undefined is not a function

// equivalent
(undefined)();

(function(){ return undefined; }())(); // crockford
// => TypeError: undefined is not a function

// equivalent
(undefined());

So the difference is when the TypeError happens. Let's check out what the arguments are up to. Note that console.log() returns undefined:

// new module1.js
(function() {
  console.log('module1');

  // return a function that logs it's
  // arguments when invoked
  return function(){
    console.log(arguments);
  };
})()
// new production.js
// unminified problem for readability
(function() {
  console.log('module1');

  // return a function that logs it's
  // arguments when invoked
  return function(){
    console.log(arguments);
  };
})()(function() {
  console.log('module2');
})();
// => module1
// => [function() {
//      console.log('module2');
//    }]
// => TypeError: undefined is not a function

// the first step is to invoke the first module
(function(){console.log('module1');return function(){console.log(arguments)};})()
// => module1

// then it invokes the return value
// with module2 as arguments
(function(){ console.log(arguments) })(function() {console.log('module2');});
// => [function() {
//      console.log('module2');
//    }]

// then it tries to invoke the return value.
// console.log returns undefined so:
(undefined)();
// => TypeError: undefined is not a function

Now let's do that same example with the crockford way:

// new crockford module1.js
(function(){
  console.log('module1');

  // return a function that logs it's
  // arguments when invoked
  return function(){
    console.log(arguments);
  };
}())
// new production.js
// unminified problem for readability
(function(){
  console.log('module1');

  // return a function that logs it's
  // arguments when invoked
  return function(){
    console.log(arguments);
  };
}())(function(){
  console.log('module2');
}());
// => module1
// => module2
// => [undefined]

But wait, there's no TypeError here...

// it invokes module1
(function(){ console.log('module1'); return function(){ console.log(arguments); };}())
// => module1

//it invokes module2
(function(){ console.log('module2'); }());
// => module2

// equivalent
(function(){ console.log(arguments); })(undefined)
// => [undefined]

There's no TypeError because of the returned function. The returned function that logs the arguments is then getting invoked with the return value of module2, which is undefined.
With that understanding, let's go back to the original example, where there was a TypeError:

// crockford problem
(function(){console.log('module1');}())(function(){console.log('module2');}());
// => module1
// => module2
// => TypeError: undefined is not a function

// it invokes module1
(function(){ console.log('module1'); }())
// => module1

//it invokes module2
(function(){ console.log('module2'); }());
// => module2

// equivalent
(undefined(undefined))
// => TypeError: undefined is not a function

Conclusion
The (function{})(); and (function(){}()); IIFEs can act differently in the missing semicolon situation.

Use linter or a tool to make sure modules aren't missing trailing semicolons when working on modules.

To be extra safe add a leading semicolon to the IIFE:

// module1.js
;(function() {
  console.log('module1');
})()

// crockford module1.js
;(function(){
  console.log('module1');
}())

// module2.js
;(function() {
  console.log('module2');
})();

// crockford module2.js
;(function() {
  console.log('module2');
}());

// production.min.js
;(function(){ console.log('module1'); })();(function(){ console.log('module2'); })();
// => module1
// => module2

// crockford production.min.js
;(function(){ console.log('module1'); }());(function(){ console.log('module2'); }());
// => module1
// => module2

Hope that helps!

@jcutrell
jcutrell commented Nov 8, 2012

A fantastically thorough explanation.

@hshoff hshoff closed this Nov 15, 2012
@egonyuri

Incredible explanation.

@mikejoyceio

Great explanation. Thanks.

@jaan
jaan commented Mar 22, 2015

Good Explanation @hshoff

@freeethy
freeethy commented May 6, 2015

Great explanation @hshoff . Thanks.

@ljharb ljharb added a commit that referenced this issue Jan 15, 2016
@ljharb ljharb [eslint config] [breaking] require outer IIFE wrapping; flesh out gui…
…de section.

There was lots of discussion [here](#21 (comment)), but now that we have both a modern build system and an eslint rule requiring terminating semicolons, the concerns with the “crockford” style no longer apply.
4ef335e
@gilbox gilbox added a commit to gilbox/javascript that referenced this issue Mar 21, 2016
@ljharb @gilbox ljharb + gilbox [eslint config] [breaking] require outer IIFE wrapping; flesh out gui…
…de section.

There was lots of discussion [here](airbnb#21 (comment)), but now that we have both a modern build system and an eslint rule requiring terminating semicolons, the concerns with the “crockford” style no longer apply.
8a8bb01
@imlucas imlucas added a commit to mongodb-js/javascript that referenced this issue Apr 6, 2016
@imlucas imlucas Pull from upstream
* Require a space between `function` and `(`, and `function` and the function's name, and disallow spaces between the function's name and `(`.

* I abhor switch statements, but their indentation should be:

```js
switch (foo) {
  case 'bar':
    break;
}
```

not what the plugin currently requires:
```js
switch (foo) {
case: 'bar':
  break;
}
```

* [eslint config] v2.0.0

 - separate changelog

* Fix example for section 7.11

The missing newline caused the markdown parser to misinterpret the beginning and end of the code block.

* Documents corresponding eslint rules

* Fix syntax highlighting in section 15

* Require space before/after arrow function's arrow (arrow-spacing)

Enable [arrow-spacing](http://eslint.org/docs/rules/arrow-spacing.html) rule, code with space before/after arrow function's arrow is easier to read.
```js
() => {};
(a) => {};
a => a;
() => {'\n'};
()=> {};     /*error Missing space before =>*/
() =>{};     /*error Missing space after =>*/
(a)=> {};    /*error Missing space before =>*/
(a) =>{};    /*error Missing space after =>*/
a =>a;       /*error Missing space after =>*/
a=> a;       /*error Missing space before =>*/
()=> {'\n'}; /*error Missing space before =>*/
() =>{'\n'}; /*error Missing space after =>*/
```

* [react] Including missing defaults to the react eslint

* Adding links to lint rules in react styleguide.

* Adding links to react rules for quick reference.

* Update README.md

Add OutBoxSoft to the list of organizations

* [eslint config] enable `object-shorthand` rule.

Fixes #621.

* [eslint config] [Dev Deps] update `eslint`

* [eslint config] fix b150039

* [eslint config] v2.1.0

* Remove deprecated react/jsx-quotes

* [eslint config] add rule link and defaults to `jsx-quotes` rule

* v2.1.1

* update best-practices config to prevent parameter object manipulation
added good/bad examples of parameter mutation to the readme

* update examples for style
add link to no-param-reassign rule documentation

* added newlines after each function

* [eslint config] [Dev Deps] update `eslint-plugin-react`

* [eslint config] [minor] enable react/prefer-es6-class rule

* [eslint config] [breaking] enable `quote-props` rule.

* [eslint config] [minor] Sort react/prefer-es6-class alphabetically

All of the other rules in this file are in alphabetical order, but this
one was added by c98990c out of order at the end. Keeping these in
alphabetical order will help developers find the rules that they are
looking for.

* [eslint-config] [minor] Enable react/no-is-mounted rule

isMounted is an anti-pattern [0], is not available when using ES6
classes, and is on its way to being officially deprecated.
eslint-plugin-react recently added the react/no-is-mounted rule in
3.12.0 that prevents its use.

[0]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

Finishes #633

* [readme] Clean up isMounted section in react readme

When I added this section in f2dc504 I didn't do a very good job of
following the style used in other parts of this repo. This commit
remedies that.

* Permit strict mode

Permit strict mode for legacy configuration as it is not intended to be used with Babel.

* [readme] Add backticks around code items in react readme

I noticed a number of places in this document where code was being
referenced but it was not marked as such. Adding backticks will instruct
the markdown parser to format these bits as code, which should improve
the readability of this document.

* [readme] Add missing heading to react TOC

I noticed that this heading wasn't listed in the table of contents.
Adding it will help people see at-a-glance what they might find in this
document.

* [readme] Use consistent whitespace in react readme

I noticed that some newlines and indentation were a little inconsistent
in this file, so I decided to smooth things out a bit. This should help
people who decide to modify this document in the future. When
determining which is the "right" way to format these things, I decided
to use the style used by base readme as a guide.

* Rename jshintrc to .jshintrc

According to http://jshint.com/docs/, the "special file" to configure JSHint is named `.jshintrc`, not `jshintrc`.

* Cleaning up the react styleguide. Adding additional info in props.

* Removing file extensions. Fixing typos.

* Removing name prefix rules.

* add missing rules from eslint-plugin-react

* add default config to all rules which take a config

* Preserve strict rule for default export

* Add section 18.12 on line length

* Update .jshintrc with new line length

* Enable ESLint rule on limiting maxiumum length of line (max-len)

I moved the rule from legacy.js to style.js and enabled it

* Reword section 6.2 to recognize the line length

* Address comments in #639

[eslint] Add comment above `max-len` rule with link to its docs
[eslint] Change tab width for `max-len` rule from 4 to 2
[eslint] Replace double quotes around `max-len` with single quotes
[eslint] Use object form of `max-len` and include all of the options

* Make reassigning a separate rule.

* Fix indentation and spacing.

* Add missing back tick

* Add ESLint link for "Never mutate parameters." rule.

* Fix max-len rule definition

The current definition is valid only in eslint@2.x.x, but this package has a dependency on eslint@1.10.3 which doesn't have all those options yet.

* Link newer ESLint rule (namely comma-dangle)

The "no-comma-dangle" rule was deprecated in favor or "comma-dangle" in
ESLint v1.0.

See http://eslint.org/docs/rules/no-comma-dangle.html,
http://eslint.org/docs/rules/comma-dangle, and line 4 of
packages/eslint-config-airbnb/rules/errors.js.

* missing 's', create(s) under Method

* Bring back note about braces

* Make 8.2 and 8.4 simpler and more explicit

* Clarify 8.4

* Fix typo

* Require parentheses

As suggested by @ljharb in airbnb#579 (comment)

* v3.0.0

* Fix a bug introduced in f2afce7 / #581

* clarity in destructuring code example

readme

* Fixed typo in URL

* [eslint config] [fix] because we use babel, keywords should not be quoted.

* v3.0.1

* Link to the eslint rule for radix in parseInt

* Ignore URLs in max-len

* v3.0.2

* Allow multiple stateless components in a single file

* [guide] clarify function spacing in 18.3

Per #668.

* v3.1.0

* [eslint config] [breaking] require outer IIFE wrapping; flesh out guide section.

There was lots of discussion [here](airbnb#21 (comment)), but now that we have both a modern build system and an eslint rule requiring terminating semicolons, the concerns with the “crockford” style no longer apply.

* 7.3: Add Link to eslint rule.

* Add `prefer-arrow-callback` to ES6 Rules.

This prefers arrow functions as described in https://github.com/airbnb/javascript#8.1

* 22.3: Add Link to eslint rule.

* assign variables: give a reason for the assignment

The variable assignment wasn't necessary. This gives a reason to have it there

* Clearly show that [] is truthy

* Fix case type on line 3 comment

Update case type to resemble other comments

* Add missing ES6 rules to ESLint config.

`arrow-body-style` (8.2) and `prefer-template` (6.4) are described in
the Style Guide.

* [Tests] run `npm run lint` as part of tests; fix errors.

hat tip to airbnb#678 (comment)

* Update license year range to 2016

* [Tests] use `parallelshell` to parallelize npm run-scripts

* Document corresponding jscs rules

* remove specific rule values in text

* fix typo

* [docs] remove trailing whitespace

* arrow-parens

* [guide] fix a few IIFE examples

* Favour stateless functions over classes when there's no state

* Remove space from jsx literal and add space to function definition

According to PR comments.

* Fix PR comments

- Space between function name and args;
- use `render(){}` syntax in object literal;
- Fix link to section;

* add avant to users

* Fixed minor capitalization change in Hoisting

Not sure if it's an actual fix, but just noticed this was the only comment that had capitalization that wasn't a proper noun

* add WeBox Studio to the organizations list

* v4.0.0

* Fixed capitalization

Bitshift changed to bitshift

* [rule links] reduce visual clutter

* [react][rule links] condense lines

* [react][rule links] add eslint labels back

* [rule links] add labels back

* Remove language interpretable as an ableist slur

* [guide] [react] add a note preferring normal functions for functional stateless components.

* Fixed capitalization

Fixed capitalization of ES6 to match the rest of the README.

* Added names to invalid function declarations

* Edit sample code in 24.1 to follow rule 8.1

Replaced function expression with arrow function notation.

* Fixed capitalization and comment spacing

Fixed line 1219 to have a lowercase 'a' to be consistent with the style guide.
Fixed line 1831 to only have one space between the // and the =>

* Minor typo

Remove extra dot

* add section 15.5 - Ternaries

add link to the relevant eslint rule

* [guide] remove trailing whitespace

* Avoid lexical declarations in case/default clauses

This commit adds guidance warning people to avoid lexical declarations
in case/default clauses and enables the corresponding ESLint rule.

  http://eslint.org/docs/rules/no-case-declarations.html

We didn't have a section on switch statements yet, so I thought about
starting one. It seemed like it would best fit between section 15
(Comparison Operators & Equality) and section 16 (Blocks), but I didn't
want to mess up all of the following numberings, since people probably
have references to them. I considered adding this near the end to
minimize this effect, but it really seemed to belong near these other
things. I landed on appending it to Section 15 (Comparison Operators &
Equality) and I think it sorta fits there since switch statements are a
related concept.

* Add example of a case clause that does not need a block

As @kesne and @ljharb pointed out, this section was unclear about
whether or not you should always include blocks for all case clauses. To
make things clearer, I am adding a case clause that does not need a
block to the good example.

We decided that we should only require blocks for case clauses that
actually need them because it matches the as-needed spirit of section
3.8 ("Only quote properties that are invalid identifiers"). Perhaps if
there was an as-needed but consistent setting for the ESLint rule, we
would consider revising this a little, but this seems good enough for
now.

* [eslint config] [breaking] disallow unneeded ternary expressions.

* [eslint config] [dev deps] update `eslint-plugin-react`, `react`, `tape`

* [eslint config] [fix] `s/no-case-declaration/no-case-declarations/g` (from #712)

* [eslint config] [fix] fix `npm run lint`

* v5.0.0

* Added parenthesis to 'good' example in 8.2

* Sort static methods above constructor for React

I think it makes more sense to put static methods above the constructor
in classes. I would like to update the ESLint configuration to match
this, but it looks like the react/sort-comp rule does not support it
quite yet.

  yannickcr/eslint-plugin-react#128

* [Deps] update `eslint` to v2, `eslint-plugin-react` to v4

* [eslint-v2] no-empty-label => no-labels

* [eslint-v2] space-after/before/return/throw/case => keyword-spacing

* [eslint-v2] fix no-labels rule

* [eslint-v2] set es6 env to true

* [eslint-v2] ecmaFeatures => parserOptions

* [eslint-v2] fix keyword-spacing style and sorting

* [pkg] add harry to contributors

* [eslint-v2] no gens, no dup props in obj literal

* [Fix] `eslint` peerDep should not include breaking changes.

* v5.0.1

* [eslint-v2] fix no-labels rule

* [eslint-v2][constructors] disallow unnecessary constructors

* [eslint-v2][constructors] fix bad + good examples

* [eslint-v2] add new rules, disabled:

 - `id-blacklist`
 - `no-extra-label`
 - `no-implicit-globals`
 - `no-restricted-imports`
 - `no-unmodified-loop-condition`
 - `no-unused-labels`
 - `sort-imports`
 - `yield-star-spacing`

* [eslint-v2][arrays] add array-callback-return rule

* [eslint-v2][whitespace] add newline-per-chained-call rule

* [eslint-v2] enforce yield-start-spacing, no-unused-labels, no-extra-label

* [eslint-v2][arrow functions] add no-confusing-arrow rule

* [eslint-v2][arrow functions] improve examples

* [eslint-v2] add no-new-symbol rule

* [eslint-v2][variables] add no-self-assign

* [eslint-v2][whitespace] add no-whitespace-before-property rule

* [eslint-v2] add one-var-declaration-per-line

* [eslint-v2] add prefer-rest-params

* [eslint-v2] fix empty constructor example

* [eslint-v2][template strings] add template-curly-spacing rule, fix #716

* [eslint-v2] fix rule links in readme

* [eslint-v2][react] acceptTranspilerName => ignoreTranspilerName

* [eslint-v2][react] add static-methods to top of sort-comp order

* [eslint-v2][react] set ignoreTranspilerName to false

* [eslint-v2][react] jsx-sort-prop-types => sort-prop-types

* [changelog] update changelog for 6.0.0

* rearrange comma-dangle rule to match es5/es6 codestyles, fixes #741

* [eslint-v2][arrays] update array-callback-return good/bad ex

* [eslint-v2][arrays] fix example

* [eslint config] v6.0.0

* Fix no-useless-constructor example.

Per airbnb#730 (comment)

* Disable `newline-per-chained-call` until `eslint`’s bug is resolved.

bug: eslint/eslint#5289

Closes #748.

* v6.0.1

* Small typo fix in 18.6 'bad'-example.

Function call 'class()' to 'classed()'.

* [Dev Deps] update `babel-tape-runner`.

Closes wavded/babel-tape-runner#11

* Disable `no-confusing-arrow` until eslint’s bug is resolved.

bug: eslint/eslint#5332

Closes #752.

* [eslint config] v6.0.2

* Fixed README typo

* Added getter/setter info

I tried to search for getter/setter in the readme but nothing was found. Added some info to make a clear statement about them

* Updated CHANGELOG.md

* Updated "In The Wild" to include Brainshark.

* Enforce literal syntax for array creation

* Fix word spacing and preserve consistent quotes

* fixes tiny typo's on JSCS rules

* change wording for 8.2 for consistency

* Fix the typo error in JSHint link

* Update README.md

Add colon to TODO and FIXME.

* Enable react/prefer-stateless-function rule

* fixed a broken link to REI's JS style guide

* [Dev Deps] update `eslint`, `tape`, `eslint-plugin-react`

* [Dev Deps] update `eslint-plugin-react`

(new rule is in #772)

* [eslint config] v6.1.0

* Update README: Document corresponding jscs rules

* Vietnamese language

* Add defaults for `react/jsx-no-bind`

* [Dev Deps] update `tape`

* [guide] add some more justification for one-var and `String()` type coercions

* [guide] Permanent links.

 - Preserve the original links, because cool URLs don’t change.

* Update README.md

Add parentheses around argument, to be consistent with section 8.4
(include parentheses when using braces).

* Add ascribe's styleguide to the list

* [eslint config] [dev deps] update `eslint`, `eslint-plugin-react`

* fix react/prefer-stateless-function link

* [editorial] clean up some constructor examples

Fixes #792

* [Fix] re-enable `no-confusing-arrow` rule, with `allowParens` option enabled.

Per #752, fixes #791.

* [peer deps] update `eslint`, `eslint-plugin-react`

* Fix "object destructuring for multiple return values" example

... to use the same destructured properties in the good and bad code.

* Allow arrow functions in JSX props

* Add SysGarage to list of organizations

* [eslint config] v6.2.0

* [eslint config] [dev deps] update `eslint`, `eslint-plugin-react`

* [eslint config] [breaking] add `no-duplicate-imports` rule.

* [eslint config] [breaking] add `no-useless-escape` rule.

* Add missing description for 7.2

* Add Chartboost to 'In the Wild'

* [eslint config] [breaking] error on debugger statements

* [eslint config] [deps] update `eslint`, `react`

* [eslint config] [breaking] Add `no-dupe-class-members` rule + section.

Closes #785.

* CHORE - Remove Trailing Spaces

* Broken link to Classes and constructors

* Add KickorStick to 'In the Wild'

* Adding The Nerdery to the "In the Wild" section
adcde14
@xiehongyang

Cool! help me a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment