源码分析之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 的内部实现中被广泛使用,同时也可以被开发者直接调用

相关推荐
blzlh7 分钟前
春招面试万字整理,全程拷打,干货满满(3)
前端·javascript·面试
咖啡教室24 分钟前
前端开发中使用whistle代理工具
前端·javascript
予安灵1 小时前
Vue.js 模板语法全解析:从基础到实战应用
前端·javascript·vue.js·vue指令·vue生命周期·vue项目结构·vue插值
瑾凌1 小时前
Cesium 自定义路径导航材质
前端·javascript·vue.js·vue·材质·cesium
池鱼ipou1 小时前
面试必看:深入浅出 JavaScript 事件循环与异步编程技巧
前端·javascript·面试
刺客-Andy2 小时前
开发中常用的设计模式 用法及注意事项
开发语言·前端·javascript·设计模式
uperficialyu2 小时前
2025年03月18日柯莱特(外包宁德)一面前端面试
前端·面试·职场和发展
MINO吖2 小时前
vue-cli如何正确关闭prefetchprefetch和preload
前端·javascript·vue.js
男Ren、麦根2 小时前
用 Pinia 点燃 Vue 3 应用:状态管理革新之旅
前端·javascript·vue.js
泷羽Sec-pp2 小时前
WebDeveloper靶机详解
linux·服务器·前端·网络·chrome