你好!!
专注于为中小企业提供成都做网站、网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业洪山免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了超过千家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
书中也存在有偏差的情况···
//发布
$.publish("test", "a", "bds", "csdf", "dsdf");
改为
$.publish("test", ["a", "bds", "csdf", "dsdf"]);
javascript语言是单线程机制。所谓单线程就是按次序执行,执行完一个任务再执行下一个。
对于浏览器来说,也就是无法在渲染页面的同时执行代码。
单线程机制的优点在于实现起来较为简单,运行环境相对简单。缺点在于,如果中间有任务需要响应时间过长,经常会导致
页面加载错误或者浏览器无响应的状况。这就是所谓的“同步模式”,程序执行顺序与任务排列顺序一致。对于浏览器来说,
同步模式效率较低,耗时长的任务都应该使用异步模式;而在服务器端,异步模式则是唯一的模式,如果采用同步模式个人认为
服务器很快就会出现12306在高峰期的表现。。。。
异步模式的四种方式:
1.回调函数callback
所谓回调函数,就是将函数作为参数传到需要回调的函数内部再执行。
典型的例子就是发送ajax请求。例如:
$.ajax({
async: false,
cache: false,
dataType: 'json',
url: "url",
success: function(data) {
console.log('success');
},
error: function(data) {
console.log('error');
}
})
当发送ajax请求后,等待回应的过程不会堵塞程序运行,耗时的操作相当于延后执行。
回调函数的优点在于简单,容易理解,但是可读性较差,耦合度较高,不易于维护。
2.事件驱动
javascript可以称之为是基于对象的语言,而基于对象的基本特征就是事件驱动(Event-Driven)。
事件驱动,指的是由鼠标和热键的动作引发的一连串的程序操作。
例如,为页面上的某个
$('#btn').onclick(function(){
console.log('click button');
});
绑定事件相当于在元素上进行监听,是否执行注册的事件代码取决于事件是否发生。
优点在于容易理解,一个元素上可以绑定多个事件,有利于实现模块化;但是缺点在于称为事件驱动的模型后,流程不清晰。
3.发布/订阅
发布订阅模式(publish-subscribe pattern)又称为观察者模式(Observer pattern)。
该模式中,有两类对象:观察者和目标对象。目标对象中存在着一份观察者的列表,当目标对象
的状态发生改变时,主动通知观察者,从而建立一种发布/订阅的关系。
jquery有相关的插件,在这不是重点不细说了。。。。回头写个实现贴上来
4.promise模式
promise对象是CommonJS工作组提供的一种规范,用于异步编程的统一接口。
promise对象通常实现一种then的方法,用来在注册状态发生改变时作为对应的回调函数。
promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled)、已完成(resolved)和拒绝(rejected)。以CommonJS
Promise/A
标准为例,promise对象上的then方法负责添加针对已完成和拒绝状态下的处理函数。then方法会返回另一个promise对象,以便于形成promise管道,这种返回promise对象的方式能够支持开发人员把异步操作串联起来,如then(resolvedHandler,
rejectedHandler); 。resolvedHandler
回调函数在promise对象进入完成状态时会触发,并传递结果;rejectedHandler函数会在拒绝状态下调用。
Jquery在1.5的版本中引入了一个新的概念叫Deferred,就是CommonJS promise A标准的一种衍生。可以在jQuery中创建
$.Deferref的对象。同时也对发送ajax请求以及数据类型有了新的修改,参考JQuery API。
除了以上四种,javascript中还可以利用各种函数模拟异步方式,更有诡异的诸如用同步调用异步的case
只能用team里同事形容java和javascript的一句话作为结尾:
“写java像在高速路上开车,写javascript像在草原上开车”-------------以此来形容javascript这种无类型的语言有多自由
but,如果草原上都是坑。
用CSS可以做到,其实是一个DIV,里面包含一个input,submit或button
DIV背景或边框就是那个框
input是透明的无背景、无边框
button的背景就是那个订阅图标。
去研究一下他的源码就明细了:
CSS首先要定义:*{margin:0;padding:0;}
form ******
div class="byemail"
input id="header-subscribe-email" type="text" value="" class="f-text" name="email" autocomplete="off" /
input type="hidden" value="20" name="cityid" /
input type="submit" class="commit" value="订阅" /
/div
/form
感觉二者非常像,都是pub/sub机制,如何进行区分?分别在什么不同的场景中进行应用?
在Obsever模式中, 不存在封装约束的单一对象。Observer 和 Subject 必须合作才能维持约束。
Communication(通讯)模式由观察者和目标互联的方式决定:单一目标通常有很多观察者,有时一个目标的观察者是另一个观察者的目标
Mediator 和 Observer 都能促进松耦合,然后Mediator 模式通过限制对象严格通过Mediator 进行通信来实现这个个目的
Observer 模式创建观察者对喜爱那个,观察者对象向订阅它们的对喜爱那个发布其感兴趣的事件。
在GoF的原文中是这样描述观察者模式的:
One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves.
具体应用场景是,当subject的某个动作需要引发一系列不同对象的动作(比如你是一个班长要去通知班里的某些人),与其一个一个的手动调用触发的方法(私下里一个一个通知),不如维护一个列表(建一个群),这个列表存有你想要调用的对象方法(想要通知的人);之后每次做的触发的时候只要轮询这个列表就好了(群发),而不用关心这个列表里有谁,只用关心想让谁加入让谁退出
这个列表就叫做ObserverList,它有一些维护列表方法:
function ObserverList(){
this.observerList = [];
}
ObserverList.prototype.Add = function( obj ){};
ObserverList.prototype.Empty = function(){};
ObserverList.prototype.Count = function(){};
ObserverList.prototype.Get = function( index ){};
ObserverList.prototype.Insert = function( obj, index ){};
ObserverList.prototype.IndexOf = function( obj, startIndex ){};
ObserverList.prototype.RemoveAt = function( index ){};
而我们的subject只用关心两件事:1.维护这个列表,2.发布事件
function Subject(){
this.observers = new ObserverList();
}
Subject.prototype.AddObserver = function( observer ){
this.observers.Add( observer );
};
Subject.prototype.RemoveObserver = function( observer ){
this.observers.RemoveAt( this.observers.IndexOf( observer, 0 ) );
};
Subject.prototype.Notify = function( context ){
var observerCount = this.observers.Count();
for(var i=0; i observerCount; i++){
this.observers.Get(i).Update( context );
// 在这里假设的是列表里的每个对象都有update方法,但个人觉得这个列表里也可以是不同对象的不同方法,只要能接受当前上下文作为参数, 可以这样执行:
// subscription.callback.apply( subscription.context, args );
}
};
中介模式(Mediator Pattern)
让我们假设这样一个场景: 有一个Manager一声令下,需要让工人A和工人B开工,代码可以是这样的
Manager.start = function () {
A.work();
B.work();
}
其实还可以这么写,新增一个中介模块,这个模块有存储了Manager的常用命令比如start,stop,resume,每一个命令其实维护的也是一个列表,比如start的列表下存储了所有员工的start方法:
Mediator["start"] = [
{
name: 'A',
callback: 'work'
},
{
name: 'B',
callback: 'workAgain'
},
]
所以Manager的方法可以重写为
Manager.start = function () {
Mediator.publish('start') // publish 为触发命令函数,以此来触发start命令下维护的所有回调函数
}
代码细节就不展示了,主要体现这么一个机制,而如果某个员工要提交自己的work方法供老板调用的话,只要注册一下就好了
2
Mediator.subscribe('C', function callback() {});
问题是新增加一个中介模块的好处是什么?
1.低耦合!如果不是经理要让员工开始工作,是董事长怎么办,或者是部门主管怎么办,难道都要这么写
XXX.start = function () {
A.work()
B.work();
}
都要把A.work什么抄一遍?当然不是,只要给中介模块发出命令就好了,
2.模块之间不需要进行通信,只要负责广播和监听事件就好了
3.在模块化的javascript中,中介模块能提高可维护性:是否启动某个模块,有没有权限启动某个模块,异步加载某些模块,模块之间的依赖关系,某些模块启动失败了怎么办。这些边界条件都可以交给它来判断,而其他模块只关心实现自己逻辑就好了
最后打个比方,中介模块真的就像房屋中介一样!如果你是房东,你只需要下令一声“我要找人租房”,他们就自然会生成那个列表,你不用直接和房客打交道。
1.JavaScript 的事件处理是所有浏览器端程序的基本必备技巧。当目标元素的事件被触发时,比如按钮被点击,鼠标移动,或者是表单提交,这些事件触发时都可以触发对应的方法。当然这个过程中我们可以传递一些参数过去来自定义很多事情。
一个要注意避免的就是事件与DOM元素的紧耦合。比如先看看下面这个代码,考虑到用一个简单表单来接受用户输入的信息。
form id="msgbox" action="#" method="get"
label for="msg"your message/label
input id="msg" value="" /
buttonSEND/button
/form
2.我们能写一段代码让屏幕上显示刚才表单提交的信息
document.getElementById("msgbox").addEventListener("submit", function(e) {
e.preventDefault();
var msg = e.currentTarget.getElementById("msg").value.trim();
if (msg) {
alert(msg);
}
}, false);
那么如果我们想对显示出来的这句话做一些操作,比如发一条tweet,或存储在服务器或者干些其他什么?则有两个选择:
1,对已有的事件处理方法添加代码
这个方案的缺陷就是每当打算测试或者更新后来添加的事件处理方法时变得非常不弹性化,每当更改或者删除一些功能的时候,总会有一大段代码要跟着去修改。
2,为每一个功能都创建事件处理方法
第二个方法很好的解决了前面方法的问题,虽然这个方法可能会一开始麻烦点。毕竟所有的方法代码都要处理重复的消息提取以及验证步骤。
设想假如能够自行触发自定义的"newMessage"事件而无需验证是否有message提交,或假如能监控整个HTML文档或者body这样的标签而不仅仅只是某个表单的节点,能否做到呢?这就是自定义事件要解决的问题了。
自行触发一个自定义事件是很简单的;如下代码就是传递一个name,details以及options到新建的 CustomEvent对象中:
var event = new CustomEvent(
"newMessage",
{
detail: {
message: "Hello World!",
time: new Date(),
},
bubbles: true,
cancelable: true
}
);
这个案例中,"newMessage"是一个自定义事件类型。而第二个参数包含了此对象的三个属性(detail,bubbles,cancelable)。
detail: 包含了自定义事件的具体信息,这里仅仅就包括了一个message与一个time
bubbles: 如果是true,则事件会一直传递给自身的父对象元素,接着父对象也会触发此类事件
cancelable: 如果是true, 事件可以被事件触发元素的 stopPropagation( ) 方法停止
现在,我们需要针对某个特定元素来触发此类事件。
document.getElementById("msgbox").dispatchEvent(event);
1. 通过MobX来解决
MobX 是一个简单、高效的前端状态管理脚本库。 根据文档,Just do something to the state and MobX will make sure your app respects the changes。
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
fullName: function () {
this.firstName + ' ' + this.lastName;
}
});
注意下区别,mobx.observable是以下做的唯一区别,再来看下console.log输出的内容。
mobx.autorun(function () {
console.log('first name: ' + person.firstName);
});
person.age = 38; // 不打印内容
person.lastName = 'RUBY!'; // 仍然不打印内容
person.firstName = 'Matthew!'; // 打印内容
通过使用autorun, MobX只会检测使用到的内容。如果觉得不够清楚,来看下下面的:
mobx.autorun(function () {
console.log('Full name: ' + person.fullName);
});
person.age = 38; // 不打印内容
person.lastName = 'RUBY!'; // 打印内容
person.firstName = 'Matthew!'; // 打印内容
相信你已经理解了。
MobX核心概念
observer
var log = function(data) {
$('#output').append('pre' +data+ '/pre');
}
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 34
});
log(person.firstName);
person.firstName = 'Mike';
log(person.firstName);
person.firstName = 'Lissy';
log(person.firstName);
MobX可观察的对象都是普通的对象。这个例子中我们没有观察任何内容,这里例子也展示了怎样将MobX使用到项目中。只需要使用mobx.observable()或 mobx.extendObservable()就可以了。
autorun
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 0
});
mobx.autorun(function () {
log(person.firstName + ' ' + person.age);
});
// this will print Matt NN 10 times
_.times(10, function () {
person.age = _.random(40);
});
// this will print nothing
_.times(10, function () {
person.lastName = _.random(40);
});
当变量值改变时,肯定想做一些事情,所以使用autorun(),将会在任何一个观察的内容改变时触发回调。注意下上面的例子中age改变时autorun()不会触发。
computed
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 0,
get fullName () {
return this.firstName + ' ' + this.lastName;
}
});
log(person.fullName);
person.firstName = 'Mike';
log(person.fullName);
person.firstName = 'Lissy';
log(person.fullName);
2. 注意下fullName()方法没有使用参数和get的用法,MobX会自动创建一个计算的值。。person.fullName有什么不同的地方?这是一个函数,但是没有调用就获取到了结果!通常,使用的是person.fullName(),而不是person.fullName。这里就是遇到的第一个getter函数。
不仅如此,MobX还会监听计算值得依赖的变量,并且只在它们修改的时候运行触发更新。如果没有修改,将会直接返回之前缓存中的值。看下面一个例子。
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 0,
get fullName () {
// Note how this computed value is cached.
// We only hit this function 3 times.
log('-- hit fullName --');
return this.firstName + ' ' + this.lastName;
}
});
mobx.autorun(function () {
log(person.fullName + ' ' + person.age);
});
// this will print Matt Ruby NN 10 times
_.times(10, function () {
person.age = _.random(40);
});
person.firstName = 'Mike';
person.firstName = 'Lissy';
3. 这里使用了person.fullName很多次,但是函数只有在firstName和lastName修改时才会运行。这就是MobX可以提供应用速度的一种方式。
使用MobX进行项目实践
直接看例子:
h1Test/h1
script
var person = {
events: {},
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
fullName: function() {
return this.firstName + ' ' + this.lastName;
},
setPersonData: function(personData) {
$.extend(this, personData);
$(this.events).trigger('changed', personData);
}
};
var renderCount = 0;
$(person.events).on('changed', function() {
renderCount += 1;
$('h1').text(person.fullName() + ' render count: '+ renderCount);
});
// this will trigger every time
_.times(10, function() {
person.setPersonData({age: _.random(20)});
});
/script
4. 这里name被重复渲染了10次,尽管没有修改firstName或lastName,可使用多个事件来优化这里,在渲染之前做判断。但是太复杂了。下面是MobX的例子;
h1Test/h1
script
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
get fullName () {
return this.firstName + ' ' + this.lastName;
}
});
var renderCount = 0;
mobx.autorun(function() {
renderCount++;
$('h1').text(person.fullName + ' render count: ' + renderCount);
});
// this will trigger the render one time
_.times(10, function() {
person.setPersonData({
age: _.random(20)
});
});
/script
注意下这里为什么没有事件、触发器或on绑定。使用MobX,你会使用修改后的最新值。而且它只被渲染一次,这是因为我们没有修改内容时autorun只是一直在监听。
// observable person
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37
});
// reduce the person to simple html
var printObject = function(objectToPrint) {
return _.reduce(objectToPrint, function(result, value, key) {
result += key + ': ' + value + 'br/';
return result;
}, '');
};
// print out the person anytime there's a change
mobx.autorun(function(){
$('#person').html(printObject(person));
});
// watch all the input for changes and update the person
// object accordingly.
$('input').on('keyup', function(event) {
person[event.target.name] = $(this).val();
});
这里我们能编辑person的所有信息来观察输出的内容情况,现在这个例子中有个小小的问题,这里input的值和person的值不同步,这里改下:
mobx.autorun(function(){
$('#person').html(printObject(person));
// update the input values
_.forIn(person, function(value, key) {
$('input[name="'+key+'"]').val(value);
});
});
这里虽有重新渲染。看到的就是为什么很多人选择使用React的原因。React允许你将输出内容拆分小的组件进行个别的渲染。
MobX不一定在实际项目中使用,如果需要这种细粒度的操作使用React就可以了。当在实际项目中使用MobX和jQuery时,使用autorun()就可以解决很多问题了。