blog-backup's People
blog-backup's Issues
JavaScript Ajax 的前世今生
JavaScript 异步的前世今生
目录
- HTTP 基础知识
- Ajax
- JSONP
- CROS
- Promise
- 实现与优化
HTTP 基础知识
定义
超文本传输协议(英文:HyperText Transfer Protocol
,缩写:HTTP
)是一种用于分布式、协作式和超媒体信息系统的应用层协议
URI
统一资源标识符(英文:Uniform Resource Identifier
,缩写:URI
),是我们经常提到的统一资源定位符的超集(英文:Uniform Resource Locator
,缩写:URL
)
URL
通常由这些内容组成:
协议://账号:密码@域名:端口/路径?查询#哈希
目前已经很少有将账号URL上面的情况了
HTTP Request / Response
同源策略
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制
如果协议,端口(如果指定了一个)和域名对于两个页面是相同的,则两个页面具有相同的源
Ajax
Ajax
即 Asynchronous JavaScript and XML
,翻译过来就是 异步的JavaScript与XML技术
其中 X
代表的是 XML
这种数据格式,是因为 Ajax
这项技术刚刚出现的时候,主流用于传递的数据格式是 XML
,而今更加普遍的数据格式是 JSON
,而 Ajax
的名称保留了下来
因而,Ajax
可以获取的数据类型不单单是 XML
,可以获取所有类型的数据资源,同时支持 HTTP
协议之外的 FILE
和 FTP
协议
前端 Ajax
的实现主要是基于 XMLHttpRequest API
现代浏览器都提供了 XMLHttpRequest
类,通过实例化 XMLHttpRequest
对象,以及调用一些实例化方法,可以发送相关的异步请求
简单的 Ajax
请求栗子
下面是个简单的发送 Ajax
请求的栗子
// 创建 XHR 请求对象
var xhr = new XMLHttpRequest()
// 启动请求(注意,此时并不会真正发送请求,而是启动请求以备发送)
xhr.open('GET', 'https://api.github.com/events', true)
// 发送请求
xhr.send()
但是仅仅发送请求无法构成完整的前后端交流,XHR
对象提供了接口和状态以获取服务端的返回状态、返回信息
XHR
对象的 readyState
属性标识了 Ajax
请求的生命周期变化
值 | 状态 | 描述 |
---|---|---|
0 | UNSET | XHR对象已创建,但是还没有调用 .open() |
1 | OPENED | .open() 已被调用 |
2 | HEADERS_RECEIVED | .send() 已被调用,同时已经可以获取到响应头的信息 |
3 | LOADING | 正在下载响应内容,responseText 属性中可以获取到部分数据 |
4 | DONE | 响应结束,异步的生命周期结束 |
XHR
对象的 status
属性标识了状态码
值 | 状态 | 描述 |
---|---|---|
200 | OK | 请求成功 |
304 | Not Modified | 资源未修改,客户端仍然保留之前下载的副本 |
400 | Bad Request | 由于明显的客户端错误,服务器没有处理该请求 |
404 | Not Found | 请求失败,资源未在服务器上发现 |
405 | Method Not Allowed | 对应请求的资源不支持对应的请求方法 |
500 | Internal Server Error | 通用的错误消息,服务器遇到了未曾遇到的状况 |
XHR
对象提供了 .onreadystatechange()
接口以获取生命周期的状态变化,每次属性 readyState
的变化都会触发该接口的执行
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// 异步请求成功
// 获取服务端返回的数据
var data = xhr.responseText
// 你就可以根据返回的数据来更新页面啦
} else {
// 异步请求失败
}
}
}
拓展知识
修改请求头、响应头
GET、POST 在 Ajax 上面的区别
转义
文件上传
首先,文件上传可以通过表单提交,就是在 form
表单中加上 <input type="file" />
标签,这种上传方式和 JavaScript
的关系不是很大,这里不过多展开
借助 Ajax
也可以实现文件上传,配合 File API
可以实现图片的预览等等
监控请求过程
JSONP
CROS
Promise
Zepto.js 的相关实现
参考资料
HTTP 请求中的 Form Data 与 Request Payload
背景
最近的开发中,使用 Vue.js
技术栈重写过去的项目,遇到如下的问题:
同样的 POST
请求,原始的代码是基于 jQuery
:
$.ajax({
url: 'test.php',
method: 'POST',
data: {
name: 'tom',
age: 12,
},
})
最后在 Network 请求的详情中,data 数据会被 Form Data 属性携带:
Form Data
name=tom&age=12
而使用 axios
发送 POST
请求:
const res = await axios({
url: 'test.php',
method: 'post',
data: {
name: 'tom',
age: 12,
},
})
Network 请求的详情中,data 数据会被 Request Payload 属性携带:
Request Payload
{"name":"tom","age":12}
因为这样的区别,导致异步接口报错
这两者有什么区别呢?
具体区别
首先是 $.ajax
对应 Network
截图
下面是 axios()
对应 Network
截图
区别如下:
- HTTP请求报文头部
Content-Type
不同 - 请求实体所在位置、结构不同
- 位置分别是:
Request Payload
&Form Data
- 结构分别是:
json
&query
- 位置分别是:
HTTP报文头部字段 Content-Type
这个报文头一般在请求、响应中都会出现,用于指示报文携带资源的类型
参考资料:
这里值得注意的是,修改 Content-Type
的值时,会改变报文主体所处的位置
Form Data & Request Payload
首先从字面就能了解两者的区别:
Form Data
表单数据Request Payload
请求载荷
以原始 XMLHTTPRequest
对象来说:
如果是原始的表单请求,譬如 <form></form>
表单发出的 POST
请求,那么 Content-Type
默认就是 application/x-www-form-urlencoded
,报文主体内容在 Form Data
上(可以被 form
表单的 enctype
属性重写)
如果请求报文的请求头 Content-Type
设置成 application/x-www-form-urlencoded
,则报文主体会被 Form Data
携带
默认情况下都是将报文主体放在 Request Payload
上
从 jQuery
实现源码上,可以发现 POST
请求的默认会把 Content-Type
配置成 application/x-www-form-urlencoded
{
...,
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
}
// Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
jqXHR.setRequestHeader( "Content-Type", s.contentType );
}
修复
为了解决这个接口报错,有如下两种方案:
- 后端接口支持从请求报文
Request Payload
中获取数据 - 前端请求更换载荷携带方式
因为是前端技术栈的迭代,这里使用前端更换载荷携带方式来实现:
就是实现所有的
axios
POST
请求的载荷全部使用From Data
来取代Request Payload
这里使用 axios
的拦截器来实现:
参考资料: axios
import axios from 'axios'
import qs from 'qs'
// 创建独立的 axios 实例
const ownAxios = axios.create()
// 挂载请求时的拦截器
ownAxios.interceptors.request.use(config => {
let _config = { ...config }
// 兼容 POST 请求
if (config.method.toLowerCase() === 'post') {
_config = {
..._config,
headers: {
..._config.headers,
'Content-Type': 'application/x-www-form-urlencoded',
},
// 默认Form Data中的 载荷是query格式的 这边需要转换
data: qs.stringify(_config.data),
}
}
return _config
})
至此,前端 POST
请求可以全部跑通
更新
axios
原生支持修改 POST
请求的载荷方式:axios
只是在项目中统一使用拦截器会更加方便
JavaScript 原型
目录
- 面向对象
- 原型 & 原型链
- 实现
面向对象
面向对象三大特性:封装、继承、多态
多数的程序语言借助 类 的概念来实现面向对象编程
在 JavaScript
中没有经典类的概念:
- 封装可以借助函数实现
- 多态可以借助
JavaScript
this
的特性来实现
可以通过其他的语言特性实现面向对象的封装与多态
继承基本上只能通过 mixins
混合这种方式来处理,同时还伴随着覆盖、先后执行的问题,不是优雅高效的解决方案
JavaScript
通过原型优雅解决了继承的实现问题
原型 & 原型链
JavaScript 中没有类的概念,创建原型对象是基于函数与 new
关键词
function Cat () { this.type = 'cat' }
const cat = new Cat()
一般这种被 new
执行的函数被称为 构造函数
new
关键词做了什么?(new
关键词后面的函数一般称为构造函数)
- 创建空对象
new Object()
或者{}
- 设置对象
__proto__
属性指向构造函数的原型对象 - 以该对象为
this
执行构造函数 - 关注构造函数执行的返回值
- 存在返回值是引用类型,则返回值
- 返回值是简单类型或者没有返回值,则返回之前创建的对象
其中最需要关注的就是:设置对象 __proto__
属性指向构造函数的原型对象
proto 做了什么?
首先我们需要解释下 __proto__
做了什么?
通常来说,每个 JavaScript 对象都会有 __proto__
这个属性, 这个属性指向当前对象构造函数的原型对象
当代码执行 const value = obj.a
,需要去 obj 对象上寻找 a 这个属性的值,首先会在对象本身的属性中寻找,如果没有找到,则会前往 __proto__
属性所指向的对象上寻找
既然 __proto__
所指向的也是对象,那么这个对象也是会有 __proto__
属性的,那样就会有一直向上回溯的流程
- 如果最后没有找到所需要的属性,则返回
undefined
- 一旦找到,则停止搜索返回结果
差不多的流程是下面这样的:
(我是图)
对应的伪代码:
function find(obj, name) {
for (let key in obj) {
if (key === name) {
return obj[name]
}
}
let temp = obj
while (temp = temp.__proto__) {
for (let key in temp) {
if (key === name) {
return temp[name]
}
}
}
return undefined
}
这样就构成了链式的结构,这大致就是原型链的雏形
(注意:__proto__
不属于语法标准,但是各大浏览器都实现了这个特性,规范的语法是:一个指向 [[Prototype]]
的引用。)
构造函数的原型对象
上面我们了解到:通过 new
关键词创建出来的实例对象会有个 __proto__
属性,这个属性指向构造函数的原型对象。当获取实例对象上的属性的时候,会随着 __proto__
属性的指向回溯搜索
下面我们聊聊对象 __proto__
所指向的 构造函数的原型对象 这个概念
原型对象 prototype
每个函数都会有原型对象:prototype
默认来说,这个对象只会有如下的两个属性:
constructor
指向构造函数__proto__
指向构造函数
constructor
给了我们知道实例对象构造函数的机会,而__proto__
则是用于将原型串联起来
复用
构造函数的原型对象很大意义上解决了复用的问题
一般我们在原型对象上实现一些通用的函数/方法
因为通过 __proto__
属性,每个 new
关键字生成的对象都可以获取对构造函数原型对象的引用
function A() {}
A.prototype.hey = function() { console.log('hey, this is from prototype') }
const a1 = new A()
const a2 = new A()
// 对象 a1 a2 共用相同的 hey 函数
a1.hey === a2.hey // true
保持灵活
而因为 JavaScript
函数中的 this
是运行时决定的值,所以相同的原型对象的函数,会因为执行对象不同,而产生各式的结果
function A(name) {
this.name = name
}
A.prototype.hey = function() { console.log('hey, tis is ' + this.name) }
const tom = new A('Tom')
const jerry = new A('Jerry')
tom.hey() // hey, this is Tom
jerry.hey() // hey, this is Jerry
通过 new
关键词创造出来的所有实例对象,在没有魔改的情况下,它们都保留了 __proto__
属性,即对构造函数原型对象的引用
这样就避免了大量对方法/函数的拷贝工作
注意
不过这样也带来了可以通过实例对象修改原型对象的弊端:
function A(name) {
this.name = name
}
A.prototype.hey = function() { console.log('hey, this is ' + this.name) }
const tom = new A('Tom')
tom.hey() // hey, this is Tom
tom.__proto__.hey = function() { console.log("wow, what's wrong?") }
tom.hey() // wow, what's wrong?'
继承
至此,我们差不多了解了这样的流程:
(我是图)
简单概括来说,只有下面两个概念:
__proto__
属性可以用来回溯搜索对象的属性new
关键字创造出来的实例对象的__proto__
属性指向构造函数的prototype
属性对象
继承的通俗来说就是把已经存在的类所定义的内容作为自己的内容,并加入若干新的内容
这里需要完善
现在我们要做的目标就是将子构造函数的 prototype
对象的 __proto__
属性指向父构造函数的 prototype
对象
简单粗暴
function Father () {}
Father.prototype.say = function() { console.log('This is father') }
function Child () { Father.call(this) }
Child.prototype.__proto__ = Father.prototype
其实这个方案,除了不是标准支持之外,都蛮好的
目前我们知道的可以指定 __proto__
属性的方案,只有使用 new
关键词
借助 new
function Father () {}
Father.prototype.say = function() { console.log('This is father') }
function Child () { Father.call(this) }
Child.prototype = new Father()
Child.prototype.constructor = Child
联系我们上面提到的 new
关键字的作用,prototype
赋值的对象会有__proto__
属性指向父构造函数的原型对象
但是这种方式会将父构造函数的实例属性也赋值到子构造函数的原型对象上,这不是我们预想的结果
规避父构造函数的实例属性
function Father () {}
Father.prototype.say = function() { console.log('This is father') }
function Middle () {}
Middle.prototype = Father.prototype
function Child () { Father.call(this) }
Child.prototype = new Middle()
Child.prototype.constructor = Child
借助空的中间函数,可以避免污染子构造函数的原型对象
标准方案
function Father () {}
Father.prototype.say = function() { console.log('This is father') }
function Child () { Father.call(this) }
Child.prototype = Object.create(Father.prototype)
Child.prototype.constructor = Child
JavaScript
提供的 Object.create()
翻译一下其实就是下面的代码:
Child.prototype = {
__proto__: Father.prototype
}
当然,Object.create()
还支持第二个参数,可以追加其他的属性描述对象
参考资料
移动端的 1px 问题
总结、记录下 iOS
移动端 1px
的问题
产生的原因
devicePixelRatio
这里有两个概念:
- 物理像素
- CSS像素
物理像素其实就是买手机的时候,手机参数中的一项,以下面这个 iPhone X 的参数为例:
CSS 像素就是页面布局的容器尺寸
这里 devicePixelRatio
就是 物理像素 / CSS像素 的值
换句话来说,就是布局中的一个像素点对于物理像素中多少个像素点来显示
在 iPhone 4
的时代,屏幕还没有引入视网膜屏,此时的 devicePixelRatio
的值还是 1
随着视网膜屏以及HiDPI屏的出现普及,devicePixelRatio 的值从 1 到 4 甚至到了 9
也就是编写网页 1px 的元素,在手机屏幕上可能是以 3 * 3 的像素点在进行渲染,会让图片更加清晰
所以这个属性本质上是描述的:
- 当前显示设备的物理分辨率与CSS像素分辨率的比值
- CSS像素的大小相对物理像素大小的比值
1px 的问题
所以为了在移动端得到最佳的效果,一般设计稿都是按照移动端设备的物理分辨率来设计的
很多的边框都会被设计成 1px
的宽度,但是在还原设计稿的时候,CSS
中 px
最小单位通常来说都是 1px
.border-bottom {
border-bottom: 1px solid #000;
}
随之真机上因为 视网膜屏或HiDPI屏 的原因,就会显示 2px
或者是 3px
的宽度,和期待的效果出入很大
解决问题
了解了出现的原因,下面就看看如何解决这个问题
0.5px
其实第一反应就是能不能出现比 1px
小情况,譬如 CSS
中编写 0.5px
,那么在 devicePixelRatio
= 2 的屏幕上真实渲染物理像素就是 1px
,这样就可以达到还原设计稿的效果
但是 0.5px
这个属性值只有 Safari+和Firefox 支持
可以在 Mac
的 Safari
上测试下面的代码:
.common-border {
border-bottom: 1px solid #000;
}
.half-border {
border-bottom: 0.5px solid #000;
}
可以看到明显的粗细不同
但是这个方法不具备普适性,只有在特定的浏览器中才会有效果,一般需要配合浏览器检测使用
if (window.devicePixelRatio && devicePixelRatio >= 2) {
const ele = document.createElement('div')
ele.style.border = '.5px solid transparent'
document.body.appendChild(ele)
if (ele.offsetHeight === 1)
{
document.querySelector('html').classList.add('half')
}
document.body.removeChild(ele)
}
border-image
这个是之前部门广泛推广的一种方式,原理就是借助 border-image-slice
来实现
本质上就是借助图片边框(图片一般如下图所示),然后其中截取的内容是一半白色一半边框色的内容,最终物理像素渲染的就是真实的物理 1px
.border-1px {
border-width: 1px;
border-image: url(border.png) 2 repeat;
}
border.png
如下图,是 6 * 6
的边框图
通常实践中因为边框图片尺寸都比较小的原因,都会在打包过程中使用 base64
编码来节省网络请求
借助 border-image
实现的主要问题在于:如果修改边框的颜色,则需要修改对应的边框图片颜色,在开发过程中也不是普遍的解决方案
但是在熟悉 border-image
样式的过程中,还是了解了很多有趣的点,具体可以参考下面张鑫旭大神的文章
参考资料
伪元素配合 transform 缩放
之前我们提到过 0.5px
这种解决方案,类似的,按比例缩小也是一种解决方案
缩小自然就是使用
transfrom: scale(0.5);
transform-origin: left top;
越来越多的现代前端组件库中会使用这种方式来兼容 1px
的问题,尤其是借助各种CSS预处理语言,可以方便的实现边框颜色自定义,支持圆角这些特性
譬如使用 Less 这样实现颜色的自定义(摘自 Vux - 1px 中的实现)
.setLine(@c: #C7C7C7) {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 200%;
height: 200%;
border: 1px solid @c;
color: @c;
transform-origin: left top;
transform: scale(0.5);
}
.vux-1px, .vux-1px-t, .vux-1px-b, .vux-1px-tb, .vux-1px-l, .vux-1px-r {
position: relative;
}
.vux-1px {
&:before {
/*
** 这里可以自定义配置颜色
** .setLine(#000);
*/
.setLine();
}
}
在此基础上,我们可以写出一版支持边框圆角的
.setLine(@c: #C7C7C7, @r: 0) {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 200%;
height: 200%;
border: 1px solid @c;
border-radius: @r;
transform-origin: left top;
transform: scale(0.5);
}
.vux-1px {
position: relative;
}
.vux-1px {
&:before {
/*
** 这里增加支持自定义边框圆角
** .setLine(#000, 2px);
*/
.setLine();
}
}
相比之下,这种解决方案需要兼容 devicePixelRatio = 3
的情况也是比较简单的
都可以通过CSS预处理语言来进行配置
奇技淫巧
关注下手淘首页是如何解决 1px
边框问题的
手淘首页是使用 rem
作为页面布局单位的,但是其中涉及到 1px
的地方却是使用元素来模拟的
对应的样式如下
element.style {
box-sizing: border-box;
line-height: 0;
background-color: rgb(232, 232, 232);
width: 10rem;
height: 1px;
}
为什么这里是 height: 1px;
?
我们回到页面顶部关注这样一个 meta
标签(模拟器是 iPhone X
环境):
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
当前页面都被缩放 1/3 显示,那么换句话说,就是 height: 1px
被压缩了 1/3
最后映射到物理像素上,也就完美的呈现了 1px
的内容
当然,因为设置了这个 <meta>
标签,所有的其他元素的大小自然也要对应的放大,不过借助 rem
,只需要改变根节点的 font-size
就行
综合来看,还是手淘首页这种解决方案最符合解决 1px
问题的初衷
为了追求完美的前端呈现效果,还是要不断综合不断尝试
Todos
- canvas 相关的问题
深入浅出 JavaScript 事件
目录
- 定义
- 事件流
- 事件处理程序
- 事件对象
- 修复特殊事件
- 委托 & 移除
- 模拟触发事件
- zepto.js 中的 event.js
- 调试
定义
事件系统:文档或者浏览器窗口中发生的一些特定的交互瞬间,可以使用侦听器(或处理程序)来预定事件,以便发生事件时执行相应的代码
事件系统是传统软件工程观察者模式的实现,浏览器通过事件系统实现了页面行为(JavaScript
代码)与页面外观(HTML
&CSS
)之间的松散耦合
通俗的来说,事件就是浏览器的某种自身行为或者是用户的某些操作,譬如来说: ready
、load
、click
或者 mouseover
,而对应相应某个事件的函数就是事件处理程序
通过事件系统,开发者可以在页面特定的生命周期进行交互、对用户的操作做出反馈等等
事件流
事件流出现的意义是为了规范同心圆的问题:
当你点击了页面上面的按钮,本质上你也点击了按钮的容器,并且以此递归,以上级别的容器都被点击了,最终可以认为你也点击了整个页面
那么事件触发是从页面开始,然后传播到这个按钮? 还是从这个按钮开始传播到整个页面呢?
事件流描述的是从页面中接受事件的顺序
DOM事件流包括三个阶段,他们按照顺序发生:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
通俗的来说,就是用户点击页面上的按钮:
- 点击事件是从最顶层的
document
开始传播,逐步详细直到用户真正点击的元素之前 (捕获阶段) - 然后实际的元素接收到点击事件 (处于目标阶段)
- 接着冒泡阶段发生,事件又回传到最顶层的文档 (冒泡阶段)
因为所有的现在浏览器都支持事件冒泡的事件流(老版本的IE不支持事件捕获),所以一般都在事件冒泡阶段对事件进行响应
事件处理程序
1. HTML 事件处理程序
借助 HTML
元素的特性,直接将事件处理程序绑定在 HTML
结构中
特性名称是该元素支持的事件名称,特性的值是能够执行的 JavaScript
代码
<input type="button" onclick="alert('Clicked')" />
或者是这样:
<script>
function showMessage() {
alert('Hello World')
}
</script>
<input type="button" onclick="showMessage()" />
2. DOM 0级事件处理程序
JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性
DOM 0级事件处理处理程序被认为是元素的方法,因此事件的处理程序是在元素的作用域中运行,所以事件处理程序中的 this
引用当前的元素
var btn = document.getElementById('myBtn')
btn.onclick = function() {
console.log(this) // this 指向 btn 这个 DOM 对象
}
通过将对应的属性设置为 null
可以删除DOM 0级事件处理程序(同名 HTML
事件处理程序也会被移除)
btn.onclick = null
注意
DOM 0级事件处理程序与 HTML
事件处理程序本质上一样的,都是操作 DOM
元素(对象)的属性(特性),两者是覆盖作用,只会保留最后赋值的处理程序
但是不会影响 DOM 2级事件处理程序
3. DOM 2级事件处理程序
DOM 2级事件定义了两个方法 addEventListener
removeEventListener
,用于处理指定和删除事件处理程序的操作
他们都接受相同的参数:要处理的事件名,作为事件处理程序的函数与(可选的)描述事件处理特点的对象(或者是布尔值)
其中事件处理程序的 this
也是引用当前绑定事件监听的函数
第三个参数在之前只支持布尔类型 - capture
,true
表示在捕获阶段调用事件处理程序,false
表示在冒泡阶段,默认值是 false
后期第三个参数被拓展,传递对象,其中可用的选项如下:
capture
true
表示捕获阶段,false
表示冒泡阶段once
表示事件监听被添加之后最多只会调用一次 被调用后事件监听会被自动移除passive
表示事件监听其中不会调用preventDefault()
DOM 2级事件处理程序可以添加多个事件处理程序,不会出现之前的覆盖的现象
注意
DOM 2级事件处理程序删除对应的事件处理程序需要保证传入的参数和绑定时参数一致:事件名称、绑定函数的引用以及相同的附加参数
或者换句话说,被绑定的匿名事件处理程序是无法通过相同函数体的匿名函数来移除的(相同的函数体不是相同的函数引用)
function handler() { console.log(1) }
btn.addEventListener('click', handler)
// 这样是无法成功解除事件绑定的
btn.removeEventListener('click', handler, true)
事件对象
当触发DOM事件时,浏览器环境会将一个 event
对象传递到事件处理程序中,这个对象包含了与事件相关的各种信息:导致事件的元素、触发的事件类型以及其他与触发的事件相关的特定信息
同时因为触发的事件类型不同,事件对象中包含的属性也会随之变化
事件对象的通用属性
虽然事件对象的熟悉会随着触发的事件类型变化而变化,但是所有支持 DOM
的事件对象都会包含下面的属性
属性
- bubbles
- cancelable
- currentTarget
- defaultPrevented (DOM3新增)
- eventPhase
- target
- type
其中比较特殊的就是 target
代表事件触发的真实 DOM
对象,currentTarget
代表事件监听绑定的对象
也就是 this === event.currentTarget
this.contains(event.target) === true
然后 eventPhase
表示当前处于什么状态:捕获阶段 - 1, 处于目标 - 2,冒泡阶段 - 3
方法
preventDefault()
阻止默认事件stopPropagation()
阻止冒泡stopImmediatePropagation()
(DOM3
新增) 阻止冒泡的同时,阻止后续绑定的事件监听
委托
事件委托利用了事件冒泡,只需要指定一个事件处理程序就可以管理绑定事件元素以及其子代元素所触发的某一类型的所有事件
尤其是在需要监听动态添加的DOM元素的事件的情景下,或者是处理点击外侧关闭这种需求,使用委托更加常见(动态生成的对话框/动态添加的列表等等)
document.addEventListener('click', function(event) {
switch(event.target) {
case xx:
// 判断是对话框元素内部,不关闭对话框
case xx:
// 判断是对话框外侧阴影,关闭对话框
...
}
})
[转] 能力成长模型
原文链接:能力成长模型 - 梁飞的博客
最近看了温伯格1986年出版的《技术领导之路》,
很老的书,讲的都是一些浅显但容易被忽视的道理,
就像第一章,讲作者自己玩弹子球的水平提升,
时间长了,以为自己的水平提升像下图这样,每年都在逐步提升:
而实际上往往不是,能力的提升过程通常都是“高原-突破”式的,
在高原时期沉淀和思考,在学会新方法后突破,
不善于思考和总结的人,高原期就会特别长,而且人在高原期总会觉得很安逸:
并且在突破前一般还会有低谷期,就像下图的“高原-低谷-突破”模型,
要想突破,就必须努力打破安逸的现状,实践新想法、新知识、新方法,
在新的方法没有成熟之前,你会觉得还不如以前好,这就是低谷期,
总想退回老办法上去,尤其是在和别人对比的时候,一定要Hold住:
当然,实际数据不会像上面模型那样平滑,下图是作者玩弹子球水平的数据,
因为作者从小就买了一台弹子球机,上面记录了他所有的成绩:
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.