首页 前端知识 jQuery UI widget源码解析

jQuery UI widget源码解析

2024-07-08 09:07:46 前端知识 前端哥 110 881 我要收藏
  1. // inheriting from

  2. //这里深复制一份options

  3. basePrototype.options = $.widget.extend({}, basePrototype.options);

  4. //在传入的ui原型中有方法调用this._super 和this.__superApply会调用到base上(最基类上)的方法

  5. $.each(prototype, function(prop, value) {

  6. //如果val不是function 则直接给对象赋值字符串

  7. if (!$.isFunction(value)) {

  8. proxiedPrototype[prop] = value;

  9. return;

  10. }

  11. //如果val是function

  12. proxiedPrototype[prop] = (function() {

  13. //两种调用父类函数的方法

  14. var _super = function() {

  15. //将当期实例调用父类的方法

  16. return base.prototype[prop].apply(this, arguments);

  17. },

  18. _superApply = function(args) {

  19. return base.prototype[prop].apply(this, args);

  20. };

  21. return function() {

  22. var __super = this._super,

  23. __superApply = this._superApply,

  24. returnValue;

  25. //                console.log(prop, value,this,this._super,‘===’)

  26. //                debugger;

  27. //在这里调用父类的函数

  28. this._super = _super;

  29. this._superApply = _superApply;

  30. returnValue = value.apply(this, arguments);

  31. this._super = __super;

  32. this._superApply = __superApply;

  33. //                console.log(this,value,returnValue,prop,‘===’)

  34. return returnValue;

  35. };

  36. })();

  37. });

  38. //    console.info(proxiedPrototype)

  39. //    debugger;

  40. //这里是实例化获取的内容

  41. constructor.prototype = $.widget.extend(basePrototype, {

  42. // TODO: remove support for widgetEventPrefix

  43. // always use the name + a colon as the prefix, e.g., draggable:start

  44. // don’t prefix for widgets that aren’t DOM-based

  45. widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name

  46. }, proxiedPrototype, {

  47. //重新把constructor指向 constructor 变量

  48. constructor: constructor,

  49. namespace: namespace,

  50. widgetName: name,

  51. widgetFullName: fullName

  52. });

  53. // If this widget is being redefined then we need to find all widgets that

  54. // are inheriting from it and redefine all of them so that they inherit from

  55. // the new version of this widget. We’re essentially trying to replace one

  56. // level in the prototype chain.

  57. //这里判定插件是否被使用了。一般来说,都不会被使用的。

  58. //因为插件的开发者都是我们自己,呵呵

  59. if (existingConstructor) {

  60. $.each(existingConstructor._childConstructors, function(i, child) {

  61. var childPrototype = child.prototype;

  62. // redefine the child widget using the same prototype that was

  63. // originally used, but inherit from the new version of the base

  64. $.widget(childPrototype.namespace + “.” + childPrototype.widgetName, constructor, child._proto);

  65. });

  66. // remove the list of existing child constructors from the old constructor

  67. // so the old child constructors can be garbage collected

  68. delete existingConstructor._childConstructors;

  69. } else {

  70. //父类添加当前插件的实例 主要用于作用域链查找 不至于断层

  71. base._childConstructors.push(constructor);

  72. }

  73. //将此方法挂在jQuery对象上

  74. $.widget.bridge(name, constructor);

  75. return constructor;

  76. };

  77. //扩展jq的extend方法,实际上类似$.extend(true,…) 深复制

  78. $.widget.extend = function(target) {

  79. var input = widget_slice.call(arguments, 1),

  80. inputIndex = 0,

  81. inputLength = input.length,

  82. key, value;

  83. for (; inputIndex < inputLength; inputIndex++) {

  84. for (key in input[inputIndex]) {

  85. value = input[inputIndex][key];

  86. if (input[inputIndex].hasOwnProperty(key) && value !== undefined) {

  87. // Clone objects

  88. if ($.isPlainObject(value)) {

  89. target[key] = KaTeX parse error: Undefined control sequence: \[ at position 22: …inObject(target\̲[̲key\]) ? .widget.extend({}, target[key], value) :

  90. // Don’t extend strings, arrays, etc. with objects

  91. $.widget.extend({}, value);

  92. // Copy everything else by reference

  93. } else {

  94. target[key] = value;

  95. }

  96. }

  97. }

  98. }

  99. return target;

  100. };

  101. //bridge 是设计模式的一种,这里将对象转为插件调用

  102. $.widget.bridge = function(name, object) {

  103. var fullName = object.prototype.widgetFullName || name;

  104. //这里就是插件了

  105. //这部分的实现主要做了几个工作,也是制作一个优雅的插件的主要代码

  106. //1、初次实例化时将插件对象缓存在dom上,后续则可直接调用,避免在相同元素下widget的多实例化。简单的说,就是一个单例方法。

  107. //2、合并用户提供的默认设置选项options

  108. //3、可以通过调用插件时传递字符串来调用插件内的方法。如:$(‘#id’).menu(‘hide’) 实际就是实例插件并调用hide()方法。

  109. //4、同时限制外部调用“_”下划线的私有方法

  110. $.fn[name] = function(options) {

  111. var isMethodCall = typeof options === “string”,

  112. args = widget_slice.call(arguments, 1),

  113. returnValue = this;

  114. // allow multiple hashes to be passed on init.

  115. //可以简单认为是$.extend(true,options,args[0],…),args可以是一个参数或是数组

  116. options = !isMethodCall && args.length ? $.widget.extend.apply(null, [options].concat(args)) : options;

  117. //这里对字符串和对象分别作处理

  118. if (isMethodCall) {

  119. this.each(function() {

  120. var methodValue, instance = $.data(this, fullName);

  121. //如果传递的是instance则将this返回。

  122. if (options === “instance”) {

  123. returnValue = instance;

  124. return false;

  125. }

  126. if (!instance) {

  127. return $.error("cannot call methods on " + name + " prior to initialization; " + “attempted to call method '” + options + “'”);

  128. }

  129. //这里对私有方法的调用做了限制,直接调用会抛出异常事件

  130. if (!$.isFunction(instance[options]) || options.charAt(0) === “_”) {

  131. return $.error(“no such method '” + options + “’ for " + name + " widget instance”);

  132. }

  133. //这里是如果传递的是字符串,则调用字符串方法,并传递对应的参数.

  134. //比如插件有个方法hide(a,b); 有2个参数:a,b

  135. //则调用时$(‘#id’).menu(‘hide’,1,2);//1和2 分别就是参数a和b了。

  136. methodValue = instance[options].apply(instance, args);

  137. if (methodValue !== instance && methodValue !== undefined) {

  138. returnValue = methodValue && methodValue.jquery ? returnValue.pushStack(methodValue.get()) : methodValue;

  139. return false;

  140. }

  141. });

  142. } else {

  143. this.each(function() {

  144. var instance = $.data(this, fullName);

  145. if (instance) {

  146. instance.option(options || {});

  147. //这里每次都调用init方法

  148. if (instance._init) {

  149. instance._init();

  150. }

  151. } else {

  152. //缓存插件实例

  153. $.data(this, fullName, new object(options, this));

  154. }

  155. });

  156. }

  157. return returnValue;

  158. };

  159. };

  160. //这里是真正的widget基类

  161. $.Widget = function( /* options, element */ ) {};

  162. $.Widget._childConstructors = [];

  163. $.Widget.prototype = {

  164. widgetName: “widget”,

  165. //用来决定事件的名称和插件提供的callbacks的关联。

  166. // 比如dialog有一个close的callback,当close的callback被执行的时候,一个dialogclose的事件被触发。

  167. // 事件的名称和事件的prefix+callback的名称。widgetEventPrefix 默认就是控件的名称,但是如果事件需要不同的名称也可以被重写。

  168. // 比如一个用户开始拖拽一个元素,我们不想使用draggablestart作为事件的名称,我们想使用dragstart,所以我们可以重写事件的prefix。

  169. // 如果callback的名称和事件的prefix相同,事件的名称将不会是prefix。

  170. // 它阻止像dragdrag一样的事件名称。

  171. widgetEventPrefix: “”,

  172. defaultElement: “

    ”,

  173. //属性会在创建模块时被覆盖

  174. options: {

  175. disabled: false,

  176. // callbacks

  177. create: null

  178. },

  179. _createWidget: function(options, element) {

  180. element = $(element || this.defaultElement || this)[0];

  181. this.element = $(element);

  182. this.uuid = widget_uuid++;

  183. this.eventNamespace = “.” + this.widgetName + this.uuid;

  184. this.options = $.widget.extend({}, this.options, this._getCreateOptions(), options);

  185. this.bindings = $();

  186. this.hoverable = $();

  187. this.focusable = $();

  188. if (element !== this) {

  189. //            debugger

  190. $.data(element, this.widgetFullName, this);

  191. this._on(true, this.element, {

  192. remove: function(event) {

  193. if (event.target === element) {

  194. this.destroy();

  195. }

  196. }

  197. });

  198. this.document = $(element.style ?

  199. // element within the document

  200. element.ownerDocument :

  201. // element is window or document

  202. element.document || element);

  203. this.window = $(this.document[0].defaultView || this.document[0].parentWindow);

  204. }

  205. this._create();

  206. //创建插件时,有个create的回调

  207. this._trigger(“create”, null, this._getCreateEventData());

  208. this._init();

  209. },

  210. _getCreateOptions: $.noop,

  211. _getCreateEventData: $.noop,

  212. _create: $.noop,

  213. _init: $.noop,

  214. //销毁模块:去除绑定事件、去除数据、去除样式、属性

  215. destroy: function() {

  216. this._destroy();

  217. // we can probably remove the unbind calls in 2.0

  218. // all event bindings should go through this._on()

  219. this.element.unbind(this.eventNamespace).removeData(this.widgetFullName)

  220. // support: jquery <1.6.3

  221. // http://bugs.jquery.com/ticket/9413

  222. .removeData($.camelCase(this.widgetFullName));

  223. this.widget().unbind(this.eventNamespace).removeAttr(“aria-disabled”).removeClass(

  224. this.widgetFullName + "-disabled " + “ui-state-disabled”);

  225. // clean up events and states

  226. this.bindings.unbind(this.eventNamespace);

  227. this.hoverable.removeClass(“ui-state-hover”);

  228. this.focusable.removeClass(“ui-state-focus”);

  229. },

  230. _destroy: $.noop,

  231. widget: function() {

  232. return this.element;

  233. },

  234. //设置选项函数

  235. option: function(key, value) {

  236. var options = key,

  237. parts, curOption, i;

  238. if (arguments.length === 0) {

  239. // don’t return a reference to the internal hash

  240. //返回一个新的对象,不是内部数据的引用

  241. return $.widget.extend({}, this.options);

  242. }

  243. if (typeof key === “string”) {

  244. // handle nested keys, e.g., “foo.bar” => { foo: { bar: ___ } }

  245. options = {};

  246. parts = key.split(“.”);

  247. key = parts.shift();

  248. if (parts.length) {

  249. curOption = options[key] = $.widget.extend({}, this.options[key]);

  250. for (i = 0; i < parts.length - 1; i++) {

  251. curOption[parts[i]] = curOption[parts[i]] || {};

  252. curOption = curOption[parts[i]];

  253. }

  254. key = parts.pop();

  255. if (arguments.length === 1) {

  256. return curOption[key] === undefined ? null : curOption[key];

  257. }

  258. curOption[key] = value;

  259. } else {

  260. if (arguments.length === 1) {

  261. return this.options[key] === undefined ? null : this.options[key];

  262. }

  263. options[key] = value;

  264. }

  265. }

  266. this._setOptions(options);

  267. return this;

  268. },

  269. _setOptions: function(options) {

  270. var key;

  271. for (key in options) {

  272. this._setOption(key, options[key]);

  273. }

  274. return this;

  275. },

  276. _setOption: function(key, value) {

  277. this.options[key] = value;

  278. if (key === “disabled”) {

  279. this.widget().toggleClass(this.widgetFullName + “-disabled”, !! value);

  280. // If the widget is becoming disabled, then nothing is interactive

  281. if (value) {

  282. this.hoverable.removeClass(“ui-state-hover”);

  283. this.focusable.removeClass(“ui-state-focus”);

  284. }

  285. }

  286. return this;

  287. },

  288. enable: function() {

  289. return this._setOptions({

  290. disabled: false

  291. });

  292. },

  293. disable: function() {

  294. return this._setOptions({

  295. disabled: true

  296. });

  297. },

  298. _on: function(suppressDisabledCheck, element, handlers) {

  299. var delegateElement, instance = this;

  300. // no suppressDisabledCheck flag, shuffle arguments

  301. if (typeof suppressDisabledCheck !== “boolean”) {

  302. handlers = element;

  303. element = suppressDisabledCheck;

  304. suppressDisabledCheck = false;

  305. }

  306. // no element argument, shuffle and use this.element

  307. if (!handlers) {

  308. handlers = element;

  309. element = this.element;

  310. delegateElement = this.widget();

  311. } else {

  312. // accept selectors, DOM elements

  313. element = delegateElement = $(element);

  314. this.bindings = this.bindings.add(element);

  315. }

  316. $.each(handlers, function(event, handler) {

  317. function handlerProxy() {

  318. // allow widgets to customize the disabled handling

  319. // - disabled as an array instead of boolean

  320. // - disabled class as method for disabling individual parts

  321. if (!suppressDisabledCheck && (instance.options.disabled === true || $(this).hasClass(“ui-state-disabled”))) {

  322. return;

  323. }

  324. return (typeof handler === “string” ? instance[handler] : handler).apply(instance, arguments);

  325. }

  326. // copy the guid so direct unbinding works

  327. if (typeof handler !== “string”) {

  328. handlerProxy.guid = handler.guid = handler.guid || handlerProxy.guid || $.guid++;

  329. }

  330. var match = event.match(/^([\w:-]*)\s*(.*)$/),

  331. eventName = match[1] + instance.eventNamespace,

  332. selector = match[2];

  333. if (selector) {

  334. delegateElement.delegate(selector, eventName, handlerProxy);

  335. } else {

  336. element.bind(eventName, handlerProxy);

  337. }

  338. });

  339. },

  340. _off: function(element, eventName) {

  341. eventName = (eventName || “”).split(" ").join(this.eventNamespace + " ") + this.eventNamespace;

  342. element.unbind(eventName).undelegate(eventName);

  343. },

  344. _delay: function(handler, delay) {

  345. function handlerProxy() {

  346. return (typeof handler === “string” ? instance[handler] : handler).apply(instance, arguments);

  347. }

  348. var instance = this;

  349. return setTimeout(handlerProxy, delay || 0);

  350. },

  351. _hoverable: function(element) {

  352. this.hoverable = this.hoverable.add(element);

  353. this._on(element, {

  354. mouseenter: function(event) {

  355. $(event.currentTarget).addClass(“ui-state-hover”);

  356. },

  357. mouseleave: function(event) {

  358. $(event.currentTarget).removeClass(“ui-state-hover”);

  359. }

  360. });

  361. },

  362. _focusable: function(element) {

  363. this.focusable = this.focusable.add(element);

  364. this._on(element, {

  365. focusin: function(event) {

  366. $(event.currentTarget).addClass(“ui-state-focus”);

  367. },

  368. focusout: function(event) {

  369. $(event.currentTarget).removeClass(“ui-state-focus”);

  370. }

  371. });

  372. },

  373. _trigger: function(type, event, data) {

  374. var prop, orig, callback = this.options[type];

  375. data = data || {};

  376. event = $.Event(event);

  377. event.type = (type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type).toLowerCase();

  378. // the original event may come from any element

  379. // so we need to reset the target on the new event

  380. event.target = this.element[0];

  381. // copy original event properties over to the new event

  382. orig = event.originalEvent;

  383. if (orig) {

  384. for (prop in orig) {

  385. if (!(prop in event)) {

  386. event[prop] = orig[prop];

  387. }

  388. }

  389. }

  390. this.element.trigger(event, data);

  391. return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());

  392. }

  393. };

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)

最后

本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等

详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车!!

前端视频资料:
曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-NvH2wCRv-1711679238362)]

[外链图片转存中…(img-8Qzy8Hqi-1711679238362)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-ORxydUkm-1711679238363)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)

最后

本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等

详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车!!

[外链图片转存中…(img-7PSy6P4p-1711679238363)]

前端视频资料:

转载请注明出处或者链接地址:https://www.qianduange.cn//article/13885.html
标签
评论
发布的文章

html左右两栏布局实现

2024-08-04 00:08:50

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!