静如瘫痪 动如癫痫 (文章随时修理,欢迎评论指错)
设计模式
发布/订阅 & 观察者模式 Publish/Subscribe
建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他订阅的对象,其他对象将相应做出反应。在此,发生改变并发送消息的对象称为 观察目标(发布者) ,而被通知的接收消息的对象称为 观察者(订阅者) 。
原始版
发布者发生改变时直接调度注册依赖于自己的订阅者的回调函数来达到通知的目的。
比如有个“天气中心”的具体目标A,专门监听天气变化,而有个显示天气的界面的观察者B,
B就自己注册到A里;
当A触发天气变化,就调度B的更新方法(回调),并带上自己的上下文。
# 一个发布者可以对应多个订阅者
目标A需要维护一个依赖列表ObserverList,当任何状态发生改变自动通知它们观察者(大多数时候是同步的)
改进版
发布者发布事件到一个调度中心(顺带上下文)
统一由调度中心 调度订阅者注册到调度中心的相对应的回调函数
比如有个界面B是实时显示天气,它就订阅天气事件(注册到调度中心,包括处理程序),当天
气变化时(定时获取数据),就作为发布者A发布天气信息到调度中心C,调度中心就调度订阅者
的天气处理程序(回调)。
# 订阅者与订阅者的松耦合
订阅者并不知道消息是谁具体提供的。
发布者的消息不会直接发送给特定的接收者 发布者并不知道自己被谁调用了接口。(代码独立)
对比优势
改进版的Pub/Sub pattern大多数时候是异步的(实现消息队列)
实现了一个对象(组件)只做一件事情,并且将它做好的思想。
降低了订阅者和发布者直接耦合(即松耦合)、改进代码管理、实现潜在的复用。
- 细节:传递的信息是目标对象的全部 或 部分数据 | 接受的对象是 广播 或 单播
- 注: 不同于中间人代理模式
- 参考:设计模式(三):观察者模式与发布/订阅模式区别
- me115/design_patterns
事件模型
事件处理机制
capture pharse (入 由window到target依次触发)
target phase (到 到达目标时)
bubbling pharse (回 由target到window依次触发) 当Event.bubbles == true 时
才发生
Event
Event.bubbles
决定这个值是 true 还是 false 的是 event 本身,有些事件是不冒泡的为false
Event.cancelBubble
设置是否取消冒泡 为true时阻止冒泡
Event.preventDefault()
阻止事件默认行为 默认行为通常发生在事件派发完成后,但在特殊情况下,它们也可能在事件派发完成前立即执行。
特别的checkbox的事件默认行为就会先于事件处理函数执行
checkbox的click事件中preventDefault会将修改的选中状态改变(发生在click事件
处理函数之后)
Event.stopPropagation()
阻止事件进一步传播 ,一般用于阻止冒泡
比如用事件委托自制下拉框的时候,我们点击浏览器的其他位置,我们需要下拉框的
options隐藏,这时我们就要用到e.stopPropagation()
addEventListener & removeEventListener
用来注册和解除事件
var inner = document.getElementById('inner');
inner.addEventListener('click', function(){
alert('click inner');
}, false);
// true代表捕获事件,false代表冒泡事件(默认undefined即:false)
事件处理机制小案例
.html
<!-- outer包裹inner标签 -->
<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
<div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
.js
var clickInner = document.getElementById('inner');
var clickOuter = document.getElementById('outer');
clickInner.addEventListener('click',function(){
alert('inner show');
event.stopPropagation();
},false);
clickOuter.addEventListener('click',function(){
alert('outer show');
event.stopPropagation();
},true);
CustomEvent
浏览器原生提供CustomEvent()构造函数,用来生成 CustomEvent 事件实例。
第一个DOMString, 第二个参数对象
detail: set some internal data of the event 之后只可读
用处: 自定义添加事件内部数据、 事件驱动方式解耦
var event = new CustomEvent('myevent', {detail:123});
event.detail = 456; // Ignored in sloppy mode, throws in strict mode
console.log(event.detail); // 123
var event = new Event('myevent');
event.detail = 123; // It's not readonly
event.detail = 456;
console.log(event.detail); // 456
var event = new Event('click'); // 事件名
el.dispatchEvent(event); // 事件触发器绑定到事件
if (!window.CustomEvent) {
window.CustomEvent = function(type, config) {
// 如果config未定义则初始化,如果已定义则不变
config = config || { bubbles: false,
cancelable: false,
detail: undefined
};
var e = document.createEvent('CustomEvent');
// 事件的类型 "UIEvents", "MouseEvents", "MutationEvents", "HTMLEvents"等
e.initCustomEvent(type, config.bubbles,
config.cancelable, config.detail);
return e;
};
window.CustomEvent.prototype = window.Event.prototype;
}
//事件名DOMString 、 options {detail:null}
var myEvent = new CustomEvent('myevent', {
detail: { // 事件的附带数据,默认null
foo: 'bar'
},
bubbles: true,
cancelable: false
});
el.addEventListener('myevent', function (event) {
console.log('Hello ' + event.detail.foo);
});
el.dispatchEvent(myEvent); // 事件触发器 触发该事件
// expected Output: Hello bar
事件委托(delegation)
(由外部标签绑定 可以监控内部标签事件)
利用 事件冒泡的机制 把里层所需要响应的事件绑定到外层,在真正执行事件的时候才去匹配判断真正的目标元素。
(注:捕获阶段也可以实现 但是大部分用冒泡机制是由于浏览器兼容问题)
var ev = e || window.event;
var target = ev.target || ev.srcElement;
# 优点:
处理 维护性更简单 内部标签改动(节点删除增加 对应事件绑定解绑)无碍 动态性强
一次委托终身调用 减少DOM绑定数、内存消耗等
# 缺点:
mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算
定位,对性能消耗高,因此也是不适合于事件委托的;
就近委托(相邻父节点): 不然会频繁调用函数 甚至事件件误判,即本不应用触发事件的
被绑上了事件,需要对应匹配检查标签是否为所想要触发的元素(如下)
// 检查方式
nodeName
e.target.matches('li.class-1') 匹配特定选择符标签
divEle.classList.contains("foo") 是否包含类名
(注: 比如 focus、blur 之类的事件本身 没有事件冒泡机制,需要捕获阶段实现委托;但是没有太多必要)
# jQuery 中的事件委托
$.on:
$('.parent').on('click', 'a', function () {
console.log('click event on tag a');
});
它是 .parent 元素之下的 a 元素的事件代理到 $(‘.parent’) 之上,只要在这个元素上有点击事件,就会自动寻找到 .parent 元素下的 a 元素,然后响应该事件。
Synchronous and asynchronous events
鼠标事件等 虚拟队列 脚本加载
AJAX
异步的JavaScript和XML(Asynchronous JavaScript And XML)
使用API XMLHttpRequest 对象实现与服务器异步通信 与XML无关 也可以使用json等格式传输
只能向同一个域中使用相同端口和协议的 URL 发送请求
使用场景
在不重新加载页面的情况下发送请求给服务器。
接受并使用从服务器发来的数据。
(function() {
var xhr;
// acceptContents()方法 注意不推荐使用this 浏览器兼容问题
function acceptContents() {
try {
// 收到响应后,响应的数据会自动填充 XHR 对象的属性 如下
if (xhr.readyState === XMLHttpRequest.DONE) { // 4 整个请求过程已经完毕. // 200 请求成功状态码 304 表示请求的资源 缓存并没有被修改 if (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { alert(xhr.responseText); // 被返回的文本 if (xhr.responseXML == 'application/json') { var response = JSON.parse(xhr.responseText); // if json data, transform json } } else { alert('There was a problem with the request.' + xhr.status); } }
} catch( e ) { // 通信错误 eg:服务器宕机
alert('Caught Exception: ' + e.description);
}
}
function mkRequest1() {
xhr = new XMLHttpRequest();
if (!xhr) {
alert('Giving up :( Cannot create an XMLHTTP instance'); return false; } // 可以用2级标准 load事件(在接收到完整的响应数据时触发) 替代 xhr.onreadystatechange = acceptContents; // Params: HTTP方法 url(注意encodeURIComponent) true异步(默认) user passwd xhr.open('GET', 'test.html?myname=myval'); xhr.setRequestHeader( // 注意 after open DOMString header, DOMString value ); xhr.send(null); } function mkRequest2(url, userName) { ... xhr.onreadystatechange = alertContents; xhr.open('POST', url); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // 发送serialized data eg: new FormData(form) xhr.send('userName=' + encodeURIComponent(userName)); } })();
ES6 & 7 异步新特性
回顾callback
留(登记)一个联系方式(回调函数)有消息(触发)之后通知(调用回调函数)你(异步等待消息者)
error first 回调函数写法的一种风格 err写在第一个参数
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店
里有货了,店员就打了你的电话,
然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留
给店员就叫登记回调函数,
店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取
货叫做响应回调事件。(摘自[知乎](https://www.zhihu.com/question/19801131))
generator
The function* declaration (function keyword followed by an asterisk) defines a generator function, which returns a Generator object.
function* generator(i) {
yield i;
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value);
// expected output: 10
console.log(gen.next().value);
// expected output: 20
fetch
获取资源
WindowOrWorkerGlobalScope.fetch(urlString, init配置项对象)
返回一个Promise对象
优点: 配合Promise 写法简单
支持async/await是ES7 API
// 通过fetch获取百度的错误提示页面
fetch('https://www.baidu.com/search/error.html', {
method: 'POST',
mode: 'cors', // 跨域
credentials: 'include' // 强制发送cookie
headers: new Headers({
// 'Content-Type': 'application/x-www-form-urlencoded', // 指定提交方式为表单提交
'Accept': 'application/json' // 通过头指定MIME 类型,获取的数据类型是JSON
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
}),
})
.then((res) => {
if(response.headers.get("content-type") === "application/json") {
return response.json().then(function(json) {
// process your JSON further
});
} else {
console.log("Oops, we haven't got JSON!");
}
return res.text()
})
.then((res) => {
console.log(res)
})
}