Code is parsed, compiled (where relevant), and executed in the mode in which it's written, regardless of what mode it's called from. The only time you can cross a boundary between the two modes is when calling a function, which raises the question, which mode is used to do the work of making that call? (Since strict mode affects several aspects of calling functions.) The answer is: The mode of the function being called. So when a loose function calls a strict one, the strict rules for function calls are used; when a strict function calls a loose one, the loose rules for function calls are used.
When you call a function, strict mode comes into play in several ways:
- In strict mode, the call can specify that
this
be a non-object value; in loose mode, any non-object is coerced to object.
- In strict mode, the default
this
for calls that don't specify it (e.g.: foo()
) is undefined
instead of the global object as it is in loose mode.
- In strict mode, the called function's
arguments
object has caller
and callee
properties that throw a TypeError
when accessed; in loose mode, the spec defines arguments.callee
as a reference to the called function; there's no arguments.caller
in the spec, but some implementations provide a reference to the calling function on it.
- In fact, in strict mode code, implementation-specific extensions (like
caller
) to arguments
are forbidden, while in loose mode they're allowed.
- The
arguments
object created by the call for use by the called function is complete disassociated from the named parameters of the function in strict mode, instead of being dynamically linked to them as in loose mode.
Here's an example of a loose function calling a strict one and vice-versa, demonstrating that it's the rules of the function called are the ones that are followed:
// Loose calling strict
const strictFunction1 = (function() {
"use strict";
return function(a, b) {
console.log("=== strict function called:");
console.log("#1 and #2", typeof this); // undefined
console.log("#2", this === window); // false
try {
const x = arguments.callee;
console.log("#3", "result of trying to access `arguments.callee`: got a " + typeof x);
} catch (e) {
console.log("#3", "result of trying to access `arguments.callee`: " + e.message);
}
// #5:
a = 42; // Setting 'a'
console.log("#5", a === arguments[0]); // false
};
})();
function looseFunction1() {
strictFunction1(67);
}
looseFunction1();
// Strict calling loose
const looseFunction2 = (function() {
return function(a, b) {
console.log("=== loose function called:");
console.log("#1 and #2", typeof this); // object
console.log("#2", this === window); // true
try {
const x = arguments.callee;
console.log("#3", "result of trying to access `arguments.callee`: got a " + typeof x);
} catch (e) {
console.log("#3", "result of trying to access `arguments.callee`: " + e.message);
}
// #5:
a = 42; // Setting 'a'
console.log("#5", a === arguments[0]); // true
};
})();
function strictFunction2() {
looseFunction2(67);
}
strictFunction2();
.as-console-wrapper {
max-height: 100% !important;
}