jQuery Basic Notes
Callbacks Queue
callback queue use Observer
pattern to
add callbacks to callback queue,
fire callbacks when events happen.
function Callbacks(options) {
let list = []
const self = {
add(fn) {
if (options === 'unique') {
if (!list.includes(fn))
list.push(fn)
} else {
list.push(fn)
}
},
fire(args) {
list.forEach((fn) => {
fn(args)
})
if (options === 'once')
list = undefined
},
}
return self
}
Deferred Queue
Same to Promise
class Promise {
// `executor` takes 2 parameters, `resolve()` and `reject()`. The executor
// function is responsible for calling `resolve()` or `reject()` to say that
// the async operation succeeded (resolved) or failed (rejected).
constructor(executor) {
if (typeof executor !== 'function')
throw new TypeError('Executor must be a function')
// Internal state. `$state` is the state of the promise, and `$chained` is
// an array of the functions we need to call once this promise is settled.
this.$state = 'PENDING'
this.$chained = []
// Implement `resolve()` and `reject()` for the executor function to use
const resolve = (res) => {
// A promise is considered "settled" when it is no longer
// pending, that is, when either `resolve()` or `reject()`
// was called once. Calling `resolve()` or `reject()` twice
// or calling `reject()` after `resolve()` was already called
// are no-ops.
if (this.$state !== 'PENDING')
return
// If `res` is a "thenable", lock in this promise to match the
// resolved or rejected state of the thenable.
const then = res != null ? res.then : null
if (typeof then === 'function') {
// In this case, the promise is "resolved", but still in the 'PENDING'
// state. This is what the ES6 spec means when it says "A resolved promise
// may be pending, fulfilled or rejected" in
// http://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects
return then(resolve, reject)
}
this.$state = 'FULFILLED'
this.$internalValue = res
// If somebody called `.then()` while this promise was pending, need
// to call their `onFulfilled()` function
for (const { onFulfilled } of this.$chained) onFulfilled(res)
return res
}
const reject = (err) => {
if (this.$state !== 'PENDING')
return
this.$state = 'REJECTED'
this.$internalValue = err
for (const { onRejected } of this.$chained) onRejected(err)
}
// Call the executor function with `resolve()` and `reject()` as in the spec.
try {
// If the executor function throws a sync exception, we consider that
// a rejection. Keep in mind that, since `resolve()` or `reject()` can
// only be called once, a function that synchronously calls `resolve()`
// and then throws will lead to a fulfilled promise and a swallowed error
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// `onFulfilled` is called if the promise is fulfilled, and `onRejected`
// if the promise is rejected. For now, you can think of 'fulfilled' and
// 'resolved' as the same thing.
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
// Ensure that errors in `onFulfilled()` and `onRejected()` reject the
// returned promise, otherwise they'll crash the process. Also, ensure
// that the promise
const _onFulfilled = (res) => {
try {
// If `onFulfilled()` returns a promise, trust `resolve()` to handle
// it correctly.
// store new value to new Promise
resolve(onFulfilled(res))
} catch (err) {
reject(err)
}
}
const _onRejected = (err) => {
try {
// store new value to new Promise
reject(onRejected(err))
} catch (_err) {
reject(_err)
}
}
if (this.$state === 'FULFILLED') {
_onFulfilled(this.$internalValue)
} else if (this.$state === 'REJECTED') {
_onRejected(this.$internalValue)
} else {
this.$chained.push({
onFulfilled: _onFulfilled,
onRejected: _onRejected,
})
}
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
}
Sizzle Selector Engine
- runtime tokenizer and parser
- api from
querySelectorAll
DOM Module
DOM Internal
createDocumentFragment:
多次使用节点方法(如:appendChild)绘制页面,每次都要刷新页面一次。 使用 document_createDocumentFragment()创建一个文档碎片,把所有的新结点附加在其上, 然后把文档碎片的内容一次性添加到 document 中,提升性能
function domManipulation(parentElements, target, callback) {
const fragment = buildFragment([target], parentElements)
callback.call(parentElements)
}
function after(...args) {
return this.domManipulation(...args, function (elem) {
this.parentNode.insertBefore(elem, this.nextSibling)
})
}
structure
$('selector').html('tag+text')
$('selector').text('text')
$('selector').clone()
$('selector').remove()
$('selector').appendTo('selector')
$('selector').parent()
$('selector').children()
$('selector').index()
class
$('selector').addClass('')
$('selector').removeClass('')
$('selector').hidden()
style
$('selector').css('color', 'red')
$('selector').prop('disable', 'true')
Events Module
Events Internal
- 通过 on 绑定事件,分析传递的数据,加工变成 add 能够识别的数据
- 通过 add 把 数据整理放到数据缓存中保存,通过 addEventListener 绑定事件
- 触发事件执行 addEventListener 回调 dispatch 方法
- 修正事件对象存在的问题,通过 fix 生成一个可写的事件对象
- 引入 handlers 把委托和原生事件(例如"click")绑定区分对待
- 执行数据缓存的事件回调,传入内部产生的事件对象
Mouse
- click
- dbclick
- mouseenter
- mouseleave
Keyboard
- keypress
- keydown
- keyup
Form
- submit
- change
- focus
- blur
Document and Window Event
- load
- resize
- scroll
- unload
$(window).scroll((event) => {})
$(document).height() // 返回整个网页的高度
$(window).height() // 返回窗口高度
$(window).scrollTop() // 返回滚动条距网页顶部距离
常用多态函数
$(selector).data()
$(selector).html()
$(selector).css()
$(document).ready(() => {})
AJAX Module
JSON API
$.getJSON
:
$.getJSON(url, data, success(data, statusCode, xhr))
$.getJSON('test.js', (json) => {
alert(`JSON Data: ${json.users[3].name}`)
})
AJAX API
$.ajax
:
$.ajax({
url: 'http://localhost:3000',
type: 'GET' / 'POST' / 'PUT' / 'DELETE',
data: dataSchema,
dataType: 'json',
success: successCallback,
error: errorHandle,
})
Animation Module
- 通过多个 animate 方法形成动画链,那么这个动画链其实都是会加入到 queue 队列里面
- 在每一次 queue 方法中会把动画数据写到队列中,然后取出队列中的第一个序列通过 dequeue 方法执行
- 开始执行之前写一个进程锁
inProgress
到 queue 里面, 代表这个动画还在执行中, 防止同个序列的多个动画重复执行,这个就是异步执行同步收集的处理方案 - 此时动画开始了,这里注意动画是在异步执行的同步的代码,继续调用下一个 animate
- 执行同样的 animate 方法逻辑但是此时问题来了, 动画可能还在执行可是后续的 animate 还在继续调用,所以这个时候后面的动画代码就需要等待了(进程锁)
- 队列头是有一把
inProgress
进程锁的,那么这时候动画只需要加入队列, 但是可以通过inProgress
是否存在来判断是否执行 - 所有的 animate 方法在加入队列都是按照以上的逻辑依次执行, 动画执行完毕了就会有一个结束通知,然后从 queue 取出第一个队列继续执行了,如此循环
Tween Object
通过一个 Tween 类构造出来的缓动对象,其实就是针对每一个属性的封装对象, 这样我们只需要设计一个定时器,在指定的时间内调用 Tween 生成的这些对象就可以了, Tween 内部控制着各自属性的状态改变。
具体右边的实现代码涉及了如下几个部分了:
- Animation 函数,入口函数用来做一些参数的初始化工作,整个动画的开始调度
- animation 对象就是实际的动画对象了,通过 Animation 函数创建,这个对象上实现了所有属性与方法
- new Tween() 通过 Tween 创建每一个属性对象相关的数据
- animation.tweens 保存了每一个属性对象的容器
- Animation.fx 就是具体开始动画执行的调用的一个调度对象了
- 定时器都是执行一个回调函数的,tick 就是定时器执行的回调, tick 函数中通过计算出变化数据,然后通过循环 animation.tweens 中的每一个动画属性对象,来实现改变