灵活的JavaScript
用对象收编变量
var checkobject = {
checkName: function () {
}
}
真假对象
var checkobject = function () {
return {
checkName: function () {
}
}
}
var a = new checkobject();
a.checkEmail();
类
var checkobject = function () {
this.checkName = function () {
}
}
var a = new checkobject();
a.checkEmail();
面向对象编程
包装明星—封装
创建—个类
var Book = function(id, bookname, price){
this.id = id;
this.bookname= bookname;
this.price = price;
}
通过在类的原型(类也是一个对象,所以也有原型prototype)上添加属性和方法,有两种方式,一种是——为原型对象属性赋值,另一种则是将一个对象赋值给类的原型对象
Book.prototype.display = function(){
};
Book.prototype = {
display:function(){}
};
属性与方法封装
由于JavaScript的函数级作用域,声明在函数内部的变量以及方法在外界是访问不到的,通过此特性 即可创建类的私有变量以及私有方法。
通过this创建的属性可看作是对象共有属性和对象共有方法
通过this创建的方法不但可以访问共有属性与共有方法而且还能访问到 类(创建时)或对象自身的私有属性和私有方法
特权方法能访问到类的私有属 性和方法
在类外面通过点语法定义的属性以及方法被称为 类的静态共有属性和类的静态共有方法
类通过 prototype 创建的属性或者方法在类实例的对象中是 可以通过this访问到的,所以我们将prototype 对象中的属性和方法称为共有属性和共有方法
var Book = function (id, name, price) {
var num = 1;
function checkId() {};
this.getName = function () {};
this.getPrice = function () {};
this.setName = function () {};
this.setPrice = function () {};
this.id = id;
this.copy = function () {};
this.setName(name);
this.setPrice(price);
};
类的私有属性以及静态共有属性在新创建的对象中访问不到,而类的共有属性可以通过点语法访问到
闭包实现
var Book = (function () {
var bookNum = 0;
function checkBook(name) {}
return function (newId, newName, newPrice) {
var name, price;
function checkID(id) {}
this.getName = function () {};
this.getPrice = function () {};
this.setName = function () {};
this.setPrice = function () {};
this.id = newId;
this.copy = function () {};
bookNum++
if (bookNum > 100)
throw new Error('我们仅出版100本书.');
this.setName(name);
this.setPrice(price);
}
})();
Book.prototype = {
isJsBook: false,
display: function () {}
};
创建对象的安全模式
var Book = function (title, time, type) {
if (this instanceof Book) {
this.title = title;
this.time = time;
this.type = type;
} else {
return new Book(title, time, type);
}
}
var book = Book('Javascript', '2014', 'js');
继承
原型链继承
主要思想:重写子类的prototype属性,将其指向父类的实例。
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
优点:
- 简单,容易实现
- 继承关系纯粹
- 可以通过子类直接访问父类原型链属性和函数
缺点
- 子类的所有实例将共享父类的属性
- 在创建子类实现时,无法向父类的构造函数传递参数
- 无法实现多继承
- 为子类增加原型对象上的属性和函数时,必须放在new Animal()函数之后
构造继承
主要思想:在子类的构造函数中通过call()函数改变this的指向,调用父类的构造函数, 从而能将父类的实例的属性和函数绑定到子类的this上
Animal.call(this);
优点:
- 可解决子类实例共享父类属性的问题
- 创建子类的实例时,可以向父类传递参数
- 可以实现多继承
缺点:
- 实例只是子类的实例,并不是父类的实例
- 只能继承父类实例的属性和函数,并不能继承原型对象上的属性和函数
- 无法复用父类的实例函数
复制继承
主要思想:首先生成父类的实例,然后通过for…in遍历父类实例的属性和函数,并将其 依次设置为子类实例的属性和函数或者原型对象上的属性和函数
优点:
- 支持多继承
- 能同时继承实例的属性和函数与原型对象上的属性和函数
- 可以向父类构造函数中传递值
缺点:
- 父类的所有属性都需要复制,消耗内存。
- 实例只是子类的实例 ,并不是父类的实例。
组合继承
**主要思想:**组合了构造继承和原型继承两种方法,一方面在子类的构造函数中通过call() 函数调用父类的构造函数,另一方面,通过改变子类 的prototype属性,继承父类的原型对象上的属性和函数
优点:
- 既能继承父类实例的属性和函数,又能继承原型对象的属性和函数。
- 即是子类的实例,又是父类的实例。
- 不存在引用属性共享的问题
- 可以向父类的构造函数中传递参数
缺点:
- 为父类的实例属性会绑定两次。
寄生组合继承
var Super = function(){};
Super.prototype = Animal.prototype;
工厂模式
简单工厂模式
var Basketball = function () {
this.intro = '篮球盛行于美国';
}
Basketball.prototype = {
getMember: function () {
console.log('每个队伍需要5名队员');
},
getBallsize: function () {
console.log('篮球很大');
}
}
var sportsFactory = function (name) {
switch (name) {
case 'NBA':
return new Basketball();
case 'wordcup':
return new Football();
}
}
一个对象有时也可代替许多类
function createPop(type, text) {
var o = new object();
o.content = text;
o.show = function () {
};
if (type == 'alert') {
}
if (type == 'prompt') {
}
if (type == 'confirm') {
}
return o;
}
var userNameAlert = createPop('alert', '用户名只能是26个字母和数字');
工厂方法模式
通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实 例。
安全的工厂方法
var Factory = function (type, content) {
if (this instanceof Factory) {
var s = new this[type](content);
return s;
} else {
return new Factory(type, content);
}
}
Factory.prototype = {
Java: function (content) {
},
Javascript: function (content) {
},
UI: function (content) {
this.content = content;
(function (content) {
var div = document.createElement('div');
div.innerHTML = content;
div.style.border = '1px solid red';
document.getElementById('container').appendchild(div);
})(content);
},
php: function (content) {
}
};
抽象工厂模式
带头模范—抽象类
通过对类的工厂抽象使其业务用于对产品类簇的创建,而不 负责创建某一类产品的实例。(手动地抛出错误来模拟抽象类。)
var car = function () {};
car.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getspeed: function () {
return new Error('抽象方法不能调用');
}
};
幽灵工厂—抽象工厂模式
作为父类来创建一些子类。
var VehicleFactory = function (subType, superType) {
if (typeof VehicleFactory[superType] === 'function') {
function F() {};
F.prototype = new VehicleFactory[superType]();
subType.constructor = subType;
subType.prototype = new F();
} else {
throw new Error('未创建该抽象类');
}
}
VehicleFactory.car = function () {
this.type = 'car';
};
VehicleFactory.car.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getspeed: function () {
return new Error('抽象方法不能调用');
}
};
VehicleFactory.Bus = function () {
this.type = 'bus';
};
VehicleFactory.Bus.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getPassengerNum: function () {
return new Error('抽象方法不能调用');
}
};
VehicleFactory.Truck = function () {
this.type = 'truck';
};
VehicleFactory.Truck.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getTrainload: function () {
return new Error('抽象方法不能调用');
}
}
原型模式
用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象 的属性以及方法。
将可复用的、可共享的、耗时大的从基类中提出来然后放在其原型中,然后子类通过 组合继承或者寄生组合式继承而将方法和属性继承下来,对于子类中那些需要重写的方法进行重写,这 样子类创建的对象既具有子类的属性和方法也共享了基类的原型方法
创建一个焦点图
var LoopImages = function (imgArr, container) {
this.imagesArray = imgArr;
this.container = container;
}
LoopImages.prototype = {
createImage: function () {
console.log('LoopImages createImage function');
},
changeImage: function () {
console.log('LoopImages changeImage function');
}
}
var slideLoopImg = function (imgArr, container) {
LoopImages.call(this, imgArr, container);
}
slideLoopImg.prototype = new LoopImages();
slideLoopImg.prototype.changeImage = function () {
console.log('slideLoopImg changeImage function');
}
var FadeLoopImg = function (imgArr, container, arrow) {
LoopImages.call(this, imgArr, container);
this.arrow = arrow;
}
FadeLoopImg.prototype = new LoopImages();
FadeLoopImg.prototype.changeImage = function () {
console.log('FadeLoopImg changeImage function');
}
console.log(fadeImg.container);
fadeImg.changeImage();
单体模式
单体模式
提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一变量进行 访问。
优点
- 可以用来划分命名空间,减少全局变量的数量。
- 使用单体模式可以使代码组织的更为一致,使代码容易阅读和维护.
- 可以被实例化,且实例化一次。
单体模式是一个用来划分命名空间并将一批属性和方法组织在一起的对象,如果它可 以被实例化,那么它只能被实例化一次。
var Singleton = function (name) {
this.name = name;
this.instance = null;
};
Singleton.prototype.getName = function () {
return this.name;
}
function getInstance(name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
var a = getInstance("aa");
var b = getInstance("bb");
console.log(a === b);
console.log(a.getName());
console.log(b.getName());
理解使用代理实现单列模式的好处
具体的业务逻辑分开了,代理只管代理的业务逻辑,在这里代理的作用是实例化对象,并且只实例化一次;
var CreateDiv = function (html) {
this.html = html;
this.init();
}
CreateDiv.prototype.init = function () {
var div = document.createElement("div");
div.innerHTML = this.html;
document.body.appendChild(div);
};
var ProxyMode = (function () {
var instance;
return function (html) {
if (!instance) {
instance = new CreateDiv("我来测试下");
}
return instance;
}
})();
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
console.log(a === b);
代理模式
代理是一个对象,它可以用来控制对本体对象的访问,它与本体对象实现了同样的接口,代理对象会把所有的调用方法传递给本体对象的;
使用代理模式 实现单体模式的实例化,其他的事情就交给本体对象去处理;
优点
- 代理对象可以代替本体被实例化,并使其可以被远程访问;
- 它还可以把本体实例化推迟到真正需要的时候;对于实例化比较费时的本体对象,或者因为尺寸比 较大以至于不用时不适于保存在内存中的本体,我们可以推迟实例化该对象;
var TeaAndMilkGirl = function (name) {
this.name = name;
};
var Ceo = function (girl) {
this.girl = girl;
this.sendMarriageRing = function (ring) {
console.log("Hi " + this.girl.name + ", ceo送你一个礼物:" + ring);
}
};
var ProxyObj = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
(new Ceo(this.girl)).sendMarriageRing(gift);
}
};
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("结婚戒");
虚拟代理实现图片的预加载
var myImage = (function () {
var imgNode = document.createElement("img");
document.body.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
}
}
})();
var ProxyImage = (function () {
var img = new Image();
img.onload = function () {
myImage.setSrc(this.src);
};
return {
setSrc: function (src) {
myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif");
img.src = src;
}
}
})();
ProxyImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX130-200.png");
优点:
- 用户可以放心地请求代理,他们只关心是否能得到想要的结果。假如我门不需要代理对象的话,直 接可以换成本体对象调用该方法即可。
- 在任何使用本体对象的地方都可以替换成使用代理。
虚拟代理合并http请求
通过一个代理函数收集一段时间内(比如说2-3秒)的所有 id,一次性发Ajax请求给服务器,相对来说网络请求降低了, 服务器压力减少了
var mainFunc = function (ids) {
console.log(ids);
};
var proxyFunc = (function () {
var cache = [],
timer = null;
return function (checkboxs) {
if (timer) {
return;
}
timer = setTimeout(function () {
for (var i = 0, ilen = checkboxs.length; i < ilen; i++) {
if (checkboxs[i].hasAttribute("isflag")) {
var id = checkboxs[i].getAttribute("data-id");
cache[cache.length] = id;
}
}
mainFunc(cache.join(','));
clearTimeout(timer);
timer = null;
cache = [];
}, 2000);
}
})();
var checkboxs = document.getElementsByClassName("j-input");
for (var i = 0, ilen = checkboxs.length; i < ilen; i += 1) {
(function (i) {
checkboxs[i].onclick = function () {
if (this.checked) {
this.setAttribute("isflag", 1);
} else {
this.removeAttribute('isflag');
}
proxyFunc(checkboxs);
}
})(i);
}
缓存代理
对第一次运行时候进行缓存,当再一次运行相同的时候,直接从缓存里面取, 这样做的好处是避免重复一次运算功能,如果运算非常复杂的话,对性能很耗费,那么使用缓存对象可 以提高性能
var mult = function () {
var a = 1;
for (var i = 0, ilen = arguments.length; i < ilen; i += 1) {
a = a * arguments[i];
}
return a;
};
var plus = function () {
var a = 0;
for (var i = 0, ilen = arguments.length; i < ilen; i += 1) {
a += arguments[i];
}
return a;
}
var proxyFunc = function (fn) {
var cache = {};
return function () {
var args = Array.prototype.join.call(arguments, ',');
if (args in cache) {
return cache[args];
}
return cache[args] = fn.apply(this, arguments);
}
};
var proxyMult = proxyFunc(mult);
console.log(proxyMult(1, 2, 3, 4));
console.log(proxyMult(1, 2, 3, 4));
var proxyPlus = proxyFunc(plus);
console.log(proxyPlus(1, 2, 3, 4));
console.log(proxyPlus(1, 2, 3, 4));
职责链模式
由多个不同的对象组成的,发送者是发送请求的对象,而接收者则是链中那些接收这种请求并 且对其进行处理或传递的对象。请求本身有时候也可以是一个对象,它封装了和操作有关的所有数据,
优点:消除请求的发送者与接收者之间的耦合。
基本实现流程:
- 发送者知道链中的第一个接收者,它向这个接收者发送该请求。
- 每一个接收者都对请求进行分析,然后要么处理它,要么它往下传递。
- 每一个接收者知道其他的对象只有一个,即它在链中的下家(successor)。
- 如果没有任何接收者处理请求,那么请求会从链中离开。
比如对象A给对象B发请求,如果B对象不处理,它就会把请求交给C,如果C对象不处理的话,它就会把请求交给D,当然没有任何对象处理该请求的话,那么请求就会从链中离开。
同步责任链:让每个节点函数同步返回一个特定的值”nextSuccessor”,来表示是否把请 求传递给下一个节点
function order500(orderType, isPay, count) {
if (orderType == 1 && isPay == true) {
console.log("亲爱的用户,您中奖了100元红包了");
} else {
order200(orderType, isPay, count);
}
};
function order200(orderType, isPay, count) {
if (orderType == 2 && isPay == true) {
console.log("亲爱的用户,您中奖了20元红包了");
} else {
orderNormal(orderType, isPay, count);
}
};
function orderNormal(orderType, isPay, count) {
if (count > 0) {
console.log("亲爱的用户,您已抽到10元优惠卷");
} else {
console.log("亲爱的用户,请再接再厉哦");
}
}
该代码违反了面向对象中的 开放-封闭原则
异步责任链:给Chain类再增加一个原型方法Chain.prototype.next,表示手动传递请求给职责链中的一 下个节点
function Fn1() {
console.log(1);
return "nextSuccessor";
}
function Fn2() {
console.log(2);
var self = this;
setTimeout(function () {
self.next();
}, 1000);
}
function Fn3() {
console.log(3);
}
var Chain = function (fn) {
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function (successor) {
return this.successor = successor;
}
Chain.prototype.passRequest = function () {
var ret = this.fn.apply(this, arguments);
if (ret === 'nextSuccessor') {
return this.successor &&
this.successor.passRequest.apply(this.successor, arguments);
}
return ret;
}
Chain.prototype.next = function () {
return this.successor &&
this.successor.passRequest.apply(this.successor, arguments);
}
var chainFn1 = new Chain(Fn1);
var chainFn2 = new Chain(Fn2);
var chainFn3 = new Chain(Fn3);
chainFn1.setNextSuccessor(chainFn2);
chainFn2.setNextSuccessor(chainFn3);
chainFn1.passRequest();
优点
- 解耦了请求发送者和N个接收者之间的复杂关系,不需要知道链中那个节点能处理你的请求,所以 你 只需要把请求传递到第一个节点即可。
- 链中的节点对象可以灵活地拆分重组,增加或删除一个节点,或者改变节点的位置都是很简单的事 情。
- 我们还可以手动指定节点的起始位置,并不是说非得要从其实节点开始传递的。
缺点:
? 职责链模式中**多了一点节点对象,**可能在某一次请求过程中,大部分节点没有起到实质性作用, 他们的作用只是让请求传递下去,从性能方面考虑,避免过长的职责链提高性能。
命令模式
命令指的是一个执行某些特定事情的指令。
使用命令模式来消除发送者与接 收者的代码耦合关系。
理解宏命令
宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。 其实类似把页面的所有函数方法放在一个数组里面去,然后遍历这个数组,依次 执行该方法的。
观察者模式和发布订阅模式
观察者模式
观察者模式定义对象间一对多的依赖关系,使得每当一个对象改变状态,则依赖它的对象都会得到通知并自动更新
观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。
例子:
Subject:面试公司,Observers:面试者,Observer1,2,3,
Subject用来维护Observers,为某些event(职务空缺)来通知(notify())观察者
发布-订阅模式
订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
在观察者模式中,Subject就像一个发布者(Publisher),观察者(Observers)完全和订阅者(Subscriber)关联,subject通知观察者就像一个发布者通知他的订阅者,只不过发布者需要通过信息中介这个第三方组件来将发布者和订阅者串联起来,因为发布者和订阅者都不知道对方的存在。
发布订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
如何实现发布订阅模式?
- 建立一个对象
- 在该对象上建立一个缓存列表(调度中心)
- on方法用来把函数fn都加到缓存列表中(订阅者注册事件到调度中心)
- emit方法取到arguments里第一个当作event,根据event值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
- off方法能够根据event值取消订阅(取消订阅)
- once方法只监听一次,调用完毕后删除缓存函数(订阅一次)
let eventEmitter={};
eventEmitter.list={};
eventEmitter.on =function (event,fn) {
let _this=this;
(_this.list[event]||(_this.list[event]=[])).push(fn);
return _this;
}
eventEmitter.emit=function () {
let _this =this;
let event =[].shift.call(arguments);
fns=_this.list[event];
if(!fns ||fns.length ===0){
return false;
}
fns.forEach(fn=>{
fn.apply(_this,arguments);
});
return _this;
};
function user1(content) {
console.log('用户1订阅了:',content);
}
function user2(content) {
console.log('用户2订阅了:',content);
}
eventEmitter.on('article', user1);
eventEmitter.on('article', user2);
eventEmitter.emit('article','JavaScript发布-订阅模式');
观察者模式和发布订阅模式的区别
- 观察者模式中,观察者知道Subject,subject一直保持对Observers进行记录,发布订阅模式中,发布者和订阅者不知道对方的存在,只能通过信息中介进行通信
- 发布订阅模式中,组件是松散耦合的,正好和观察者模式相反
- 观察者模式大多数是同步的,好比事件触发,subject就会去调用观察者的方法,而发布-订阅模式大多数是异步的(使用消息队列)
- 观察者必须在单个应用程序地址空间实现,而发布订阅更像交叉应用模式
中介者模式
所有的相关 对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要 通知中介者对象即可
核心
使网状的多对多关系变成了相对简单的一对多关系(复杂的调度处理都交给中介者)
实现
多个对象,指的不一定得是实例化的对象,也可以将其理解成互为独立的多个项。当这些项在处理 时,需要知晓并通过其他项的数据来处理。
如果每个项都直接处理,程序会非常复杂,修改某个地方就得在多个项内部修改我们将这个处理过 程抽离出来,封装成中介者来处理,各项需要处理时,通知中介者即可。
常用算法
|