Zepto事件模块源码分析

一、保存事件数据的handlers

我们知道js原生api中要移除事件,需要传入绑定时的回调函数。而Zepto则可以不传入回调函数,直接移除对应类型的所有事件。原因就在于Zepto在绑定事件时,会把相关的数据都保存到handlers对象中,因此就可以在这个变量中查找对应事件的回调函数,来移除事件。

handlers对象的数据格式如下:

  1. {
  2. 1: [ // handlers的值为DOM元素的_zid
  3. {
  4. del: function() {}, // 实现事件代理的函数
  5. e: "click", // 事件名称
  6. fn: function() {}, // 用户传入的回调函数
  7. i: 0, // 该对象在数组里的下标
  8. ns: "", // 事件的命名空间,只用使用$.fn.triggerHandler时可用,$.fn.trigger不能使用。
  9. proxy: function(e) {}, // 真正绑定事件时的回调函数,里面判断调用del或者fn
  10. sel: undefined // 要进行事件代理时传入的selector
  11. }
  12. ]
  13. }

二、绑定事件

主要流程图

流程说明

处理参数实现函数重载

实现函数重载的重点就是判断参数的类型,处理参数:

  1. // 处理参数,实现函数重载
  2. if (!isString(selector) && !isFunction(callback) && callback !== false) // 没有传入selector参数
  3. callback = data, data = selector, selector = undefined
  4. if (callback === undefined || data === false) // 没有传入data参数
  5. callback = data, data = undefined
  6. if (callback === false) callback = returnFalse // 回调函数传入的是false,使用returnFalse函数代替

构建事件代理函数

事件代理函数的重点为:在触发元素和e.target之间找到和参数selector相匹配的元素,生成事件对象,触发回调函数:

  1. // 如果有传入selector参数,构建代理函数
  2. if (selector) delegator = function(e){
  3. var evt, match = $(e.target).closest(selector, element).get(0) // match为e.target到触发元素范围内和selector相匹配的元素
  4. if (match && match !== element) { // 如果有相匹配的元素,改写事件对象,触发回调函数
  5. evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
  6. return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
  7. }
  8. }

保存事件的相关信息

保存事件的相关信息就是根据参数组成handler对象,保存到上面提过的handlers对象中。需要注意的是Zepto是通过DOM对象中添加一个_zid来连接DOM对象和对应的handler对象。

使用zid函数来赋值和获取_zid:

  1. function zid(element) {
  2. return element._zid || (element._zid = _zid++)
  3. }

通过一个_zid而不是通过DOM对象的引用来连接handler是因为:防止移除掉DOM元素后,handlers对象还保存着对这个DOM元素的引用。通过使用_zid就可以防止这种情况发生,避免了内存泄漏。

构建真正的回调函数proxy

Zepto对事件对象进行了扩展,例如添加isImmediatePropagationStopped函数等,所以就要构建proxy函数来进行一层代理,改变触发时的事件对象。此外,如果用户需要进行事件代理,proxy函数执行时就会调用上面构建好的代理函数,否则调用用户传进来的回调函数。因此使用addEventListener所传入的真正回调函数就是proxy函数。

focus和blur事件的冒泡

focus和blur事件本身是不冒泡的,如果需要对这两个事件进行事件代理,就要运用一些小技巧。首先,如果浏览器支持focusin和focusout,就使用这两个可以冒泡事件来代替。如果浏览器不支持focusion和focusout,就利用focus和blur捕获不冒泡的特性,传入addEventListener中的第三个参数设置true,以此来进行事件代理。

三、取消绑定事件

取消绑定是比较简单的,由于事件绑定时都把相应的数据都保存到了handlers对象上,所以只要根据参数在这个对象里寻找对应的回调函数,使用removeEventListener取消绑定就可以了。

四、触发事件

触发事件也比较简单,通过document.createEvent来创建事件对象,然后调用dispatchEvent来触发事件。这里有点小优化就是focus和blur事件的触发直接调用focus()和blur()。

创建事件的代码:

  1. $.Event = function(type, props) {
  2. if (!isString(type)) props = type, type = props.type
  3. var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
  4. if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
  5. event.initEvent(type, bubbles, true)
  6. return compatible(event)
  7. }

上面有点要注意的就是当创建鼠标相关的事件时要在document.createEvent的第一个参数中传入’MouseEvents‘,以提供更多的事件属性。鼠标相关的事件指的是:click、mousedown、mouseup和mousemove。

五、其他

源码大概只有300多行,中其实还有很多值得我们学习的地方,所以大家大可以花点时间阅读一下。

六、event模块源代码

  1. // Zepto.js
  2. // (c) 2010-2016 Thomas Fuchs
  3. // Zepto.js may be freely distributed under the MIT license.
  4. ;(function($){
  5. /*
  6. _zid :用来生成标示元素和回调函数的id,每标示一个就+1
  7. slice :Array.prototype.slice
  8. handlers :保存着所有的handler,结构如下,handlers对象的值为对应元素的_zid,值为对象数组
  9. {
  10. 1: [ // handlers的值为DOM元素的_zid
  11. {
  12. del: function() {}, // 实现事件代理的函数
  13. e: "click", // 事件名称
  14. fn: function() {}, // 用户传入的回调函数
  15. i: 0, // 该对象在数组里的下标
  16. ns: "", // 事件的命名空间,只用使用$.fn.triggerHandler时可用,$.fn.trigger不能使用。
  17. proxy: function(e) {}, // 真正绑定事件时的回调函数,里面判断调用del或者fn
  18. sel: undefined // 要进行事件代理时传入的selector
  19. }
  20. ]
  21. }
  22. specialEvents :生成一个模拟事件时,click、mousedown、mouseup和mousemove时使用'MouseEvents'参数
  23. focusinSupported:是否支持focusin的Boolean值
  24. focus :浏览器支持focusin和focusout时,focus和blur就使用focusin和focusout来代替
  25. hover :mouseenter和mouseout用mouseover和mouseout实现
  26. */
  27. var _zid = 1, undefined,
  28. slice = Array.prototype.slice,
  29. isFunction = $.isFunction,
  30. isString = function(obj){ return typeof obj == 'string' },
  31. handlers = {},
  32. specialEvents={},
  33. focusinSupported = 'onfocusin' in window,
  34. focus = { focus: 'focusin', blur: 'focusout' },
  35. hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
  36. specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
  37. // 获取元素或者函数的_zid,没有的话就生成一个。赋值给元素或者函数的_zid属性
  38. function zid(element) {
  39. return element._zid || (element._zid = _zid++)
  40. }
  41. // 根据给定的参数在handlers变量中寻找对应的handler
  42. function findHandlers(element, event, fn, selector) {
  43. event = parse(event) // 解析event参数,分离出事件名和ns
  44. if (event.ns) var matcher = matcherFor(event.ns) //
  45. // 取出所有属于element的handler,并且根据event、fn和selector参数进行筛选
  46. return (handlers[zid(element)] || []).filter(function(handler) {
  47. return handler
  48. && (!event.e || handler.e == event.e) // 事件名不同的过滤掉
  49. && (!event.ns || matcher.test(handler.ns)) // 命名空间不同的过滤掉
  50. && (!fn || zid(handler.fn) === zid(fn)) // 回调函数不同的过滤掉(通过_zid属性判断是否同一个函数)
  51. && (!selector || handler.sel == selector) // selector不同的过滤掉
  52. })
  53. }
  54. // 解析event参数: "click.abc" -> {e: "click", ns: "abc"}
  55. function parse(event) {
  56. var parts = ('' + event).split('.')
  57. return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
  58. }
  59. // 生成匹配的namespace表达式:'abc def' -> /(?:^| )abc .* ?def(?: |$)/
  60. function matcherFor(ns) {
  61. return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
  62. }
  63. // 获取绑定事件时,指定是否冒泡或捕获阶段的Boolean值
  64. // 需要事件代理和浏览器不支持focusin并且是focus和blur事件时返回ture,即绑定在捕获阶段
  65. // (因为focus和blur不会冒泡,但是会捕获)
  66. function eventCapture(handler, captureSetting) {
  67. return handler.del &&
  68. (!focusinSupported && (handler.e in focus)) ||
  69. !!captureSetting
  70. }
  71. // 返回真正绑定的事件名,例如要绑定mouseenter事件,其实真正绑定的是mouseover事件来模拟mouseenter事件。
  72. function realEvent(type) {
  73. return hover[type] || (focusinSupported && focus[type]) || type
  74. }
  75. /**
  76. * 添加事件的实际方法
  77. * @param {Object} element DOM元素
  78. * @param {String} events 事件字符串
  79. * @param {Function} fn 回调函数
  80. * @param {All} data 绑定事件时传入的data,可以是各种类型
  81. * @param {String} selector 被代理元素的css选择器
  82. * @param {[type]} delegator 进行事件代理的函数
  83. * @param {[type]} capture 指定捕获或者冒泡阶段
  84. */
  85. function add(element, events, fn, data, selector, delegator, capture){
  86. var id = zid(element), set = (handlers[id] || (handlers[id] = []))
  87. events.split(/\s/).forEach(function(event){
  88. // 如果事件名为ready,直接调用$.fn.ready方法
  89. if (event == 'ready') return $(document).ready(fn)
  90. // 构建handler
  91. var handler = parse(event)
  92. handler.fn = fn
  93. handler.sel = selector
  94. // emulate mouseenter, mouseleave
  95. // mouseenter、mouseleave通过mouseover、mouseout来模拟。
  96. if (handler.e in hover) fn = function(e){
  97. // relatedTarget为相关元素,只有mouseover和mouseout事件才有
  98. // 对mouseover事件而言,相关元素就是那个失去光标的元素。对mouseout事件而言,相关元素则是获得光标的元素。
  99. var related = e.relatedTarget
  100. // 只当鼠标从元素外部移到元素内部才触发mouseenter,只当鼠标从元素内部移到元素外部才出发mouseleave。
  101. if (!related || (related !== this && !$.contains(this, related)))
  102. return handler.fn.apply(this, arguments)
  103. }
  104. handler.del = delegator
  105. var callback = delegator || fn // 需要进行事件代理时,调用的是封装了fn的delegator函数
  106. // 真正绑定事件时的回调函数
  107. // 通过该函数改写事件对象,为事件对象添加一些方法和属性,然后调用用户传进来的回调函数。并且使用户的回调函数返回false时禁止默认行为和禁止冒泡
  108. handler.proxy = function(e){
  109. e = compatible(e)
  110. if (e.isImmediatePropagationStopped()) return
  111. e.data = data
  112. var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
  113. if (result === false) e.preventDefault(), e.stopPropagation()
  114. return result
  115. }
  116. handler.i = set.length // 把handler在set中的下标赋值给handler.i
  117. set.push(handler) // 把handler保存起来
  118. // 最后绑定事件
  119. if ('addEventListener' in element)
  120. element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
  121. })
  122. }
  123. // 删除handler
  124. function remove(element, events, fn, selector, capture){
  125. var id = zid(element)
  126. ;(events || '').split(/\s/).forEach(function(event){
  127. findHandlers(element, event, fn, selector).forEach(function(handler){
  128. delete handlers[id][handler.i]
  129. if ('removeEventListener' in element)
  130. element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
  131. })
  132. })
  133. }
  134. $.event = { add: add, remove: remove }
  135. $.proxy = function(fn, context) {
  136. var args = (2 in arguments) && slice.call(arguments, 2) // 第三个以及之后的参数
  137. // 参数fn为函数
  138. if (isFunction(fn)) {
  139. var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) }
  140. proxyFn._zid = zid(fn)
  141. return proxyFn
  142. // 第一个参数为对象的情况:$.proxy(context, "fnName" )
  143. } else if (isString(context)) {
  144. if (args) {
  145. args.unshift(fn[context], fn)
  146. return $.proxy.apply(null, args)
  147. } else {
  148. return $.proxy(fn[context], fn)
  149. }
  150. } else {
  151. throw new TypeError("expected function")
  152. }
  153. }
  154. $.fn.bind = function(event, data, callback){
  155. return this.on(event, data, callback)
  156. }
  157. $.fn.unbind = function(event, callback){
  158. return this.off(event, callback)
  159. }
  160. $.fn.one = function(event, selector, data, callback){
  161. return this.on(event, selector, data, callback, 1)
  162. }
  163. var returnTrue = function(){return true}, // 在compatible里有用到
  164. returnFalse = function(){return false}, // 在compatible里有用到
  165. // 构建事件对象时所不要的几个属性:returnValue、layerX和layerY(还有以大写字母开头的属性?)
  166. ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/,
  167. // 事件对象需要添加的三个方法名
  168. eventMethods = {
  169. preventDefault: 'isDefaultPrevented',
  170. stopImmediatePropagation: 'isImmediatePropagationStopped',
  171. stopPropagation: 'isPropagationStopped'
  172. }
  173. // 添加eventMethods里面的三个方法:isDefaultPrevented、isDefaultPrevented和isPropagationStopped
  174. function compatible(event, source) {
  175. if (source || !event.isDefaultPrevented) {
  176. source || (source = event)
  177. // 通过改写原生的preventDefault、stopImmediatePropagation和stopPropagation方法实现
  178. $.each(eventMethods, function(name, predicate) {
  179. var sourceMethod = source[name]
  180. event[name] = function(){
  181. this[predicate] = returnTrue
  182. return sourceMethod && sourceMethod.apply(source, arguments)
  183. }
  184. event[predicate] = returnFalse
  185. })
  186. // 设置isDefaultPrevented默认指向的函数
  187. if (source.defaultPrevented !== undefined ? source.defaultPrevented : // 如果有defaultPrevented属性,就根据defaultPrevented的值来判断
  188. 'returnValue' in source ? source.returnValue === false : // returnValue属性只在beforeunload事件中有用。而且用途是设置弹出框的文案,所以这行代码应该有问题,可以去掉
  189. source.getPreventDefault && source.getPreventDefault()) // getPreventDefault和defaultPrevented属性类似,不过是非标准的。为了兼容没有defaultPrevented参数的浏览器。
  190. event.isDefaultPrevented = returnTrue
  191. }
  192. return event
  193. }
  194. // 构建事件代理中的事件对象
  195. function createProxy(event) {
  196. var key, proxy = { originalEvent: event } // 新的事件对象有个originalEvent属性指向原对象
  197. // 将原生事件对象的属性复制给新对象,除了returnValue、layerX、layerY和值为undefined的属性
  198. // returnValue属性为beforeunload事件独有
  199. for (key in event)
  200. if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
  201. // 添加eventMethods里面的几个方法,并返回新的事件对象
  202. return compatible(proxy, event)
  203. }
  204. $.fn.delegate = function(selector, event, callback){
  205. return this.on(event, selector, callback)
  206. }
  207. $.fn.undelegate = function(selector, event, callback){
  208. return this.off(event, selector, callback)
  209. }
  210. $.fn.live = function(event, callback){
  211. // 通过document.body元素代理事件
  212. $(document.body).delegate(this.selector, event, callback)
  213. return this
  214. }
  215. $.fn.die = function(event, callback){
  216. // 移除掉document.body元素的相关代理事件
  217. $(document.body).undelegate(this.selector, event, callback)
  218. return this
  219. }
  220. // 只负责处理参数,绑定事件交由add函数处理
  221. $.fn.on = function(event, selector, data, callback, one){
  222. var autoRemove, delegator, $this = this
  223. // event参数为对象,回调自身进行批量绑定事件
  224. if (event && !isString(event)) {
  225. $.each(event, function(type, fn){
  226. $this.on(type, selector, data, fn, one)
  227. })
  228. return $this
  229. }
  230. // 处理参数,实现函数重载
  231. if (!isString(selector) && !isFunction(callback) && callback !== false) // 没有传入selector参数
  232. callback = data, data = selector, selector = undefined
  233. if (callback === undefined || data === false) // 没有传入data参数
  234. callback = data, data = undefined
  235. if (callback === false) callback = returnFalse // 回调函数传入的是false,使用returnFalse函数代替
  236. // 给每一个Z对象里面的元素绑定事件
  237. return $this.each(function(_, element){
  238. // 如果有传入one参数,使用下面的autoRemove函数代替callback。执行一次后会自动移除该事件的绑定
  239. if (one) autoRemove = function(e){
  240. remove(element, e.type, callback)
  241. return callback.apply(this, arguments)
  242. }
  243. // 如果有传入selector参数,构建代理函数
  244. if (selector) delegator = function(e){
  245. var evt, match = $(e.target).closest(selector, element).get(0) // match为e.target到触发元素范围内和selector相匹配的元素
  246. if (match && match !== element) { // 如果有相匹配的元素,改写事件对象,触发回调函数
  247. evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
  248. return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
  249. }
  250. }
  251. // 通过add函数来绑定事件
  252. add(element, event, callback, data, selector, delegator || autoRemove)
  253. })
  254. }
  255. $.fn.off = function(event, selector, callback){
  256. var $this = this
  257. // event参数为对象,回调自身进行批量取消绑定事件
  258. if (event && !isString(event)) {
  259. $.each(event, function(type, fn){
  260. $this.off(type, selector, fn)
  261. })
  262. return $this
  263. }
  264. // 处理参数,实现函数重载
  265. if (!isString(selector) && !isFunction(callback) && callback !== false) // 没有传入selector参数
  266. callback = selector, selector = undefined
  267. if (callback === false) callback = returnFalse // 回调函数传入的是false,使用returnFalse函数代替
  268. return $this.each(function(){
  269. remove(this, event, callback, selector)
  270. })
  271. }
  272. $.fn.trigger = function(event, args){
  273. event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
  274. event._args = args
  275. return this.each(function(){
  276. // handle focus(), blur() by calling them directly
  277. // 通过直接调用focus()和blur()方法来触发对应事件,这算是对触发事件方法的一个优化
  278. if (event.type in focus && typeof this[event.type] == "function") this[event.type]()
  279. // items in the collection might not be DOM elements
  280. else if ('dispatchEvent' in this) this.dispatchEvent(event)
  281. else $(this).triggerHandler(event, args)
  282. })
  283. }
  284. // triggers event handlers on current element just as if an event occurred,
  285. // doesn't trigger an actual event, doesn't bubble
  286. // 直接触发事件的回调函数,而不是直接触发一个事件,所以也不冒泡
  287. $.fn.triggerHandler = function(event, args){
  288. var e, result
  289. this.each(function(i, element){
  290. e = createProxy(isString(event) ? $.Event(event) : event)
  291. e._args = args
  292. e.target = element
  293. $.each(findHandlers(element, event.type || event), function(i, handler){
  294. result = handler.proxy(e)
  295. if (e.isImmediatePropagationStopped()) return false
  296. })
  297. })
  298. return result
  299. }
  300. // shortcut methods for `.bind(event, fn)` for each event type
  301. // 绑定和触发事件的快捷方式
  302. ;('focusin focusout focus blur load resize scroll unload click dblclick '+
  303. 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
  304. 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
  305. $.fn[event] = function(callback) {
  306. return (0 in arguments) ?
  307. this.bind(event, callback) :
  308. this.trigger(event)
  309. }
  310. })
  311. // 生成一个模拟事件,如果是鼠标相关事件,document.createEvent传入的第一个参数为'MouseEvents',以提供更多的参数
  312. $.Event = function(type, props) {
  313. if (!isString(type)) props = type, type = props.type
  314. var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
  315. if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
  316. event.initEvent(type, bubbles, true)
  317. return compatible(event)
  318. }
  319. })(Zepto)

Zepto事件模块源码分析的更多相关文章

  1. Backbone事件模块源码分析

    事件模块Backbone.Events在Backbone中占有十分重要的位置,其他模块Model,Collection,View所有事件模块都依赖它.通过继承Events的方法来实现事件的管理,可以说 ...

  2. Spark Scheduler模块源码分析之TaskScheduler和SchedulerBackend

    本文是Scheduler模块源码分析的第二篇,第一篇Spark Scheduler模块源码分析之DAGScheduler主要分析了DAGScheduler.本文接下来结合Spark-1.6.0的源码继 ...

  3. Spark Scheduler模块源码分析之DAGScheduler

    本文主要结合Spark-1.6.0的源码,对Spark中任务调度模块的执行过程进行分析.Spark Application在遇到Action操作时才会真正的提交任务并进行计算.这时Spark会根据Ac ...

  4. nginx健康检查模块源码分析

    nginx健康检查模块 本文所说的nginx健康检查模块是指nginx_upstream_check_module模块.nginx_upstream_check_module模块是Taobao定制的用 ...

  5. ApplicationEvent事件机制源码分析

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  6. Django(51)drf渲染模块源码分析

    前言 渲染模块的原理和解析模块是一样,drf默认的渲染有2种方式,一种是json格式,另一种是模板方式. 渲染模块源码入口 入口:APIView类中dispatch方法中的:self.response ...

  7. Fabric2.2中的Raft共识模块源码分析

    引言 Hyperledger Fabric是当前比较流行的一种联盟链系统,它隶属于Linux基金会在2015年创建的超级账本项目且是这个项目最重要的一个子项目.目前,与Hyperledger的另外几个 ...

  8. Django(48)drf请求模块源码分析

    前言 APIView中的dispatch是整个请求生命过程的核心方法,包含了请求模块,权限验证,异常模块和响应模块,我们先来介绍请求模块 请求模块:request对象 源码入口 APIView类中di ...

  9. Django(49)drf解析模块源码分析

    前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...

随机推荐

  1. vue文件名规范

    之前有看过一些命名规范,也看到说vue文件命名要么全是小写要么就是用小写 + '-':其实看到的时候有点不以意,因为本地能跑起项目:发布能正常访问也就OK了. 但是今天在做自动化部署的时候碰到一个问题 ...

  2. CRC16算法之一:CRC16-CCITT-FALSE算法的java实现

    CRC16算法系列文章: CRC16算法之一:CRC16-CCITT-FALSE算法的java实现 CRC16算法之二:CRC16-CCITT-XMODEM算法的java实现 CRC16算法之三:CR ...

  3. SIP业务基本知识

    1.SIP业务基本知识 1.1 业务介绍会话初始协议(Session Initiation Protocol)是一种信令协议,用于初始.管理和终止网络中的语音和视频会话,具体地说就是用来生成.修改和终 ...

  4. session.write类型引发的思考---Mina Session.write流程探索.doc--zhengli

    基于Mina开发网络通信程序,在传感器数据接入领域应用的很广泛,今天我无意中发现一个问题,那就是我在前端session.write(msg)数据出去之后,却没有经过Filter的Encoder方法,同 ...

  5. Springboot学习七 spring的一些注解

    一 事务控制 @Service public class CityServiceImpl implements CityService { @Autowired private CityMapper ...

  6. java去任意范围的随机数

    一.java.uitl.Randomrandom.nextInt(20),任意取[0,20)之间整数,其中0可以取到,20取不到 二.取某个范围的任意数public static String get ...

  7. [golang] Glide 包管理工具,在windows10 64位系统上的bug修复方案

    bug重现 [ERROR] Unable to export dependencies to vendor directory: Error moving files: exit status 1. ...

  8. linux jar 后台运行

    在linux系统中可以利用nohup来执行任何命令,并把命令自动调到linux后台运行,不锁定当前ssh窗口,也不会被ctrl + c,alt + F4之类打断程序的动行.如: nohup java ...

  9. Java8函数式接口之Predicate<T>

    作用: 这是一个功能接口,因此可以作为lambda表达式或方法引用的赋值目标. 实例: /** * Created by luo on 2017/5/3. */ public class Predic ...

  10. Codeforces Round #459 (Div. 2):D. MADMAX(记忆化搜索+博弈论)

    题意 在一个有向无环图上,两个人分别从一个点出发,两人轮流从当前点沿着某条边移动,要求经过的边权不小于上一轮对方经过的边权(ASCII码),如果一方不能移动,则判负.两人都采取最优策略,求两人分别从每 ...