Изучите общую архитектуру исходного кода jQuery и создайте собственную библиотеку js.

исходный код JavaScript
Изучите общую архитектуру исходного кода jQuery и создайте собственную библиотеку js.

предисловие

Здравствуйте, яВакагава. Это学习源码整体架构первый раз. Слово «общая архитектура» кажется немного большим, скажем так, это общая структура исходного кода. В этой статье рассказывается об упакованном и интегрированном коде, а не о разделенном коде на реальном складе.

学习源码整体架构Цикл статей следующий:

1.Изучите общую архитектуру исходного кода jQuery и создайте собственную библиотеку js.
2.Изучите общую архитектуру исходного кода подчеркивания и создайте собственную библиотеку классов функционального программирования.
3.Изучите общую архитектуру исходного кода lodash и создайте собственную библиотеку классов функционального программирования.
4.Изучите общую архитектуру исходного кода sentry и создайте собственный SDK для мониторинга исключений переднего плана.
5.Изучите общую архитектуру исходного кода vuex и создайте собственную библиотеку управления состоянием.
6.Изучите общую архитектуру исходного кода axios и создайте собственную библиотеку запросов.
7.Изучите общую архитектуру исходного кода koa, проанализируйте принцип луковой модели koa и принцип совместной работы.
8.Изучите общую архитектуру исходного кода Redux и глубоко поймите принципы Redux и его промежуточного программного обеспечения.

Заинтересованные читатели могут нажать, чтобы прочитать.

Хотя сейчас почти не используетсяjQuery, ноjQueryПопулярность10多年изJS库, еще надо узнать его исходный код. Вы также можете научиться создавать свои собственныеjsБиблиотека классов, которая может многое добавить к собеседованию.

Эта статья учитv3.4.1Версия.unpkg.comИсходный адрес: https://unpkg.com/jquery@3.4.1/dist/jquery.js

jQuery githubсклад

самовыполняющаяся анонимная функция

(function(global, factory){

})(typeof window !== "underfined" ? window: this, function(window, noGlobal){

});

Внешний мир не может получить доступ к внутренним переменным и функциям, но к внешним переменным можно получить доступ изнутри, но если внутри определены его собственные переменные, то к внешним переменным не будет доступа. Анонимные функции оборачивают код внутрь, предотвращая конфликты с другим кодом и загрязняя глобальную среду. Читатели, не очень хорошо знакомые с самоисполняемыми функциями, могут обратиться к этой статье.JavaScript: немедленное выполнение функционального выражения (IIFE)

В среде браузера, наконец, поместите$а такжеjQueryфункция монтируется наwindow, поэтому к нему можно получить доступ извне$а такжеjQuery.

if ( !noGlobal ) {
 window.jQuery = window.$ = jQuery;
}
// 其中`noGlobal`参数只有在这里用到。

Поддержка использования в различных средах, таких как commonjs, спецификации amd.

поддержка спецификации commonjs

commonjsДостичь главного представителяnodejs

// global是全局变量,factory 是函数
( function( global, factory ) {

 //  使用严格模式
 "use strict";
 // Commonjs 或者 CommonJS-like  环境
 if ( typeof module === "object" && typeof module.exports === "object" ) {
  // 如果存在global.document 则返回factory(global, true);
  module.exports = global.document ?
   factory( global, true ) :
   function( w ) {
    if ( !w.document ) {
     throw new Error( "jQuery requires a window with a document" );
    }
    return factory( w );
   };
 } else {
  factory( global );
 }

// Pass this if window is not defined yet
// 第一个参数判断window,存在返回window,不存在返回this
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {});

Спецификация amd в основном представляет собой requirejs

if ( typeof define === "function" && define.amd ) {
 define( "jquery", [], function() {
  return jQuery;
 } );
}

Спецификация cmd в основном представляет seajs

К сожалению,jQueryВ исходном коде нет экспозицииseajsслужба поддержки. Но есть и варианты онлайн. Здесь он не будет упоминаться подробно. Ведь сейчас это в принципе бесполезноseajs.

нет новой конструкции

На самом деле это можетnew, потому чтоjQueryэто функция. и безnewЭффект тот же. new показывает возвращаемый объект, поэтому и вызывается напрямуюjQueryЭффект от функции такой же. если правильноnewЯ не знаю, что именно делает оператор. См. мою предыдущую статью.

Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?

Исходный код:

 var
 version = "3.4.1",

 // Define a local copy of jQuery
 jQuery = function( selector, context ) {
  // 返回new之后的对象
  return new jQuery.fn.init( selector, context );
 };
jQuery.fn = jQuery.prototype = {
 // jQuery当前版本
 jquery: version,
 // 修正构造器为jQuery
 constructor: jQuery,
 length: 0,
};
init = jQuery.fn.init = function( selector, context, root ) {
 // ...
 if ( !selector ) {
  return this;
 }
 // ...
};
init.prototype = jQuery.fn;
jQuery.fn === jQuery.prototype;  // true
init = jQuery.fn.init;
init.prototype = jQuery.fn;
// 也就是
jQuery.fn.init.prototype === jQuery.fn;  // true
jQuery.fn.init.prototype === jQuery.prototype;  // true

Об этом автор нарисовал картинуjQueryДиаграмма-прототип, так называемая картинка, стоит тысячи слов.jQuery-v3.4.1原型关系图

<sciprt src="https://unpkg.com/jquery@3.4.1/dist/jquery.js">
</script>
console.log({jQuery});
// 在谷歌浏览器控制台,可以看到jQuery函数下挂载了很多静态属性和方法,在jQuery.fn 上也挂着很多属性和方法。

VueВ исходном коде также следуйтеjQueryТочно так же выполняетсяVue.prototype._initметод.

function Vue (options) {
 if (!(this instanceof Vue)
 ) {
  warn('Vue is a constructor and should be called with the `new` keyword');
 }
 this._init(options);
}
initMixin(Vue);
function initMixin (Vue) {
 Vue.prototype._init = function (options) {};
};

Одна из основных функций расширения

Применение:

jQuery.extend( target [, object1 ] [, objectN ] )        Returns: Object

jQuery.extend( [deep ], target, object1 [, objectN ] )

jQuery.extend API jQuery.fn.extend API

Посмотрите несколько примеров: (Пример можно разместить в коде онлайн-редактированияПример кода jQuery.extend, вы можете запустить его напрямую).

// 1. jQuery.extend( target)
var result1 = $.extend({
 job: '前端开发工程师',
});

console.log(result1, 'result1', result1.job); // $函数 加了一个属性 job  // 前端开发工程师

// 2. jQuery.extend( target, object1)
var result2 = $.extend({
 name: '若川',
},
{
 job: '前端开发工程师',
});

console.log(result2, 'result2'); // { name: '若川', job: '前端开发工程师' }

// deep 深拷贝
// 3. jQuery.extend( [deep ], target, object1 [, objectN ] )
var result3 = $.extend(true,  {
 name: '若川',
 other: {
  mac: 0,
  ubuntu: 1,
  windows: 1,
 },
}, {
 job: '前端开发工程师',
 other: {
  mac: 1,
  linux: 1,
  windows: 0,
 }
});
console.log(result3, 'result3');
// deep true
// {
//     "name": "若川",
//     "other": {
//         "mac": 1,
//         "ubuntu": 1,
//         "windows": 0,
//         "linux": 1
//     },
//     "job": "前端开发工程师"
// }
// deep false
// {
//     "name": "若川",
//     "other": {
//         "mac": 1,
//         "linux": 1,
//         "windows": 0
//     },
//     "job": "前端开发工程师"
// }

В заключение:extendФункция может быть реализована какjQueryФункция может реализовать поверхностную копию или глубокую копию. Вы можете добавить статические методы и свойства в jQuery или использовать их какjQuery.fn(то есть,jQuery.prototype) для добавления свойств и методов, благодаряthis,jQuery.extendкогда звонятthisуказать даjQuery,jQuery.fn.extendкогда звонятthisуказать наjQuery.fn.

Неглубокая реализация копирования

Зная это, на самом деле относительно легко реализовать поверхностную копию:

// 浅拷贝实现
jQuery.extend = function(){
 // options 是扩展的对象object1,object2...
 var options,
 // object对象上的键
 name,
 // copy object对象上的值,也就是是需要拷贝的值
 copy,
 // 扩展目标对象,可能不是对象,所以或空对象
 target = arguments[0] || {},
 // 定义i为1
 i = 1,
 // 定义实参个数length
 length = arguments.length;
 // 只有一个参数时
 if(i === length){
  target = this;
  i--;
 }
 for(; i < length; i++){
  // 不是underfined 也不是null
  if((options = arguments[i]) !=  null){
   for(name in options){
    copy = options[name];
    // 防止死循环,continue 跳出当前此次循环
    if ( name === "__proto__" || target === copy ) {
     continue;
    }
    if ( copy !== undefined ) {
     target[ name ] = copy;
    }
   }
  }

 }
 // 最后返回目标对象
 return target;
}

Глубокая копия в основном оценивается по следующему коду. Может быть значением массива и типом ссылки на объект, делайте выводы.

if ( copy !== undefined ) {
 target[ name ] = copy;
}

Чтобы облегчить читателям отладку, код также помещен вjQuery.extend неглубокий код копирования для реализации codepen, который можно запустить онлайн.

реализация глубокого копирования

$.extend = function(){
 // options 是扩展的对象object1,object2...
 var options,
 // object对象上的键
 name,
 // copy object对象上的值,也就是是需要拷贝的值
 copy,
 // 深拷贝新增的四个变量 deep、src、copyIsArray、clone
 deep = false,
 // 源目标,需要往上面赋值的
 src,
 // 需要拷贝的值的类型是函数
 copyIsArray,
 //
 clone,
 // 扩展目标对象,可能不是对象,所以或空对象
 target = arguments[0] || {},
 // 定义i为1
 i = 1,
 // 定义实参个数length
 length = arguments.length;

 // 处理深拷贝情况
 if ( typeof target === "boolean" ) {
  deep = target;

  // Skip the boolean and the target
  // target目标对象开始后移
  target = arguments[ i ] || {};
  i++;
 }

 // Handle case when target is a string or something (possible in deep copy)
 // target不等于对象,且target不是函数的情况下,强制将其赋值为空对象。
 if ( typeof target !== "object" && !isFunction( target ) ) {
  target = {};
 }

 // 只有一个参数时
 if(i === length){
  target = this;
  i--;
 }
 for(; i < length; i++){
  // 不是underfined 也不是null
  if((options = arguments[i]) !=  null){
   for(name in options){
    copy = options[name];
    // 防止死循环,continue 跳出当前此次循环
    if ( name === "__proto__" || target === copy ) {
     continue;
    }

    // Recurse if we're merging plain objects or arrays
    // 这里deep为true,并且需要拷贝的值有值,并且是纯粹的对象
    // 或者需拷贝的值是数组
    if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
     ( copyIsArray = Array.isArray( copy ) ) ) ) {

     // 源目标,需要往上面赋值的
     src = target[ name ];

     // Ensure proper type for the source value
     // 拷贝的值,并且src不是数组,clone对象改为空数组。
     if ( copyIsArray && !Array.isArray( src ) ) {
      clone = [];
      // 拷贝的值不是数组,对象不是纯粹的对象。
     } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
      // clone 赋值为空对象
      clone = {};
     } else {
      // 否则 clone = src
      clone = src;
     }
     // 把下一次循环时,copyIsArray 需要重新赋值为false
     copyIsArray = false;

     // Never move original objects, clone them
     // 递归调用自己
     target[ name ] = jQuery.extend( deep, clone, copy );

    // Don't bring in undefined values
    }
    else if ( copy !== undefined ) {
     target[ name ] = copy;
    }
   }
  }

 }
 // 最后返回目标对象
 return target;
};

Чтобы облегчить читателям отладку, этот код также помещен вjQuery.extend код глубокого копирования для реализации codepen, который можно запустить онлайн.

производная функция глубокого копирования isFunction

Определяет, является ли параметр функцией.

var isFunction = function isFunction( obj ) {

 // Support: Chrome <=57, Firefox <=52
 // In some browsers, typeof returns "function" for HTML <object> elements
 // (i.e., `typeof document.createElement( "object" ) === "function"`).
 // We don't want to classify *any* DOM node as a function.
 return typeof obj === "function" && typeof obj.nodeType !== "number";
};

Функция глубокого копирования, полученная из jQuery.isPlainObject

jQuery.isPlainObject(obj)Проверяет, является ли объект чистым объектом (созданный с помощью «{}» или «новый объект»).

jQuery.isPlainObject({}) // true
jQuery.isPlainObject("test") // false
var getProto = Object.getPrototypeOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call( Object );

jQuery.extend( {
 isPlainObject: function( obj ) {
  var proto, Ctor;

  // Detect obvious negatives
  // Use toString instead of jQuery.type to catch host objects
  // !obj 为true或者 不为[object Object]
  // 直接返回false
  if ( !obj || toString.call( obj ) !== "[object Object]" ) {
   return false;
  }

  proto = getProto( obj );

  // Objects with no prototype (e.g., `Object.create( null )`) are plain
  // 原型不存在 比如 Object.create(null) 直接返回 true;
  if ( !proto ) {
   return true;
  }

  // Objects with prototype are plain iff they were constructed by a global Object function
  Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
  // 构造器是函数,并且 fnToString.call( Ctor )  === fnToString.call( Object );
  return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
 },
});

extendфункцию, вы также можете удалить и записать ее самостоятельно.jQueryОдна из основных функций. И он имеет широкий спектр применений, которые можно использовать внутри или снаружи, например, в виде подключаемых модулей расширения.

цепной вызов

jQueryМожет быть сцеплен, потому что некоторые функции выполняются послеreturn this. НапримерjQueryв исходном кодеaddClass,removeClass,toggleClass.

jQuery.fn.extend({
 addClass: function(){
  // ...
  return this;
 },
 removeClass: function(){
  // ...
  return this;
 },
 toggleClass: function(){
  // ...
  return this;
 },
});

jQuery.noConflictмногоjsФункция предотвращения столкновений, которую будет иметь библиотека

jQuery.noConflict API

Применение:

 <script>
 var $ = '我是其他的$,jQuery不要覆盖我';
</script>
<script src="./jquery-3.4.1.js">
</script>
<script>
 $.noConflict();
 console.log($); // 我是其他的$,jQuery不要覆盖我
</script>

Исходный код jQuery.noConflict

var

 // Map over jQuery in case of overwrite
 _jQuery = window.jQuery,

 // Map over the $ in case of overwrite
 _$ = window.$;

jQuery.noConflict = function( deep ) {
 // 如果已经存在$ === jQuery;
 // 把已存在的_$赋值给window.$;
 if ( window.$ === jQuery ) {
  window.$ = _$;
 }

 // 如果deep为 true, 并且已经存在jQuery === jQuery;
 // 把已存在的_jQuery赋值给window.jQuery;
 if ( deep && window.jQuery === jQuery ) {
  window.jQuery = _jQuery;
 }

 // 最后返回jQuery
 return jQuery;
};

Суммировать

Тезис через анализjQueryОбщая структура, самовыполняющаяся анонимная функция, нетnewСоздание, поддержка различных спецификаций (таких как commonjs, amd), основные функцииextendцепной звонок,jQuery.noConflictи т.п.

Реорганизуйте структуру исходного кода, описанную ниже.

// 源码结构
( function( global, factory )
 "use strict";
 if ( typeof module === "object" && typeof module.exports === "object" ) {
  module.exports = global.document ?
   factory( global, true ) :
   function( w ) {
    if ( !w.document ) {
     throw new Error( "jQuery requires a window with a document" );
    }
    return factory( w );
   };
 } else {
  factory( global );
 }

} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
 var version = "3.4.1",

  // Define a local copy of jQuery
  jQuery = function( selector, context ) {
   return new jQuery.fn.init( selector, context );
  };

 jQuery.fn = jQuery.prototype = {
  jquery: version,
  constructor: jQuery,
  length: 0,
  // ...
 };

 jQuery.extend = jQuery.fn.extend = function() {};

 jQuery.extend( {
  // ...
  isPlainObject: function( obj ) {},
  // ...
 });

 init = jQuery.fn.init = function( selector, context, root ) {};

 init.prototype = jQuery.fn;

 if ( typeof define === "function" && define.amd ) {
  define( "jquery", [], function() {
   return jQuery;
  } );
 }
 jQuery.noConflict = function( deep ) {};

 if ( !noGlobal ) {
  window.jQuery = window.$ = jQuery;
 }

 return jQuery;
});

может научитьсяjQueryГениальный дизайн и архитектура для вашего собственного использования, чтобы создать свой собственныйjsБиблиотека класса. Соответствующий код и ресурсы размещены вgithub blog, читатели, которым это нужно, могут подобрать его самостоятельно.

Следующая статья, чтобы узнатьunderscorejsОбщая структура исходного кода.Изучите общую архитектуру исходного кода подчеркивания и создайте собственную библиотеку классов функционального программирования.

Читатели, которые находят что-то неправильным или могут быть улучшены, могут оставить комментарий. Кроме того, если вы считаете, что написано хорошо, вы можете поставить лайк, прокомментировать и переслать, что также является своего рода поддержкой для автора.

Предыдущие статьи автора

Интервьюер спросил: наследование JS
Интервьюер спросил: этот пункт JS
Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?
Внешний интерфейс использует сканер puppeteer для создания PDF-файла «React.js Book» и его слияния.

Расширенное чтение

chokcoco: интерпретация исходного кода jQuery-v1.10.2
chokcoco: [углубленный jQuery] анализ исходного кода — общая архитектура
songjz: серия исходных кодов jQuery (1) Общая архитектура

Очередная серия автора

Интервьюер спросил: наследование JS
Интервьюер спросил: этот пункт JS
Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?

о

Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. По дороге на фронт | Энтузиасты РРТ | Знаю очень мало, только хорошо учусь.
Блог Вакагавы,использоватьvuepressРефакторинг, опыт чтения может быть лучше
Колонка самородков, добро пожаловать, обратите внимание~
segmentfaultПередняя колонка обзора, добро пожаловать, обратите внимание~
Колонна переднего обзора Yuque, новая колонка Yuque, добро пожаловать на внимание~
Знайте переднюю колонку видения, добро пожаловать, обратите внимание~
github blog, соответствующий исходный код и ресурсы размещены здесь, попросите одинstar^_^~

Добро пожаловать, чтобы добавить общедоступную учетную запись WeChat для общения в WeChat

Может быть более интересным общедоступный аккаунт WeChat, нажмите и удерживайте, чтобы отсканировать код, чтобы следовать. Добро пожаловать, чтобы добавить автора WeChatruochuan12(Укажите источник, в основном никто не будет отклонен), пригласите вас в [Группу обмена видением переднего плана] для долгосрочного обмена и обучения ~

若川视野
Видение Вакагава

В этой статье используетсяmdniceнабор текста