源码分析之Leaflet核心模块core中的Util工具方法

概述

​Leaflet 框架中的 Util 模块,提供了许多实用工具函数,用于简化开发、处理常见任务(如对象操作、函数绑定、字符串处理等),并支持跨浏览器兼容性.

源码分析

源码实现如下

js 复制代码
export function extend(dest) {
  var i, j, len, src;
  for (j = 1, len = arguments.length; j < len; j++) {
    src = arguments[j];
    for (i in src) {
      dest[i] = src[i];
    }
  }
  return dest;
}

export var create =
  Object.create ||
  (function () {
    function F() {}
    return function (proto) {
      F.prototype = proto;
      return new F();
    };
  })();

export function bind(fn, obj) {
  var slice = Array.prototype.slice;
  if (fn.bind) {
    return fn.bind.apply(fn, slice.call(arguments, 1));
  }

  var args = slice.call(arguments, 2);

  return function () {
    return fn.apply(
      obj,
      args.length ? args.concat(slice.call(arguments)) : arguments
    );
  };
}

export var lasted = 0;

export function stamp(obj) {
  if (!("_leaflet_id" in obj)) {
    obj["_leaflet_id"] = ++lasted;
  }

  return obj._leaflet_id;
}

export function throttle(fn, time, context) {
  var lock, args, wrapperFn, later;

  later = function () {
    lock = false;
    if (args) {
      wrapperFn.apply(context, args);
      args = false;
    }
  };

  wrapperFn = function () {
    if (lock) {
      args = arguments;
    } else {
      fn.apply(context, arguments);
      setTimeout(later, time);
      lock = true;
    }
  };

  return wrapperFn;
}

export function wrapNum(x, range, includeMax) {
  var max = range[1],
    min = range[0],
    d = max - min;

  return x === max && includeMax ? x : ((((x - min) % d) + d) % d) + min;
}

export function falseFn() {
  return false;
}

export function formatNum(num, precision) {
  if (precision === false) {
    return num;
  }
  var pow = Math.pow(10, precision === undefined ? 6 : precision);
  return Math.round(num * pow) / pow;
}

export function trim(str) {
  return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
}

export function splitWords(str) {
  return trim(str).split(/\s+/);
}

export function setOptions(obj, options) {
  if (!Object.prototype.hasOwnProperty.call(obj, "options")) {
    obj.options = obj.options ? create(obj.options) : {};
  }
  for (var i in options) {
    obj.options[i] = options[i];
  }

  return obj.options;
}

export function getParamString(obj, existingUrl, uppercase) {
  var params = [];
  for (var i in obj) {
    params.push(
      encodeURIComponent(uppercase ? i.toUpperCase() : i) +
        "=" +
        encodeURIComponent(obj[i])
    );
  }

  return (
    (!existingUrl || existingUrl.indexOf("?") === -1 ? "?" : "&") +
    params.join("&")
  );
}

var templateRe = /\{*([\w_-]+)*\}/g;

export function template(str, data) {
  return str.replace(templateRe, function (str, key) {
    var value = data[key];
    if (value === undefined) {
      throw new Error("No value provided for variable " + str);
    } else if (typeof value === "function") {
      value = value(data);
    }
    return value;
  });
}

export var isArray =
  Array.isArray ||
  function (obj) {
    return Object.prototype.toString.call(obj) === "[object Array]";
  };

export function indexOf(arr, el) {
  for (var i = 0, len = arr.length; i < len; i++) {
    if (arr[i] === el) {
      return i;
    }
  }
  return -1;
}

export var emptyImageUrl =
  "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";

function getPrefixed(name) {
  return window["webkit" + name] || window["moz" + name] || window["ms" + name];
}

var lastTime = 0;

function timeoutDefer(fn) {
  var time = +new Date(),
    timeToCall = Math.max(0, 16 - (time - lastTime));

  lastTime = time + timeToCall;
  return window.setTimeout(fn, timeToCall);
}

export var requestFn =
  window.requestAnimationFrame ||
  getPrefixed("RequestAnimationFrame") ||
  timeoutDefer;

export var cancelFn =
  window.cancelAnimationFrame ||
  getPrefixed("CancelAnimationFrame") ||
  getPrefixed("CancelRequestAnimationFrame") ||
  function (id) {
    window.clearTimeout(id);
  };

export function requestAnimFrame(fn, context, immediate) {
  if (immediate && requestFn === timeoutDefer) {
    fn.call(context);
  } else {
    return requestFn.call(window, bind(fn, context));
  }
}

export function cancelAnimFrame(id) {
  if (id) {
    cancelFn.call(window, id);
  }
}

函数详细介绍

  1. extend(dest,...src)
  • 功能:将多个对象的属性合并到目标对象中.

  • 参数:

    • dest:目标对象
    • src:源对象(可传入多个)
  • 返回值:合并后的目标对象

  • 示例

js 复制代码
const obj = { a: 1 };
Util.extend(obj, { b: 2 }, { c: 3 });
console.log(obj); // { a: 1, b: 2, c: 3 }
  1. create(proto)
  • 功能:创建一个新对象,并继承指定原型.

  • 参数:

    • proto:新对象的原型
  • 返回值:新创建的对象.

  • 示例

js 复制代码
const obj = { a: 1 };
const newObj = Util.create(obj);
console.log(newObj.__proto__ === obj); // true
  1. bind(fn,obj,...args)
  • 功能 :创建一个新函数,该函数的this值被绑定到指定的对象,同时可以传入额外的参数.

  • 参数:

    • fn:要绑定的函数

    • obj:绑定的对象

    • args:额外的参数(可选)

  • 返回值:绑定后的函数

  • 示例

js 复制代码
const obj = { value: 10 };
const fn = function (a, b) { return this.value + a + b; };
const boundFn = Util.bind(fn, obj, 1);
console.log(boundFn(2)); // 13
  1. stamp(obj)
  • 功能:为对象生成唯一标识符(ID),如果对象已有标识符则直接返回.

  • 参数:

    • obj:需要生成标识符的对象
  • 返回值: 对象的唯一标识符

  • 示例

js 复制代码
const obj = {};
console.log(Util.stamp(obj)); // 1
console.log(Util.stamp(obj)); // 1 (相同对象返回相同 ID)
  1. throttle(fn,time,context)
  • 功能:节流函数,限制函数在指定时间间隔内最多只执行一次.

  • 参数:

    • fn:需要节流的函数
    • time:时间间隔(毫秒)
    • context:函数的上下文(this指向)
  • 返回值:节流后的函数

  • 示例

js 复制代码
const throttledFn = Util.throttle(() => console.log('Throttled!'), 1000);
window.addEventListener('resize', throttledFn);
  1. wrapNum(x,range,includeMax)
  • 功能 :将数值限制在指定范围内,支持循环(例如,经度范围在-180180)

  • 参数

    • x:需要处理的数值
    • range:范围数组,如[min,max]
    • includeMax:是否包含最大值,默认为false
  • 返回值:处理后的数值

  • 示例

js 复制代码
console.log(Util.wrapNum(190, [-180, 180])); // -170
  1. falseFn()
  • 功能 :返回false的函数

  • 返回值 :false

  • 示例

js 复制代码
console.log(Util.falseFn()); // false
  1. formatNum(num,precision)
  • 功能:格式化数字,保留指定精度

  • 参数

    • num:需要格式化的数字
    • precision:小数位数(可选,默认为6)
  • 返回值:格式化后的数值

  • 示例

js 复制代码
console.log(Util.formatNum(3.1415926535, 2)); // 3.14
  1. trim(str)
  • 功能 :去除字符串两端的空白字符。

  • 参数

    • str:需要处理的字符串。
  • ​返回值 :处理后的字符串。

  • 示例

js 复制代码
console.log(Util.trim('  Hello, Leaflet!  ')); // 'Hello, Leaflet!'
  1. splitWords(str)
  • 功能 :将字符串按空白字符分割为单词数组。

  • 参数

    • str:需要处理的字符串。
  • ​返回值 :单词数组。

  • 示例

js 复制代码
console.log(Util.splitWords('Hello World')); // ['Hello', 'World']
  1. setOptions(obj, options)
  • ​功能 :为对象设置选项(options),如果 options 不存在则创建。
  • 参数
    • obj:目标对象。
    • options:需要设置的选项。
  • 返回值 :设置后的 options 对象。

​- 示例

js 复制代码
const obj = {};
Util.setOptions(obj, { a: 1 });
console.log(obj.options); // { a: 1 }
  1. getParamString(obj, existingUrl, uppercase)
  • 功能 :将对象转换为 URL 参数字符串。

  • 参数

  • obj:需要转换的对象。

  • existingUrl:已存在的 URL(可选)。

  • uppercase:是否将参数名转换为大写(可选)。

  • ​返回值URL 参数字符串。

  • 示例

js 复制代码
console.log(Util.getParamString({ a: 1, b: 2 })); // '?a=1&b=2'
  1. template(str, data)
  • ​功能 :将字符串中的占位符替换为数据。
  • 参数
    • str:模板字符串。
    • data:数据对象。
  • 返回值 :替换后的字符串。
  • 示例
js 复制代码
console.log(Util.template('Hello, {name}!', { name: 'Leaflet' })); // 'Hello, Leaflet!'
  1. isArray(obj)
  • 功能 :检查对象是否为数组。
  • 参数
    • obj:需要检查的对象。
  • 返回值 :布尔值。
  • 示例
js 复制代码
console.log(Util.isArray([1, 2, 3])); // true
  1. indexOf(arr, el)
  • 功能:查找元素在数组中的索引。

  • ​参数

  • arr:数组。

  • el:需要查找的元素。

  • 返回值:元素的索引(如果未找到则返回 -1)。

  • ​示例

js 复制代码
console.log(Util.indexOf([1, 2, 3], 2)); // 1
  1. requestAnimFrame(fn, context, immediate)
  • 功能 :请求动画帧,支持立即执行。

  • 参数

    • fn:需要执行的函数。
    • context:函数的上下文(this 指向)。
    • immediate:是否立即执行(可选)。
  • ​返回值 :动画帧 ID。

  • 示例

js 复制代码
Util.requestAnimFrame(() => console.log('Frame!'));
  1. cancelAnimFrame(id)
  • ​功能 :取消动画帧。
  • 参数
  • id:动画帧 ID。
  • 示例
js 复制代码
const id = Util.requestAnimFrame(() => console.log('Frame!'));
Util.cancelAnimFrame(id);

总结

Util 模块是 Leaflet 的核心工具模块,提供了许多实用函数,用于简化开发、处理常见任务(如对象操作、函数绑定、字符串处理等),并支持跨浏览器兼容性。这些函数在 Leaflet 的内部实现中被广泛使用,同时也可以被开发者直接调用

相关推荐
灵犀学长6 分钟前
解锁HTML5页面生命周期API:前端开发的新视角
前端·html·html5
江号软件分享15 分钟前
轻松解决Office版本冲突问题:卸载是关键
前端
致博软件F2BPM22 分钟前
Element Plus和Ant Design Vue深度对比分析与选型指南
前端·javascript·vue.js
慧一居士1 小时前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead1 小时前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码7 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子7 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年7 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子7 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina7 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试