functionassignHandler() { let element = document.getElementById('someElement'); element.onclick = () =>console.log(element.id); }
上面的代码中匿名函数的存在导致element始终被引用,其内存不会被回收。 因此可以进行手动回收。
1 2 3 4 5 6
functionassignHandler() { let element = document.getElementById('someElement'); let id = element.id; element.onclick = () =>console.log(id); element = null; }
10.4.4 立即调用的函数表达式
立即调用的函数表达式(IIFE,Immediately Invoked Function Expression)
1 2 3
(function(){ // 块级作用域 })();
IIFE主要用于模拟块级作用域
主要针对于ECMAScript5.1之前不支持块级作用域的情况。
1 2 3 4 5 6
let count = 5; (function () { for (let i = 0; i < count; i++) { console.log(i); } })(); // 0 1 2 3 4
因为不存在对这个匿名函数的引用, 因此只要函数执行完毕其作用域链就会被销毁。
ECMAScript6以后出现了块级作用域:
1 2 3 4 5 6 7
{ let i; for (i = 0; i < couhnt; i++){ console.log(i); } } console.log(i); //Error
IIFE还可以用来锁定参数值
1 2 3 4 5 6
let divs = document.querySelectorAll('div'); for (var i = 0; i < divs.length; ++i){ divs[i].addEventListener('click', function () { console.log(i); }) }
以上这段代码会导致每次触发click都打印同样的值, 可以通过下面方式锁定索引值:
1 2 3 4 5 6 7 8
let divs = document.querySelectorAll('div'); for (var i = 0; i < divs.length; ++i){ divs[i].addEventListener('click', (function (frozenCounter) { returnfunction () { console.log(frozenCounter); } })(i)); }
使用 ECMAScript6 块级作用域就简单那多了:
1 2 3 4 5 6
let divs = document.querySelectorAll('div'); for (let i = 0; i < divs.length; ++i){ divs[i].addEventListener('click', function () { console.log(i); }) }
当然要保证for循环使用块级作用域变量关键字:
1 2 3 4 5 6 7
let divs = document.querySelectorAll('div'); let i; for (i = 0; i < divs.length; ++i){ divs[i].addEventListener('click', function () { console.log(i); //same output }) }
10.4.5 私有变量
JavaScript任何定义在函数或块中的变量,都可以认为是私有的。
闭包与私有变量
如果在函数中创建一个能通过作用域链访问外部函数变量的闭包, 就能够创建出访问私有变量的公有方法。
特权方法(privileged method)
特权方法指的是能够访问函数私有变量/方法的公有方法。
方法一:在构造函数中实现
即在构造函数中定义私有变量和私有方法, 再创建一个能够访问这些私有成员的特权方法:
1 2 3 4 5 6 7 8 9 10 11 12
functionPerson(name) { this.getName = function () { return name; }; this.setName = function (value) { name = value; } } let person = newPerson('Nicholas'); console.log(person.getName()); //Nicholas person.setName('Greg'); console.log(person.getName()); //Greg
方法二:使用私有作用域定义私有变量和函数
1 2 3 4 5 6 7 8 9 10 11
(function () { let privateVariable = 10; functionprivateFunction() { returnfalse; } MyObject = function () { }; MyObject.prototype.publicMethod = function () { privateVariable++; returnprivateFunction(); } })();
(function () { let name = ''; Person = function (value) { name = value; }; Person.prototype.getName = function () { return name; } Person.prototype.setName = function (value) { name = value; } })(); let person1 = newPerson('Nicholas'); console.log(person1.getName()); //Nicholas person1.setName('Matt'); console.log(person1.getName()); //Matt
let person2 = newPerson('Michael'); console.log(person2.getName()); //Michael console.log(person1.getName()); //Michael
模块模式
模块模式是在单例对象基础上的扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13
let singleton = function () { let proivateVariable = 10; functionprivateFunction() { returnfalse; } return { publicProperty: true, publicMethod() { privateVariable++; returnprivateFunction(); } } }();
如果单例对象需要进行某种初始化, 并且需要访问私有变量时, 可以采用这个模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
let application = function () { let components = newArray(); components.push(newBaseComponent()); return { getComponentCount() { return components.length; }, registerComponent(component) { if (typeof component == 'object') { components.push(component); } } } }();