this
. Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // "this"? }, 0); };
Uncaught TypeError: undefined is not a function
setTimeout()
, you actually call window.setTimeout()
. As a result, the anonymous function passed to setTimeout()
is defined in the context of a window
object that does not have a clearBoard()
method.this
in a variable that can be stored in the closure: Game.prototype.restart = function () { this.clearLocalStorage(); var self = this; // 'this', 'this'! this.timer = setTimeout(function(){ self.clearBoard(); // }, 0); };
bind()
method, which allows you to associate a function with the execution context: Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0); // 'this' }; Game.prototype.reset = function(){ this.clearBoard(); // 'this'! };
for (var i = 0; i < 10; i++) { /* ... */ } console.log(i); // ?
console.log()
call will result in an undefined
output or an error, then you are mistaken: “10” will be output. Why? In most other languages, this code would lead to an error, because the scope of the variable i
would be limited to a for
block. However, in JavaScript, this variable remains in scope even after the for
loop is completed, retaining its last value (this behavior is known as “ var hoisting ”). It should be noted that block-level support for scopes has been introduced in JavaScript since version 1.7 using the let
descriptor. var theThing = null; var replaceThing = function () { var priorThing = theThing; var unused = function () { // 'unused' - , 'priorThing', // 'unused' if (priorThing) { console.log("hi"); } }; theThing = { longStr: new Array(1000000).join('*'), // 1M someMethod: function () { console.log(someMessage); } }; }; setInterval(replaceThing, 1000); // 'replaceThing'
longStr
on every call to replaceThing
. What is the reason?theThing
object contains its own 1Mb longStr
object. Every second when calling replaceThing
, the function stores a reference to the previous theThing
object in the theThing
variable. This is not a problem, because each time the previous priorThing
link will be ground ( priorThing = theThing;
). So what is the reason for the leak?unused
and someMethod
) defined inside replaceThing
actually use priorThing
, then it is important to understand that they receive the same object, even if priorThing
rewritten again and again, since both functions use the same lexical scope . And as soon as the variable is used in any of the closures, it falls into the lexical scope, used by all closures in this scope. And this little nuance leads to a powerful memory leak. function addClickHandler(element) { element.click = function onClick(e) { alert("Clicked the " + element.nodeName) } }
onClick
has a closure in which the element
reference is stored. By onClick
as the click
event handler for element
, we created a circular reference: element
-> onClick
-> element
-> onClick
-> element
...element
from the DOM, a circular reference will hide element
and onClick
from the garbage collector and a memory leak will occur. What is the best way to avoid leaks? JavaScript memory management (and, in particular, garbage collection) is largely based on the concept of object reachability. The following objects are considered reachable and are known as root: // 'true'! console.log(false == '0'); console.log(null == undefined); console.log(" \t\r\n" == 0); console.log('' == 0); // ! if ({}) // ... if ([]) // ...
{}
and []
are actually objects. And any object in JavaScript corresponds to the boolean value true
. However, many developers believe that the value will be false
.===
and !==
instead of ==
and !=
To avoid the side effects of type conversion.NaN
with something (even with NaN
!) NaN
always give the result false
. Thus, you cannot use the equality operators ( ==
, ===
!=
!==
) to determine if the value of NaN
matches. Instead, use the isNaN()
built-in global function: console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(isNaN(NaN)); // true
var div = document.getElementsByTagName("my_div"); var fragment = document.createDocumentFragment(); for (var e = 0; e < elems.length; e++) { fragment.appendChild(elems[e]); } div.appendChild(fragment.cloneNode(true));
var elements = document.getElementsByTagName('input'); var n = elements.length; // , 10 for (var i = 0; i < n; i++) { elements[i].onclick = function() { console.log("This is element #" + i); }; }
onclick
is called by any of the elements, the upstream for
loop will have completed and the value of i
will be 10. var elements = document.getElementsByTagName('input'); var n = elements.length; // , 10 var makeHandler = function(num) { // return function() { // console.log("This is element #" + num); }; }; for (var i = 0; i < n; i++) { elements[i].onclick = makeHandler(i+1); }
makeHandler
immediately launched at each iteration of the loop, gets the current value of i+1
and stores it in the variable num
. The external function returns an internal function (which also uses the num
variable) and sets it as an onclick
handler. This ensures that each onclick
receives and uses the correct i
value. BaseObject = function(name) { if(typeof name !== "undefined") { this.name = name; } else { this.name = 'default' } }; var firstObj = new BaseObject(); var secondObj = new BaseObject('unique'); console.log(firstObj.name); // -> 'default' console.log(secondObj.name); // -> 'unique'
delete secondObj.name;
console.log(secondObj.name); // -> 'undefined'
default
? This can be done easily if you apply inheritance through prototypes: BaseObject = function (name) { if(typeof name !== "undefined") { this.name = name; } }; BaseObject.prototype.name = 'default';
BaseObject
instance inherits the name property of its prototype, in which it is assigned the value of default
. Thus, if the constructor is called without a name
, the default name
will be default
. And in the same way, if the name
property is removed from the BaseObject
instance, the prototype chain will be searched and the name
property will be obtained from the prototype
object, in which it is still equal to default
: var thirdObj = new BaseObject('unique'); console.log(thirdObj.name); // -> 'unique' delete thirdObj.name; console.log(thirdObj.name); // -> 'default'
var MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject();
whoAmI
method: var whoAmI = obj.whoAmI;
whoAmI
value of our new variable whoAmI
: console.log(whoAmI);
function () { console.log(this === window ? "window" : "MyObj"); }
obj.whoAmI()
and whoAmI()
: obj.whoAmI(); // "MyObj" ( ) whoAmI(); // "window"
var whoAmI = obj.whoAmI;
The new variable has been defined in the global namespace. As a result, the value of this
turned out to be equal to window
, and not obj
, to the MyObject
instance. Thus, if we really need to create a reference to an existing object method, we need to do it within the namespace of this object. For example: var MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject(); obj.w = obj.whoAmI; // obj.whoAmI(); // "MyObj" ( ) obj.w(); // "MyObj" ( )
setTimeout
or setInterval
, it will be passed to the Function
constructor for conversion to a new function. This process can be slow and inefficient. An alternative is to use the function as the first argument: setInterval(logTime, 1000); // logTime setInterval setTimeout(function() { // setTimeout logMessage(msgValue); // (msgValue ) }, 1000);
var object = {foo: "bar", foo: "baz"};
) or the function argument names, an error message will be displayed. This allows you to quickly detect and eliminate the bug.eval()
. In "strict mode", variables and functions declared inside eval()
are not created in the current scope.delete
operator in error . This statement cannot be applied to properties of an object whose configurable
flag is false
, and an attempt to do this will display an error message.Source: https://habr.com/ru/post/233553/
All Articles