| 面向对象(类型判断、封装、多态、闭包、高阶函数)
| 单例模式 - 命名空间、只被实例化一次
| 工厂模式 - 加工在输厂的过程
| 外观模式 - 将复杂的接口统一,用于低层兼容
| 适配器模式 - 将一个接口转化为另一个接口
| 代理模式 - 一个对象不能直接访问另一个对象时,使用代理对象
| 装饰者模式 - 不改变原有对象的,并对其进行拓展
| 桥接模式 - 提取公用的方法,并通过桥接来进行连接
| 享元模式 -
| 观察者模式 -
| 状态模式 -
| 访问者模式 -
|
| 组合模式
| 策略模式 - 将一组算法封装起来,使其可以相互之间替换
| 中介者模式
类型判断
只能将带有duckSinging()方法的对象加入到 choir 数组中
<script>
// 方法一
var duck = {
duckSinging: function(){
console.log('嘎嘎嘎');
}
}
// 方法二
var chicken = {
chicken: function(){
console.log('嘎嘎嘎');
}
}
// 存储带有duckSinging()方法的对象
var choir = [];
var joinChoir = function(animal){
if(animal && typeof animal.duckSinging == 'function'){ // 只能加入带有duckSinging()方法的对象
choir.push(animal);
console.log('恭喜入合唱团');
console.log('合唱团已有成员数量', choir.length);
}
}
joinChoir(duck);
joinChoir(chicken);
</script>
封装
封装: 目的是将信息隐藏, 其它语言通过private、public、protected来处理访问权限
js没有这些关键字通过变量的作用域来实现封装
<script>
var Book = funtcion(id, bookname, price){
// 私有属性
var name = 1;
// 私有方法
function checkId(){ }
// 公有属性
this.id = id;
this.bookname = bookname;
// 公有方法
this.copy = function(){}
// 特权方法
this.getName() = function{ return name }
}
// 在原型 (prototype) 来添加属性和方法,有两种方式
// 1、为原型一一添加属性和方法
// 2、将一个对象赋给原型
Book.prototype.display = function(){
// 展示这本书
}
Book.prototype = { // 这种方法会将prototype下的constructor被覆盖掉
display: function(){
// 展示这本书
}
}
</script>
<script>
// 闭包实现 1
var Book = (function(){
// 静态私有变量
var bookNum = 0;
// 静态私有方法
function checkBook(name){}
// 返回构造函数
return function(newId, newName, newPrice){
var name, price;
function checkId(name){}
}
});
Book.prototype = {
isJSBook: false,
display: function(){}
}
// 闭包实现 2
var Book = (function(){
// 私有变量
var bookNum = 0;
// 私有方法
function checkBook(name){ }
// 创建类
function _book(newId, newName, newPrice){}
_book.prototype = {}
return _book
})();
</script>
继承
一、类式继承
将父类的实例赋给子类的原形,类式继承的原因: 类的原型对象作用就是为类的原型添加公有方法,但不能直接访问这些属性和方法,必须通过原型prototype来访问
<script>
// 父类
function Parent(){
this.name = 'siguang'
}
Parent.prototype.say = function(){
console.log(this.name);
}
// 子类
function Childer(){}
Childer.prototype = new Parent(); // 类式继承
// 上一步执行后prototype的constructor会指向Parent,因为constructor指向还是在Paremt类上
Childer.prototype.constructor = Childer;
Childer.prototype.getSubValue = function(){
console.log('getSubValue')
}
// 实例
var oParent = new Parent();
var oChilder = new Childer();
oParent.say(); // siguang
oChilder.say(); // siguang
console.log(oChilder.name); // siguang
oChilder.name = 'haha';
oChilder.say(); // haha
oChilder.getSubValue(); // getSubValue
</script>
二、构造函数式继承
/*
* 构造函数继承
* 继承非prototype下的属性和方法
*/
<script>
// 父类继承
function SuperClass(id){
this.books = ['javascript', 'html', 'css'];
this.id = id;
this.showId = function(){
console.log(this.id);
}
}
SuperClass.prototype.showBooks = function(){
console.log(this.books);
}
// 声明子类
function SubClass(id){
// 继承父类所有公用属性和方法(this下的内容),但不继承prototype下的属性和方法
SuperClass.call(this, id);
}
// SubClass.prototype.constructor = SubClass;
var sub1 = new SubClass(10);
var sub2 = new SubClass(20);
sub1.books.push('设计模式');
console.log(sub1.books); // ['javascript', 'html', 'css', '设计模式']
console.log(sub1.id); // 10
// sub1.showBooks(); // 报错,不
sub1.showId(); // 10
console.log(sub2.books); // ['javascript', 'html', 'css']
console.log(sub2.id); // 20
</script>
总结: 类似继承与构造函数式继承的区别
1、类似继承: ChildreClass.prototype = new ParentClass();
继承父类的公用属性和方法(this.的内容)和 prototype下的属性和方法, 需要改变constructor的名字
2、构造函数式继承: ParentClass.call(this);
只能继承公用属性和就去,不能继承父类prototype下的属性和方法, 不需要改变constructor的名字
三、原型继承
通过一个继承方法,创建一个过渡对象,将继承的父对象赋给过渡对象的原型,在返回过渡对象的实例
<script>
var Parent = function(){
this.blod = 20;
this.level = 1;
}
Parent.prototype.getBlod = function(){
console.log(this.blod, this.level);
}
Object.create = Object.create || function(obj){
var F = function(){}; // 声明一个过渡对象
F.prototype = obj; // 过渡对象的原型继承父对象
return new F(); // 返回过渡对象的一个实例,该实例的原型继承了父对象
}
var oParent = new Parent();
var oChilde = Object.create(oParent);
oChilde.blod = 30; // 如果clone不设置blod属性,会到prototype的指向中找,最终会在oParent中找到属性
oChilde.getBlod(); // 30 1
oParent.getBlod(); // 20 1
</script>
四、寄生式继承
就是将原型继承在二次封装,并返回一个对象
<script>
// 寄生式继承
// 声明基对象
var book = {
name: 'js book',
alikeBook: ['css book', 'html book']
}
function createBook(obj){
// 通过原型继承方式创建新对象
var o = new inhreitObject(obj);
// 拓展新对象
o.getName = function(){
console.log(this.name);
}
// 返回拓展新对象
return o;
}
function inhreitObject(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
var oCreateBook = createBook(book);
oCreateBook.getName(); // 'js book'
</script>
五、多继承
只适用于对象,不适用构造函数,继承某一个类,和继承多个类
<script>
// 单继承 属性复制
// 这种是浅复制,象jquery等框架实现了深度复制
var extend = function(target, source){
// 遍历源对象中的属性
for(var property in source){
// 将源对象中的属性复制到目标对象中
target[property] = source[property];
}
// 返回目标对象
return target;
}
var book = {
name: 'javascript 设计模式',
alike: ['css', 'html', 'javascript'],
type: {
name: '计算机',
money: 20
}
}
var anotherBook = {
color: 'blue'
}
extend(anotherBook, book);
console.log(anotherBook.name);
console.log(anotherBook.alike);
console.log(book, anotherBook);
// 多继承 多个对象继承 属性复制
var mix = function(){
var i = 1, // 从第二个参数起为被继承的对象
len = arguments.length, // 获取参数长度
target = arguments[0], // 第一个对象为目标对象
arg; // 缓存参数对象
// 遍历被继承的对象
for(; i<len; i++){
// 缓存当前对象
arg = arguments[i];
// 遍历被继承对象中的属性
for(var property in arg){
// 将被继承对象中的属性复制到目标对象中
target[property] = arg[property];
}
}
return target;
}
var book1 = {
name: 'javascript 设计模式',
alike: ['css', 'html', 'javascript'],
type: {
name: '计算机',
money: 20
}
}
var book2 = {
tag: '书'
}
var book3 = {
detail: '产品优势'
}
var newBook = mix(book1, book2, book3);
</script>
多态
多态: 就是调用同一对象,通过参数发出不同指令,执行不同的结果
<script>
var googleMap = {
show: function(){
console.log('开始渲染谷歌地图');
}
}
var baiduMap = {
show: function(){
console.log('开始渲染百度地图');
}
}
// 渲染地图
var renderMap = function(map){
// 如果对象下有show方法就执行
if(map.show instanceof Function){
map.show();
}
// 另一个判断类型模式
// if(map && typeof map.show == 'function'){
// map.show();
// }
}
renderMap(googleMap);
renderMap(baiduMap);
</script>
闭包、高阶函数
高阶函数至少满足以下条件之一
一、函数可以作为参数被传递
<script>
var getUserInfo = function(userId, callback){
$.ajax('http://xx.com/user?uid='+userId, function(data){
if(typeof callback == 'function'){
callback();
}
})
}
</script>
二、函数可以作为返回值输出
<script>
// 函数作为返回值输出
var isType = function(type){
return function(obj){
return Object.prototype.toString.call(obj) === '[object '+ type +']'
}
}
var str = "sdfsdf"
var isString = isType('String');
console.log(isString(str)); // true
</script>
全局变量的解决方法
避免全局函数: 1、对象方法 2、函数对象 3、构造函数
<script>
// 都是全局变量
function checkName(){
// 验证姓名
}
function checkEmail(){
// 验证邮箱
}
function checkPassword(){
// 验证密码
}
/*
* 方法一 对象收编全局变量
*/
var ChceckObject = {
checkName: function(){
// 验证姓名
},
checkEmail: function(){
// 验证邮箱
},
checkPassword: function(){
// 验证密码
}
}
/*
* 方法二 函数对象
*/
var CheckObject = function(){
return {
checkName: function(){
// 验证姓名
},
checkEmail: function(){
// 验证邮箱
},
checkPassword: function(){
// 验证密码
}
}
}
// 调用
var oCheck = CheckObject();
oCheck.checkName();
/*
* 方法三 构造函数写法
*/
var CheckObject = function(){
this.checkName = function(){
// 验证姓名
}
this.checkEmail = function(){
// 验证邮箱
}
this.checkPassword = function(){
// 验证密码
}
}
</script>
/* 创建型模式 */
单例模式
单例模式: 又被称为单体模式,只允许实例化一次的对象类
全局变量 var a = {}; 属于单例对象, 不属于单例模式
jquery就是一个很大的单例,js载入时被初始化一次
(function(){
var jquery = (function(){})();
window.jQuery = window.$ = jQuery;
})(window);
一、最简单的单例就是一个对象
var app = {
util: {},
tool: {},
ajax: {}
}
二、私有变量的单例模式,减少全局变量方法
1、使用命名空间
var MyApp = {};
MyApp.namespace = function(name){
var parts = name.split('.');
var current = MyApp;
for(var i in parts){
if(!current[parts[i]]){
current[parts[i]] = {}
}
current = current[parts[i]];
}
}
MyApp.namespace('event');
MyApp.namespace('dom.style');
2、使用闭包封装私有变量
var user = (function(){
var _name = 'sven',
_age = 30;
return {
getUserInfo: function(){
return _name +'-'+ _age;
}
}
})();
三、惰性单例模式
<script>
// 惰性单例模式 只有在需要的时候才会创建
var createLoginLayer = (function() {
var div;
return function() {
if (!div) {
div = document.createElement('div');
div.className = 'dialog';
div.innerHTML = '我是登录窗口';
div.style.display = 'none';
document.body.appendChild(div);
}
return div
}
})();
var oLoginBtn = document.querySelector('#loginBtn');
oLoginBtn.onclick = function(){
var oLayer = createLoginLayer();
oLayer.style.display = 'block';
}
</script>
工厂模式
工厂模式: 将不同的参数传入到工厂方法中,进行加工后,然后在返回新的产品
// 创建一个工厂函数,并将结果返回
function createPerson(name, age, sex){
var createObject = {};
// 加工
createObject = {
name: name,
age: age,
sex: sex,
showInfo: function(){
console.log('姓名:'+ this.name +" 年龄:" + this.age);
}
}
// 返回成品
return createObject;
}
var person = createPerson('siguang', 30, '男');
person.showInfo();
建造者模式 builder
将一个复杂对象的构建层与表示层相互分离
原型模式 prototype
用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性及方法
<script>
// 定义一个基本的图片轮播对象
var LoopImages = function(imgArr, container){
this.imgArr = imgArr;
this.container = container;
}
LoopImages.prototype = {
// 创建
createImage: function(){
console.log('loopImage createImage function');
},
// 切换方法
changeImage: function(){
console.log('LoopImage changeImage function');
}
}
// 定义一个上下滑动的效果
var SliderLoopImage = function(imgArr, container){
LoopImages.call(this, imgArr, container);
}
SliderLoopImage.prototype = new LoopImages(); // 就是类似继承,并将要改变的方法重写
// 重写切换方法
SliderLoopImage.prototype.changeImage = function(){
console.log('SlideLoopImg changeImage function');
}
var oSlider = new SliderLoopImage(['1.jpg', '2.jpg'], '#box');
oSlider.createImage();
oSlider.changeImage();
</script>
/* 结构型模式 */
外观模式
外观模式: 为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易
// 外观模式实现
function addEvent(dom, type, fn){
if(dom.addEventListener){
dom.addEventListener(type, fn, false);
}
else if(dom.attachEvent){
dom.attachEvent('on'+type, fn);
}
else{
dom['on'+type] = fn;
}
}
var myInpurt = document.querySelector('#myinput');
addEvent(myInpurt, 'click', function(){
console.log('绑定第一个事件')
})
适配器模式
适配器模式: 将一个类的接口转化成另外一个接口,使类之间的接口不兼容问题通过适配器得以解决
一、如果有jQuery框架,现在A框架与jQuery框架基本相同可以将两种框架适配
// 定义A框架
var A = jQuery || {};
A.g = function(id){
return document.getElementById(id);
}
A.on = function(id, type, fn){
var dom = typeofi id === 'String' ? A.g(id) : id;
if(dom.addEventListener){
dom.addEventListener(type, fn, false);
}
else if(dom.attachEvent){
dom.attachEvent('on'+type, fn);
}
else{
dom['on'+type] = fn;
}
}
二、jquery适配器
window.A = A = jQuery;
三、参数适配
function doSomeThing(obj){
var adapter = {
name: '雨夜',
titel: '设计',
age: 30,
color: 'pink',
size: 100,
prize: 50
}
for(var key in adapter){
adapter[key] = obj[key] || adapter[key]; // 处理如果obj没有的参数
}
return adapter; // 也可以使用extend();
}
代理模式
代理模式: 由于一个对象不能直引用另一个对象,所以需要一个代理对象来为两个对象之间起到中介作用
<script>
var Flower = function(){};
var xiaoming = {
sendFlower: function(target){
var flower = new Flower();
target.receiveFlower(flower);
}
}
// B代理小明去给A送花
var B = {
receiveFlower: function(flower){
A.listenGoodModd(function(){ // 侦听A的心情
A.receiveFlower(flower);
})
}
}
// A接收小明的花
var A = {
receiveFlower: function(flower){
console.log('收到花'+ flower);
},
listenGoodModd: function(fn){
setTimeout(function(){
fn()
}, 10000);
}
}
xiaoming.sendFlower(B);
</script>
装饰者模式
装饿者模式: 在不改变原对象的基础上,通过对其进行包装拓展(添加属性或方法)使原有对象可以满足用户的更复杂需求
<p>姓名: <input type="text" name="" id="username" /><span id="usernameError">Error username xxxx</span><br></p>
<p>密码: <input type="text" name="" id="password" /><span id="passwordError">Error password xxxx</span></p>
<script>
/*
* 装饰者模式: 在不改变原对象的基础上,通过对其进行包装拓展(添加属性或方法)使原有对象可以满足用户的更复杂需求
*/
// 点击文本框,显示提示信息,并保留原input上的事件
var username = document.querySelector('#username');
var password = document.querySelector('#password');
// 基础需求,点击文本框后会加入文本内容
username.onclick = function(){
this.value = '初始化username';
}.bind(username);
password.onclick = function(){
this.value = '初始化password';
}.bind(password);
// 新增的需求,点击后需要有提示消息,这里不破坏原有的功能
// 装饰者方法
var decorator = function(input, fn){
var input = document.querySelector(input);
// 判断事件是否已经绑定事件
if(typeof input.onclick == 'function'){
// 缓存input本身的处理的事件
var oldClickFn = input.onclick;
// 为事件定义新的事件, 执行将新好的方法全执行
input.onclick = function(){
oldClickFn();
fn();
}
}
else{
input.onclick = fn;
}
}
// 调用
decorator('#username', function(){
document.querySelector("#usernameError").style.display = 'block';
})
decorator('#password', function(){
document.querySelector("#passwordError").style.display = 'block';
})
</script>
桥接模式
桥接模式: 将一个功能有共用的部分抽取出来,将实现与抽出来共用方法,通过桥接方法链接在一起,这就是桥接模式
<div class="box">
<span>最新回答</span>
<span>热门回答</span>
<span>等待回答</span>
</div>
<script>
var box = document.querySelector('.box');
var spans = box.getElementsByTagName('span');
// 抽出的公用
function changeColor(dom, color, bg){
dom.style.color = color;
dom.style.backgroundColor = bg;
}
// 桥接模式
function bindSpan(){
var i = 0;
var len = spans.length;
for(i; i<len; i++){
spans[i].onmouseover = function(){
changeColor(this, '#ad2102', '#ffbb96');
}
spans[i].onmouseout = function(){
changeColor(this, '#ad2102', '#fff2e8');
}
}
}
bindSpan();
</script>
享元模式
享元模式: 运用共享技术有效地支持大量细粒的对象,避免对象间有相同内容造成多余的开销
/* 行为型设计模式 */
观察者模式
观察者模式 - 又称发布订阅模式或消息机制
状态模式
状态模式 - 当一个对象内部状态发生改变时,会导致行为改变
状态模式是解决程序中臃肿的分支判断语句问题,将每个分支转化一种状态独立出来
<script>
// 创建一个玛丽类
var MarryState = function(){
// 存储内部状态
var _currentState = {};
// 动作与状态方法映射
var states = {
jump: function(){
console.log('jump');
},
move: function(){
console.log('move');
},
shoot: function(){
console.log('shoot');
},
squat: function(){
console.log('squat');
}
};
// 动作控制类
var Action = {
// 改变状态方法
changeState: function(){
var arg = arguments;
_currentState = {};
if(arg.length){
for(var i=0, len=arg.length; i<len; i++){
_currentState[arg[i]] = true;
}
}
return this;
},
// 执行动作
goes: function(){
console.log('触发一次动作');
for(var i in _currentState){
states[i] && states[i](); // 如果动作存在并执行
}
return this;
}
}
return {
change: Action.changeState,
goes: Action.goes
}
}
MarryState()
.change('jump', 'shoot')
.goes()
</script>
策略模式
策略模式: 定义一系列算法,把他们封装起来,并使它们可以相互替换
策略模式由两部分组成:
1、策略类,策略类封装了具体的算法,并负责具体的计算过程
2、环境类,接受客户的请求,把请求委托给一个策略类
Example:
<script>
// 需求根据KPI等级返回奖金金额
// 定义策略类
var strategies = {
'S': function(salary){
return salary * 4;
},
'A': function(salary){
return salary * 3;
},
'B': function(salary){
return salary * 2;
}
}
// 环境类
var calculateBonus = function(level, salary){
return strategies[level] && strategies[level](salary);
}
console.log(calculateBonus('B', 2000)); // 4000
console.log(calculateBonus('S', 5000)); // 20000
</script>
职责链模式
职责链模式 - 解决请求的发送者与请求的接受者之间的耦合
命令模式
命令模式 - 将请求与实现解耦并封装成独立对象,从而使不同的请求对客户端的实现参数
迭代器模式
迭代器模式: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部
<script>
var isType = function(type){
var callType = Object.prototype.toString.call(type);
switch(callType){
case '[object Object]':
return 'object';
break;
case '[object Array]':
return 'array';
break;
default:
return false;
break;
}
}
var each = function(arr, callback){
if(isType(arr) == 'object'){
for(var key in arr){
callback.call(arr[key], key, arr[key]);
}
}
else if (isType(arr) == 'array'){
for(var i=0; i<arr.length; i++){
callback.call(arr[i], i, arr[i]);
}
}
}
each([1, 2, 3], function(index, val){
console.log(index, val);
})
each({name: 'siguang', age: 32, sex: '男'}, function(key, val){
console.log(key, val);
})
</script>
发布订阅模式模式
发布订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变,所有依赖于它的对象都将得到通知.
js中事件模型来替代传统的发布订阅模式
| http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html