面向对象之闭包及设计模式的简单介绍day06
闭包及设计模式的简单介绍
1.作用域变量搜索规则
在函数内部声明变量不会覆盖上一层作用域中同名的变量
总结作用域的画法:
-
(1) 先画出0及级作用域链
001 先找出有哪些全局的变量 a f1 f2 b
002 连线(箭头 同级别的变量互相访问) -
(2) 画出1级作用域链
001 找函数 f1 延伸出一条线
002 再在这条线上面画出当前函数内部的所有变量
-
(3) 重复第二个步骤
作用域:
- 全局作用域
- 函数作用域
- 在代码中函数可以创建作用域
- 函数内部又可以声明函数,创建了新的作用域
- 函数内部的函数内部又可以声明函数
作用域的访问规则:
- 01 先在自己的作用域中查找,如果找不到那么就去上一级查找
- 02 在外部作用域中不能访问函数内部作用域
对象:实例属性|方法 原型属性|方法
obj.name
eg:
<script>
var a = 'testA';
function f1(){
function f3(){
var c = 'testC';
}
}
function f2(){
}
var b = 'testB';
</script>
<script>
var a = 'testA';
function test1(){
var a ;
console.log(a);//undefined
a = 20;
console.log(a);//20
}
console.log(a);//testA
test1();//undefined 20
console.log(a);//testA
</script>
图形解析:
作用域链的图解
eg:图为上图:
<script>
var a = "testA";
function f1() {
var b = "testB";
function f2() {
var i ;
var k ;
}
}
function f3() {
var c = "testC";
function f4() {
var d = "testD";
}
function f5() {
}
var e = "e"
}
</script>
2.闭包的简单介绍
闭包: 封闭+包裹
解决问题:
延长变量的生命周期 (变量创建-销毁)
提供一种间接访问函数封闭空间内部数据的方法.
需求: 需要获取a的值:
- 01 直接返回就可以了? 解决不了.一次性的数据,如果调用多次函数返回的其实并不是同一个数据 +不能修改(设置)变量的值
- 02 闭包解决 (把要访问的数据使用函数包裹返回)
eg:
<script>
function test(){
var a= 10;
return a;
}
var x = test();
console.log(x);
var y = test();//10
console.log(y);
</script>
<script>
function test(){
var obj = {};
return obj;
}
var x = test();
var y = test();
console.log(x == y);//false
</script>
3.闭包获取数据以及闭包设置数据
eg:
<script>
function test(){
var a = 10;
return function (value){
if(value != undefined){
a = value;
}
return a;
}
}
var func = test();
console.log(func(30));//30
</script>
闭包如何获取函数内部的多个数据?
解决方案01
<script>
function test(){
var name = '张三';
var age = 88;
var bookName = '未知书';
return function (){
return [name,age,bookName];
}
}
var func = test();
console.log(func()[0]);
console.log(func()[1]);
console.log(func()[2]);
</script>
解决方案2
<script>
function test(){
var name = '张三';
var age = 88;
var bookName = '未知书';
return [function (){
return name;
},function (){
return age;
},function (){
return bookName;
}]
}
var func = test();
console.log(func[0]());
console.log(func[1]());
console.log(func[2]());
</script>
解决方案3
<script>
function test(){
var name = '张三';
var age = 88;
var bookName = '未知书';
return {
getName:function (){
return name;
},
getAge:function (){
return age;
},
getBookName:function (){
return bookName;
},
setName:function (nameValue){
name = nameValue;
},
setAge:function (ageValue){
age = ageValue;
},
setBookName:function (bookNameValue){
bookName = bookNameValue;
}
}
}
console.log('_________________');
var func = test();
console.log(func.getName());
console.log(func.getAge());
console.log(func.getBookName());
//设置数据
func.setName('鲁迅');
func.setAge('20');
func.setBookName('骆驼祥子');
console.log(func.getName());
console.log(func.getAge());
console.log(func.getBookName());
</script>
4.闭包的作用
闭包的作用:
- 01 提供一种间接访问(读写)函数内部封闭空间中数据的方法
- 02 延长变量的生命周期
- 03 保护数据让代码干呢更加安全和稳定 有时候在设置数据之前需要校验数据
eg:
<script>
function foo(){
var age = 0;
return {
getAge:function (){
return age;
},
setAge:function (aT){//校验数据
if(aT<0){
aT = 0;
}
age = aT;
}
}
}
var test = foo();
test.setAge(-20);
console.log(test.getAge());//0
</script>
5.setTimeout和闭包的执行
定时器:
- setTimeout :一次定时器 ,只会执行一次(延迟执行函数)
参数:第一个参数:要执行的任务 ,第二个参数:间隔时间(单位毫秒 1000ms = 1s)
- setInterval 每隔固定的时间就执行一次
eg:
<script>
var now = new Date();
console.log(now);
setTimeout(function(){
console.log('一次定时器',new Date());
},1000);
</script>
<script>
var now = new Date();
console.log(now);
setInterval(function(){
console.log('多次定时器',new Date());
},1000);
</script>
6.线程与进程等相关概念
队列: 先进先出
栈:先进后出(数据结构 存储结构)
进程: 系统中正在运行的一个应用程序
线程: 是在进程中真正执行任务的部分.一个进程中至少有一条线程,可以有多个线程;
eg:学校-老师
串行执行:一条线程执行多个任务,在执行的时候按照固定的顺序一个接着一个的执行
并发执行:多条线程一起执行多个任务,同一个时间有多个任务在同时执行
多线程:
进程中可以有多条线程
优点:速度快,效率高 缺点:安全性差
主线程: 在进行开启的时候默认会创建一条线程,是主线程
并行和并发的区别:
并发: 多条线程同时执行任务的现象
并行: 同时执行多个任务的能力;
js本身是单线程
局限性: 如果要执行多个任务,只能串行的执行任务
js的主要任务(顺序):
- 01 渲染任务
- 02 脚本中的主要代码
- 03 事件处理任务(定时器事件|点击事件等等)
eg:
<script>
for(var i= 0;i<10;i++){
//执行定时器打印
/* (function (j){
setTimeout(function (){
console.log(j);
},0)
})(i);*/
setTimeout((function (j){
return function (){
console.log(j);
}
})(i),0)
}
</script>
7.闭包在点击事件的应用
eg:
<body>
<div>我是第1个div</div>
<div>我是第2个div</div>
<div>我是第3个div</div>
<div>我是第4个div</div>
<div>我是第5个div</div>
<div>我是第6个div</div>
<div>我是第7个div</div>
<div>我是第8个div</div>
<div>我是第9个div</div>
<div>我是第10个div</div>
<script>
var divs = document.getElementsByTagName('div');
for(var i = 0;i<divs.length;i++){
var div = divs[i];
/* (function (j){
div.onclick = function (){
alert('我是第'+j+'个标签');
}
})(i);*/
div.onclick = (function (j){
return function (){
alert('我是第'+j+'个标签');
}
})(i);
}
</script>
</body
8.函数的特殊性
函数: 可以被调用()
- 本质是一个对象,因此凡是可以使用对象的地方都可以使用函数,函数可以作为其他函数的参数,也可以作为其他函数的返回值
- 函数可以创建作用域
- 函数可以有自己的方法和属性
函数的创建方法:
-
01 函数声明 function 函数名(参数){函数体}
-
02 表达式
01 var func = function (){} 匿名的函数表达式
02 var func = function name(){} 命名函数表达式
-
03 构造函数 var func = new Function()
注意:函数有名称:name属性
eg:
<script>
var func01 = function (){};
var func02 = function funcName(){};
//注意:函数有名称:name属性
console.log(func01.name);//func01 注意:在火狐浏览器中为空
console.log(func02.name);//funcName
</script>
9.函数的回调(作为参数的传递)
- 函数作为其他函数的参数
回调: 回过头来调用
- 函数是第一型对象(因此可以使用对象的地方都可以使用函数)
- 01 函数可以作为函数的参数
- 02 函数可以作为函数的返回值
-
回调函数(回调),当我们把某个参数传递给另一个函数的时候,这个函数就称为回调函数
eg: <script> function demo(callBack){ //其他代码 callBack(); }; function foo(){ console.log('foo!'); } demo(foo);//foo! </script>
分析代码:
原因: 函数调用方式改变
解决: call | apply
eg:
<script>
var name = '我是测试代码';
function demo(callBack){
callBack();//使用回调函数
}
function func(){
var name = '张三';
console.log('只是一个回调函数' + this.name);
}
//注意点:window 有一个属性为name,并且为空.
/*console.log(window.name);*/
demo(func);//只是一个回调函数 我是测试代码
</script>
<script>
//如果回调函数是某个对象的方法,而该对象方法中使用了this指针,那么该方法作为回调函数来使用的时候,需要注意this
var name = '我是测试代码';
function demo(callBack,callBackObj){
//判断
if(typeof callBack == 'string'){//字符串
callBack = callBackObj[callBack];
}
if(typeof callBack == 'function'){//函数
callBack.call(callBackObj);//借用callBack的方法
}
//其他代码
}
var obj = {
name:'张三',
show:function (){
console.log('show'+ this.name);
}
};
var o = {
name:'李四',
show:function (){
console.log('show'+ this.name);
}
};
obj.show();
demo(obj.show,obj);//show 张三
demo(o.show,o);
demo('show',o);
demo('show',obj);
</script>
10.函数作为返回值
eg:
//使用闭包实现一个计数器(在该实例中)
//通过调用返回值(一个函数)
<script>
function countF(){
var count = 0;
return function (){
return count++;
}
}
var next = countF();
console.log(next());
console.log(next());
console.log(next());
console.log(next());
console.log(next());
console.log(next());
</script>
11.自定义函数(惰性函数)
惰性函数定义:
- 特点是在第二次调用函数的时候这个函数才被正确的定义;第一次调用函数的时候只是做了一些初始化的处理
- 特征:可以实现函数的自我更新
注意点:
- 01 实现自我更新之后函数上面的属性会丢失
- 02 把函数赋值给变量或者是对象的方法之后,以变量或者是对象的方法来调用函数的时候,不会实现自我更新
eg:
<script>
function foo(){
console.log('foo!');
//初始化操作
foo = function (){
console.log('foo!','foo!');
}
}
foo.des = 'foo的描述信息';
/* foo();//foo!
foo();//foo! foo!
foo();//foo! foo!*/
console.log(foo.des);//undefined
var demo = foo;
var obj = {};
obj.show = foo;
demo();//foo!
demo();//foo!
demo();//foo!
console.log('+++++++++++');
foo();//foo! foo!
obj.show();//foo!
obj.show();//foo!
obj.show();//foo!
</script>
12.即时函数的补充以及初始化
基本写法[2]:
即时函数组成:
- 001 函数(函数体 + function + 参数 + 返回值)
- 002 ()包住函数
- 003 ()调用函数
其他的写法:! + _ ~
即时函数可以接受参数也可以返回数据
eg:
<script>
//01第一种写法
/*(function (){
console.log('第一种写法');
})()
//02第二种写法
(function (){
console.log('第二种写法');
}())
//03 其他写法
!(function (){
console.log('不建议的演示');
})();
+(function (){
console.log('不建议的演示');
})();
-(function (){
console.log('不建议的演示');
})();
~(function (){
console.log('不建议的演示');
})();*/
var result = (function (string){
var str = 'hello'+ string;
console.log(str);
return str;
})('demo');
console.log(result);
</script>
即时对象初始化
写法:
- 01 提供一个对象{}属性和方法
- 02 在对象中提供一个初始化的方法,主要做一些初始化操作
- 03 在对象外部添加(),包装成一个表达式
- 04 直接使用.init()来进行初始化处理
eg:
<script>
({
name:'张三',
age:99,
showName:function (){
console.log(this.name);
},
showAge:function (){
console.log(this.age);
},
init:function (){
//初始化处理
this.showName();
this.showAge();
}
}).init();
console.log(name);//空
console.log(age);//报错
#### 二.设计模式的简单介绍
1. 设计模式:
- 设计模式本身是一种解决特定问题的套路
- 设计模式的分类:23种
- 设计模式的来源于建筑学邻域
- 设计模式的由来:四人帮(设计模式)
列举:单利模式 | 代理模式 |观察者模式 |工厂模式 | 策略模式 | 桥接模式 ......
2.工厂模式的说明
核心过程:
- 01 提供父构造函数
- 02 设置父构造函数的原型对象(属性|方法) 目的的共享
- 03 为父构造函数提供一个静态工厂方法(每次要生产产品就调用这个方法)
- 001 接收产品的类型
- 002 判断是否支持生产该类型的产品
- 003 设置原型继承,获得原型对象上面的方法
- 004 生产该类型的一个产品
- 005 返回对象
- 04 定制合作伙伴(子构造函数)
- 05 调用父构造函数的工厂函数来创建对象
eg:
<script>
//01 提供父构造函数
function MakePhone(){};
//02 设置父构造函数的原型对象(属性|方法) 目的的共享
MakePhone.prototype.desLog = function (){
console.log('我的广告是:' + this.des);
};
//03 为父构造函数提供一个静态工厂方法(每次要生产产品就调用这个方法)
MakePhone.factory = function (stringType){
//001 接收产品的类型
var phoneType = stringType;
if(phoneType == undefined){
throw '请下订单';
}
//002 判断是否支持生产该类型的产品
if(typeof MakePhone[phoneType] != 'function'){
throw '和该品牌没有任何的合作关系,不支持生产';
};
//003 设置原型继承,获得原型对象上面的方法
MakePhone[phoneType].prototype = MakePhone.prototype;
//004 生产该类型的一个产品
var newPhone = new MakePhone[phoneType]();
//005返回对象
return newPhone;
};
//04 定制合作伙伴(子构造函数)
MakePhone.iphone = function (){
this.des = '最贵的手机,最垃圾的系统';
};
MakePhone.vivo = function (){
this.des = '充电五分钟,通话两小时';
};
MakePhone.oppo = function (){
this.des = '照亮你的美';
};
//05调用父构造函数的工厂函数来创建对象
var iphone = MakePhone.factory('iphone');
iphone.desLog();
var oppo = MakePhone.factory('oppo');
oppo.desLog();
var vivo = MakePhone.factory('vivo');
vivo.desLog();
</script>