# es6-cheatsheet
A cheatsheet containing ES2015 [ES6] tips, tricks, best practices and code
snippet examples for your day to day workflow. Contributions are welcome!
## Table of Contents
- [var versus let / const](#var-versus-let--const)
- [Replacing IIFEs with Blocks](#replacing-iifes-with-blocks)
- [Arrow Functions](#arrow-functions)
- [Strings](#strings)
- [Destructuring](#destructuring)
- [Modules](#modules)
- [Parameters](#parameters)
- [Classes](#classes)
- [Symbols](#symbols)
- [Maps](#maps)
- [WeakMaps](#weakmaps)
- [Promises](#promises)
- [Generators](#generators)
- [Async Await](#async-await)
- [Getter/Setter functions](#getter-and-setter-functions)
## var versus let / const
> Besides `var`, we now have access to two new identifiers for storing values
—`let` and `const`. Unlike `var`, `let` and `const` statements are not hoisted
to the top of their enclosing scope.
An example of using `var`:
```javascript
var snack = 'Meow Mix';
function getFood(food) {
if (food) {
var snack = 'Friskies';
return snack;
}
return snack;
}
getFood(false); // undefined
```
However, observe what happens when we replace `var` using `let`:
```javascript
let snack = 'Meow Mix';
function getFood(food) {
if (food) {
let snack = 'Friskies';
return snack;
}
return snack;
}
getFood(false); // 'Meow Mix'
```
This change in behavior highlights that we need to be careful when refactoring
legacy code which uses `var`. Blindly replacing instances of `var` with `let`
may lead to unexpected behavior.
> **Note**: `let` and `const` are block scoped. Therefore, referencing
block-scoped identifiers before they are defined will produce
a `ReferenceError`.
```javascript
console.log(x); // ReferenceError: x is not defined
let x = 'hi';
```
> **Best Practice**: Leave `var` declarations inside of legacy code to denote
that it needs to be carefully refactored. When working on a new codebase, use
`let` for variables that will change their value over time, and `const` for
variables which cannot be reassigned.
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Replacing IIFEs with Blocks
> A common use of **Immediately Invoked Function Expressions** is to enclose
values within its scope. In ES6, we now have the ability to create block-based
scopes and therefore are not limited purely to function-based scope.
```javascript
(function () {
var food = 'Meow Mix';
}());
console.log(food); // Reference Error
```
Using ES6 Blocks:
```javascript
{
let food = 'Meow Mix';
};
console.log(food); // Reference Error
```
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Arrow Functions
Often times we have nested functions in which we would like to preserve the
context of `this` from its lexical scope. An example is shown below:
```javascript
function Person(name) {
this.name = name;
}
Person.prototype.prefixName = function (arr) {
return arr.map(function (character) {
return this.name + character; // Cannot read property 'name' of undefined
});
};
```
One common solution to this problem is to store the context of `this` using
a variable:
```javascript
function Person(name) {
this.name = name;
}
Person.prototype.prefixName = function (arr) {
var that = this; // Store the context of this
return arr.map(function (character) {
return that.name + character;
});
};
```
We can also pass in the proper context of `this`:
```javascript
function Person(name) {
this.name = name;
}
Person.prototype.prefixName = function (arr) {
return arr.map(function (character) {
return this.name + character;
}, this);
};
```
As well as bind the context:
```javascript
function Person(name) {
this.name = name;
}
Person.prototype.prefixName = function (arr) {
return arr.map(function (character) {
return this.name + character;
}.bind(this));
};
```
Using **Arrow Functions**, the lexical value of `this` isn't shadowed and we
can re-write the above as shown:
```javascript
function Person(name) {
this.name = name;
}
Person.prototype.prefixName = function (arr) {
return arr.map(character => this.name + character);
};
```
> **Best Practice**: Use **Arrow Functions** whenever you need to preserve the
lexical value of `this`.
Arrow Functions are also more concise when used in function expressions which
simply return a value:
```javascript
var squares = arr.map(function (x) { return x * x }); // Function Expression
```
```javascript
const arr = [1, 2, 3, 4, 5];
const squares = arr.map(x => x * x); // Arrow Function for terser implementation
```
> **Best Practice**: Use **Arrow Functions** in place of function expressions
when possible.
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Strings
With ES6, the standard library has grown immensely. Along with these changes
are new methods which can be used on strings, such as `.includes()` and
`.repeat()`.
### .includes( )
```javascript
var string = 'food';
var substring = 'foo';
console.log(string.indexOf(substring) > -1);
```
Instead of checking for a return value `> -1` to denote string containment,
we can simply use `.includes()` which will return a boolean:
```javascript
const string = 'food';
const substring = 'foo';
console.log(string.includes(substring)); // true
```
### .repeat( )
```javascript
function repeat(string, count) {
var strings = [];
while(strings.length < count) {
strings.push(string);
}
return strings.join('');
}
```
In ES6, we now have access to a terser implementation:
```javascript
// String.repeat(numberOfRepetitions)
'meow'.repeat(3); // 'meowmeowmeow'
```
### Template Literals
Using **Template Literals**, we can now construct strings that have special
characters in them without needing to escape them explicitly.
```javascript
var text = "This string contains \"double quotes\" which are escaped.";
```
```javascript
let text = `This string contains "double quotes" which don't need to be escaped anymore.`;
```
**Template Literals** also support interpolation, which makes the task of
concatenating strings and values:
```javascript
var name = 'Tiger';
var age = 13;
console.log('My cat is named ' + name + ' and is ' + age + ' years old.');
```
Much simpler:
```javascript
const name = 'Tiger';
const age = 13;
console.log(`My cat is named ${name} and is ${age} years old.`);
```
In ES5, we handled new lines as follows:
```javascript
var text = (
'cat\n' +
'dog\n' +
'nickelodeon'
);
```
Or:
```javascript
var text = [
'cat',
'dog',
'nickelodeon'
].join('\n');
```
**Template Literals** will preserve new lines for us without having to
explicitly place them in:
```javascript
let text = ( `cat
dog
nickelodeon`
);
```
**Template Literals** can accept expressions, as well:
```javascript
let today = new Date();
let text = `The time and date is ${today.toLocaleString()}`;
```
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Destructuring
Destructuring allows us to extract values from arrays and objects (even deeply
nested) and store them in variables with a more convenient syntax.
### Destructuring Arrays
```javascript
var arr = [1, 2, 3, 4];
var a = arr[0];
var b = arr[1];
var c = arr[2];
var d = arr[3];
```
```javascript
let [a, b, c, d] = [1, 2, 3, 4];
console.log(a); // 1
console.log(b); // 2
```
### Destructuring Objects
```javascript
var luke = { occupation: 'jedi', father: 'anakin' };
var occupation = luke.occupation; // 'jedi'
var father = luke.father; // 'anakin'
```
```javascript
let luke = { occupation: 'jedi', father: 'anakin' };
let {occupation, father} = luke;
console.log(occupation); // 'jedi'
console.log(father); // 'anakin'
```
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Modules
Prior to ES6, we used libraries such as [Browserify](http://browserify.org/)
to create modules on the client-side, and [require](https://nodejs.org/api/modules.html#modules_module_require_id)
in **Node.js**. With ES6, we can now directly use modules of all types
(AMD and CommonJS).
### Exporting in CommonJS
```javascript
module.exports = 1;
module.exports = { foo: 'bar' };
module.exports = ['foo', 'bar'];
module.exports = function bar () {};
```
### Exporting in ES6
With ES6, we have various flavors of exporting. We can perform
**Named Exports**:
```javascript
export let name = 'David';
export let age = 25;
```
As well as **exporting a list** of objects:
```javascript
function sumTwo(a, b) {
return a + b;
}
function sumThree(a, b, c) {
return a + b + c;
}
export { sumTwo, sumThree };
```
We can also export functions, objects and values (etc.) simply by using the `export` keyword:
```javascript
export function sumTwo(a, b) {
return a + b;
}
export function sumThree(a, b, c) {
return a + b + c;
}
```
And lastly, we can **export default bindings**:
```javascript
function sumTwo(a, b) {
return a + b;
}
function sumThree(a, b, c) {
return a + b + c;
}
let api = {
sumTwo,
sumThree
};
export default api;
/* Which is the same as
* export { api as default };
*/
```
> **Best Practices**: Always use the `export default` method at **the end** of
the module. It makes it clear what is being exported, and saves time by having
to figure out what name a value was exported as. More so, the common practice
in CommonJS modules is to export a single value or object. By sticking to this
paradigm, we make our code easily readable and allow ourselves to interpolate
between CommonJS and ES6 modules.
### Importing in ES6
ES6 provides us with various flavors of importing. We can import an entire file:
```javascript
import 'underscore';
```
> It is important to note that simply **importing an entire file will execute
all code at the top level of that file**.
Similar to Python, we have named imports:
```javascript
import { sumTwo, sumThree } from 'math/addition';
```
We can also rename the named imports:
```javascript
import {
sumTwo as addTwoNumbers,
sumThree as sumThreeNumbers
} from 'math/addition';
```
In addition, we can **import all the things** (also called namespace import):
```javascript
import * as util from 'math/addition';
```
Lastly, we can import a list of values from a module:
```javascript
import * as additionUtil from 'math/addition';
const { sumTwo, sumThree } = additionUtil;
```
Importing from the default binding like this:
```javascript
import api from 'math/addition';
// Same as: import { default as api } from 'math/addition';
```
While it is better to keep the exports simple, but we can sometimes mix default import and mixed import if needed.
When we are exporting like this:
```javascript
// foos.js
export { foo as default, foo1, foo2 };
```
We can import them like the following:
```javascript
import foo, { foo1, foo2 } from 'foos';
```
When importing a module exported using commonjs syntax (such as React) we can do:
```javascript
import React from 'react';
const { Component, PropTypes } = React;
```
This can also be simplified further, using:
```javascript
import React, { Component, PropTypes } from 'react';
```
> **Note**: Values that are exported are **bindings**, not references.
Therefore, changing the binding of a variable in one module will affect the
value within the exported module. Avoid changing the public interface of these
exported values.
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Parameters
In ES5, we had varying ways to handle functions which needed **default values**,
**indefinite arguments**, and **named parameters**. With ES6, we can accomplish
all of this and more using more concise syntax.
### Default Parameters
```javascript
function addTwoNumbers(x, y) {
x = x || 0;
y = y || 0;
return x + y;
}
```
In ES6, we can simply supply default values for parameters in a function:
```javascript
function addTwoNumbers(x=0, y=0) {
return x + y;
}
```
```javascript
addTwoNumbers(2, 4); // 6
addTwoNumbers(2); // 2
addTwoNumbers(); // 0
```
### Rest Parameters
In ES5, we handled an indefinite number of arguments like so:
```javascript
function logArguments() {
for (var i=0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
```
Using the **rest** operator, we can pass in an indefinite amount of arguments:
```javascript
function logArguments(...args) {
for (let arg of args) {
console.log(arg);
}
}
```
### Named Parameters
One of the patterns in ES5 to handle named parameters was to use the **options
object** pattern, adopted from jQuery.
```javascript
function initializeCanvas(options) {
var height = options.height || 600;
var width = options.width || 400;
var lineStroke = options.lineStroke || 'black';
}
```
We can achieve the same functionality using destructuring as a formal parameter
to a function:
```javascript
function initializeCanvas(
{ height=600, width=400, lineStroke='black'}) {
// Use variables height, width, lineStroke here
}
```
If we want to make the entire value optional, we can do so by destructuring an
empty object:
```javascript
function initializeCanvas(
{ height=600, width=400, lineStroke='black'} = {}) {
// ...
}
```
### Spread Operator
In ES5, we could find the max of values in an array by using the `apply` method on `Math.max` like this:
```javascript
Math.max.apply(null, [-1, 100, 9001, -32]); // 9001
```
In ES6, we can now use the spread operator to pass an array of values to be used as
parameters to a function:
```javascript
Math.max(...[-1, 100, 9001, -32]); // 9001
```
We can concat array literals easily with this intuitive syntax:
```javascript
let cities = ['San Francisco', 'Los Angeles'];
let places = ['Miami', ...cities, 'Chicago']; // ['Miami', 'San Francisco', 'Los Angeles', 'Chicago']
```
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Classes
Prior to ES6, we implemented Classes by creating a constructor function and
adding properties by extending the prototype:
```javascript
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.incrementAge = function () {
return this.age += 1;
};
```
And created extended classes by the following:
```javascript
function Personal(name, age, gender, occupation, hobby) {
Person.call(this, name, age, gender);
this.occupation = occupation;
this.hobby = hobby;
}
Personal.prototype = Object.create(Person.prototype);
Personal.prototype.constructor = Personal;
Personal.prototype.incrementAge = function () {
Person.prototype.incrementAge.call(this);
this.age += 20;
console.log(this.age);
};
```
ES6 provides much needed syntactic sugar for doing this under the hood. We can
create Classes directly:
```javascript
class Person {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
incrementAge() {
this.age += 1;
}
}
```
And extend them using the `extends` keyword:
```javascript
class Personal extends Person {
constructor(name, age, gender, occupation, hobby) {
super(name, age, gender);
this.occupation = occupation;
this.hobby = hobby;
}
incrementAge() {
super.incrementAge();
this.age += 20;
console.log(this.age);
}
}
```
> **Best Practice**: While the syntax for creating classes in ES6 obscures how
implementation and prototypes work under the hood, it is a good feature for
beginners and allows us to write cleaner code.
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Symbols
Symbols have existed prior to ES6, but now we have a public interface to using
them directly. Symbols are immutable and unique and can be used as keys in any hash.
### Symbol( )
Calling `Symbol()` or `Symbol(description)` will create a unique symbol that cannot be looked up
globally. A Use case for `Symbol()` is to patch objects or namespaces from third parties with your own
logic, but be confident that you won't collide with updates to that library. For example,
if you wanted to add a method `refreshComponent` to the `React.Component` class, and be certain that
you didn't trample a method they add in a later update:
```javascript
const refreshComponent = Symbol();
React.Component.prototype[refreshComponent] = () => {
// do something
}
```
### Symbol.for(key)
`Symbol.for(key)` will create a Symbol that is still immutable and unique, but can be looked up globally.
Two identical calls to `Symbol.for(key)` will return the same Symbol instance. NOTE: This is not true for
`Symbol(description)`:
```javascript
Symbol('foo') === Symbol('foo') // false
Symbol.for('foo') === Symbol('foo') // false
Symbol.for('foo') === Symbol.for('foo') // true
```
A common use case for Symbols, and in particular with `Symbol.for(key)` is for interoperability. This can be
achieved by having your code look for a Symbol member on object arguments from third parties that contain some
known interface. For example:
```javascript
function reader(obj) {
const specialRead = Symbol.for('specialRead');
if (obj[specialRead]) {
const reader = obj[specialRead]();
// do something with reader
} else {
throw new TypeError('object cannot be read');
}
}
```
And then in another library:
```javascript
const specialRead = Symbol.for('specialRead');
class SomeReadableType {
[specialRead]() {
const reader = createSomeReaderFrom(this);
return reader;
}
}
```
> A notable example of Symbol use for interoperability is `Symbol.iterator` which exists on all iterable
types in ES6: Arrays, strings, generators, etc. When called as a method it returns an object with an Iterator
interface.
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Maps
**Maps** is a much needed data structure in JavaScript. Prior to ES6, we created
**hash** maps through objects:
```javascript
var map = new Object();
map[key1] = 'value1';
map[key2] = 'value2';
```
However, this does not protect us from accidentally overriding functions with
specific property names:
```javascript
> getOwnProperty({ hasOwnProperty: 'Hah, overwritten'}, 'Pwned');
> TypeError: Property 'hasOwnProperty' is not a function
```
Actual **Maps** allow us to `set`, `get` and `search` for values (and much more).
```javascript
let map = new Map();
> map.set('name', 'david');
> map.get('name'); // david
> map.has('name'); // true
```
The most amazing part of Maps is that we are no longer limited to just using
strings. We can now use any type as a key, and it will not be type-cast to
a string.
```javascript
let map = new Map([
['name', 'david'],
[true, 'false'],
[1, 'one'],
[{}, 'object'],
[function () {}, 'function']
]);
for (let key of map.keys()) {
console.log(typeof key);
// > string, boolean, number, object, function
}
```
> **Note**: Using non-primitive values such as functions or objects won't work
when testing equality using methods such as `map.get()`. As such, stick to
primitive values such as Strings, Booleans and Numbers.
We can also iterate over maps using `.entries()`:
```javascript
for (let [key, value] of map.entries()) {
console.log(key, value);
}
```
<sup>[(back to table of contents)](#table-of-contents)</sup>
## WeakMaps
In order to store private data versions < ES6, we had various ways of doing this.
One such method was using naming conventions:
```javascript
class Person {
constructor(age) {
this._age = age;
}
_incrementAge() {
this._age += 1;
}
}
```
But naming conventions can cause confusion in a codebase and are not always
going to be upheld. Instead, we can use WeakMaps to store our values:
```javascript
let _age = new WeakMap();
class Person {
constructor(age) {
_age.set(this, age);
}
incrementAge() {
let age = _age.get(this) + 1;
_age.set(this, age);
if (age > 50) {
console.log('Midlife crisis');
}
}
}
```
The cool thing about using WeakMaps to store our private data is that their
keys do not give away the property names, which can be seen by using
`Reflect.ownKeys()`:
```javascript
> const person = new Person(50);
> person.incrementAge(); // 'Midlife crisis'
> Reflect.ownKeys(person); // []
```
A more practical example of using WeakMaps is to store data which is associated
to a DOM element without having to pollute the DOM itself:
```javascript
let map = new WeakMap();
let el = document.getElementById('someElement');
// Store a weak reference to the element with a key
map.set(el, 'reference');
// Access the value of the element
let value = map.get(el); // 'reference'
// Remove the reference
el.parentNode.removeChild(el);
el = null;
// map is empty, since the element is destroyed
```
As shown above, once the object is destroyed by the garbage collector,
the WeakMap will automatically remove the key-value pair which was identified
by that object.
> **Note**: To further illustrate the usefulness of this example, consider how
jQuery stores a cache of objects corresponding to DOM elements which have
references. Using WeakMaps, jQuery can automatically free up any memory that
was associated with a particular DOM element once it has been removed from the
document. In general, WeakMaps are very useful for any library that wraps DOM
elements.
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Promises
Promises allow us to turn our horizontal code (callback hell):
```javascript
func1(function (value1) {
func2(value1, function (value2) {
func3(value2, function (value3) {
func4(value3, function (value4) {
func5(value4, function (value5) {
// Do something with value 5
});
});
});
});
});
```
Into vertical code:
```javascript
func1(value1)
.then(func2)
.then(func3)
.then(func4)
.then(func5, value5 => {
// Do something with value 5
});
```
Prior to ES6, we used [bluebird](https://github.com/petkaantonov/bluebird) or
[Q](https://github.com/kriskowal/q). Now we have Promises natively:
```javascript
new Promise((resolve, reject) =>
reject(new Error('Failed to fulfill Promise')))
.catch(reason => console.log(reason));
```
Where we have two handlers, **resolve** (a function called when the Promise is
**fulfilled**) and **reject** (a function called when the Promise is **rejected**).
> **Benefits of Promises**: Error Handling using a bunch of nested callbacks
can get chaotic. Using Promises, we have a clear path to bubbling errors up
and handling them appropriately. Moreover, the value of a Promise after it has
been resolved/rejected is immutable - it will never change.
Here is a practical example of using Promises:
```javascript
var request = require('request');
return new Promise((resolve, reject) => {
request.get(url, (error, response, body) => {
if (body) {
resolve(JSON.parse(body));
} else {
resolve({});
}
});
});
```
We can also **parallelize** Promises to handle an array of asynchronous
operations by using `Promise.all()`:
```javascript
let urls = [
'/api/commits',
'/api/issues/opened',
'/api/issues/assigned',
'/api/issues/completed',
'/api/issues/comments',
'/api/pullrequests'
];
let promises = urls.map((url) => {
return new Promise((resolve, reject) => {
$.ajax({ url: url })
.done((data) => {
resolve(data);
});
});
});
Promise.all(promises)
.then((results) => {
// Do something with results of all our promises
});
```
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Generators
Similar to how [Promises](https://github.com/DrkSephy/es6-cheatsheet#promises) allow us to avoid
[callback hell](http://callbackhell.com/), Generators allow us to flatten our code - giving our
asynchronous code a synchronous feel. Generators are essentially functions which we can
[pause their execution](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield)
and subsequently return the value of an expression.
A simple example of using generators is shown below:
```javascript
function* sillyGenerator() {
yield 1;
yield 2;
yield 3;
yield 4;
}
var generator = sillyGenerator();
> console.log(generator.next()); // { value: 1, done: false }
> console.log(generator.next()); // { value: 2, done: false }
> console.log(generator.next()); // { value: 3, done: false }
> console.log(generator.next()); // { value: 4, done: false }
```
Where [next](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next)
will allow us to push our generator forward and evaluate a new expression. While the above example is extremely
contrived, we can utilize Generators to write asynchronous code in a synchronous manner:
```javascript
// Hiding asynchronousity with Generators
function request(url) {
getJSON(url, function(response) {
generator.next(response);
});
}
```
And here we write a generator function that will return our data:
```javascript
function* getData() {
var entry1 = yield request('http://some_api/item1');
var data1 = JSON.parse(entry1);
var entry2 = yield request('http://some_api/item2');
var data2 = JSON.parse(entry2);
}
```
By the power of `yield`, we are guaranteed that `entry1` will have the data needed to be parsed and stored
in `data1`.
While generators allow us to write asynchronous code in a synchronous manner, there is no clear
and easy path for error propagation. As such, as we can augment our generator with Promises:
```javascript
function request(url) {
return new Promise((resolve, reject) => {
getJSON(url, resolve);
});
}
```
And we write a function which will step through our generator using `next` which in turn will utilize our
`request` method above to yield a Promise:
```javascript
function iterateGenerator(gen) {
var generator = gen();
(function iterate(val) {
var ret = generator.next();
if(!ret.done) {
ret.value.then(iterate);
}
})();
}
```
By augmenting our Generator with Promises, we have a clear way of propagating errors through the use of our
Promise `.catch` and `reject`. To use our newly augmented Generator, it is as simple as before:
```javascript
iterateGenerator(function* getData() {
var entry1 = yield request('http://some_api/item1');
var data1 = JSON.parse(entry1);
var entry2 = yield request('http://some_api/item2');
var data2 = JSON.parse(entry2);
});
```
We were able to reuse our implementation to use our Generator as before, which shows their power. While Generators
and Promises allow us to write asynchronous code in a synchronous manner while retaining the ability to propagate
errors in a nice way, we can actually begin to utilize a simpler construction that provides the same benefits:
[async-await](https://github.com/DrkSephy/es6-cheatsheet#async-await).
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Async Await
While this is actually an upcoming ES2016 feature, `async await` allows us to perform the same thing we accomplished
using Generators and Promises with less effort:
```javascript
var request = require('request');
function getJSON(url) {
return new Promise(function(resolve, reject) {
request(url, function(error, response, body) {
resolve(body);
});
});
}
async function main() {
var data = await getJSON();
console.log(data); // NOT undefined!
}
main();
```
Under the hood, it performs similarly to Generators. I highly recommend using them over Generators + Promises. A great resource
for getting up and running with ES7 and Babel can be found [here](http://masnun.com/2015/11/11/using-es7-asyncawait-today-with-babel.html).
<sup>[(back to table of contents)](#table-of-contents)</sup>
## Getter and setter functions
ES6 has started supporting getter and setter functions. Using the following example:
```javascript
class Employee {
constructor(name) {
this._name = name;
}
get name() {
if(this._name) {
return 'Mr. ' + this._name.toUpperCase();
} else {
return undefined;
}
}
set name(newName) {
if (newName == this._name) {
console.log('I already have this name.');
} else if (newName) {
this._name = newName;
} else {
return false;
}
}
}
var emp = new Employee("James Bond");
// uses the get method in the background
if (emp.name) {
console.log(emp.name); // Mr. JAMES BOND
}
// uses the setter in the background
emp.name = "Bond 007";
console.log(emp.name); // Mr. BOND 007
```
Latest browsers are also supporting getter/setter functions in Objects and we can use them for computed properties, adding listeners and preprocessing before setting/getting:
```javascript
var person = {
firstName: 'James',
lastName: 'Bond',
get fullName() {
console.log('Getting FullName');
return this.firstName + ' ' + this.lastName;
},
set fullName (name) {
console.log('Setting FullName');
var words = name.toString().split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
}
person.fullName; // James Bond
person.fullName = 'Bond 007';
person.fullName; // Bond 007
```
<sup>[(back to table of contents)](#table-of-contents)</sup>