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> 以上乃模块化开发学习的初步过程,后续有待完善,如有错误,敬请指出!
