最近公司各种上线,所以回家略感疲惫就懒得写了,这次我准备把剩下的所有方法全部分析完,可能篇幅过长...那么废话不多说让我们进入正题。

没看过前几篇的可以猛戳这里:

underscore.js源码解析(一)

underscore.js源码解析(二)

underscore.js源码解析(三)

underscore.js源码解析(四)

underscore.js源码GitHub地址: https://github.com/jashkenas/underscore/blob/master/underscore.js

本文解析的underscore.js版本是1.8.3

baseCreate

  1. var baseCreate = function(prototype) {
  2. //判断参数是否是对象
  3. if (!_.isObject(prototype)) return {};
  4. //如果有原生的就调用原生的
  5. if (nativeCreate) return nativeCreate(prototype);
  6. //继承原型
  7. Ctor.prototype = prototype;
  8. var result = new Ctor;
  9. Ctor.prototype = null;
  10. return result;
  11. };

_.bind

  1. _.bind = restArgs(function(func, context, args) {
  2. //如果不是函数抛异常
  3. if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
  4. var bound = restArgs(function(callArgs) {
  5. //调用executeBound方法,具体解释见下方
  6. return executeBound(func, bound, context, this, args.concat(callArgs));
  7. });
  8. return bound;
  9. });

executeBound

  1. var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
  2. //判断boundFunc 是否在callingContext 的原型链上
  3. if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
  4. //创建实例
  5. var self = baseCreate(sourceFunc.prototype);
  6. //对实例进行apply操作
  7. var result = sourceFunc.apply(self, args);
  8. //如果是对象则返回对象
  9. if (_.isObject(result)) return result;
  10. //否则返回实例本身
  11. return self;
  12. };

_.partial

  1. _.partial = restArgs(function(func, boundArgs) {
  2. //占位符
  3. var placeholder = _.partial.placeholder;
  4. var bound = function() {
  5. var position = 0, length = boundArgs.length;
  6. var args = Array(length);
  7. //循环遍历boundArgs
  8. for (var i = 0; i < length; i++) {
  9. //判断是否是占位符,如果是就把arguments里的第一个放进去(按顺序以此类推),
  10. //如果不是占位符就正常把boundArgs里的数据再拷贝一份到args中
  11. args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
  12. }
  13. //循环遍历完boundArgs,就是把剩下的数据放入到args当中,这里调用executeBound,executeBound的分析可以看上面
  14. while (position < arguments.length) args.push(arguments[position++]);
  15. return executeBound(func, bound, this, this, args);
  16. };
  17. return bound;
  18. });

_.bindAll

  1. _.bindAll = restArgs(function(obj, keys) {
  2. keys = flatten(keys, false, false);
  3. var index = keys.length;
  4. //如果没有 function names抛异常
  5. if (index < 1) throw new Error('bindAll must be passed function names');
  6. while (index--) {
  7. var key = keys[index];
  8. //调用bind方法进行绑定
  9. obj[key] = _.bind(obj[key], obj);
  10. }
  11. });

多个方法绑定到对象上

_.memoize

  1. _.memoize = function(func, hasher) {
  2. var memoize = function(key) {
  3. //缓存值
  4. var cache = memoize.cache;
  5. //是否使用hashFunction,如果使用就把hashFunction的返回值作为缓存的key值
  6. var address = '' + (hasher ? hasher.apply(this, arguments) : key);
  7. //如果没有就做一个缓存的操作
  8. if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
  9. //最后返回缓存值
  10. return cache[address];
  11. };
  12. memoize.cache = {};
  13. return memoize;
  14. };

作用是缓存函数的计算结果,再做里面有重复运算的情况优化效果比较明显

_.delay

  1. _.delay = restArgs(function(func, wait, args) {
  2. return setTimeout(function() {
  3. return func.apply(null, args);
  4. }, wait);
  5. });

就是对setTimeout的封装,一目了然就不做过多解释了

_.defer

  1. _.defer = _.partial(_.delay, _, 1);

就是让这段程序最后执行,也是调用setTimeout来实现的,这里“_”是函数参数的占位,1是时间1毫秒。不懂的可以去看看setTimeout的机制,如果这里再展开的话篇幅过长,有时间也可以写一篇setTimeout的文章

_.throttle

  1. _.throttle = function(func, wait, options) {
  2. var timeout, context, args, result;
  3. //previous是缓存的上一次执行的时间点,默认为0
  4. var previous = 0;
  5. //判断是否有配置参数
  6. if (!options) options = {};
  7.  
  8. var later = function() {
  9. previous = options.leading === false ? 0 : _.now();
  10. //清除timeout
  11. timeout = null;
  12. //储存函数执行的结果
  13. result = func.apply(context, args);
  14. if (!timeout) context = args = null;
  15. };
  16.  
  17. var throttled = function() {
  18. //当前时间
  19. var now = _.now();
  20. if (!previous && options.leading === false) previous = now;
  21. //wait是setTimeout延迟的时间
  22. var remaining = wait - (now - previous);
  23. context = this;
  24. args = arguments;
  25. if (remaining <= 0 || remaining > wait) {
  26. if (timeout) {
  27. clearTimeout(timeout);
  28. timeout = null;
  29. }
  30. //缓存当前时间
  31. previous = now;
  32. result = func.apply(context, args);
  33. if (!timeout) context = args = null;
  34. } else if (!timeout && options.trailing !== false) {
  35. //生成定时器
  36. timeout = setTimeout(later, remaining);
  37. }
  38. return result;
  39. };
  40. //清除操作
  41. throttled.cancel = function() {
  42. clearTimeout(timeout);
  43. previous = 0;
  44. timeout = context = args = null;
  45. };
  46.  
  47. return throttled;
  48. };

_.throttle的作用是控制函数的执行频率,第一次执行的时候previous默认为零,那么remaining就是负数,没有定时器,之后当remaining大于0时,启动定时器,当定时器的时间到的时候,执行定时器里面的函数,并且会请一次timeout,remaining此时大于零并且timeout为空,则进入else if再次生成一个setTimeout。remaining > wait也就意味着now < previous,这是为了规避用户改变系统是简单的情况,这时候需要清除timeout的操作。

_.debounce

  1. _.debounce = function(func, wait, immediate) {
  2. var timeout, result;
  3.  
  4. var later = function(context, args) {
  5. timeout = null;
  6. if (args) result = func.apply(context, args);
  7. };
  8.  
  9. var debounced = restArgs(function(args) {
  10. //判断是否立即调用
  11. var callNow = immediate && !timeout;
  12. if (timeout) clearTimeout(timeout);
  13. if (callNow) {
  14. //如果立即调用则,立即执行函数
  15. timeout = setTimeout(later, wait);
  16. result = func.apply(this, args);
  17. } else if (!immediate) {
  18. //如果本次调用时,上一个定时器没有执行完,将再生成一个定时器
  19. timeout = _.delay(later, wait, this, args);
  20. }
  21.  
  22. return result;
  23. });
  24.  
  25. debounced.cancel = function() {
  26. clearTimeout(timeout);
  27. timeout = null;
  28. };
  29.  
  30. return debounced;
  31. };

_.debounce也是函数节流,但是与throttle不同的是debounce中两个函数的时间间隔不能小于wait,这样的话定时器就会被重新创建

_.wrap

  1. _.wrap = function(func, wrapper) {
  2. return _.partial(wrapper, func);
  3. };

作用就是把func当做参数传给wrapper执行,_.partial前文介绍过,就是给函数设置一些默认的参数

_.compose

  1. _.compose = function() {
  2. var args = arguments;
  3. var start = args.length - 1;
  4. return function() {
  5. var i = start;
  6. var result = args[start].apply(this, arguments);
  7. //从后往前调用
  8. while (i--) result = args[i].call(this, result);
  9. return result;
  10. };
  11. };

_.compose的作用就是组合复合函数,结构就是从最后一个函数开始执行,然后返回结果给前一个函数调用,直到第一个。

_.after

  1. _.after = function(times, func) {
  2. return function() {
  3. if (--times < 1) {
  4. return func.apply(this, arguments);
  5. }
  6. };
  7. };

原理很简单,就是只有调用到最后一次的时候才开始执行里面的函数

_.before

  1. _.before = function(times, func) {
  2. var memo;
  3. return function() {
  4. if (--times > 0) {
  5. //正常调用,记录返回值
  6. memo = func.apply(this, arguments);
  7. }
  8. //最后一次调用时,清空fun
  9. if (times <= 1) func = null;
  10. return memo;
  11. };
  12. };

_.before的作用是限制函数的调用次数,最后一次调用清空fun,返回上一次调用的结果

_.once

  1. _.once = _.partial(_.before, 2);

_.once调用了_.before并且times参数为2,说明无论调用几次,只返回第一次的调用结果

_.mapObject

  1. _.mapObject = function(obj, iteratee, context) {
  2. iteratee = cb(iteratee, context);
  3. var keys = _.keys(obj),
  4. length = keys.length,
  5. results = {};
  6. for (var index = 0; index < length; index++) {
  7. var currentKey = keys[index];
  8. results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
  9. }
  10. return results;
  11. };

_.mapObject跟map类似,只不过它最后返回的是对象

_.pairs

  1. _.pairs = function(obj) {
  2. var keys = _.keys(obj);
  3. var length = keys.length;
  4. var pairs = Array(length);
  5. for (var i = 0; i < length; i++) {
  6. pairs[i] = [keys[i], obj[keys[i]]];
  7. }
  8. return pairs;
  9. };

_.pairs的结构也很简单,就是把对象转化为数组

_.invert

  1. _.invert = function(obj) {
  2. var result = {};
  3. var keys = _.keys(obj);
  4. for (var i = 0, length = keys.length; i < length; i++) {
  5. result[obj[keys[i]]] = keys[i];
  6. }
  7. return result;
  8. };

结构也是很清晰,就是一个翻转对象的过程,将对象的键和值互换位置

_.functions

  1. _.functions = _.methods = function(obj) {
  2. var names = [];
  3. for (var key in obj) {
  4. if (_.isFunction(obj[key])) names.push(key);
  5. }
  6. return names.sort();
  7. };

就是获取对象的所有方法名,然后存在数组当中

_.pick

  1. _.pick = restArgs(function(obj, keys) {
  2. var result = {}, iteratee = keys[0];
  3. //如果没有传入obj,则返回空
  4. if (obj == null) return result;
  5. //判断keys参数里是否传的是函数
  6. if (_.isFunction(iteratee)) {
  7. 如果是函数,则调用函数进行上下文this的绑定
  8. if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
  9. keys = _.allKeys(obj);
  10. } else {
  11. //如果不是函数,则为所需的属性
  12. iteratee = keyInObj;
  13. keys = flatten(keys, false, false);
  14. obj = Object(obj);
  15. }
  16. for (var i = 0, length = keys.length; i < length; i++) {
  17. var key = keys[i];
  18. var value = obj[key];
  19. if (iteratee(value, key, obj)) result[key] = value;
  20. }
  21. return result;
  22. });

作用是过滤出所需的键值对,对参数是属性的和函数的情况分别处理

_.omit

  1. _.omit = restArgs(function(obj, keys) {
  2. var iteratee = keys[0], context;
  3. if (_.isFunction(iteratee)) {
  4. //这里一个取反的操作
  5. iteratee = _.negate(iteratee);
  6. if (keys.length > 1) context = keys[1];
  7. } else {
  8. keys = _.map(flatten(keys, false, false), String);
  9. iteratee = function(value, key) {
  10. //不存在的情况返回true
  11. return !_.contains(keys, key);
  12. };
  13. }
  14. //最后调用pick()
  15. return _.pick(obj, iteratee, context);
  16. });

_.omit相比较_.pick是一种相反的操作,作用是保留标记以外的对象

_.create

  1. _.create = function(prototype, props) {
  2. //继承原型
  3. var result = baseCreate(prototype);
  4. //属性拷贝的操作
  5. if (props) _.extendOwn(result, props);
  6. return result;
  7. };

模拟Object.create方法

_.clone

  1. _.clone = function(obj) {
  2. if (!_.isObject(obj)) return obj;
  3. return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  4. };

对象浅拷贝,如果是数组就调用slice,不是数组就调用_.extend

_.isMatch

  1. _.isMatch = function(object, attrs) {
  2. var keys = _.keys(attrs), length = keys.length;
  3. if (object == null) return !length;
  4. //防止不是对象
  5. var obj = Object(object);
  6. for (var i = 0; i < length; i++) {
  7. var key = keys[i];
  8. //如果对象属性不在obj中或者不在obj中
  9. if (attrs[key] !== obj[key] || !(key in obj)) return false;
  10. }
  11. return true;
  12. };

判断后者的对象属性是否全在前者的对象当中

eq

  1. eq = function(a, b, aStack, bStack) {
  2. //虽然0 === -0成立,但是1 / 0 == 1 / -0 是不成立的,因为1 / 0值为Infinity, 1 / -0值为-Infinity, 而Infinity不等于-Infinity
  3. if (a === b) return a !== 0 || 1 / a === 1 / b;
  4. //null == undefined
  5. if (a == null || b == null) return a === b;
  6. //对NaN情况的判断,因为NaN!=NaN,所以a !== a说明a是NaN,如果b !== b为true,那么说明b是NaN,a和b相等,b !== b为false,说明b不是NaN,那么a和b不等
  7. if (a !== a) return b !== b;
  8. var type = typeof a;
  9. if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
  10. return deepEq(a, b, aStack, bStack);
  11. };

deepEq

  1. deepEq = function(a, b, aStack, bStack) {
  2. // 如果是underscore封装的对象,则通过_.wrapped中获取本身数据再进行对比
  3. if (a instanceof _) a = a._wrapped;
  4. if (b instanceof _) b = b._wrapped;
  5. // 对两者的数据类型进行比较
  6. var className = toString.call(a);
  7. if (className !== toString.call(b)) return false;
  8. switch (className) {
  9. case '[object RegExp]':
  10. case '[object String]':
  11. // 正则转化字符串
  12. return '' + a === '' + b;
  13. case '[object Number]':
  14. // 对NaN情况的判断,跟eq里的判断一样,只不过多了转化数字这一步
  15. if (+a !== +a) return +b !== +b;
  16. // 如果不是NaN,那就要判断0的情况了,也是跟eq里面的判断同理
  17. return +a === 0 ? 1 / +a === 1 / b : +a === +b;
  18. case '[object Date]':
  19. case '[object Boolean]':
  20. //日期和布尔值转化为数字来比较,日期转化为数字是毫秒数
  21. return +a === +b;
  22. }
  23.  
  24. var areArrays = className === '[object Array]';
  25. if (!areArrays) {
  26. //如果不是数组,只要有一个不是object类型就不等
  27. if (typeof a != 'object' || typeof b != 'object') return false;
  28.  
  29. var aCtor = a.constructor, bCtor = b.constructor;
  30. //不同的构造函数是不等的,不同frames的object和Array是相等的
  31. if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
  32. _.isFunction(bCtor) && bCtor instanceof bCtor)
  33. && ('constructor' in a && 'constructor' in b)) {
  34. return false;
  35. }
  36. }
  37.  
  38. aStack = aStack || [];
  39. bStack = bStack || [];
  40. var length = aStack.length;
  41. while (length--) {
  42. // 对嵌套结构的做判断
  43. if (aStack[length] === a) return bStack[length] === b;
  44. }
  45.  
  46. // 将a和b放入栈中
  47. aStack.push(a);
  48. bStack.push(b);
  49.  
  50. // 对数组的判断处理
  51. if (areArrays) {
  52. length = a.length;
  53. //如果长度不等,那么肯定不等
  54. if (length !== b.length) return false;
  55. // 递归比较每一个元素
  56. while (length--) {
  57. if (!eq(a[length], b[length], aStack, bStack)) return false;
  58. }
  59. } else {
  60. //如果是对象
  61. var keys = _.keys(a), key;
  62. length = keys.length;
  63. // 相比较亮两个对象的属性数量是否相等
  64. if (_.keys(b).length !== length) return false;
  65. while (length--) {
  66. //递归比较每个属性是否相等
  67. key = keys[length];
  68. if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
  69. }
  70. }
  71. // 移除栈里的元素
  72. aStack.pop();
  73. bStack.pop();
  74. return true;
  75. };

_.isEmpty

  1. _.isEmpty = function(obj) {
  2. if (obj == null) return true;
  3. if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
  4. return _.keys(obj).length === 0;
  5. };

就是一个判断为空的函数,结构很简单

_.isElement

  1. _.isElement = function(obj) {
  2. return !!(obj && obj.nodeType === 1);
  3. };

判断是都是DOM元素

_.times

  1. _.times = function(n, iteratee, context) {
  2. var accum = Array(Math.max(0, n));
  3. iteratee = optimizeCb(iteratee, context, 1);
  4. for (var i = 0; i < n; i++) accum[i] = iteratee(i);
  5. return accum;
  6. };

调用迭代函数n次,最后结果返回一个数组

_.template

  1. _.template = function(text, settings, oldSettings) {
  2. //如果没有第二个参数,就将第三个参数赋值给第二个
  3. if (!settings && oldSettings) settings = oldSettings;
  4. //这里_.default函数前面介绍过填充属性为undefined的属性
  5. settings = _.defaults({}, settings, _.templateSettings);
  6.  
  7. // 定义正则表达式,将settings里面的三个正则组合在一起,这里'|$'是为了让replace里面的函数多执行一遍
  8. var matcher = RegExp([
  9. (settings.escape || noMatch).source,
  10. (settings.interpolate || noMatch).source,
  11. (settings.evaluate || noMatch).source
  12. ].join('|') + '|$', 'g');
  13.  
  14. var index = 0;
  15. var source = "__p+='";
  16. //拼接字符串
  17. text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
  18. source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
  19. index = offset + match.length;
  20. //针对不同的情况进行拼接
  21. if (escape) {
  22. source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
  23. } else if (interpolate) {
  24. source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
  25. } else if (evaluate) {
  26. source += "';\n" + evaluate + "\n__p+='";
  27. }
  28.  
  29. return match;
  30. });
  31. //下面是一个模板预编译的处理,主要用于调试
  32. source += "';\n";
  33.  
  34. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
  35.  
  36. source = "var __t,__p='',__j=Array.prototype.join," +
  37. "print=function(){__p+=__j.call(arguments,'');};\n" +
  38. source + 'return __p;\n';
  39.  
  40. var render;
  41. try {
  42. render = new Function(settings.variable || 'obj', '_', source);
  43. } catch (e) {
  44. e.source = source;
  45. throw e;
  46. }
  47. //创建templete函数
  48. var template = function(data) {
  49. return render.call(this, data, _);
  50. };
  51. //设置source属性
  52. var argument = settings.variable || 'obj';
  53. template.source = 'function(' + argument + '){\n' + source + '}';
  54.  
  55. return template;
  56. };

_.template就是一个模板函数,核心的部分还是前半段字符串拼接的过程(17-38行)。

首先先解释一下参数text是模板字符串,settings是正则匹配的规则,escape、interpolate、evaluate和variable

  1. _.templateSettings = {
  2. evaluate: /<%([\s\S]+?)%>/g,
  3. interpolate: /<%=([\s\S]+?)%>/g,
  4. escape: /<%-([\s\S]+?)%>/g
  5. };

evaluate是用来执行任意的JavaScript代码,interpolate是用来插入变量的,escape是HTML转义的

里面有个noMatch,他是为了避免settings中缺少属性的情况

  1. var noMatch = /(.)^/;

17行里offset是用来记录匹配当前位置的,剩下的主要就是走21-27行了,插入值就走23-24行判断,如果遇到一些需要js判断转换的数据就走25-26行判断,最后匹配$也是为了再执行一遍将最后面的html拼接进字符串。

其中18行中的escapeRegExp和escapeChar就是用来转化一些特殊字符的

  1. var escapes = {
  2. "'": "'",
  3. '\\': '\\',
  4. '\r': 'r',
  5. '\n': 'n',
  6. '\u2028': 'u2028',
  7. '\u2029': 'u2029'
  8. };
  9.  
  10. var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
  11.  
  12. var escapeChar = function(match) {
  13. return '\\' + escapes[match];
  14. };

小结

到这里underscore.js就都分析完了,断断续续用了一个月的时间,在读这些函数方法的时候收获很多,也发现了一些以前自己理解不全面的地方,对自己也是个检验,就拿上面的template来说,就发现了自己正则方面的掌握还不够,在分析方法时学习了一些这些好的编程思想。

下一篇可能是对定时器的分析,到时候再看自己的研究效果吧。

夜已深,去睡觉了...

感谢大家的观看,也希望能够和大家互相交流学习,有什么分析的不对的地方欢迎大家批评指出

参考资料

http://www.w3cfuns.com/house/17398/note/class/id/bb6dc3cabae6651b94f69bbd562ff370

underscore.js源码解析(五)—— 完结篇的更多相关文章

  1. underscore.js源码解析(四)

    没看过前几篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二) underscore.js源码解析(三) underscore.js源码GitHub地 ...

  2. underscore.js源码解析(三)

    最近工作比较忙,做不到每周两篇了,周末赶着写吧,上篇我针对一些方法进行了分析,今天继续. 没看过前两篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二 ...

  3. underscore.js源码解析(二)

    前几天我对underscore.js的整体结构做了分析,今天我将针对underscore封装的方法进行具体的分析,代码的一些解释都写在了注释里,那么废话不多说进入今天的正文. 没看过上一篇的可以猛戳这 ...

  4. underscore.js源码解析(一)

    一直想针对一个框架的源码好好的学习一下编程思想和技巧,提高一下自己的水平,但是看过一些框架的源码,都感觉看的莫名其妙,看不太懂,最后找到这个underscore.js由于这个比较简短,一千多行,而且读 ...

  5. Vue.js 源码分析(五) 基础篇 方法 methods属性详解

    methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head&g ...

  6. underscore.js源码解析【'_'对象定义及内部函数】

    (function() { // Baseline setup // -------------- // Establish the root object, `window` (`self`) in ...

  7. underscore.js源码解析【对象】

    // Object Functions // ---------------- // Keys in IE < 9 that won't be iterated by `for key in . ...

  8. underscore.js源码解析【函数】

    // Function (ahem) Functions // ------------------ // Determines whether to execute a function as a ...

  9. underscore.js源码解析【数组】

    // Array Functions // --------------- // Get the first element of an array. Passing **n** will retur ...

随机推荐

  1. CH4402 小Z的袜子(莫队)

    描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号, ...

  2. python使用ctypes模块下的windll.LoadLibrary报OSError: [WinError 193] % 不是有效的 Win32 应用程序

    原因:python是64位的python,而windll.LoadLibrary只能由32位的python使用 参考: 64位Python调用32位DLL方法(一) 解决方法:使用32位的python ...

  3. PHP设置Redis key在当天有效|SCP对拷如何连接指定端口(非22端口)的远程主机

    $redis->set($key,$value); $expireTime = mktime(23, 59, 59, date("m"), date("d" ...

  4. 一图看懂hadoop Spark On Yarn工作原理

    hadoop Spark On Yarn工作原理

  5. goland实现函数式链式编程

    先来看一段代码 package main import ( "fmt" elastic "gopkg.in/olivere/elastic.v2" ) type ...

  6. iOS 基于APNS消息推送原理与实现(包括JAVA后台代码)

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  7. 读书笔记《PHP高级程序设计、模式、框架与测试》

    序言 闲来无事,下载了一些电子书,然后看书名不错<PHP高级程序设计_模式.框架与测试>,翻了一下虽然书有点老了但是讲的内容经常会碰到!给大家推荐一下,然后这里放上我的读书笔记,每日更新. ...

  8. MySQL事务异常

    在做大屏系统的时候,遇到十分奇怪的问题,同样的代码,测试环境插入与更新操作正常,但是上了生产环境之后,插入与更新不生效, 插入数据的时候,主键会自增,但是查询表中没有数据,同样一个@Transacti ...

  9. XAMPP之Mysql启动失败

    启动XAMPP中的Mysql出现如下: 可能的原因是本地有多个MySQL,所以要在注册表编辑器中将imagePath改成XAMPP中的mysql的地址.(打开注册表编辑器:win+R,输入regedi ...

  10. 数据结构与算法之数组(1)——in dart

    import 'dart:math' show Random; List<int> _array; final _rnd = Random(); final _capacity = 100 ...