JS核心技术开发解密读书笔记3
第五章 作用域与作用域链
1. 作用域
常见的作用域有两种,全局作用域和函数作用域。ES6中新增了块级作用域。
全局作用域中声明的变量与函数可以在代码的任何地方被访问。全局作用域有以下三种情形:
1)全局对象下拥有的属性与方法
window.name; window.location; window.top; ……
2)在最外层声明的变量与方法
var foo = function () {} ; var str = 'out variable'; var arr = [1, 2, 3]; function bar () {}
3)在非严格模式下,函数作用域中未定义但直接复制的变量与方法,即函数fn可以直接使用全局作用域里面的变量
function foo () { bar = 20; } function fn () { foo(); return bar + 30; } fn(); // 50
函数作用域中声明的变量与方法,只能被下层子作用域访问,而不能被其他不相干的作用域访问。
function foo () { var a = 20; var b = 30; } foo(); function bar () { return a + b; } bar(); // Uncaught ReferenceError
因为作用域的限制,bar中无法访问变量a和b,因此函数执行报Uncaught ReferenceError。
function foo () { var a = 20; var b = 30; function bar () { return a + b; } return bar(); } foo(); // 50
bar中作用域为foo的自作用域,因此能访问到变量a和b。
块级作用域通过ES6新增的let和const来体现。
function f1 () { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
上面的函数有两个代码块,都声明了变量n,运行后输出5,这表示外层代码块不收内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是10。
2. 立即执行函数(Immediately Invoked Function Expression, IIFE)
块级作用域出现前,常使用IIFE去模拟块级作用域。
var a = 2; (function foo () { var a = 3; console.log(a); // 3 })(); console.log(a); // 2
由于函数被包含在一对括号内部,因此成为了一个表达式,通过在末尾加上了另外一个括号可以立即执行这个函数。
IIFE另一个皮鞭的进阶用法是把它们当做函数调用并传递参数进去。
var a = 2; (function IIFE (global) { var a = 3; console.log(a); // 3 console.log(global.a); // 2 })(window); console.log(a); // 2
我们将window对象的引用传递进去,但将参数命名为global,因此在代码风格上对全局对象的引用变得比引用一个没有“全局”字样的变量更加清晰。了解更多块级作用域的知识,请点击这里。
第六章 闭包
1.应用闭包和循环(闭包循环相关问题)
for(va r i = 1;i <= 5;i++){ setTimeout(function timer(){ console.log(i); },i*1000); }
这道题相比很多人都见过,会造成的结果是每一个值都输出6。而我们想隔秒输出1,2,3,4,5,6,则需要产生模拟块级作用域,把每一个的i值保存在一个闭包里面,当setTimeout中定义操作执行时,访问对应的闭包即可。
方法1:
for(var i = 1;i <= 5;i++){ (function(i){ setTimeout(function timer(){ console.log(i); },i*1000); })(i); }
方法2:
for(var i =1;i <= 5;i++) { setTimeout(funcyion(i){ return function yimer(){ console.log(i); } })(i),i*1000); }
2.单例模式与闭包(设计模式:单例模式,工厂模式,观察者模式,装饰模式,原型模式,建造者模式,桥接模式,组合模式等)
单例模式,所谓的单例,就是只有一个实例。
实现过程:
1.可以将属性和方法放入到字面量里面,但是他的属性可以被外部修改,这往往不符合我们的需求。
var person = { name = "pengchangxi"; age = 22; getName:function(){ return this.name; }, getAge:function(){ return this.age; } }
2.有自己的私有方法/属性(创建一个小型代码库)
var person = (function() { var name = "pengchangxi"; var age =22; return { getName:funcyion(){ return name; } getAge:function(){ return age; } } })(); //访问私有变量 person.getAge();
3.调用时初始化
var person = (function() { //定义一个变量,用来保存实例 var instancce = null; var name = "pengchangxi"; var age = 22; //初始化方法 fucntion initial(){ return { getName:function(){return name;}, getAge:function() {return age}, } } return { getInstance:fucntion(){ if(!instance) { instance =initial(); } return instance; } } })(); //只有在使用的时候获得实例对象 var p1 = person.getInstance(); var p2 = person.getInstance(); console.log(p1===p2)
总结一下,关键的地方在于,在匿名函数中定义的instance变量只是用来保存实例,在getInstance方法中判断了是都对它进行重新赋值,当instance被第一次调用的时候他就会被赋initial的值,所以这是一种单例模式。
3. 模块化和闭包
模块化是建立在单例模式上面的,而且模块化与闭包息息相关。
1.每一个单例就是一个模块.
2.每一个模块要与其他模块交互,则必须有获取其他模块的能力,比如requirejs中的require和ES6 modules中的import
require var $ =require('jquery'); //ES6 modules import $ from 'jquery';
3.每一个模块都有一个对外接口,以保证与其他模块交互的能力。
var module_test = (function() { return { testfn1: function(){}, testfn1: function(){}, } })();
总结一下js模块化开发的流程(实现一个body背景色随着数字的递增在固定的三种颜色之间切换的效果)
(1)首先创建一个管理全局状态的模块,这个模块有自己的方法和属性,并且提供一个对外接口来保证外部访问内部私有属性和方法。
var module_status = (function() { var status = { number: 0, color: null } var get = function(prop) { return status[prop]; } var set =function(prop, value){ status [prop] =value; } return { get: get, set: set } })();
(2)创建一个模块,这个模块专门负责满足需求的实现(负责body背景色的改变)。
var module_color = (function() { **//假she用这种方式执行第二步引入模块, //类似于import state from 'module-status'** var state = module_status; var colors = ['orange', '#ccc', 'pink']; function render() { var color = colors[state.get ('number') % 3]; document.body.style.backgroundColor = color; } return { render: render } })();
(3)创建另外一个模块,负责显示当前的number值,用于参考和对比
var module_context = (function() { var state = module_status; function render() { document.body.innerHTML ='this Number is '+ state.get ('number '); } return { render: render } })();
(4)这些功能模块都创建完毕后,最后我们只需要穿创建一个主模块,这个主模块的目的是借助这些功能莫魁岸,来实现我想要实现的效果
var module_main = (function() { var state = module_status; var color = module_color; var context = module_context; setInterval(function() { var newNumber = state.get ('number') + 1; state.set('number', newNumber); color.render(); context.render(); }, 1000); })();
完整代码(初步学习和实现js模块化):
<script> var module_status = (function() { var status = { number: 0, color: null } var get = function(prop) { return status[prop]; } var set =function(prop, value){ status [prop] =value; } return { get: get, set: set } })(); var module_color = (function() { **//假she用这种方式执行第二步引入模块, //类似于import state from 'module-status'** var state = module_status; var colors = ['orange', '#ccc', 'pink']; function render() { var color = colors[state.get ('number') % 3]; document.body.style.backgroundColor = color; } return { render: render } })(); var module_context = (function() { var state = module_status; function render() { document.body.innerHTML ='this Number is '+ state.get ('number '); } return { render: render } })(); var module_main = (function() { var state = module_status; var color = module_color; var context = module_context; setInterval(function() { var newNumber = state.get ('number') + 1; state.set('number', newNumber); color.render(); context.render(); }, 1000); })(); </script>
以上乃模块化开发学习的初步过程,后续有待完善,如有错误,敬请指出!