-
-
Notifications
You must be signed in to change notification settings - Fork 637
Improve concept exercise: Null and Undefined #1170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
SleeplessByte
merged 22 commits into
exercism:main
from
junedev:improve-concept-exercise-null-undefined
Jun 23, 2021
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
baa897b
rename concept
junedev 4b379e4
set up files
junedev 79204ab
add about.md
junedev 46c435f
add introduction
junedev b7cf3ab
add instructions
junedev 9c88f5d
add more tasks
junedev 8c3317c
add exemplar solution, tests and stub
junedev 87a954c
add hints
junedev 439d15b
add design
junedev f8639a4
minor fixes
junedev c0d8576
Merge branch 'main' into improve-concept-exercise-null-undefined
junedev 4422c3f
sync
junedev 99127c3
fix test
junedev b3c812b
some more fixes
junedev 53fbd3b
fix punctuation
junedev 53b7d45
Apply suggestions from code review
junedev 8623432
add typings
junedev 90959e9
address review comments
junedev e85b350
Merge branch 'main' into improve-concept-exercise-null-undefined
junedev 03fca6e
sync
junedev d763ed7
format
junedev aeecd6c
Merge branch 'main' into improve-concept-exercise-null-undefined
junedev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"blurb": "In JavaScript there are two different entities that represent the absence of a (meaningful) value: `null` and `undefined`.", | ||
"authors": ["SleeplessByte", "Jlamon", "junedev"], | ||
"contributors": [] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# About | ||
|
||
In contrast to many other languages, there are two different entities in JavaScript that represent the absence of a (meaningful) value. | ||
There is `null` and `undefined`. | ||
|
||
## Null | ||
|
||
The primitive value `null` is used as intentional "empty value" for a variable. | ||
In other languages a similar construct might be used only for (missing) objects or pointer types. | ||
In JavaScript `null` generally represents an empty value for any type. | ||
|
||
```javascript | ||
let name = null; | ||
// name is intentionally set to "empty" because it is not | ||
// available | ||
``` | ||
|
||
You can check whether a variable is null by using the [strict equality operator][mdn-strict-equality] `===`. | ||
Although `null` is a primitive value, the [`typeof` operator][mdn-typeof] "wrongly" returns `object` for [historic reasons][mdn-typeof-null]. | ||
That means it cannot be used by itself to check whether a variable is null. | ||
|
||
```javascript | ||
let name = null; | ||
|
||
name === null; | ||
// => true | ||
|
||
// Pitfall: | ||
typeof name; | ||
// => 'object' | ||
``` | ||
|
||
## Undefined | ||
|
||
> A variable that has not been assigned a value is of type `undefined`.<sup>1</sup> | ||
|
||
That means while `null` represents an empty value (but still a value), `undefined` represents the total absence of a value. 🤯 | ||
|
||
`undefined` appears in different contexts. | ||
|
||
- If a variable is declared without a value (initialization), it is `undefined`. | ||
- If you try to access a value for a non-existing key in an object, you get `undefined`. | ||
- If a function does not return a value, the result is `undefined`. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- If an argument is not passed to a function, it is `undefined`, unless that argument has a default value. | ||
|
||
```javascript | ||
let name; | ||
console.log(name); | ||
// => undefined | ||
|
||
let obj = { greeting: 'hello world' }; | ||
console.log(obj.missingKey); | ||
// => undefined | ||
|
||
function returnNothing() { | ||
return; | ||
} | ||
console.log(returnNothing()); | ||
// => undefined | ||
``` | ||
|
||
You can check whether a variable is undefined using the strict equality operator `===` or the `typeof` operator. | ||
|
||
```javascript | ||
let name; | ||
|
||
name === undefined; | ||
// => true | ||
|
||
typeof name === 'undefined'; | ||
// => true | ||
``` | ||
|
||
It is not recommended to manually assign `undefined` to a variable, always use `null` instead to make it clear you set an empty value intentionally. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Optional Chaining | ||
|
||
As mentioned above, accessing a non-existent key in an object returns `undefined` in JavaScript. | ||
However if you try to retrieve a nested value and the parent key does not exist, the evaluation of the nested key is performed on `undefined` and leads to `TypeError: Cannot read property ... of undefined`. | ||
Theoretically, you would always need to check the parent key exists before you can try to retrieve the nested key. | ||
This was often done with the AND operator `&&` but for deeply nested values this leads to very lengthy expressions. | ||
|
||
```javascript | ||
obj.level1 && obj.level1.level2 && obj.level1.level2.level3; | ||
``` | ||
|
||
To solve this problem, [optional chaining][mdn-optional-chaining] was added to the language specification in 2020. | ||
With the optional chaining operator `?.` you can ensure that JavaScript only tries to access the nested key if the parent was not `null` or `undefined`. | ||
Otherwise `undefined` is returned. | ||
|
||
```javascript | ||
const obj = { | ||
address: { | ||
street: 'Trincomalee Highway', | ||
city: 'Batticaloa', | ||
}, | ||
}; | ||
|
||
obj.residence; | ||
// => undefined | ||
|
||
obj.address.zipCode; | ||
// => undefined | ||
|
||
obj.residence.street; | ||
// => TypeError: Cannot read property 'street' of undefined | ||
|
||
obj.residence?.street; | ||
// => undefined | ||
|
||
obj.residence?.street?.number; | ||
// => undefined | ||
``` | ||
|
||
## Nullish Coalescing | ||
|
||
There are situations where you want to apply a default value in case a variable is null or undefined. | ||
In the past this was often times done with a ternary operator `?` or by utilizing lazy evaluation of the OR operator `||`. | ||
This has the disadvantage that the default value is applied in all cases where the variable is [falsy][mdn-falsy] (e.g. `''` or `0`), not only when it is null or undefined. | ||
This can easily cause unexpected outcomes. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```javascript | ||
let amount = null; | ||
amount = amount || 1; | ||
// => 1 | ||
|
||
amount = 0; | ||
amount = amount || 1; | ||
// => 1 | ||
|
||
amount = 0; | ||
amount ? amount : 1; | ||
// => 1 | ||
``` | ||
|
||
To address this, the [nullish coalescing operator][mdn-nullish-coalescing] `??` was introduced. | ||
Just like optional chaining, it was added to the language specification in 2020. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The nullish coalescing operator `??` returns the right-hand side operand only when the left-hand side operand is `null` or `undefined`. | ||
Otherwise the left-hand side operand is returned. | ||
With that, a default value can now be applied more specifically. | ||
|
||
```javascript | ||
let amount = null; | ||
amount = amount ?? 1; | ||
// => 1 | ||
|
||
amount = 0; | ||
amount = amount ?? 1; | ||
// => 0 | ||
``` | ||
|
||
--- | ||
|
||
[1] Undefined, MDN. (2021). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined (accessed June 4, 2021). | ||
|
||
[mdn-strict-equality]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality | ||
[mdn-typeof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof | ||
[mdn-typeof-null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#typeof_null | ||
[mdn-optional-chaining]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining | ||
[mdn-falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy | ||
[mdn-nullish-coalescing]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# Introduction | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
In contrast to many other languages, there are two different entities in JavaScript that represent the absence of a (meaningful) value. | ||
There is `null` and `undefined`. | ||
|
||
## Null | ||
|
||
The primitive value `null` is used as intentional "empty value" for variables of any type. | ||
|
||
```javascript | ||
let name = null; | ||
// name is intentionally set to "empty" because it is not | ||
// available | ||
``` | ||
|
||
You can check whether a variable is null by using the [strict equality operator][mdn-strict-equality] `===`. | ||
|
||
```javascript | ||
let name = null; | ||
|
||
name === null; | ||
// => true | ||
``` | ||
|
||
## Undefined | ||
|
||
> A variable that has not been assigned a value is of type `undefined`.<sup>1</sup> | ||
|
||
That means while `null` represents an empty value (but still a value), `undefined` represents the total absence of a value. 🤯 | ||
|
||
`undefined` appears in different contexts. | ||
|
||
- If a variable is declared without a value (initialization), it is `undefined`. | ||
- If you try to access a value for a non-existing key in an object, you get `undefined`. | ||
- If a function does not return a value, the result is `undefined`. | ||
- If an argument is not passed to a function, it is `undefined`, unless that argument has a default value. | ||
|
||
```javascript | ||
let name; | ||
console.log(name); | ||
// => undefined | ||
``` | ||
|
||
You can check whether a variable is undefined using the strict equality operator `===`. | ||
|
||
```javascript | ||
let name; | ||
|
||
name === undefined; | ||
// => true | ||
``` | ||
|
||
## Optional Chaining | ||
|
||
If you try to retrieve a nested value in an object but the parent key does not exist, JavaScript will throw an error. | ||
To easily avoid this, optional chaining was added to the language specification in 2020. | ||
With the optional chaining operator `?.` you can ensure that JavaScript only tries to access the nested key if the parent was not `null` or `undefined`. | ||
Otherwise `undefined` is returned. | ||
|
||
```javascript | ||
const obj = { | ||
address: { | ||
street: 'Trincomalee Highway', | ||
city: 'Batticaloa', | ||
}, | ||
}; | ||
|
||
obj.address.zipCode; | ||
// => undefined | ||
|
||
obj.residence.street; | ||
// => TypeError: Cannot read property 'street' of undefined | ||
|
||
obj.residence?.street; | ||
// => undefined | ||
``` | ||
|
||
## Nullish Coalescing | ||
|
||
There are situations where you want to apply a default value in case a variable is null or undefined (but only then). | ||
To address this, the nullish coalescing operator `??` was introduced in 2020. | ||
It returns the right-hand side operand only when the left-hand side operand is `null` or `undefined`. | ||
Otherwise the left-hand side operand is returned. | ||
|
||
```javascript | ||
let amount = null; | ||
amount = amount ?? 1; | ||
// => 1 | ||
|
||
amount = 0; | ||
amount = amount ?? 1; | ||
// => 0 | ||
``` | ||
|
||
--- | ||
|
||
[1] Undefined, MDN. (2021). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined (accessed June 4, 2021). | ||
|
||
[mdn-strict-equality]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality |
12 changes: 8 additions & 4 deletions
12
concepts/nullability/links.json → concepts/null-undefined/links.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,18 @@ | ||
[ | ||
{ | ||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null", | ||
"description": "Nullability - MDN Docs" | ||
"description": "MDN: null" | ||
}, | ||
{ | ||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator", | ||
"description": "Nullish coalescing operator - MDN Docs" | ||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined", | ||
"description": "MDN: undefined" | ||
}, | ||
{ | ||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining", | ||
"description": "Optional chaining - MDN Docs" | ||
"description": "MDN: Optional chaining" | ||
}, | ||
{ | ||
"url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator", | ||
"description": "MDN: Nullish coalescing operator" | ||
} | ||
] |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.