Золотая девятка, серебряная десятка, обзор интервью младшего и среднего звена "Глава JavaScript"

JavaScript

JavaScript

тип данных

String, Number, Boolean, Null, Undefined, Symbol, BigInt, Object

куча, стопка

Оба являются местами для хранения данных.

Стек (стек) — это автоматически выделяемое пространство памяти, в котором хранятся значения примитивных типов и адреса памяти ссылочных типов.

Куча — это динамически выделяемое пространство памяти, в котором хранятся значения ссылочных типов.

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

неявное преобразование типов

Как язык со слабой типизацией, JavaScript будет автоматически преобразовывать типы в некоторых сценариях для гибкого использования.

Существует три распространенных сценария неявного преобразования типов:операция,отрицать,Сравнивать

операция

Неявное преобразование типа операции преобразует члены операции вnumberТипы.

Преобразование примитивного типа:

true + false   // 1
null + 10      // 10
false + 20     // 20
undefined + 30 // NaN
1 + '2'        // "12"
NaN + ''       // "NaN"
undefined + '' // "undefined"
null + ''      // "null"
'' - 3         // -3
  • null,false,''конвертироватьnumberтип 0
  • undefinedконвертироватьnumberтипNaN,такundefinedи другие операции примитивного типа будут выводитьNaN
  • Кроме того, строки являются сильными (фактически конкатенация строк), и добавление любого типа приведет к выводу строки (symbolкроме), даже еслиNaN,undefined. Другие операции обычно преобразуются вnumberвыполнять операции.

Преобразование ссылочного типа:

[1] + 10    // "110"
[] + 20     // "20"
[1,2] + 20  // "1,220"
[20] - 10   // 10
[1,2] - 10  // NaN
({}) + 10   // "[object Object]10"
({}) - 10   // NaN
  • Когда выполняется операция ссылочного типа, она будет вызываться первойvalueOfОпределите, является ли возвращаемое значение примитивным типом данных, и если да, выполните операцию. Если нет, звонитеtoStringсначала преобразовать вstringпересчитывать
  • Тот же вывод, за исключением того, что сложение выведет строку, другие случаи преобразуются первымиstringповернуть сноваnumber

Процесс преобразования эталонного типа разрешения:

[1,2] + 20
// 过程:
[1,2].toString() // '1,2'
'1,2' + 20       // '1,220'

[20] - 10
// 过程
[20].toString()  // '20'
Number('20')     // 20
20 - 10          // 10

отрицать

Отрицательное неявное преобразование типа преобразует член операции вbooleanТипы.

Это неявное преобразование типов относительно простое, оно заключается в преобразовании значения в логическое значение, а затем его отрицании:

![]     // false
!{}     // false
!false  // true

Обычно для того, чтобы быстро получить булев тип значения, его можно инвертировать дважды:

!![]  // true
!!0   // false

Сравнивать

сравнение делится на строгое сравнение===и нестрогое сравнение==,из-за===Сравниваются типы, и преобразование типов не выполняется. Обсуждать только здесь==.

Неявное преобразование типа сравнения в основном преобразует член операции вnumberТипы.

undefined == null  // true
'' == 0            // true
true == 1          // true
'1' == true        // true
[1] == '1'         // true
[1,2] == '1,2'     // true
({}) == '[object Object]' // true
  • undefinedравныйnull
  • строка, логическое значение,nullПри сравнении получитсяnumber
  • Ссылочные типы преобразуются вstringСравните, если не равны, то конвертируйте вnumberСравнивать

предварительно скомпилировано

Прекомпиляция выполняется перед выполнением кода JavaScript, она выполняет синтаксический анализ и генерацию кода, инициализирует создание и хранение переменных и подготавливает код к выполнению.

Процесс предварительной компиляции:

  1. Создать объект Go/AO (GO — глобальный объект, AO — активный объект)
  2. Назначьте параметры и объявления переменных дляundefined
  3. Унификация фактических параметров и формальных параметров
  4. Поднятие объявления функции (присвоение переменной телу функции)

пример:

function foo(x, y) {
    console.log(x)
    var x = 10
    console.log(x)
    function x(){}
    console.log(x)
}
foo(20, 30)
// 1. 创建AO对象
AO {}
// 2. 寻找形参和变量声明赋值为 undefined
AO {
    x: undefined
    y: undefined
}
// 3. 实参形参相统一
AO {
    x: 20
    y: 30
}
// 4. 函数声明提升
AO {
    x: function x(){}
    y: 30
}

После завершения компиляции код начинает выполняться, первыйxВозьмите значение из AO, выход - это функцияx;xприсваивается значение 10, второмуxвыход 10, функцияxОн был объявлен повышенным и больше не будет здесь назначаться.x,ТретийxВыход 10.

объем

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

объем функции

Функция создает свою собственную область во время выполнения, «скрывая» внутренние переменные и определения функций, а внешняя область не может получить доступ ни к чему внутри функции-оболочки.

область действия блока

Чтобы создать область блока до ES6, вы можете использоватьwithилиtry/catch. Представлено в ES6letключевое слово, чтобы упростить объявления блочной области.letКлючевое слово связывает переменную с любой областью, в которой она находится (обычно внутри {...}).

{
    let num = 10
}
console.log(num) // ReferenceError: num is not defined

область действия параметра

Как только значение параметра по умолчанию установлено, параметр формирует отдельную область, когда функция объявляется инициализированной. Когда инициализация будет завершена, эта область исчезнет. Такое поведение синтаксиса не происходит, если значение параметра по умолчанию не установлено.

let x = 1;

function f(x, y = x) {
  console.log(y);
}

f(2) // 2

параметрyЗначение по умолчанию равно переменнойx. При вызове функции f аргументы образуют отдельную область видимости. В этой области переменная значения по умолчанию x указывает на первый параметрxвместо глобальной переменнойx, поэтому выход равен 2.

let x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x // 1

yПо умолчанию используется анонимная функция, внутри анонимной функцииxуказывает на первый аргумент той же области видимостиx. функцияfooвнутренняя переменнаяxуказывает на первый параметрx, а внутри анонимной функцииxсогласуется.yфункция выполняется с аргументамиxПереназначение, конечный результат равен 2, а внешняя глобальная переменнаяxВсе еще не затронут.

Закрытие

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

Проще говоря, функция ссылается на переменную в своей области видимости, и она сохраняется в других областях видимости для выполнения, область действия ссылочной переменной не исчезает, а следует за функцией. Когда эта функция выполняется, переменная может быть найдена в цепочке областей видимости.

let bar
function foo() {
    let a = 10
    // 函数被保存到了外部
    bar = function () {
        // 引用着不是当前作用域的变量a
        console.log(a)
    }
}
foo()
// bar函数不是在本身所处的作用域执行
bar() // 10

Преимущества: приватные переменные или методы, кеширование

Недостаток: замыкания препятствуют освобождению цепочки областей видимости, что может привести к утечкам памяти.

Сеть прототипов

Объекты в JavaScript имеют специальное встроенное свойствоprototype(прототип), который является ссылкой на другие объекты. При поиске переменной она сначала будет искать свой собственный объект, если не будет найдена, она перейдет к объекту.prototypeискать и т. д., и, наконец, закончить сObject.prototypeдля конечной точки. несколькоprototypeСвязанные вместе называются цепочкой прототипов.

прототипное наследование

Существует много методов прототипного наследования, не все из них здесь упомянуты, зафиксированы только два часто используемых метода.

Режим Святого Грааля

function inherit(Target, Origin){
  function F() {};
  F.prototype = Origin.prototype;
  Target.prototype = new F();
  // 还原 constuctor
  Target.prototype.constuctor = Target;
  // 记录继承自谁
  Target.prototype.uber = Origin.prototype; 
}

Преимущество режима Holy Grail в том, что при использовании изоляции промежуточного объекта, когда дочерний элемент добавляет атрибуты, он будет добавлен к этому объекту и не повлияет на родителя. в то время как свойство поиска соответствует строкам__proto__Найти, вы можете успешно найти свойства родителя для достижения наследования.

использовать:

function Person() {
    this.name = 'people'
}
Person.prototype.sayName = function () { console.log(this.name) }
function Child() {
    this.name = 'child'
}
inherit(Child, Person)
Child.prototype.age = 18
let child = new Child()

ES6 Class

class Person {
    constructor() {
        this.name = 'people'
    }
    sayName() {
        console.log(this.name)
    }
}
class Child extends Person {
    constructor() {
        super()
        this.name = 'child'
    }
}
Child.prototype.age = 18
let child = new Child()

Класс может пройтиextendsНаследование реализации ключевого слова, которое достигается путем изменения цепочки наследования прототипа, чем ES5, для очистки и намного проще.

Основной тип упаковки

let str = 'hello'
str.split('')

Базовый тип логически не имеет свойств и методов, но на практике мы можем вызывать методы из базового типа точно так же, как строка может вызыватьsplitметод.

Чтобы облегчить работу со значениями базового типа, всякий раз, когда считывается значение базового типа, в фоновом режиме будет создаваться объект соответствующего базового типа упаковки, чтобы мы могли вызывать методы для работы с данными. Примерный процесс выглядит следующим образом:

  1. СоздайтеStringэкземпляр типа
  2. вызвать указанный метод в экземпляре
  3. уничтожить этот экземпляр
let str = new String('hello')
str.split('')
str = null

this

thisэто привязка, которая происходит при вызове функции, то, на что она указывает, полностью зависит от того, где была вызвана функция. я понимаюthisявляется вызывающим объектом функции, когда используется внутри функцииthis, который может получить доступ к свойствам и методам вызывающего объекта.

thisЧетыре случая привязки:

  1. новая привязка.newсоздавать экземпляр
  2. Показать привязки.call,apply,bindИзменить указатель вручную
  3. Неявно связан. Вызывается объектом контекста, напримерobj.fn(),thisнаправлениеobj
  4. Привязка по умолчанию. Глобальный объект привязан по умолчанию, а в строгом режиме он будет привязан кundefined

Новая привязка имеет наивысший приоритет, а привязка по умолчанию — последняя.

новый процесс

  1. Создать пустой объект
  2. Установите прототип, объект__proto__конструкторуprototype
  3. в конструктореthisВыполните объект и выполните конструктор, добавляя свойства и методы для пустого объекта
  4. возвращаемый объект экземпляра

Примечание. Появляется в конструктореreturn, если возвращается тип-примитив, процесс построения будет прекращен досрочно и будет возвращен объект-экземпляр, если же возвращен ссылочный тип, будет возвращен ссылочный тип.

// 返回基本类型
function Foo(){
    this.name = 'Joe'
    return 123
    this.age = 20
}
new Foo() // Foo {name: "Joe"}

// 返回引用类型
function Foo(){
    this.name = 'Joe'
    return [123]
    this.age = 20
}
new Foo() // [123]

звоните, обращайтесь, связывайтесь

Все три меняютсяthisуказал на.

callа такжеapplyИзменятьthisУкажите и вызовите функцию. Разница между ними заключается в том, что параметры передаются в разных формах. Параметры первого передаются один за другим, а параметры второго передаются в списке параметров типа массива.

bindИзменятьthisи возвращает ссылку на функцию,bindМножественные вызовы недействительны, они меняютсяthisНаведение будет основываться только на первом вызове.

рукописный вызов

Function.prototype.mycall = function () {
  if(typeof this !== 'function'){
    throw 'caller must be a function'
  }
  let othis = arguments[0] || window
  othis._fn = this
  let arg = [...arguments].slice(1)
  let res = othis._fn(...arg)
  Reflect.deleteProperty(othis, '_fn') //删除_fn属性
  return res
}

applyЧтобы добиться того же, просто измените форму параметра

рукописный переплет

Function.prototype.mybind = function (oThis) {
  if(typeof this != 'function'){
    throw 'caller must be a function'
  }
  let fThis = this
  //Array.prototype.slice.call 将类数组转为数组
  let arg = Array.prototype.slice.call(arguments,1)
  let NOP = function(){}
  let fBound = function(){
    let arg_ = Array.prototype.slice.call(arguments)
    // new 绑定等级高于显式绑定
    // 作为构造函数调用时,保留指向不做修改
    // 使用 instanceof 判断是否为构造函数调用
    return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
  }
  // 维护原型
  if(this.prototype){
    NOP.prototype = this.prototype
    fBound.prototype = new NOP()
  }
  return fBound
}

Знание синтаксиса ES6.

Часто используемые: let, const, оператор расширения, строки шаблона, деструктуризация объекта, стрелочные функции, параметры по умолчанию, Promise

Структура данных: набор, карта, символ

Другие: Прокси, Рефлект

Набор, Карта, WeakSet, WeakMap

Set:

  • Все значения членов уникальны, нет повторяющихся значений, как в массиве
  • можно пройти

WeakSet:

  • элемент должен быть ссылочным типом
  • Элементы являются слабыми ссылками и могут быть удалены сборщиком мусора. После повторного использования внешней ссылки, на которую указывает элемент, этот элемент также может быть повторно использован.
  • не может пройти

Map:

  • Коллекция пар клавишных значений, значение ключа может быть любого типа
  • можно пройти

WeakMap:

  • принимает только ссылочные типы в качестве ключей
  • Имя ключа является слабой ссылкой, а значение ключа может быть любым и может быть удалено сборщиком мусора. После повторного использования внешней ссылки, на которую указывает имя ключа, соответствующее имя ключа также может быть повторно использовано.
  • не может пройти

Отличие стрелочных функций от обычных функций

  1. стрелочная функцияthisУказатель определяется при написании кода, то есть область видимости, где находится сама стрелочная функция, нормальная функция определяется при ее вызовеthis.
  2. Стрелочные функции неarguments
  3. Стрелочные функции неprototypeАтрибуты

Promise

PromiseЭто новое решение для асинхронного программирования в ES6, позволяющее избежать проблемы ада обратных вызовов.PromiseОбъекты представляют асинхронные операции через синхронные процессы через изменения состояния.Пока состояние изменяется, соответствующая функция будет автоматически запущена.

PromiseОбъект имеет три состояния, а именно:

  • pending:Состояние по умолчанию, если не сказатьpromiseУспешна миссия или нет, зависитpendingусловие
  • fulfilled:просто позвониresolveфункционировать, государство становитсяfulfilled, что означает, что операция прошла успешно
  • rejected:просто позвониrejectedфункционировать, государство становитсяrejected, что означает, что операция не удалась

Как только состояние изменяется, оно необратимо и может отслеживаться с помощью функцийPromiseИзменение состояния успешно выполненоthenОбратный вызов функции, не удалось выполнитьcatchобратный вызов функции

мелкая копия

Поверхностная копия — это копия значения, поскольку объект — это копия адреса памяти, ссылка целевого объекта и ссылка исходного объекта указывают на одно и то же пространство памяти. Если один из объектов изменяется, это влияет на другой.

Распространенные методы поверхностного копирования:

  • Array.prototype.slice
let arr = [{a:1}, {b:2}]
let newArr = arr1.slice()
  • спред оператор
let newArr = [...arr1]

глубокая копия

Глубокая копия - это полная копия объекта из памяти. Память не будет разделена между объектом и объектом, но будет создано новое пространство в куче памяти для хранения, поэтому модификация нового объекта не повлияет на оригинальный объект.

Распространенные методы глубокого копирования:

  • JSON.parse(JSON.stringify())
JSON.parse(JSON.stringify(obj))
  • рукописная глубокая копия
function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj !== "object") return obj; 
  const type = Object.prototype.toString.call(obj).slice(8, -1) 
  let strategy = {
    Date: (obj) => new Date(obj),
    RegExp: (obj) => new RegExp(obj),
    Array: clone,
    Object: clone
  }
  function clone(obj){
    // 防止循环引用,导致栈溢出,相同引用的对象直接返回
    if (map.get(obj)) return map.get(obj);
    let target = new obj.constructor();
    map.set(obj, target);
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        target[key] = deepClone(obj[key], map);
      }
    }
    return target;
  }
  return strategy[type] && strategy[type](obj)
}

делегация мероприятия

Делегирование событий, также известное как прокси событий, — это средство оптимизации событий dom. Делегирование событий использует механизм всплытия событий для управления всеми событиями определенного типа путем указания только одного обработчика событий.

Предположим, у вас есть список, в котором каждый дочерний элемент будет иметь событие клика. При наличии большего количества дочерних элементов память, занимаемая привязками событий, будет увеличиваться линейно, и для оптимизации этого сценария можно использовать делегирование событий. Делегированные события обычно привязываются к родительскому элементу без добавления событий к каждому дочернему элементу.

<ul @click="clickHandler">
    <li class="item">1</li>
    <li class="item">2</li>
    <li class="item">3</li>
</ul>
clickHandler(e) {
    // 点击获取的子元素
    let target = e.target
    // 输出子元素内容
    consoel.log(target.textContent)
}

Стабилизатор

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

/**
 * @param {function} func - 执行函数
 * @param {number} wait - 等待时间
 * @param {boolean} immediate - 是否立即执行
 * @return {function}
 */
function debounce(func, wait = 300, immediate = false){
  let timer, ctx;
  let later = (arg) => setTimeout(()=>{
    func.apply(ctx, arg)
    timer = ctx = null
  }, wait)
  return function(...arg){
    if(!timer){
      timer = later(arg)
      ctx = this
      if(immediate){
        func.apply(ctx, arg)
      }
    }else{
      clearTimeout(timer)
      timer = later(arg)
    }
  }
}

дросселирование

Дросселирование используется для уменьшения количества запросов к функциям.

/**
 * @param {function} func - 执行函数
 * @param {number} delay - 延迟时间
 * @return {function}
 */
function throttle(func, delay){
  let timer = null
  return function(...arg){
    if(!timer){
      timer = setTimeout(()=>{
        func.apply(this, arg)
        timer = null
      }, delay)
    }
  }
}

карри

Каррирование — это метод преобразования функции, которая принимает несколько параметров, в функцию, которая принимает один параметр и возвращает новую функцию, которая принимает остальные параметры и возвращает результат.

Общая функция каррирования:

function currying(fn, arr = []) {
  let len = fn.length
  return (...args) => {
    let concatArgs = [...arr, ...args]
    if (concatArgs.length < len) {
      return currying(fn, concatArgs)
    } else {
      return fn.call(this, ...concatArgs)
    }
  }
}

использовать:

let sum = (a,b,c,d) => {
  console.log(a,b,c,d)
}

let newSum = currying(sum)

newSum(1)(2)(3)(4)

преимущество:

  1. Повторное использование параметров, поскольку параметры можно передавать отдельно, мы можем повторно использовать функцию после передачи параметров.
  2. Отложить выполнение, какbindТо же самое может принимать параметры и возвращать ссылку на функцию без вызова

вывоз мусора

Куча делится на новое поколение и старое поколение, а вторичный сборщик мусора и основной сборщик мусора отвечают за сбор мусора соответственно.

Кайнозой

Как правило, только что использованные объекты будут размещены в новом поколении.Его пространство относительно невелико, всего несколько десятков МБ.Новое поколение также будет разделено на два пространства:formпространство иtoпространство.

Сначала объекты распределяются поformпространство, до фазы сборки мусора,formЖивые объекты пространства копируются наtoВ пространстве возвращаются невыжившие объекты, а затем два пространства меняются местами.Этот алгоритм называется «Scanvage».

Новое поколение имеет высокую частоту и скорость высвобождения памяти, но низкое использование пространства, потому что половина пространства памяти «простаивает».

старое поколение

Старое поколение имеет большое пространство, и объекты, пережившие несколько коллекций в новом поколении, будут отправлены в старое поколение.

Старое поколение использует метод «отметить и очистить», чтобы перейти от корневого элемента и пометить уцелевшие объекты. После завершения маркировки немаркированные объекты отправляются на переработку.

Пространство памяти после маркировки и очистки будет генерировать много прерывистого фрагментированного пространства, в результате чего некоторые большие объекты не могут быть сохранены. Поэтому после завершения рециклинга эти прерывистые фрагментарные пространства будут отсортированы.

Шаблоны проектирования JavaScript

одноэлементный шаблон

Определение: гарантирует, что класс имеет только один экземпляр, и предоставляет к нему глобальную точку доступа.

JavaScript как бесклассовый язык, традиционная концепция одноэлементного шаблона не применяется в JavaScript. Небольшое изменение мнения: шаблон singleton гарантирует, что существует только один объект и обеспечивает глобальный доступ.

Распространенным сценарием приложения является компонент всплывающего окна, который использует одноэлементный режим для инкапсуляции глобального метода компонента всплывающего окна:

import Vue from 'vue'
import Index from './index.vue'

let alertInstance = null
let alertConstructor = Vue.extend(Index)

let init = (options)=>{
  alertInstance = new alertConstructor()
  Object.assign(alertInstance, options)
  alertInstance.$mount()
  document.body.appendChild(alertInstance.$el)
}

let caller = (options)=>{
  // 单例判断
  if(!alertInstance){
    init(options)
  }
  return alertInstance.show(()=>alertInstance = null)
}

export default {
  install(vue){
    vue.prototype.$alert = caller
  }
}

Независимо от того, сколько раз он вызывается, экземпляр компонента создается только один раз, и в конце получается один и тот же экземпляр.

режим стратегии

Определение: определите серию алгоритмов, инкапсулируйте их один за другим и сделайте их взаимозаменяемыми.

Режим стратегии состоит в том, чтобы разработать наиболее часто используемые шаблоны дизайна в некоторых сценариях, если есть много, если / else, и функция каждой точки ветви независимости, на этот раз мы можем рассмотреть стратегии для оптимизации режима.

Как и в случае с глубокой копией, написанной выше, шаблон стратегии используется для реализации:

function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj !== "object") return obj; 
  const type = Object.prototype.toString.call(obj).slice(8, -1) 
  // 策略对象
  let strategy = {
    Date: (obj) => new Date(obj),
    RegExp: (obj) => new RegExp(obj),
    Array: clone,
    Object: clone
  }
  function clone(obj){
    // 防止循环引用,导致栈溢出,相同引用的对象直接返回
    if (map.get(obj)) return map.get(obj);
    let target = new obj.constructor();
    map.set(obj, target);
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        target[key] = deepClone(obj[key], map);
      }
    }
    return target;
  }
  return strategy[type] && strategy[type](obj)
}

Такой код будет выглядеть более лаконично, нужно только поддерживать объект стратегии и добавлять стратегию, когда потребуется новый функционал. Поскольку элементы политики представляют собой индивидуально инкапсулированные методы, их также легче использовать повторно.

прокси-режим

Определение: Предоставляет суррогат для объекта, чтобы управлять доступом к нему.

Когда прямой доступ к объекту неудобен или не соответствует потребностям, для управления доступом к объекту предоставляется прокси-объект.Фактическим доступом является прокси-объект.После того, как прокси-объект обрабатывает запрос, он передается в объект онтологии.

Запросить данные с помощью кеширующего прокси:

function getList(page) {
    return this.$api.getList({
        page
    }).then(res => {
        this.list = res.data
        return res
    })
}

// 代理getList
let proxyGetList = (function() {
    let cache = {}
    return async function(page) {
        if (cache[page]) {
            return cache[page]
        }
        let res = await getList.call(this, page)
        return cache[page] = res.data
    }
})()

Вышеупомянутый сценарий является общим требованием разбиения по страницам. Данные одной и той же страницы необходимо получить в фоновом режиме только один раз, а полученные данные кэшируются. Когда та же страница запрашивается в следующий раз, предыдущие данные могут использоваться напрямую.

модель публикации-подписки

Определение: он определяет зависимость между объектами «один ко многим».. Когда состояние объекта изменяется, все объекты, которые зависят от него, будут уведомлены.

Основным преимуществом режима публикации-подписки является устранение связи между объектами.Он широко используется и может использоваться в асинхронном программировании, а также может помочь нам завершить написание слабосвязанного кода. картинаeventBusМетод связи — модель публикации-подписки.

let event = {
    events: [],
    on(key, fn){
        if(!this.events[key]) {
            this.events[key] = []
        }
        this.events[key].push(fn)
    },
    emit(key, ...arg){
        let fns = this.events[key]
        if(!fns || fns.length == 0){
            return false
        }
        fns.forEach(fn => fn.apply(this, arg))
    }
}

Вышеупомянутое является простой реализацией модели публикации-подписки, вы также можете добавитьoff方法来取消监听事件。 существуетVue, обычно создают новый экземплярVueЭкземпляры используются в качестве центра публикации и подписки для обеспечения связи между компонентами. В апплете режим публикации-подписки может быть реализован вручную для решения проблемы страничной связи.

Шаблон декоратора

Определение: динамическое добавление некоторых дополнительных обязанностей к объекту, не затрагивая сам объект.

Шаблон декоратора также является очень распространенным шаблоном проектирования в разработке, он может легко расширять свойства и методы, не затрагивая исходный код. Например, следующий сценарий приложения заключается в отправке формы.

methods: {
    submit(){
        this.$api.submit({
            data: this.form
        })
    },
    // 为提交表单添加验证功能
    validateForm(){
        if(this.form.name == ''){
            return
        }
        this.submit()
    }
}

Представьте, что вы только что взялись за проект иsubmitЛогика очень сложная и может включать в себя много мест. Это рискованно вторжение в исходный код для расширения функции, а рисунок декоратора будет много помогать в это время.

модульный

Здесь записаны только два часто используемых модуля: модули CommonJS и модули ES6.

Модули CommonJS

Node.js использует спецификацию модуля CommonJS, который загружается синхронно при работе на сервере и может быть запущен только после компиляции на клиенте.

Функции

  • Модули могут быть загружены несколько раз. Но при первой загрузке результат будет закэширован, снова загрузите модуль и сразу получите кешированный результат
  • Порядок загрузки модулей, порядок их появления в коде

грамматика

  • Выставить модуль:module.exports = valueилиexports.xxx = value
  • Импортируйте модуль:require('xxx'), если это сторонний модуль, xxx — это имя модуля; если это пользовательский модуль, xxx — это путь к файлу модуля
  • Очистите кеш модуля:delete require.cache[moduleName];, кеш хранится вrequire.cache, вы можете использовать этот атрибут для удаления

механизм загрузки модуля

  • Загрузка модуля фактически загружает модульmodule.exportsАтрибуты
  • exportsуказывает наmodule.exportsцитаты
  • module.exportsНачальное значение - пустой объект,exportsтакже является пустым объектом,module.exportsкогда объект не пустойexportsобъект игнорируется
  • Модули загружают копию значения. После того, как значение будет выведено, изменения внутри модуля не повлияют на значение, за исключением ссылочных типов.

module.exportsне ноль:

// nums.js
exports.a = 1
module.exports = {
    b: 2
}
exports.c = 3
let nums = require('./nums.js') // { b: 2 }

module.exportsПусто:

// nums.js
exports.a = 1
exports.c = 3
let nums = require('./nums.js') // { a: 1, c: 3 }

Воплощение стоимостной копии:

// nums.js
let obj = {
    count: 10
}
let count = 20
function addCount() {
    count++
}
function getCount() {
    return count
}
function addObjCount() {
    obj.count++
}
module.exports = { count, obj, addCount, getCount, addObjCount }
let { count, obj, addCount, getCount, addObjCount } = require('./nums.js')

// 原始类型不受影响
console.log(count) // 20
addCount()
console.log(count) // 20
// 如果想获取到变化的值,可以使用函数返回
console.log(getCount()) // 21

// 引用类型会被改变
console.log(obj) // { count: 10 }
addObjCount()
console.log(obj) // { count: 11 }

Модули ES6

Идея дизайна модулей ES6 состоит в том, чтобы быть как можно более статичными, чтобы зависимости модулей, а также входные и выходные переменные можно было определить во время компиляции.

Функции

  • Загрузка модуля ES6 может использоваться только на верхнем уровне кода из-за статического анализа.
  • Модули не могут загружать одну и ту же переменную несколько раз

грамматика

  • Выставить модуль:exportилиexport default
  • Импортируйте модуль:import

механизм загрузки модуля

  • Модули загружают копию ссылки, и изменения в модуле влияют на значение
// nums.js
export let count = 20
export function addCount() {
  count++
}
export default {
  other: 30
}
// 同时引入 export default 和 export 的变量
import other, { count, addCount } from './async.js'

console.log(other) // { other: 30 }
console.log(count) // 20
addCount()
console.log(count) // 21

Различия между модулями ES6 и модулями CommonJS

  1. Модули CommonJS выводят копию значения, модули ES6 выводят ссылку на значение.
  2. Модули CommonJS загружаются во время выполнения, а модули ES6 представляют собой скомпилированные интерфейсы.

использованная литература

Рекомендуемое чтение