Skip to content

Instantly share code, notes, and snippets.

@vaz vaz/1_use_strict.js
Created Oct 12, 2016

Embed
What would you like to do?
// If the first statement in a file or a function is "use strict"
// then Strict Mode will be activated for that file or function.
//
// You could invoke strict mode for the file by uncommenting this:
// "use strict";
function nonStrictFunction () {
x = 123; // undeclared!
}
nonStrictFunction();
console.log("undeclared variable x leaked as a global: " + (global.x === 123));
function strictFunction () {
"use strict";
y = 654; // undeclared! throws ReferenceError
} // <-- strict mode ends here
strictFunction();
//
// Output:
//
// undeclared variable x leaked as a global: true
// /Users/vaz/tmp/es6/1_use_strict.js:16
// y = 654; // throws ReferenceError
// ^
// ReferenceError: y is not defined
// at strictFunction (/Users/vaz/tmp/es6/1_use_strict.js:16:5)
// ...

"use strict"

The statement "use strict"; turns on "strict mode" when it's the first statement in a file or function. It only applies to the file for function it's present in.

"use strict" is actually an expression (a literal string), not a statement. This allows it to be used regardless of the version of JavaScript used: it will be ignored in older versions.

It is used at the beginning of a file or the beginning of a function.

Strict mode and "use strict"; are actually from ES5 but it's important to mention.

Strict Mode

In strict mode, certain parts of JS behave differently.

  • converts some mistakes into errors. The most important example is a typo in a variable name:

    "use strict";
    var numbers;
    
    // non-strict mode: silently create new global var
    // strict mode:     throws ReferenceError
    nubmers = [1, 3, 6, 34, 765];
    

    Also, object keys and function parameter names must be unique in strict mode.

  • prohibits the with statement. (What's that? A feature poorly-designed enough to get prohibited)

  • eval()'d code gets its own scope for variables, won't leak vars (for security)

  • can't delete variables: var x; delete x; (but you can still delete object properties: delete obj.x)

  • this will be undefined if called without a this (either by obj.method() syntax or bound with bind, apply or call), instead of defaulting this to the global object, which is insecure and error-prone.

  • no arguments.callee, arguments.caller, fun.caller, fun.arguments (insecure and/or slow with dubious use-cases)

  • added new reserved keywords, some of which are now used, some not (let was a reserved keyword before, now it has a meaning)_

// for...of: loop over "iterable" objects.
// Arrays are iterable. Many other collection-like objects are iterable, too.
var arr = [1, 5, 7, 3, 7];
for (var number of arr) {
console.log("square of " + number + " is " + number * number);
}
// Objects are not iterable.
// for (var value of obj) {} // Error!
// Contrast with for...in, which works (specifically) on objects:
var obj = { a: 1, b: 2 };
for (var key in obj) { /* ... */ }
// let and const
// the `outer` function creates a new scope (function scope)
function outer () {
for (var i = 0; i < 10; i++) {
// As far as `var` is concerned, there's no such thing as
// for-block scope (or any block, other than function-body).
}
}
// rewritten, but the exact same:
function outerHoisted () {
var i; // <-- might as well declare it here, because it's hoisted
for (i = 0; i < 10; i++) { /* ... */ }
}
// Introducing let:
///////////////////////
// With `let`, we now have block scope:
function outerLet () {
for (let i = 0; i < 10; i++) {
// `i` is scoped to this for-block
}
// `i` is undefined and undeclared out here.
}
// showing block-scoping by shadowing function-scoped variable:
function outerShadowing () {
let value = 'outer var';
if (true) {
// this is a block too!
// Braces generally imply a block (unless it's an object literal).
let value = 'inner var';
}
console.log(value) // logs "outer var"
}
outerShadowing();
// You can now even have blocks without for, if, etc...
{
let iOnlyExistInsideTheseBraces = true;
// but use cases for this are fairly limited
}
// What about `const`?
//////////////////////////////
// const defines constants. Kind of?
// Precisely speaking, it declares a variable and its initial value,
// and a variable declared with `const` cannot be re-assigned to a different value.
const konstant = { quality: "unchanging!" };
console.log(konstant);
// try to re-assign it:
// konstant = "something else"; // <-- throws TypeError: Assignment to constant variable.
// In the case of objects, it becomes important to say that *only the variable is
// constant*, but the object it points to can be modified.
konstant.quality = "changing!"
console.log(konstant);
// This does not violate const-ness. It's all assignment directly to the const variable.
// Object properties (keys) cannot be const, as they are not variables.
// String interpolation is simple:
//
var person = {
name: 'joe javascript',
age: 24
};
// Instead of:
console.log("My name is " + person.name + " and my age is " + person.age);
// You can use:
console.log(`My name is ${person.name} and my age is ${person.age}`);
// You can have any JS expression in the braces:
console.log(`324 * 543 = ${324 * 543}`);
// A string with backticks as delimiters is called a "template string".
// backtick-enclosed strings are the only strings that do iterpolation.
// Interpolation just means it will execute the expression as javascript
// and include that value at that point in the string.
// That's pretty much all you need to know.
// Arrow functions
///////////////////
// Look at different examples: http://es6-features.org/#ExpressionBodies
// regular function expression:
var add = function add (x, y) {
return x + y;
};
// similar function in arrow function syntax:
var add2 = (x, y) => { return x + y; };
// add2 is still a function. It's almost the same. BUT:
// - arrow functions don't *replace* non-arrow functions
// - arrow functions behave differently than non-arrow functions
//
// We can have arrow functions with a body with multiple statements, using braces:
var add3 = (x, y) => {
var result = x + y;
return result;
}
// Or arrow functions with simple bodies of only one expression, omitting the `return` keyword:
var add4 = (x, y) => x + y;
// parentheses are optional if there's only one argument:
var double = x => x * 2;
var double2 = (x) => x * 2; // <-- if unsure, just do this, consistency is nice
// Generally:
// - top-level functions should use `function`
// - functions meant to be invoked with a `this` value **must** use `function`
// - functions defined and passed inline as arguments (*i.e.* callbacks) are good candidates for arrow functions
// Also:
// - arrow functions are always anonymous!
// - they never have their own `this` value - they inherit it from the surrounding scope
// - same with `arguments` magic object.
let cat = {
name: "le chaton",
favouriteThings: ['mice', 'outdoors', 'not water'],
meow: function () {
console.log(`${this.name} meows!`);
var that = this; // <-- hold on to current `this` (the cat)
this.favouriteThings.forEach(function (value, index) {
// in forEach, `this` is not specifically bound to anything, so:
// - in strict mode, `this` will be undefined
// - in non-strict: `this` will be the global object (yuck!)
// either way, it's not the cat! So you end up with:
console.log(`${that.name} likes ${value}`);
// `that` is an ugly workaround.
});
// with arrow function though:
this.favouriteThings.forEach((thing) => {
// `this` is unchanged in here (it's still the cat):
console.log(`${this.name} likes ${thing}`);
});
}
}
cat.meow(); // `this` will be the cat
// Output:
//
// le chaton meows!
// le chaton likes mice
// le chaton likes outdoors
// le chaton likes not water
// le chaton likes mice
// le chaton likes outdoors
// le chaton likes not water
// by the way, you cannot even force an arrow function to have `this`:
var manualThis = { name: "not global!" };
function strictWrapper () {
"use strict";
console.log("`this` in strictWrapper is manually bound: " + (this === manualThis));
var foo = function () { return this; }
console.log("`this` in regular inner function defaults to undefined (strict mode): " + (foo() === undefined));
var bar = () => { return this; }
console.log("`this` in inner arrow function is inherited, though: " + (bar() === manualThis))
}
strictWrapper.call(manualThis); // calls and forces value of `this` to be `manualThis`
// Output:
//
// `this` in strictWrapper is manually bound: true
// `this` in regular inner function defaults to undefined (strict mode): true
// `this` in inner arrow function is inherited, though: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.