
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