seajs1.3.0源碼解析之module依賴有序加載
這里是seajs loader的核心部分,有些IE兼容的部分還不是很明白,主要是理解各個(gè)模塊如何依賴有序加載,以及CMD規(guī)范。
代碼有點(diǎn)長(zhǎng),需要耐心看:
/**
* The core of loader
*/
;(function(seajs, util, config) {
// 模塊緩存
var cachedModules = {}
// 接口修改緩存
var cachedModifiers = {}
// 編譯隊(duì)列
var compileStack = []
// 模塊狀態(tài)
var STATUS = {
'FETCHING': 1, // The module file is fetching now. 模塊正在下載中
'FETCHED': 2, // The module file has been fetched. 模塊已下載
'SAVED': 3, // The module info has been saved. 模塊信息已保存
'READY': 4, // All dependencies and self are ready to compile. 模塊的依賴項(xiàng)都已下載,等待編譯
'COMPILING': 5, // The module is in compiling now. 模塊正在編譯中
'COMPILED': 6 // The module is compiled and module.exports is available. 模塊已編譯
}
function Module(uri, status) {
this.uri = uri
this.status = status || 0
// this.id is set when saving
// this.dependencies is set when saving
// this.factory is set when saving
// this.exports is set when compiling
// this.parent is set when compiling
// this.require is set when compiling
}
Module.prototype._use = function(ids, callback) {
//轉(zhuǎn)換為數(shù)組,統(tǒng)一操作
util.isString(ids) && (ids = [ids])
// 使用模塊系統(tǒng)內(nèi)部的路徑解析機(jī)制來(lái)解析并返回模塊路徑
var uris = resolve(ids, this.uri)
this._load(uris, function() {
// Loads preload files introduced in modules before compiling.
// 在編譯之前,再次調(diào)用preload預(yù)加載模塊
// 因?yàn)樵诖a執(zhí)行期間,隨時(shí)可以調(diào)用seajs.config配置預(yù)加載模塊
preload(function() {
// 編譯每個(gè)模塊,并將各個(gè)模塊的exports作為參數(shù)傳遞給回調(diào)函數(shù)
var args = util.map(uris, function(uri) {
return uri ? cachedModules[uri]._compile() : null
})
if (callback) {
// null使回調(diào)函數(shù)中this指針為window
callback.apply(null, args)
}
})
})
}
// 主模塊加載依賴模塊(稱之為子模塊),并執(zhí)行回調(diào)函數(shù)
Module.prototype._load = function(uris, callback) {
// 過(guò)濾uris數(shù)組
// 情況一:緩存中不存在該模塊,返回其uri
// 情況二:緩存中存在該模塊,但是其status < STATUS.READY(即還沒(méi)準(zhǔn)備好編譯)
var unLoadedUris = util.filter(uris, function(uri) {
return uri && (!cachedModules[uri] ||
cachedModules[uri].status < STATUS.READY)
})
var length = unLoadedUris.length
// 如果length為0,表示依賴項(xiàng)為0或者都已下載完成,那么執(zhí)行回調(diào)編譯操作
if (length === 0) {
callback()
return
}
var remain = length
for (var i = 0; i < length; i++) {
// 閉包,為onFetched函數(shù)提供上下文環(huán)境
(function(uri) {
// 創(chuàng)建模塊對(duì)象
var module = cachedModules[uri] ||
(cachedModules[uri] = new Module(uri, STATUS.FETCHING))
//如果模塊已下載,那么執(zhí)行onFetched,否則執(zhí)行fetch操作(請(qǐng)求模塊)
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)
function onFetched() {
// cachedModules[uri] is changed in un-correspondence case
module = cachedModules[uri]
// 如果模塊狀態(tài)為SAVED,表示模塊的依賴項(xiàng)已經(jīng)確定,那么下載依賴模塊
if (module.status >= STATUS.SAVED) {
// 從模塊信息中獲取依賴模塊列表,并作循環(huán)依賴的處理
var deps = getPureDependencies(module)
// 如果存在依賴項(xiàng),繼續(xù)下載
if (deps.length) {
Module.prototype._load(deps, function() {
cb(module)
})
}
// 否則直接執(zhí)行cb
else {
cb(module)
}
}
// Maybe failed to fetch successfully, such as 404 or non-module.
// In these cases, just call cb function directly.
// 如果下載模塊不成功,比如404或者模塊不規(guī)范(代碼出錯(cuò)),導(dǎo)致此時(shí)模塊狀態(tài)可能為fetching,或者fetched
// 此時(shí)直接執(zhí)行回調(diào)函數(shù),在編譯模塊時(shí),該模塊就只會(huì)返回null
else {
cb()
}
}
})(unLoadedUris[i])
}
function cb(module) {
// 更改模塊狀態(tài)為READY,當(dāng)remain為0時(shí)表示模塊依賴都已經(jīng)下完,那么執(zhí)行callback
(module || {}).status < STATUS.READY && (module.status = STATUS.READY)
--remain === 0 && callback()
}
}
Module.prototype._compile = function() {
var module = this
// 如果該模塊已經(jīng)編譯過(guò),則直接返回module.exports
if (module.status === STATUS.COMPILED) {
return module.exports
}
// Just return null when:
// 1. the module file is 404.
// 2. the module file is not written with valid module format.
// 3. other error cases.
// 這里是處理一些異常情況,此時(shí)直接返回null
if (module.status < STATUS.SAVED && !hasModifiers(module)) {
return null
}
// 更改模塊狀態(tài)為COMPILING,表示模塊正在編譯
module.status = STATUS.COMPILING
// 模塊內(nèi)部使用,是一個(gè)方法,用來(lái)獲取其他模塊提供(稱之為子模塊)的接口,同步操作
function require(id) {
// 根據(jù)id解析模塊的路徑
var uri = resolve(id, module.uri)
// 從模塊緩存中獲取模塊(注意,其實(shí)這里子模塊作為主模塊的依賴項(xiàng)是已經(jīng)被下載下來(lái)的)
var child = cachedModules[uri]
// Just return null when uri is invalid.
// 如果child為空,只能表示參數(shù)填寫出錯(cuò)導(dǎo)致uri不正確,那么直接返回null
if (!child) {
return null
}
// Avoids circular calls.
// 如果子模塊的狀態(tài)為STATUS.COMPILING,直接返回child.exports,避免因?yàn)檠h(huán)依賴反復(fù)編譯模塊
if (child.status === STATUS.COMPILING) {
return child.exports
}
// 指向初始化時(shí)調(diào)用當(dāng)前模塊的模塊。根據(jù)該屬性,可以得到模塊初始化時(shí)的Call Stack.
child.parent = module
// 返回編譯過(guò)的child的module.exports
return child._compile()
}
// 模塊內(nèi)部使用,用來(lái)異步加載模塊,并在加載完成后執(zhí)行指定回調(diào)。
require.async = function(ids, callback) {
module._use(ids, callback)
}
// 使用模塊系統(tǒng)內(nèi)部的路徑解析機(jī)制來(lái)解析并返回模塊路徑。該函數(shù)不會(huì)加載模塊,只返回解析后的絕對(duì)路徑。
require.resolve = function(id) {
return resolve(id, module.uri)
}
// 通過(guò)該屬性,可以查看到模塊系統(tǒng)加載過(guò)的所有模塊。
// 在某些情況下,如果需要重新加載某個(gè)模塊,可以得到該模塊的 uri, 然后通過(guò) delete require.cache[uri] 來(lái)將其信息刪除掉。這樣下次使用時(shí),就會(huì)重新獲取。
require.cache = cachedModules
// require是一個(gè)方法,用來(lái)獲取其他模塊提供的接口。
module.require = require
// exports是一個(gè)對(duì)象,用來(lái)向外提供模塊接口。
module.exports = {}
var factory = module.factory
// factory 為函數(shù)時(shí),表示模塊的構(gòu)造方法。執(zhí)行該方法,可以得到模塊向外提供的接口。
if (util.isFunction(factory)) {
compileStack.push(module)
runInModuleContext(factory, module)
compileStack.pop()
}
// factory 為對(duì)象、字符串等非函數(shù)類型時(shí),表示模塊的接口就是該對(duì)象、字符串等值。
// 如:define({ "foo": "bar" });
// 如:define('I am a template. My name is {{name}}.');
else if (factory !== undefined) {
module.exports = factory
}
// 更改模塊狀態(tài)為COMPILED,表示模塊已編譯
module.status = STATUS.COMPILED
// 執(zhí)行模塊接口修改,通過(guò)seajs.modify()
execModifiers(module)
return module.exports
}
Module._define = function(id, deps, factory) {
var argsLength = arguments.length
// 根據(jù)傳入的參數(shù)個(gè)數(shù),進(jìn)行參數(shù)匹配
// define(factory)
// 一個(gè)參數(shù)的情況:
// id : undefined
// deps : undefined(后面會(huì)根據(jù)正則取出依賴模塊列表)
// factory : function
if (argsLength === 1) {
factory = id
id = undefined
}
// define(id || deps, factory)
// 兩個(gè)參數(shù)的情況:
else if (argsLength === 2) {
// 默認(rèn)情況下 :define(id, factory)
// id : '...'
// deps : undefined
// factory : function
factory = deps
deps = undefined
// define(deps, factory)
// 如果第一個(gè)參數(shù)為數(shù)組 :define(deps, factory)
// id : undefined
// deps : [...]
// factory : function
if (util.isArray(id)) {
deps = id
id = undefined
}
}
// Parses dependencies.
// 如果deps不是數(shù)組(即deps未指定值),那么通過(guò)正則表達(dá)式解析依賴
if (!util.isArray(deps) && util.isFunction(factory)) {
deps = util.parseDependencies(factory.toString())
}
// 元信息,之后會(huì)將信息傳遞給對(duì)應(yīng)的module對(duì)象中
var meta = { id: id, dependencies: deps, factory: factory }
var derivedUri
// Try to derive uri in IE6-9 for anonymous modules.
// 對(duì)于IE6-9,嘗試通過(guò)interactive script獲取模塊的uri
if (document.attachEvent) {
// Try to get the current script.
// 獲取當(dāng)前的script
var script = util.getCurrentScript()
if (script) {
// 將當(dāng)前script的url進(jìn)行unpareseMap操作,與模塊緩存中key保持一致
derivedUri = util.unParseMap(util.getScriptAbsoluteSrc(script))
}
if (!derivedUri) {
util.log('Failed to derive URI from interactive script for:',
factory.toString(), 'warn')
// NOTE: If the id-deriving methods above is failed, then falls back
// to use onload event to get the uri.
}
}
// Gets uri directly for specific module.
// 如果給定id,那么根據(jù)id解析路徑
// 顯然如果沒(méi)指定id:
// 對(duì)于非IE瀏覽器而言,則返回undefined(derivedUri為空)
// 對(duì)于IE瀏覽器則返回CurrentScript的src
// 如果指定id:
// 則均返回有seajs解析(resolve)過(guò)的路徑url
var resolvedUri = id ? resolve(id) : derivedUri
// uri存在的情況,進(jìn)行模塊信息存儲(chǔ)
if (resolvedUri) {
// For IE:
// If the first module in a package is not the cachedModules[derivedUri]
// self, it should assign to the correct module when found.
if (resolvedUri === derivedUri) {
var refModule = cachedModules[derivedUri]
if (refModule && refModule.realUri &&
refModule.status === STATUS.SAVED) {
cachedModules[derivedUri] = null
}
}
// 存儲(chǔ)模塊信息
var module = save(resolvedUri, meta)
// For IE:
// Assigns the first module in package to cachedModules[derivedUrl]
if (derivedUri) {
// cachedModules[derivedUri] may be undefined in combo case.
if ((cachedModules[derivedUri] || {}).status === STATUS.FETCHING) {
cachedModules[derivedUri] = module
module.realUri = derivedUri
}
}
else {
// 將第一個(gè)模塊存儲(chǔ)到firstModuleInPackage
firstModuleInPackage || (firstModuleInPackage = module)
}
}
// uri不存在的情況,在onload回調(diào)中進(jìn)行模塊信息存儲(chǔ),那里有個(gè)閉包
else {
// Saves information for "memoizing" work in the onload event.
// 因?yàn)榇藭r(shí)的uri不知道,所以將元信息暫時(shí)存儲(chǔ)在anonymousModuleMeta中,在onload回調(diào)中進(jìn)行模塊save操作
anonymousModuleMeta = meta
}
}
// 獲取正在編譯的模塊
Module._getCompilingModule = function() {
return compileStack[compileStack.length - 1]
}
// 從seajs.cache中快速查看和獲取已加載的模塊接口,返回值是module.exports數(shù)組
// selector 支持字符串和正則表達(dá)式
Module._find = function(selector) {
var matches = []
util.forEach(util.keys(cachedModules), function(uri) {
if (util.isString(selector) && uri.indexOf(selector) > -1 ||
util.isRegExp(selector) && selector.test(uri)) {
var module = cachedModules[uri]
module.exports && matches.push(module.exports)
}
})
return matches
}
// 修改模塊接口
Module._modify = function(id, modifier) {
var uri = resolve(id)
var module = cachedModules[uri]
// 如果模塊存在,并且處于COMPILED狀態(tài),那么執(zhí)行修改接口操作
if (module && module.status === STATUS.COMPILED) {
runInModuleContext(modifier, module)
}
// 否則放入修改接口緩存中
else {
cachedModifiers[uri] || (cachedModifiers[uri] = [])
cachedModifiers[uri].push(modifier)
}
return seajs
}
// For plugin developers
Module.STATUS = STATUS
Module._resolve = util.id2Uri
Module._fetch = util.fetch
Module.cache = cachedModules
// Helpers
// -------
// 正在下載的模塊列表
var fetchingList = {}
// 已下載的模塊列表
var fetchedList = {}
// 回調(diào)函數(shù)列表
var callbackList = {}
// 匿名模塊元信息
var anonymousModuleMeta = null
var firstModuleInPackage = null
// 循環(huán)依賴棧
var circularCheckStack = []
// 批量解析模塊的路徑
function resolve(ids, refUri) {
if (util.isString(ids)) {
return Module._resolve(ids, refUri)
}
return util.map(ids, function(id) {
return resolve(id, refUri)
})
}
function fetch(uri, callback) {
// fetch時(shí),首先將uri按map規(guī)則轉(zhuǎn)換
var requestUri = util.parseMap(uri)
// 在fethedList(已下載的模塊列表)中查找,有的話,直接返回,并執(zhí)行回調(diào)函數(shù)
// TODO : 為什么這一步,fetchedList可能會(huì)存在該模?
if (fetchedList[requestUri]) {
// See test/issues/debug-using-map
cachedModules[uri] = cachedModules[requestUri]
callback()
return
}
// 在fetchingList(正在在下載的模塊列表)中查找,有的話,只需添加回調(diào)函數(shù)到列表中去,然后直接返回
if (fetchingList[requestUri]) {
callbackList[requestUri].push(callback)
return
}
// 如果走到這一步,表示該模塊是第一次被請(qǐng)求,
// 那么在fetchingList插入該模塊的信息,表示該模塊已經(jīng)處于下載列表中,并初始化該模塊對(duì)應(yīng)的回調(diào)函數(shù)列表
fetchingList[requestUri] = true
callbackList[requestUri] = [callback]
// Fetches it
// 獲取該模塊,即發(fā)起請(qǐng)求
Module._fetch(
requestUri,
function() {
// 在fetchedList插入該模塊的信息,表示該模塊已經(jīng)下載完成
fetchedList[requestUri] = true
// Updates module status
var module = cachedModules[uri]
// 此時(shí)status可能為STATUS.SAVED,之前在_define中已經(jīng)說(shuō)過(guò)
if (module.status === STATUS.FETCHING) {
module.status = STATUS.FETCHED
}
// Saves anonymous module meta data
// 因?yàn)槭悄涿K(此時(shí)通過(guò)閉包獲取到uri,在這里存儲(chǔ)模塊信息)
// 并將anonymousModuleMeta置為空
if (anonymousModuleMeta) {
save(uri, anonymousModuleMeta)
anonymousModuleMeta = null
}
// Assigns the first module in package to cachedModules[uri]
// See: test/issues/un-correspondence
if (firstModuleInPackage && module.status === STATUS.FETCHED) {
cachedModules[uri] = firstModuleInPackage
firstModuleInPackage.realUri = uri
}
firstModuleInPackage = null
// Clears
// 在fetchingList清除模塊信息,因?yàn)橐呀?jīng)該模塊fetched并save
if (fetchingList[requestUri]) {
delete fetchingList[requestUri]
}
// Calls callbackList
// 依次調(diào)用回調(diào)函數(shù),并清除回調(diào)函數(shù)列表
if (callbackList[requestUri]) {
util.forEach(callbackList[requestUri], function(fn) {
fn()
})
delete callbackList[requestUri]
}
},
config.charset
)
}
function save(uri, meta) {
var module = cachedModules[uri] || (cachedModules[uri] = new Module(uri))
// Don't override already saved module
// 此時(shí)status可能有兩個(gè)狀態(tài):
// STATUS.FETCHING,在define里面調(diào)用(指定了id),存儲(chǔ)模塊信息
// STATUS.FETCHED,在onload的回調(diào)函數(shù)里調(diào)用,存儲(chǔ)模塊信息
if (module.status < STATUS.SAVED) {
// Lets anonymous module id equal to its uri
// 匿名模塊(即沒(méi)有指定id),用它的uri作為id
module.id = meta.id || uri
// 將依賴項(xiàng)(數(shù)組)解析成的絕對(duì)路徑,存儲(chǔ)到模塊信息中
module.dependencies = resolve(
util.filter(meta.dependencies || [], function(dep) {
return !!dep
}), uri)
// 存儲(chǔ)factory(要執(zhí)行的模塊代碼,也可能是對(duì)象或者字符串等)
module.factory = meta.factory
// Updates module status
// 更新模塊狀態(tài)為SAVED,(注意此時(shí)它只是擁有了依賴項(xiàng),還未全部下載下來(lái)(即還未READY))
module.status = STATUS.SAVED
}
return module
}
// 根據(jù)模塊上下文執(zhí)行模塊代碼
function runInModuleContext(fn, module) {
// 傳入與模塊相關(guān)的兩個(gè)參數(shù)以及模塊自身
// exports用來(lái)暴露接口
// require用來(lái)獲取依賴模塊(同步)(編譯)
var ret = fn(module.require, module.exports, module)
// 支持返回值暴露接口形式,如:
// return {
// fn1 : xx
// ,fn2 : xx
// ...
// }
if (ret !== undefined) {
module.exports = ret
}
}
// 判斷模塊是否存在接口修改
function hasModifiers(module) {
return !!cachedModifiers[module.realUri || module.uri]
}
// 修改模塊接口
function execModifiers(module) {
var uri = module.realUri || module.uri
var modifiers = cachedModifiers[uri]
// 內(nèi)部變量 cachedModifiers 就是用來(lái)存儲(chǔ)用戶通過(guò) seajs.modify 方法定義的修改點(diǎn)
// 查看該uri是否又被modify更改過(guò)
if (modifiers) {
// 對(duì)修改點(diǎn)統(tǒng)一執(zhí)行factory,返回修改后的module.exports
util.forEach(modifiers, function(modifier) {
runInModuleContext(modifier, module)
})
// 刪除 modify 方法定義的修改點(diǎn) ,避免再次執(zhí)行
delete cachedModifiers[uri]
}
}
//獲取純粹的依賴關(guān)系,得到不存在循環(huán)依賴關(guān)系的依賴數(shù)組
function getPureDependencies(module) {
var uri = module.uri
// 對(duì)每個(gè)依賴項(xiàng)進(jìn)行過(guò)濾,對(duì)于有可能形成循環(huán)依賴的進(jìn)行剔除,并打印出警告日志
return util.filter(module.dependencies, function(dep) {
// 首先將被檢查模塊的uri放到循環(huán)依賴檢查棧中,之后的檢查會(huì)用到
circularCheckStack = [uri]
//接下來(lái)檢查模塊uri是否和其依賴的模塊存在循環(huán)依賴
var isCircular = isCircularWaiting(cachedModules[dep])
if (isCircular) {
// 如果循環(huán),則將uri放到循環(huán)依賴檢查棧中
circularCheckStack.push(uri)
// 打印出循環(huán)警告日志
printCircularLog(circularCheckStack)
}
return !isCircular
})
}
function isCircularWaiting(module) {
// 如果依賴模塊不存在,那么返回false,因?yàn)榇藭r(shí)也無(wú)法獲得依賴模塊的依賴項(xiàng),所以這里無(wú)法做判斷
// 或者如果模塊的狀態(tài)值等于saved,也返回false,因?yàn)槟K狀態(tài)為saved的時(shí)候代表該模塊的信息已經(jīng)有了,
// 所以盡管形成了循環(huán)依賴,但是require主模塊時(shí),同樣可以正常編譯,返回主模塊接口(好像nodejs會(huì)返回undefined)
if (!module || module.status !== STATUS.SAVED) {
return false
}
// 如果不是以上的情況,那么將依賴模塊的uri放到循環(huán)依賴檢查棧中,之后的檢查會(huì)用到
circularCheckStack.push(module.uri)
// 再次取依賴模塊的依賴模塊
var deps = module.dependencies
if (deps.length) {
// 通過(guò)循環(huán)依賴檢查棧,檢查是否存在循環(huán)依賴(這里是第一層依賴模塊檢查,與主模塊循環(huán)依賴的情況)
if (isOverlap(deps, circularCheckStack)) {
return true
}
// 如果不存在上述情形,那么進(jìn)一步查看,依賴模塊的依賴模塊,查看他們是否存在對(duì)循環(huán)依賴檢查棧中的uri的模塊存在循環(huán)依賴
// 這樣的話,就遞歸了,循環(huán)依賴檢查棧就像形成的一條鏈,當(dāng)前模塊依次對(duì)主模塊,主模塊的主模塊...直到最頂上的主模塊,依次進(jìn)行判斷是否存在依賴
for (var i = 0; i < deps.length; i++) {
if (isCircularWaiting(cachedModules[deps[i]])) {
return true
}
}
}
// 如果不存在循環(huán)依賴,那么pop出之前已經(jīng)push進(jìn)的模塊uri,并返回false
circularCheckStack.pop()
return false
}
// 打印出循環(huán)警告日志
function printCircularLog(stack, type) {
util.log('Found circular dependencies:', stack.join(' --> '), type)
}
//判斷兩個(gè)數(shù)組是否有重復(fù)的值
function isOverlap(arrA, arrB) {
var arrC = arrA.concat(arrB)
return arrC.length > util.unique(arrC).length
}
// 從配置文件讀取是否有需要提前加載的模塊
// 如果有預(yù)先加載模塊,首先設(shè)置預(yù)加載模塊為空(保證下次不必重復(fù)加載),并加載預(yù)加載模塊并執(zhí)行回調(diào),如果沒(méi)有則順序執(zhí)行
function preload(callback) {
var preloadMods = config.preload.slice()
config.preload = []
preloadMods.length ? globalModule._use(preloadMods, callback) : callback()
}
// Public API
// 對(duì)外暴露的API
// ----------
// 全局模塊,可以認(rèn)為是頁(yè)面模塊,頁(yè)面中的js,css文件都是通過(guò)它來(lái)載入的
// 模塊初始狀態(tài)就是COMPILED,uri就是頁(yè)面的uri
var globalModule = new Module(util.pageUri, STATUS.COMPILED)
// 頁(yè)面js,css文件加載器
seajs.use = function(ids, callback) {
// Loads preload modules before all other modules.
// 預(yù)加載模塊
preload(function() {
globalModule._use(ids, callback)
})
// Chain
return seajs
}
// For normal users
// 供普通用戶調(diào)用
seajs.define = Module._define
seajs.cache = Module.cache
seajs.find = Module._find
seajs.modify = Module._modify
// For plugin developers
// 供開發(fā)者使用
seajs.pluginSDK = {
Module: Module,
util: util,
config: config
}
})(seajs, seajs._util, seajs._config)
- JavaScript的Module模式編程深入分析
- nodejs中exports與module.exports的區(qū)別詳細(xì)介紹
- node.js的exports、module.exports與ES6的export、export default深入詳解
- 詳解Sea.js中Module.exports和exports的區(qū)別
- node.js報(bào)錯(cuò):Cannot find module ''ejs''的解決辦法
- node.js中module.exports與exports用法上的區(qū)別
- 深入淺析AngularJS中的module(模塊)
- 詳解AngularJS中module模塊的導(dǎo)入導(dǎo)出
- AngularJS Module方法詳解
- js module大戰(zhàn)
相關(guān)文章
JS實(shí)現(xiàn)刷新父頁(yè)面不彈出提示框的方法
這篇文章主要介紹了JS實(shí)現(xiàn)刷新父頁(yè)面不彈出提示框的方法,實(shí)例分析了javascript子窗口的打開以及子窗口與父窗口的交互操作技巧,需要的朋友可以參考下2016-06-06JS中實(shí)現(xiàn)函數(shù)return多個(gè)返回值的實(shí)例
下面小編就為大家?guī)?lái)一篇JS中實(shí)現(xiàn)函數(shù)return多個(gè)返回值的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02文本框中禁止非數(shù)字字符輸入比如手機(jī)號(hào)碼、郵編
總是遇到很多禁止非數(shù)字字符輸入的文本框,比如手機(jī)號(hào)碼了 郵編了于是下面為大家介紹下如何禁止,感興趣的朋友可以了解下2013-08-08JavaScript實(shí)現(xiàn)二維坐標(biāo)點(diǎn)排序效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)二維坐標(biāo)點(diǎn)排序效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07javascript使用appendChild追加節(jié)點(diǎn)實(shí)例
這篇文章主要介紹了javascript使用appendChild追加節(jié)點(diǎn)的方法,實(shí)例分析了appendChild()函數(shù)增加結(jié)點(diǎn)的使用技巧,需要的朋友可以參考下2015-01-01javascript中undefined與null的區(qū)別
在JavaScript中存在這樣兩種原始類型:Null與Undefined。這兩種類型常常會(huì)使JavaScript的開發(fā)人員產(chǎn)生疑惑,在什么時(shí)候是Null,什么時(shí)候又是Undefined?2015-08-08Javascript節(jié)流函數(shù)throttle和防抖函數(shù)debounce
這篇文章主要介紹了Javascript節(jié)流函數(shù)throttle和防抖函數(shù)debounce,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12JavaScript 關(guān)于元素獲取焦點(diǎn)(隱藏元素與div)
關(guān)于元素獲取焦點(diǎn)要注意2個(gè)小問(wèn)題,需要的朋友可以參考下。2011-01-01