JS 万字总结 重量级干货! ! !

JavaScript
JS 万字总结  重量级干货! ! !

предисловие

Эта статья принадлежитСводка знанийтип, суммировал множество разрозненных точек знаний, которыесухие товарыо~

Если вы новичок, то эта статья как раз для вас, а если вы ветеран, то можете консолидироваться и посмотреть, что осталось!

Предложение: подходитjs на основедрузья смотрят,дольше, рекомендуется сначаласобиратьпросматривать медленно

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

личная техническая документация

содержание

один,Переменные типы

два,

три,Прототипы и цепочки прототипов

Четыре,

Пятерки,наследование и реализация

шесть,Область действия, контекст выполнения и замыкания

Семь,this

Восемь,применить, вызвать, привязать реализацию

9,Синхронный и асинхронный

десять,Модульность AMD, CMD, CommonJS и ES6

11,async и отложить теги скрипта

двенадцать,Измените API самого массива

Тринадцать,местоположение окна, навигатор

четырнадцать,ajax и выборка

15,WebSocket

шестнадцать,Короткий опрос, длинный опрос и WebSocket

семнадцать,Долго подключен к короткому соединению

18,место хранения

девятнадцать,перекрестный домен

20,setTimeout и setInterval

двадцать один,requestAnimationFrame

двадцать два,мероприятие

двадцать три,Суммировать

двадцать четыре,Другие статьи

Во-первых, тип переменной

== и ===

Суждение для ==

  • Не так строго судить, равны ли левый и правый концы.
  • Это будет отдавать приоритет сравнению, соответствует ли тип данных
  • непоследовательныйсделать неявное преобразование, если они согласуются, оценивается размер значения и получается результат.
  • Продолжайте судить, являются ли два типа нулевыми и неопределенными, и если да, возвращайте true
  • Затем оцените, является ли это строкой и числом, если это преобразование строки в число, а затем сравните размер
  • Определите, является ли одна из сторон логическим значением, и если да, преобразуйте ее в число для дальнейшего суждения.
  • Десятилетия, есть ли один объект, другая сторона - это строка, номер, символ, если это так, поверните объект к исходному типу, а затем судить
Сравнение
  1. Ссылка == значение, ссылочный тип будет преобразован в исходный тип, а затем сравнен
  2. значение == значение, напрямую сравните тип, а затем сравните размер значения
  3. Строка == число, затем преобразуйте строку в число и сравните
  4. Другие типы == логическое значение, затем преобразуйте логическое значение в значение для дальнейшего сравнения
  5. undefined == null, также произойдет неявное преобразование, и два могут быть преобразованы друг в друга, то есть два равны и равны сами себе
  6. Object == non-object, если необъект является строкой или числом, возвращается результат ToPrimitive(object) == non-object, если параметр метода ToPrimitive является примитивным типом, он возвращается напрямую ; если это объект, вызывается метод valueOf, если это примитивное значение, то выполняется преобразование примитивного типа и сравнение размеров; если это не примитивное значение, вызывается toString, а если результатом является примитивное значение, выполнить сравнение примитивного типа, и если это не примитивное значение, выдать ошибку
// 以下结果都为true
console.log([5]==5,['5']==5)
console.log({name:'5'}=='[object Object]')
console.log('5'==5,true==1,false==0)
console.log(undefined==null)
console.log([5,6]=='5,6',['5','6']=='5,6')

Vernacular: сначала сравнить типы, тот же тип, соотношение размеров, не примитивный, настроить ToPrimitive, настроить valueOf для объектов, настроить toString, если он не примитивный, и сообщить об ошибке, если он не примитивный, сравнить типы, если они примитивные, и преобразовать, если разные типы , а затем сравните размер.

Так называемое сравнение == должно быть преобразовано в сравнение того же типа, если оно не может быть преобразовано в тот же тип, будет сообщено об ошибке.

Приоритет отдается типу, затем null и undefined, затем строка и число, затем логическое значение и любое, а затем объект и строка, число и символ Размер значения. Это сравнение неявного преобразования ==, которое более извилисто, и будет понятно, если вы приведете картинку!

Ниже приведены шаги по суждению

==与===

думать? Как судить об этом выражении (обратите внимание ==! и !==)[]==![]

  • Основываясь на приоритете операторов, это выражение сначала оценит результат ![]
  • ! имеет приоритет над ==, и [] является истинным значением (преобразуется в логическое значение, результат истинен, это истинное значение, включая {}; преобразованное в ложное является ложным значением), результатом ![] является false, поэтому текущее выражение Формула преобразуется в []==false
  • С помощью отношения преобразования, описанного ранее, любой тип сравнивается с логическим типом, поэтому []==false преобразуется в сравнение []==0.
  • В это время он становится объектом, и сравнивается 0, и вызывается метод valueOf, который преобразует объект в исходный тип, и результат по-прежнему остается valueOf
  • Затем вызовите TOSTRING и результат - «», а затем преобразуйте строку на номер, то [] преобразуется в число число 0
  • Далее выражение преобразуется в 0==0, и результат становится истинным.

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

Ниже приведено преобразование toString и valueOf.

ToPrimitive转换

Суждение для ===

  • === принадлежитстрогое суждение, напрямую определите, одинаковы ли два типа, если они отличаются, верните ложь
  • Если они одинаковы по размеру,не выполняются неявные преобразования
  • Для ссылочных типов сравниваются все ссылочные адреса памяти, поэтому, если === сравнивается таким образом, если два сохраненных адреса памяти не совпадают, они равны, в противном случае false
const a=[]
const b=a
a===b //true
---------------
const a=[]
const b=[]
a===b //false

7 примитивных типов и типов объектов

  1. Boolean
  2. Null
  3. Undefined
  4. Number
  5. BigInt
  6. String
  7. Symbol
  8. Object

Типовое суждение

Суждение примитивного типа
  • примитивный типстрока, число, неопределенное, логическое значение, символ, bigint может бытьпо типу(возвращает форму строки), чтобы напрямую судить о типе, и функция типа объекта также может быть оценена
  • кроме нуляTypeof (для объекта) не может напрямую судить о типе (историческое наследие), включая тип объекта, typeof рассматривает null как тип объекта, поэтому typeof не может судить о типе объекта,Функция Typeof может быть определена
непримитивное суждение о типе (и нуль)

Массив суждений

  • использоватьArray.isArray()Массив суждений
  • использовать[] instanceof ArrayСудя по цепочке прототипов массива, вы можете судить, является ли он массивом.
  • [].constructor === ArrayОпределить, является ли массив массивом по его конструктору
  • Также можно использоватьObject.prototype.toString.call([])Судите, является ли значение «массивом объекта], чтобы судить о массиве

Цель определения

  • Object.prototype.toString.call({})
  • {} instanceof ObjectОн определяет, можно ли определить объект цепи прототипа объекта
  • {}.constructor === Object

Функция суждения

  • использоватьfunc typeof functionОпределите, функция Func является функцией
  • использоватьfunc instanceof FunctionОпределить, является ли func функцией
  • пройти черезfunc.constructor === Functionопределить, является ли это функцией
  • также можно использоватьObject.prototype.toString.call(func)Судите, является ли значение «[object Function]», чтобы судить о функции

Оценка нуля

  • Проще всего пройтиnull===nullЧтобы определить, является ли это NULL
  • (!a && typeof (a) != 'undefined' && a != 0 && a==a)Проверить, является ли a нулевым
  • Object.prototype.__proto__===aОпределить, является ли a прототипом исходного прототипа объекта, то есть нулевым
  • typeof (a) == 'object' && !aОценка того, что null является объектом по typeof, а тип объекта только преобразует null в Boolean, является ложным

Определите, является ли это NaN

  • isNaN(any)Вызовите этот метод напрямую, чтобы определить, является ли оно нечисловым значением.

некоторые другие суждения

  • Object.is(a,b)Оценка того, полностью ли a и b полностью равны, в основном такая же, как ===, разница заключается в суждении Object.is+0不等于-0,NaN等于自身
  • О некоторых других типах объектов можно судить на основе цепочки прототипов и оценки конструктора.
  • prototypeObj.isPrototypeOf(object)

function isWho(x) {
  // null
  if (x === null) return 'null'
  const primitive = ['number', 'string', 'undefined',
    'symbol', 'bigint', 'boolean', 'function'
  ]
  let type = typeof x
  //原始类型以及函数
  if (primitive.includes(type)) return type
  //对象类型
  if (Array.isArray(x)) return 'array'
  if (Object.prototype.toString.call(x) === '[object Object]') return 'object'
  if (x.hasOwnProperty('constructor')) return x.constructor.name
  const proto = Object.getPrototypeOf(x)
  if (proto) return proto.constructor.name
  // 无法判断
  return "can't get this type"
}

Во-вторых, глубокая копия и поверхностная копия

В проекте есть много мест, требующих клонирования данных, особенно объектов ссылочного типа.Мы не можем клонировать с помощью обычных методов присваивания.Хотя мы обычно используем сторонние библиотеки, такие как lodash, для достижения глубокого копирования, нам также необходимо знать некоторые принципы .

мелкая копия

  • Object.assign({},obj)Неглубокий объект копирования
  • obj1={...obj2}Поверхностное копирование obj2 через оператор распространения
  • Object.fromEntries(Object.entries(obj))Создайте объект, создав итератор, а затем создав итератор
  • Object.create({},Object.getOwnPropertyDescriptors(obj))Неглубокая копия объекта
  • Object.defineProperties({},Object.getOwnPropertyDescriptors(obj))Неглубокая копия объекта

Простая реализация поверхностного копирования

// a原拷贝对象,b新对象
for (const key in a) {
  b[key] = a[key]
}
------------------------------------------
for (const key of Object.keys(a)) {
  b[key] = a[key]
}

мелкая копияСкопировать только один слой свойств дляТипы ссылок не могут быть скопированы

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

  • JSON.parse(JSON.stringify(obj))пройти через2 преобразования JSONГлубокое копирование объекта, но копирование невозможноundefinedа такжеsymbolатрибут, не может быть скопированциклическая ссылкаобъект
  • Реализуйте глубокое копирование самостоятельно

простая глубокая копия

//简单版深拷贝,只能拷贝基本原始类型和普通对象与数组,无法拷贝循环引用
function simpleDeepClone(a) {
  const b=Array.isArray(a) ? [] : {}
  for (const key of Object.keys(a)) {
    const type = typeof a[key]
    if (type !== 'object' || a[key] === null) {
      b[key] = a[key]
    } else {
      b[key] = simpleDeepClone(a[key])
    }
  }
  return b
}
//精简版深拷贝只能拷贝基本原始类型和普通对象与数组,可以拷贝循环引用
function deepClone(a, weakMap = new WeakMap()) {
  if (typeof a !== 'object' || a === null) return a
  if (s = weakMap.get(a)) return s
  const b = Array.isArray(a) ? [] : {}
  weakMap.set(a, b)
  for (const key of Object.keys(a)) b[key] = clone(a[key], weakMap)
  return b
}
//js原生深拷贝,无法拷贝Symbol、null、循环引用
function JSdeepClone(data) {
  if (!data || !(data instanceof Object) || (typeof data == "function")) {
    return data || undefined;
  }
  const constructor = data.constructor;
  const result = new constructor();
  for (const key in data) {
    if (data.hasOwnProperty(key)) {
      result[key] = deepClone(data[key]);
    }
  }
  return result;
}

Относительно идеальная глубокая копия

//深拷贝具体版,非完全,但大部分都可以
function deepClonePlus(a, weakMap = new WeakMap()) {
  const type = typeof a
  if (a === null || type !== 'object') return a
  if (s = weakMap.get(a)) return s
  const allKeys = Reflect.ownKeys(a)
  const newObj = Array.isArray(a) ? [] : {}
  weakMap.set(a, newObj)
  for (const key of allKeys) {
    const value = a[key]
    const T = typeof value
    if (value === null || T !== 'object') {
      newObj[key] = value
      continue
    }
    const objT = Object.prototype.toString.call(value)
    if (objT === '[object Object]' || objT === '[object Array]') {
      newObj[key] = deepClonePlus(value, weakMap)
      continue
    }
    if (objT === '[object Set]' || objT === '[object Map]') {
      if (objT === '[object Set]') {
        newObj[key] = new Set()
        value.forEach(v => newObj[key].add(deepClonePlus(v, weakMap)))
      } else {
        newObj[key] = new Map()
        value.forEach((v, i) => newObj[key].set(i, deepClonePlus(v, weakMap)))
      }
      continue
    }
    if (objT === '[object Symbol]') {
      newObj[key] = Object(Symbol.prototype.valueOf.call(value))
      continue
    }
    newObj[key] = new a[key].constructor(value)
  }
  return newObj
}

  • Я использую рекурсивный алгоритм для практики глубокого копирования. Из-за использования рекурсии код выглядит более понятно. В случае не прикасаться к переполнению стека вызовов, рекомендуется использовать рекурсию
  • Глубокая копия, на самом деле, является тем, как проверить ссылочный тип для копирования, есть символ специальных типов, как добиться более полной глубокой копии будет включатьразные детиспособ копирования
  1. примитивный тип, поскольку исходный тип хранится в памяти, значение может быть передано непосредственно через операцию присвоения значения, сначала определите, является ли входящий параметр примитивным типом, включая null, который классифицируется как примитивный тип, чтобы судить, нет необходимо ввести ссылку на объект, а прямое назначение функции не влияет на использование
  2. После фильтрации по примитивным типам оставшиесятип объекта, вынуть ключи всех объектов, поReflect.OwnKeys(obj)Удалить все предметы, сам ключ, в том числе ключ можно удалить Символ
  3. Поскольку объект имеет 2 проявления,множествоа такженормальный объект, чтобы эти два оценивались по отдельности, сначала создайте контейнер копии, который будет newObj
  4. Далее вы можете начать обход всех ключей объекта, полученных на шаге 2 (только ключи, содержащиеся в нем самом), пройти через for..of и вынуть копируемый в данный момент объект a, соответствующий значению текущий ключ обхода, то есть a[key ]
  5. Судя по типу значения [key], возможность этого типа значения включает все типы, поэтому вернитесь к шагу 1, чтобы сначала оценить данные исходного типа; если это исходный тип, вы можете напрямую назначить значение, чтобы пропустить это раунд и перейти к следующему шагу один раунд обхода
  6. После скрининга на предыдущем шаге на данный момент остается только тип объекта.Поскольку тип объекта нельзя напрямую отличить по typeof, можно позаимствовать исходный метод прототипа объекта.Object.prototype.toString.call(obj)КСуждение
  7. Вышеупомянутое не использует рекурсию Из-за вышеуказанного копирования нет необходимости использовать рекурсию для многоуровневых вложенных отношений, которые не задействованы.
  8. Рядом с судьейвложенный типДанные, (этот порядок переменный, но первыми должны ставиться те, что с высокой частотой) Сначала судите об общих объектах и ​​массивах, если да, то кидайте их сразу в рекурсивную обработку, т.к. логика обработки массивов и общих объектов была обработана раньше это, теперь просто повторите вышеуказанные шаги, так что пряморекурсивный вызовНу и рекурсия на последний слой, это должны быть данные исходного типа, он не войдет в бесконечный вызов
  9. Далее стоит судить 2 видаособый типSET и MAP, из-за разных типов методов копирования, далее через ветвь IF, значение сохраняется в обходе, SET использует метод Add для добавления того же значения, что и копируемый объект, это значение Копия также должна использовать глубокую копировать, т.е. значение будет потеряно непосредственно рекурсивной функцией, которая вернет копию копии. Карта аналогична вызову ключа и значения настройки метода SET, но только ключ карты может хранить различные типы.
  10. Когда дело доходит до копирования Symbol, этот тип является относительно особенным.Значение Symbol уникально, поэтому для получения значения Symbol, соответствующего исходному символу, вы должны заимствовать метод прототипа Symbol, чтобы указать, что вы хотите получить исходное значение. символа, соответствующего символу, на основе Исходное значение создает объект-оболочку, значение этого объекта совпадает с исходным
  11. Отфильтрованные здесь оставшиеся объекты в основном представляют собой некоторые встроенные объекты или объекты, которым не нужно рекурсивно перемещаться по свойствам, тогда они могут быть основаны на этих объектах.конструктор прототиповдля создания экземпляра соответствующего объекта
  12. Наконец, после обхода всех свойств новый объект-контейнер после копирования может быть возвращен в качестве замены для скопированного объекта.
  13. На основании анализа круговых эталонных объектов, поскольку круговые эталонные объекты приведут к рекурсии петли и привести к переполнению стека вызова, необходимо учитывать, что объект не может быть скопирован несколько раз. На основании этого условия объект карты можно использовать для сохранения копии соответствующей таблицы, поскольку особенный эффект ключа карты может сохранить объект, поэтому он просто подходит для записи скопированного объекта, и значение является Соответствующий новый контейнер для копирования, когда следующая рекурсия приходит, когда ключ находится в таблице копирования, сначала проверьте, существует ли ключ. Если он существует, это означает, что он был скопирован, и результат предыдущей копии будет возвращен напрямую. В противном случае продолжайте
  14. Поскольку ключи, хранящиеся в Map, принадлежатСтрогий ссылочный тип, а объем данных в глубокой копии не мал. Если эти скопированные таблицы-копии не освободить вовремя, это может привести к накоплению мусора и повлиять на производительность. Поэтому необходимо использоватьметод weakMap вместо Map, ключ, хранящийся в weakMap,Слабый тип ссылки,а такжеВы должны быть ключевым типом объекта, непосредственно перед newObj заключается в том, что тип объекта может быть сохранен. Преимущество использования слабых ссылок может оптимизировать сборку мусора. weakMap хранит таблицу копирования. Эта таблица копирования не действует после завершения копирования. Ранее сохраненный объект копии глубоко скопировано Для нового контейнера копии после уничтожения этих старых объектов объекты, соответствующие таблице копии, также должны быть очищены и не должны сохраняться Это причина использования слабых ссылок для сохранения таблицы.

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

Далее давайте рассмотрим преимущества WeakMap.

let obj = {
  name: {
    age: [{
      who: 'me'
    }]
  }
}
let wm = new WeakMap()
deepClonePlus(obj, wm)
obj=null
console.dir(wm) // No properties 即为空

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

посмотри на эту ситуацию

const obj = {
  name: {
    age: [{
      who: 'me'
    }]
  }
}
let wm = new WeakMap()
console.time('start')
for (let i = 0; i < 1000000; i++) {
  deepClonePlus(obj, wm) // wm为手动传入的weakmap
  // 此处为了与下面对比,这里故意重置weakmap存储的拷贝值
  wm = new WeakMap() 
}
console.timeEnd('start') // 耗时2645ms
------------------------------------------------
let wm = new WeakMap()
let m
console.time('start')
for (let i = 0; i < 1000000; i++) {
  deepClonePlus(obj, wm)
  // 此次为对照组,也执行创建WeakMap但是不重置之前拷贝的wm
  m = new WeakMap()
}
console.timeEnd('start') // 耗时73ms

Из вышеприведенного сравнения видно, что если один и тот же объект копируется несколько раз, лучше всего использовать WeakMap для хранения таблицы копирования, тогда при каждой последующей копии нужно только получить значение из таблицы копирования, потому что это мелкая копия, поэтому времени мало(Примечание. Однако значение, непосредственно взятое из WeakMap, является поверхностной копией, и тот же объект wm используется для копирования поверхностной копии.Если для каждого требуется глубокая копия, WeakMap можно только воссоздавать каждый раз)

3. Прототип и цепочка прототипов

Чтобы облегчить последующее объяснение, знание здесь мы вводим несколько очков:

один,__proto__Атрибуты

объект__proto__Свойства не являются стандартами ECMAScript.Поскольку прототип объекта, то есть внутреннее свойство [[Prototype]] объекта, нельзя получить на ранней стадии, основные производители браузеров реализуют Object.prototype, обращаясь к дескриптору.__proto__Геттер и сеттер используются для доступа к [[Prototype]] вызывающего объекта. Свойство [[Prototype]] принадлежит внутреннему свойству объекта и не может быть доступно напрямую. Это свойство указывает на прототип объекта.

__proto__примерно понял

Object.defineProperty(Object.prototype,'__proto__',{
  get: function(){
    return Object.getPrototypeOf(this)  // 获取引用对象的[[Prototype]]
  },
  set: function(o){
    Object.setPrototypeOf(this,o) // 设置引用对象[[Prototype]]属性关联的原型为o
    return o
  }
})

Таким образом, прототип, связанный с заданным объектом, получается через свойство доступа, которое можно понять с помощью__proto__Возможность получать и устанавливать ссылки на прототипы

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

2. Свойство прототипа функции

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

function Foo(){} // 定义构造函数
console.dir(Foo.prototype) // 定义Foo构造函数时,自动创建的“干净的实例原型”,在原型链第二幅图的左下角有体现

const obj = new Foo() //创建一个实例对象

console.dir(obj.__proto__===Foo.prototype) // true,表名实例关联的原型即为构造函数的prototype指向的原型对象

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

В совокупности относятся к трем видам методов и свойств.

Методы, определенные в конструкторе, вместе называютсястатический метод, свойства, определенные в конструкторе, вместе называютсястатические свойства. Свойства, определенные в прототипе, которые мы вместе называемСвойства прототипаВ методе, определенном в прототипе, мы назвали егоМетод прототипа.实例中的属性以及方法,我们也就称呼为свойства/методы экземпляра.当然方法也属于属性,只是我们通常把定义在对象中的函数称为方法

прототип

  • Только типы объектов имеют концепцию прототипа
  • __proto__Reflect.getPrototypeOf(obj)а такжеObject.getPrototypeOf(obj)Reflect.getPrototypeOf({}) === Object.getPrototypeOf({}) === {}.__proto__
  • Обычные функции имеют 2 свойства, одно из них__proto__(аналогично обычным объектам), а один зависит от функцииprototypeАтрибут, потому что функция имеет двойную идентичность, то есть она может быть экземпляром или конструктором, поэтому связь особенная.
  • Не все объекты будут иметь прототипы, например прототипы объектов.Object.prototypeПрототипObject.prototype.__proto__Указывает на null, словарь объекта-прототипа также имеет значение null (объект__proto__установите значение null или используйтеObject.create(null)Создайте объект словаря без прототипа, но этот объект все еще принадлежит объектному типу), поэтому исходный прототип объекта (Object.prototype) является самым примитивным прототипом, и другие типы объектов должны наследоваться от него.
  • Хотя стрелочные функции относятся к функциям и генерируются функциями, они не имеют свойства прототипа и характеристик конструктора, поэтому так называемого конструктора нет, поэтому их нельзя использовать в качестве конструкторов.

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

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

原型链

В чем можно убедиться из соотношенияconsole.log(Function.prototype.__proto__.constructor.__proto__.constructor === Function) //true

  • Все функции создаются из конструктора функций.
  • Прототип всех экземпляров указывает на прототип конструктора, создавшего его.
  • Собственный метод каждого конструкторастатический метод, методы прототипа доступны для всех экземпляров, которые его наследуют или наследуют косвенно
  • Конструктор также является функцией, и он также создается функцией, поэтому конструктор__proto__Это функция, но прототип, направленный на прототип свойства конструктора, является прототип, указанный в экземпляре, созданный этим конструктором; Короче говоря, прототип конструктора является прототип его экземпляра.

Взгляните на функцию цепочки прототипов

函数原型链

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

В-четвертых, реализуйте класс и расширяйте

реализовать класс

  • Класс добавленный es6 на самом деле для удобства разработчиков создавать классы.Он максимально согласован в написании с другими языками.Однако такого понятия как класс в js нет изначально.Для достижения эффекта class, это может быть реализовано через конструктор js, и класс использует новый ключ Word генерирует экземпляр, и конструктор также создается через new, поэтому можно сделать вывод, что класс также является конструктором
  • Реализовать класс вручную
const Class = (function () {
  function Constructor(name) {
    this.name = name
  }
  //添加原型方法
  Constructor.prototype.getName = function name(name) {
    console.log('原型方法getName:' + this.name);
  }
  //添加原型属性
  Constructor.prototype.age = '原型属性age'
  //添加静态方法
  Constructor.log = function log() {
    console.log('我是构造器的静态方法log');
  }
  //添加静态属性
  Constructor.isWho = '构造器静态属性isWho'
  return Constructor
})()
const i = new Class('我是实例')

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

  • Возвращенный Constructor является конструктором экземпляра, а его прототипом является пустой объект, который вызывается Function
  • Функция, вызываемая после new, должна быть функцией конструктора, используемой для создания экземпляра, this конструктора указывает на экземпляр
  • Конструктор должен реализовать настройку свойств экземпляра в соответствии с параметрами, переданными в
  • Необходимость достижения свойств прототипа и статических свойств определения класса монтирования

Вышеприведенное реализует только определение класса, а затем реализует метод записи, совместимый с наследованием.

  • Унаследованный прототип для удовлетворения потребностей наследования
//父类
const Parent = (function () {
  function Constructor(age) {
    this.age = age
  }
  Constructor.prototype.getName = function () {
    console.log(this.name);
  }
  return Constructor
})()
//子类
const Class = (function (_Parent = null) {
  if (_Parent) {
    Constructor.prototype = Object.create(_Parent.prototype, {
      constructor: {
        value: Constructor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    })
    Constructor.__proto__ = _Parent
  }
  function Constructor(name, age) {
    _Parent ? _Parent.call(this, age) : this
    this.name = name
  }
  Constructor.prototype.getAge = function () {
    console.log(this.age);
  }
  return Constructor
})(Parent)
  • __proto__
  • Определите, есть ли в конструкторе родительский класс. Если да, вызовите конструктор родительского класса и передайте текущий this, чтобы можно было сгенерировать свойства, определенные в конструкторе родительского класса. Это настоящее наследование. Наследование не только наследует прототип, но и свойства, определенные в конструкторе родительского класса.
  • For prototyping methods and static methods it is similar to the definition, pay attention to the definition used in this way if the need to use the function keyword-defined functions, anonymous functions can not be used, otherwise this can not point to call the object сам

V. Наследование и реализация

наследовать

  • Наследование в js обычно делится на три части: наследование атрибута прототипа, наследование статического атрибута и наследование атрибута экземпляра.Методы, определенные в прототипе, обычно определяются на основе использования его экземпляра, то есть метод прототипа должен быть экземпляром .Часто используемые общие методы и методы-конструкторы — это, как правило, методы, которые можно использовать в определенных ситуациях и вызывать по запросу.Методы-прототипы могут использоваться только их экземплярами.
  • Наследование может обогащать цепочку прототипов, настраивать различные цепочки прототипов в соответствии с потребностями, не будет потери памяти, будет сохранен только один прототип, и его можно будет вызывать при использовании, а также это может сэкономить место.

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

原型继承

  • Видно, что прототип — это вообще какие-то общие черты, а экземпляр — уникальная черта.Чем больше наследования, тем оно конкретнее.Вершина цепочки прототипов — самая абстрактная, а нижняя — более конкретная. Таким образом, мы можем быть в нужном месте в соответствии с потребностями Наследование для достижения персонализированных пользовательских атрибутов, унифицированных и разнообразных

Наследование между прототипами

function Parent(){} // 定义父类构造器
function Children(){} // 定义子类构造器

let ChildPrototype = Children.prototype // 构造器原型
let ChildPrototypeProto = Children.prototype.__proto__ // 构造器原型的对象原型

// 方法一
ChildPrototypeProto = Parent.prototype // 父类构造器原型作为子类构造器原型(ChildPrototype)的对象原型(ChildPrototypeProto)

//方法二
ChildPrototype = Object.create(Parent.prototype) // Object.create返回一个对象,其__proto__指向传入的参数,也就实现返回的对象继承参数对象

//方法三
Object.setPrototypeOf(ChildPrototype, Parent.prototype) // 直接设置参数1的原型(__proto__)为参数2

Они достигаются только между наследством прототипа наследования

наследование статического свойства

  • Наследование статических свойств означает, что статические свойства, определенные в родительском конструкторе, могут вызываться непосредственно в дочернем конструкторе. Через прототипы объектов можно наследовать не только экземпляры, но и конструкторы. Упомянутые выше функцииprototypeа также__proto__prototypeдля экземпляров и__proto__Это для моего собственного использования.
  • Все прототипы объектов конструктора по умолчанию указывают на исходный прототип конструктора функций (т. е. Function.prototype). Понятно, что все функции генерируются исходным конструктором функций.
  • через собственный прототип объекта конструктора (__proto__), чтобы реализовать наследование статического свойства
function Parent() {} // 定义父构造函数
function Children() {} //定义子构造函数

// 定义父构造函数的静态方法
Parent.foo = function () {
  console.log(this.name)
}

// 方法一
Children.__proto__ = Parent // 子构造函数的对象原型指向父构造函数,也就实现继承

// 方法二
Object.setPrototypeOf(Children, Parent) // 同原型继承

console.log(Children.foo) // function(){ console.log(this.name) } ,实现继承

Вышеупомянутое предназначено для наследования статических свойств прототипом объекта между конструктором. Примечание. Функция также является объектом.

// 定义父构造函数
function Parent(name) {
  this.name = name
}

//定义子构造函数
function Children(name,age) {
  Parent.call(this,name)  // 这里调用父构造器,实现实例属性继承
  this.age = age
}

const obj = new Children('tom', 5)

console.log(obj) // {name: 'tom', age: 5} ,实现实例属性继承

реализация наследства

  • Наследовать прототипы через ключевое слово extends в es6.
  • Реализация прототипного наследования вручную
// 定义父构造函数,功能:初始化实例name属性
function Parent(name) {
  'use strict'
  this.name = name
}
// 定义父构造函数的静态方法,功能:设置调用对象的name属性
Parent.setName = function setName(obj, name) {
  obj.name = name
}
// 定义父构造器原型(prototype)的方法,功能:获取调用对象的name属性
Parent.prototype.getName = function getName() {
  return this.name
}

/*-----以上已定义父类的原型方法(获取name),父类静态方法(设置name),以及构造器默认初始化的属性name------*/

// 定义子构造函数,功能:初始化实例age属性,以及通过父构造器初始化实例name属性
function Children(name, age) {
  'use strict'
  Parent.call(this, name) // 调用父构造器,初始化name属性
  this.age = age // 子构造器初始化age属性
}
// 定义子构造函数的静态方法,功能:设置调用对象的age属性
Children.setAge = function setAge(obj, age) {
  obj.age = age
}

// 原型继承
// 设置Children.prototype['[[Prototype]]']= Parent.prototype,此处的'[[Prototype]]'与设置__proto__相同
Children.prototype = Object.create(Parent.prototype)
// 注意此处原型继承之后,不带有constructor属性,应该手动指明为Children
Object.defineProperty(Children.prototype, 'constructor', {
  value: Children,
  writable: true, // 可写
  enumerable: false, // 不可枚举
  configurable: true, // 可配置
})
//以上2句可以直接写成一句
/*
Children.prototype = Object.create(Parent.prototype, {
  constructor: {
    value: Children,
    writable: true, // 可写
    enumerable: false, // 不可枚举
    configurable: true, // 可配置
  }
})
*/

// 由于子构造器原型方法必须在继承之后再定义,否则会被继承覆盖
// 定义子构造器原型(prototype)的方法,功能:获取调用对象的age属性
Children.prototype.getAge = function getAge() {
  return this.age
}

// 构造函数(继承静态属性)继承
// 设置Children.__proto__ = Parent,注意此处不能使用Children = Object.create(Parent),因为Object.create返回的是一个对象不能替换构造函数
Object.setPrototypeOf(Children, Parent)

// 测试父级
const obj = new Parent('tom') // 实例化父级实例
console.log(obj.getName()) // tom
Parent.setName(obj, 'jerry') // 通过父级静态方法设置name
console.log(obj.getName()) // jerry
console.log(obj instanceof Parent) // true

// 测试子级
const obj1 = new Children(null, 5) // 实例化子级实例
console.log(obj1.getAge()) // 5
Children.setAge(obj1, 8) // 通过子级静态方法设置age
console.log(obj1.getAge()) // 8
console.log(obj1 instanceof Parent) // true
console.log(obj1 instanceof Children) // true

// 完整测试继承
const test = new Children('tom', 5) // 实例化子级实例,name='tom',age=5
console.log(test.getName()) // tom
Parent.setName(test, 'jerry') // 通过父级静态方法设置name=jerry
console.log(test.getName()) // jerry

console.log(test.getAge()) // 5
Children.setAge(test, 8) // 通过子级静态方法设置age=8
console.log(test.getAge()) // 8

class P {
  constructor(name) {
    this.name = name
  }
  static setName(obj, name) {
    obj.name = name
  }
  getName() {
    return this.name
  }
}
class C extends P {
  constructor(name, age) {
    super(name)
    this.age = age
  }
  static setAge(obj, age) {
    obj.age = age
  }
  getAge() {
    return this.age
  }
}

// 这里就不带测试了,可以自行验证,比对一下有什么区别
console.dir(Children)
console.dir(C)

与class对比

Чтобы реализовать наследование, вам необходимо реализовать наследование для прототипов, конструкторов и свойств экземпляра.

6. Объем, контекст выполнения и замыкания

Объем и цепочка объемов

объем

  • Прямое назначение всех неопределенных переменных автоматически объявит их как переменные в глобальной области видимости (неявные глобальные переменные могут быть удалены с помощью удаления, но не определены с помощью var)
a=1 // 隐式全局变量 严格模式报错
var b=2 // 显式全局变量
console.log(a,b) //1 2
delete a  // 严格模式报错
delete b  // 严格模式报错
console.log(b,a) // 2   a is not defined 
  • Все свойства объекта окна имеют глобальную область видимости.
  • Внутренняя область может получить доступ к внешней области, но не наоборот
  • Объявление переменной Var в дополнение к области действия функции не создает независимую область действия в других операторах блока.
  • Переменные, объявленные с помощью let и const, имеют область действия блока и не поднимают переменную.
  • В одной и той же области действия let и const не могут повторно использоваться для объявления переменных с одним и тем же именем, а var может, и последнее переопределяет первое.
  • Область действия условного оператора цикла for отличается от области действия его тела цикла, а блок условного оператора принадлежит родительской области действия тела цикла.
// 以下语句使用let声明不报错,说明为不同作用域
for (let i = 0; i < 5; i++) {
  let i = 5
}
--------------------------------------------
// 此语句报错,说明循环体为条件语句块的子作用域
// for循环执行顺序为:条件语句块1->条件语句块2->循环体->条件语句块3->条件语句块2 依次类推
for (let i = 0; i < 5; i=x) { // x is not defined
  let x = 5
}

цепочка прицелов

  • Цепочка областей действия также известна как область поиска переменных.
  • Когда на переменную ссылаются в текущей области, если такой переменной нет, она будет проходить весь путь до родительской области, чтобы найти переменную до глобальной области.Если нет, она будет автоматически объявлена ​​в нестрогой случаях, поэтому он не определен. В строгих условиях следующее сообщит об ошибке
b = 1
function a() {
  // 定义b,找到
  const b = 2
  function s() {
    // 使用到b,当前作用域并没有,向上找
    console.log(b);
  }
  return s
}
const s = a()
var b = 3
s() // 2

контекст выполнения

  • Несколько контекстов выполнения хранятся в стеке вызовов, и они создаются и уничтожаются в соответствии с правилом «последним пришел – первым обслужен». глобальный контекст, который сначала помещается в стек, а это в контексте указывает на окно, неопределенное в строгом режиме
  • При создании контекста выполнения текущий this будет привязан, лексическое окружение будет определено, а содержимое объявления функции в текущем окружении будет сохранено.Переменная let привязана к const, но не связана ни с каким значением. Когда переменная среды подтверждена, начальное значение связанной переменной не определено.
  • Перед объявлением var при вызове переменной, объявленной var, значение не определено, потому что создается контекст выполнения, а переменная, объявленная var, была привязана к исходному undefined.В контексте выполнения, но значение не инициализируется, поэтому вызов перед объявлением вызовет справочную ошибку (т.е. временную мертвую зону TDZ), что является продвижением объявлений функций и объявлений var в контексте выполнения.

Узнайте о функциях и продвижении переменных здесь

console.dir(foo) // foo(){}
function foo() {}
var foo = 5
/*
console.dir(foo) // undefined
var foo = 5
*/
------------------------------
var foo = 5
function foo() {}
console.dir(foo) // 5

Вывод можно сделать из вышеприведенного кода:

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

Закрытие

  • Так называемое замыкание представляет собой ссылку на функцию и ее лексическое окружение (создавать любые локальные переменные во время текущей роли). Замыкания позволяют внутренним функциям получать доступ к области действия внешних функций, а замыкания генерируются при создании функции.
function fn1() {
  var name = 'hi';
  function fn2() {
    console.log(name);
  }
  return fn2
}
fn1()() // hi
  • Когда вы возвращаете внутреннюю функцию изнутри функции, возвращаемая функция сохранит текущее замыкание, которое является текущим лексическим окружением.
  • Замыкания сохраняют только последнее значение любой переменной в среде, потому что замыкание содержит весь объект переменной.
  • Цепочка областей видимости замыкания содержит собственную область видимости, а также область действия родительской функции и глобальную область видимости.
  • При возврате замыкания держите все объекты, на которые есть внешние ссылки, под этим замыканием.
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2));  // 7
console.log(add10(2)); // 12
  • Способ выставления закрытия не только для возврата внутренних функций, но и для использования функций обратного вызова для генерации замыкающих сред или назначать внутренние функции для других внешних объектов для использования
  • Хотя замыкания удобны для манипулирования и сохранения внутренней среды, замыкания негативно влияют на производительность скрипта с точки зрения скорости обработки и потребления памяти, если только они не используются в определенных обстоятельствах.

Интересно посмотреть здесь

function foo(){
  let a={name:'me'}
  let b={who:'isMe'}
  let wm=new WeakMap()
  function bar(){
    console.log(a)  // a被闭包保留
    wm.set(b,1) // 弱引用b对象
    return wm //wm被闭包保留
  }
  return bar
}
const wm=foo()()
console.dir(wm) // No properties 即为空
-------------------------------------------
function foo(){
  let a={name:'me'}
  let wm=new WeakMap()
  function bar(){
    console.log(a)
    wm.set(a,1)
    return wm
  }
  return bar
}
const wm=foo()()
console.dir(wm) // 保存了对象a与其值1
  • Как видно из приведенного выше кода, bar возвращается во внешнюю среду, поэтому внутри образует замыкание, а используемые в bar переменные (a, wm) сохраняются, но когда wm наконец печатается, он оказывается пустым? Это связано с тем, что объект b не ссылается извне, но значение b сохраняется слабой ссылкой wm, Это видно из того факта, что wm пуст, b внутри замыкания очищается, поэтому wm также автоматически очищается слабая ссылка b, которая может быть продемонстрирована ранее. Тем не менее, замыкания сохраняют только те переменные, которые используются извне.
  • Непосредственно из следующего блока кода видно, что a — это a в замыкании, а bar необходимо использовать a и wm при внешнем выполнении, поэтому он сохраняется.
  • Некоторые люди могут не понять, почему B в блочном коде также ссылается на Wm.Set(B, 1), но в конечном итоге это не так, то есть из-за слабых ссылок на B в Weakmap это можно понять как WM B полагаясь на B в исходной функции.Когда WM возвращается, B, он не зависит ни от чего извне, но кто-то полагается на него. Можно так понять, что Б гуляет, потому что Б не выводят наружу, поэтому Б эта цепочка размыкается, и это влияет и на людей, которых держат.

Семь, это

первый взгляд на картинку

函数this

  • Привязка this определяется при создании контекста выполнения.
  • В большинстве случаев способ вызова функции определяет значение this, и this не может быть присвоено значение во время выполнения.
  • Значением this является исполняемый в данный момент объект среды, который всегда указывает на объект при нестрогих условиях и может быть любым значением при строгих условиях.
  • В глобальном окружении this всегда указывает на окно.В строгом режиме, если вызов функции не вызывает объект явно, this внутри функции указывает на undefined, а в нестрогом режиме указывает на окно.
  • this стрелочной функции всегда указывает на this при создании текущего лексического окружения.
  • При использовании в качестве конструктора this в функции указывает на объект экземпляра.
  • На привязку this ссылается только член, ближайший к вызывающему его
  • После выполнения будет создан контекст выполнения, он будет связывать это при создании контекста выполнения, поэтому эта точка всегда определяется во время выполнения
function foo(){
  console.dir(this) // window ,严格下undefined
}
foo()
-----------------------------------------------
function foo(){
  console.dir(this) //非严格Number对象,严格模式 5
}
foo.call(5)

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

перед казнью

this演示1

после казни

this演示2

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

это в нормальной функции

позвонить напрямую

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

// 直接调用函数
function foo() {
  console.dir(this) //window,严格下 undefined
  function boo(){
    console.dir(this) //window,严格下 undefined
  }
  boo()
}
----------------------------------------------
// 取出对象中的函数,再进行调用
const obj = {
  foo: function foo() {
    console.dir(this) //window,严格下 undefined
    function boo() {
      console.dir(this) //window,严格下 undefined
    }
    return boo
  }
}
const foo = obj.foo
foo()()
----------------------------------------------
// 直接通过对象调用函数,再调用返回的函数,可以看出this的指向随调用对象改变
const obj = {
  foo: function foo() {
    console.dir(this) //obj,严格下 obj
    function boo() {
      console.dir(this) //window,严格下 undefined
    }
    return boo
  }
}
const foo = obj.foo()
foo()
----------------------------------------------
// 基于回调函数也是如此
function foo(func) {
  console.dir(this) // window ,严格下 undefined
  func()
}
foo(function () {
  console.dir(this) // window ,严格下 undefined
})

В зависимости от вызывающего абонента и различных способов вызова

Вызов функции также добавляется за именем функции (), обозначая вызов, если функция ни к чему не добавляется, то по умолчаниюпростой вызов, в строгих и нестрогих средах this внутри функции, которая просто вызывается, указывает на undefined и window, но this в глобальной среде всегда является окном

основанный на объекте

когда функцияметод объектаКогда вызывается, он не зависит от того, как или где определена функция

// 函数this指向调用者对象
const obj = {
  foo: function () {
    console.dir(this) // obj1,严格下 obj1
    function boo() {
      console.dir(this) // window,严格下 undefined
    }
    boo()
    return boo
  }
}
const obj1 = {}
obj1.boo = obj.foo
obj1.boo()
----------------------------------------------
// 不同调用对象时,this指向调用者
const obj = {
  foo: function () {
    console.dir(this) // obj,严格下 obj
    function boo() {
      console.dir(this)
    }
    boo() // window,严格下 undefined
    return boo
  }
}
const obj1 = {}
obj1.boo = obj.foo()
obj1.boo() // obj1,严格下 obj1
----------------------------------------------
// this指向最近的调用者
const obj = {
  name: 'obj',
  obj1: {
    name: 'obj1',
    foo: function () {
      console.dir(this.name) // obj1
    }
  }
}
obj.obj1.foo()

на основе нового ключевого слова

// 基于new关键字调用的函数内部this指向实例
function foo() {
  console.dir(this) // foo实例
  console.log(this instanceof foo) //true
  console.log(foo.prototype.isPrototypeOf(this)) //true
  that = this
}
var that
const f = new foo()
console.log(that === f) // true
----------------------------------------------
// 嵌套函数内部this与调用函数所在环境的this无关
function foo() {
  console.dir(this) // foo实例
  function boo() {
    console.dir(this) //window,严格下undefined
  }
  boo()
}
const f = new foo()

На основе таймеров и микрозадач

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

// 异步任务中简单调用的函数都是进入队列,最后由全局环境调用
const id = setInterval(function () {
  console.dir(this) // window ,严格下 window
  setTimeout(() => {
    console.dir(this) // window ,严格下 window
    clearInterval(id)
  });
})
----------------------------------------------
new Promise(function (resolve, reject) {
  console.dir(this) // window ,严格下 undefined
  resolve()
}).then(function (res) {
  console.dir(this) // window ,严格下 undefined
});
----------------------------------------------
(async function foo() {
  function boo() {
    console.dir(this) // window ,严格下 undefined
  }
  await boo()
  console.dir(this) // window ,严格下 undefined
})()
----------------------------------------------
// 定时器的回调最终都会被作为简单函数被执行,定时器属于window对象的方法
function foo(){
  setTimeout(function (){
    console.log(this) //window ,严格下window
  })
}
foo.call(5)
----------------------------------------------
// 函数内部的this就是指向调用者,并且可以看出简单调用的回调函数中的this也指向window
const obj = {
  foo(callback) {
    callback()
    console.log(this.foo === obj.foo) // true
    console.log(this === obj) // true
  }
}
obj.foo(function () {
  console.log(this) //window ,严格下undefined
})
----------------------------------------------
// 通过arguments调用的回调函数中的this指向调用者,注意严格与非严格下的arguments对象有所不同
const obj = {
  foo(callback) {
    arguments[0]()
    console.log(this.foo === obj.foo) // true
    console.log(this === obj) // true
  }
}
obj.foo(function () {
  console.log(this) //arguments对象 ,严格下 arguments对象
})

это в стрелочных функциях

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

  • Чтобы судить об этой точке стрелочной функции, мы можем рассматривать стрелочную функцию как прозрачную, а это в ее контексте — это ее это.
// 可以看出箭头函数中的this就是其所在环境的this,箭头函数无法固定this,由其环境决定
const foo = () => {
  console.dir(this) //window ,严格下还是window
}
foo()
----------------------------------------------
// 可见对象中的this指向window,箭头函数中的this指向对象中的this。由于只有创建执行上下文才会绑定this指向,而除了全局上下文,只有函数作用域才会创建上下文环境从而绑定this,创建对象不会绑定this,所以还是全局this
const obj={
  this:this,
  foo:()=>{
    console.dir(this) //window ,严格下 window
  }
}
console.dir(obj.this) //window ,严格下 window
obj.foo()
---------------------------------------------
// 对象方法内部嵌套箭头函数,则此箭头函数的this属于外部非箭头函数this。当调用obj.foo时foo函数创建的执行上下文中的this绑定对象obj,而箭头函数并不会绑定this,所以其this属于foo下的this,即对象obj
const obj = {
  foo: function () {
    return () => {
      console.dir(this) //obj ,严格下 obj
    }
  }
}
obj.foo()()

Как изменить указатель this функции

Самый простой способ связать это с функцией — использовать apply, call, bind

  • Первый параметр в методе применения — это указатель this в вызываемой функции, и вы можете передать это значение, которое хотите связать.Второй параметр — это набор параметров вызываемой функции, обычно массив
  • Методы вызова и применения в основном одинаковы, разница в том, что входящие параметры имеют разную форму, параметры, передаваемые вызовом, представляют собой списки переменных параметров, а параметры передаются один за другим.
  • По этой переменной сохраняется указанное фиксированное для достижения этого
// 通过变量保留父级this,进行对_this变量修改也就达到修改原this的效果
const obj = {
  name: 'obj',
  foo: function () {
    let _this = this
    function boo() {
      _this.name = 'OBJ'
      console.dir(obj.name) // OBJ
    }
    return boo
  }
}
obj.foo()()

Реализовать применение вручную

  • Идея состоит в том, чтобы найти способ вызвать функцию переданным thisArg, тогда this функции указывает на вызывающую программу.
Function.prototype.Apply = function (thisArg, args = Symbol.for('args')) {
  console.dir(this)            //this为这个方法的调用者=>foo函数
  const fn = Symbol('fn')      //生成一个不重复的键
  thisArg[fn] = this || window //把foo函数作为传入this的一个方法
  args === Symbol.for('args') 
  ? thisArg[fn]()
  : thisArg[fn](...args)       //调用这方法,传参
  delete thisArg[fn]           //使用完删除
}
var name = 'foo'
var age = 5
function foo(age,height) {
  console.log(this.name) // obj
  console.log(age)       // 3
  console.log(height)    // null
}
const obj = {
  name: 'obj',
  age: 3
}
foo.Apply(obj,[obj.age,null])

Выполнено вызовом вручную

Основная идея с применением, это массовое участие в виде изменения, вот список параметров по аргументам

Function.prototype.Call = function (thisArg) {
  console.dir(this)            //this为这个方法的调用者=>foo函数
  const fn = Symbol('fn')      //生成一个不重复的键
  thisArg[fn] = this || window //把foo函数作为传入this的一个方法
  const args = Array.from(arguments).slice(1)
  args.length ? thisArg[fn](...args) : thisArg[fn]()  //调用这方法,传参
  delete thisArg[fn]           //使用完删除
}

Вручную реализовать привязку

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

Function.prototype.Bind = function (thisArg) {
  const fn = Symbol('fn')       //生成一个不重复的键
  thisArg[fn] = this || window  //把foo函数作为传入this的一个方法
  const f = thisArg[fn]         // 负责一份函数
  delete thisArg[fn]            //删除原来对象上的函数,但是保留了this指向
  const args = Array.from(arguments).slice(1)
  return function () {
    const arg = args.concat(...arguments)
    f(...arg)
  }
}
var name = 'foo'
var age = 5
var height = 4
function foo(age, height) {
  console.log(this.name)       // obj
  console.log(age)             // 3
  console.log(height)          // 2
}
const obj = {
  name: 'obj',
  age: 3
}
foo.Bind(obj, obj.age)(2)

9. Синхронный и асинхронный

Синхронизировать

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

асинхронный

  • Асинхронный означает, что текущий исполняемый код будет обработан асинхронным потоком до того, как обратный вызов будет обработан основным потоком.
  • Асинхронные результаты доступны не сразу, а в какой-то момент в будущем
  • Обычно асинхронный код имеет больше шагов, чем синхронный код, потому что асинхронный код не выполняется напрямую в стеке вызовов, а отправляется (может быть не нужен) другим потокам для обработки, а обратный вызов после обработки завершается и сохраняется где-то (например, в очередь задач), дождитесь завершения выполнения синхронной очереди, прежде чем извлекать код асинхронного обратного вызова для выполнения.

Асинхронный, однопоточный и EventLoop

Сначала посмотрите на картинку, есть общая структура

事件循环

  • Основной поток js обрабатывает исполняемый в данный момент код, он выполняет контекст выполнения в верхней части текущего стека вызовов, извлекает данные из пространства кучи (обычно хранит объекты) и пространства стека (обычно хранит необъектные значения и ссылки на объекты ), а затем обрабатывает текущие данные, используемые стеком вызовов.
  • Весь синхронный код будет помещен в стек вызовов в порядке ожидания выполнения основного потока.Если в коде встречается асинхронный код, он будет передан в асинхронный поток для выполнения в соответствии с асинхронным типом.
  • Асинхронный тип, в основном разделенный на микрозадачи и макрозадачи
  • Очередь задач фактически представляет собой часть пространства памяти.Задачи в ней выполняются по правилу FIFO first-in-first-out.Все callback-функции после завершения выполнения асинхронного кода добавляются в очередь асинхронных задач для дождитесь вызова основного потока.
  • Асинхронность может повысить загрузку ЦП
микрозадачи
  • Разница между очередью микрозадач и очередью макрозадач заключается в том, что основной поток имеет различие в планировании задач.
  • Микрозадачи могут быть вызваны этими ключевыми словами метода для генерации Promise, async, await, MutaionObserver, process.nextTick (среда Node.js).
задача макроса
  • В общей очереди задач макроса хранится обратный вызов WebApis.WebApis содержит много потоков, поток рендеринга графического интерфейса (взаимоисключающий с основным потоком js и не может выполняться одновременно), поток триггера события, поток таймера, поток асинхронного сетевого запроса
  • В макрозадачах хранятся функции обратного вызова, созданные асинхронным WebApis, но они имеют более низкий приоритет, чем микрозадачи.
js один поток
  • Однопоточный дизайн js изначально был разработан для упрощения кода и разрешения конфликтов DOM.Если js является многопоточным языком, возможно, что несколько потоков обрабатывают DOM одновременно, что вызовет конфликты между операциями js на одном и том же DOM Механизм блокировки потока для разрешения конфликтов, но увеличивает сложность кода js.
  • Основываясь на однопоточной конструкции js, вводится асинхронный метод выполнения, так что js имеет эффект многопоточного процесса, но независимо от того, является ли он асинхронным или синхронным, у js всегда будет выполняться только один поток.
EventLoop
  • Механизм цикла событий — это метод планирования для основного потока.
  • Можно понять, что процесс основного потока, ищущий выполнение задачи, — это цикл обработки событий, а его метод поиска — вызывающий механизм.
  • Сначала посмотрите, как браузер выполняется код JS
    • Обычно точка входа для браузера для запуска кода JS в самом начале - код, покрытый тегом сценария в HTML
    • Когда поток рендеринга GUI разрешается в тег script, код js, охватываемый тегом, будет добавлен в очередь задач макроса.
    • Во-первых, движок js (такой как движок V8) сначала берет первую задачу макроса, то есть блок кода скрипта, а затем основной поток разбирает код js в стеке вызовов
    • После того, как весь код будет разобран, запустите код js.
    • Если вы столкнулись с синхронным кодом, выполните его напрямую
    • Когда выполнение всего кода синхронизации будет завершено, основной поток будет обнаружен в очереди задач, если часть микрозадачи полностью асинхронная задача выполняется микро
    • Далее выполните процесс рендеринга браузера, чтобы отрисовать страницу, затем запустите следующий раунд цикла событий и вернитесь к задаче макроса.
    • Обратите внимание, что все микрозадачи генерируются кодом, выполняемым в макрозадаче, и только очередь макрозадач имеет задачи в начале.

Ниже показан общий поток цикла обработки событий.

事件循环

Ниже приведена основная логика оценки потока.

事件判断

Интерфейсные асинхронные сценарии

  • Фронтальная асинхронность в основном используется, когда код может ожидать, а ожидающий процесс не может блокировать выполнение основного потока.
  • Обычно интерфейс WebApis вызывается асинхронно, так как он должен обрабатываться другими потоками, ему нужно дождаться своего возвращаемого результата, поэтому основной поток js не должен ждать все время, поэтому его нужно обрабатывать асинхронно.
  • Например, задачи таймера setTimeout, setInterval, ajax-запросы, динамическая загрузка изображений и срабатывание событий DOM — все это асинхронные задачи, выполняемые браузером; например, Promise, async и await в js относятся к асинхронным операциям самого языка js. , который может быть асинхронным.
  • Когда вам нужно динамически загружать изображения, вам нужно использовать асинхронный; когда синхронный код js для выполнения должен занять много времени, чтобы занять основной поток, вы можете использовать асинхронный режим для разделения на несколько шагов для выполнения, что может избежать длительного бездействия страницы браузера. Ответить или заморозить
  • Когда код должен выполняться в течение длительного времени, чтобы получить результат, вы также можете использовать веб-воркер в html5, чтобы открыть новый поток в процессе рендеринга браузера, чтобы конкретно выполнить код, и вернуть текущий результат через postMessage, чтобы он не будет занимать js.основной поток, но этот поток не может манипулировать DOM и BOM

Многопоточность WebWorker

  • На основе однопоточности JS, если выполняется очень трудоемкая функция, основной поток будет занят в течение длительного времени, что приведет к приостановке цикла событий, так что браузер не сможет отобразить и ответить вовремя, тогда страница приведет к сбою страницы, пользователям. Опыт был отклонен, поэтому Webworker поддерживает HTML5.
  • WebWork вкратце понимает, что конкретный JS-код может выполняться в других потоках, а результат поступает в основной поток после окончания выполнения.
  • Например, алгоритм для распознавания изображений необходимо реализовать в JS, и этот алгоритм занимает много времени для расчета. Если основной нить JS используется для выполнения вышеупомянутых вещей, его можно реализовать с помощью использования технологии WebWork.
  • Самая большая разница между потоком веб-воркера и основным потоком js заключается в том, что поток веб-воркера не может работать с объектами окна и документа.
// test.html(主线程)
const w= new Worker('postMessage.js')
w.onmessage=function(e){
  console.log(e.data);
}
w.postMessage('b') // b is cat
w.terminate() // 手动关闭子线程
----------------------------------------------
// postMessage.js(worker线程)
this.addEventListener('message', (e) => {
  switch (e.data) {
    case 'a': this.postMessage(e.data+' is tom')
      break;
    case 'b': this.postMessage(e.data + ' is cat')
      break;
    default:  this.postMessage(e.data + " i don't know")
    this.close() // 自身关闭
      break;
  }
})

10. Модуляризация AMD, CMD, CommonJS и ES6

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

Определение асинхронного модуля AMD

  • Полное название AMD — определение асинхронного модуля.
  • AMD не поддерживает встроенную поддержку js, это продукт модульной разработки RequireJS.AMD использует библиотеку функций RequireJS для упаковки и генерации кода js с соответствующими эффектами.
  • RequireJS в основном используется для разрешения зависимостей между несколькими файлами js, браузеры загружают много кода js, что приводит к зависанию, и асинхронно загружают модули.
  • Требуется пропуск JSdefine(id?,dependencies?,factory)Определить модуль, идентификатор необязателен, это идентификатор определенного модуля, по умолчанию имя файла модуля не включает суффикс, зависимости необязательны, представляет собой массив путей к модулям, от которых зависит текущий модуль, фабрика - это фабрика метод, функция или объект для инициализации модуля, если это функция, будет выполняться только один раз, если это объект, это будет вывод модуля
  • пройти черезrequire(dependencies,factory)Импорт модуля, где зависимости — это массив путей к модулям, которые нужно импортировать, factory — это функция обратного вызова после импорта модуля, а список параметров этой функции — это соответствующий импортируемый модуль.
  • Настройте путь и справочное имя каждого модуля через require.config (объект конфигурации).
require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",  //实际路径为js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
})

Определение общего модуля CMD

  • CMD расшифровывается как Common Module Definition. Общее определение модуля.
  • Как и AMD, CMD также имеет библиотеку функций SeaJS и аналогичные функции RequireJS.
  • CMD учитывает один файл и один модуль, учитывает близлежащие зависимости и определяет модули.define(id?,deps?,factory)Идентификатор вообще не пишется в зависимости от АМД, но в Фабрике модуль вводится когда нужно, а Фабричная функция получает 3 параметра, параметр один Требовать метод, при вызове внутреннего модуля внедрения параметр 2 Экспорт является объектом для предоставлять интерфейсы модулей наружу, а модуль 3 параметра также является объектом, в котором хранятся некоторые атрибуты и методы, связанные с текущим модулем.
  • пройти черезseajs.use(deps,func)Загрузить модуль, deps — это массив путей, введенный в модуль, а func — функция обратного вызова после завершения загрузки.

Основное различие между AMD и CMD заключается в том, что

CommonJS

  • пройти черезmodule.exports
  • Когда модуль имеет и экспорт, и module.exports, последний переопределяет первый.
  • в спецификации__dirnameПредставляет путь к папке, в которой находится текущий файл модуля,__filenameПредставляет текущий путь к папке модуля + имя файла
  • Модули загрузки Commonjs, синхронизирующие, выход модуля является объектом копирования, поэтому модификацию исходного модуля не оказывает влияния на введенный модуль, и модуль загружен, когда код работает

Модульный ES6

  • Экспорт и импорт, введенные es6, используются для устранения дефекта, заключающегося в том, что в самой js нет функции модуля.
  • Экспортируйте интерфейс модуля с помощью экспорта или экспорта по умолчанию и импортируйте модуль с помощью импорта xxx из «пути».
  • Для интерфейса, экспортируемого экспортом, вы можете использовать import {interface} from 'path' для импорта по мере необходимости путем деструктурирования
  • Для экспорта по умолчанию для экспорта по умолчанию вы можете использовать импорт xxx из «пути», чтобы импортировать экспортированный интерфейс по умолчанию, xxx может быть настраиваемым именем, а модуль может иметь только один экспорт по умолчанию, и может быть несколько экспортов.
  • Вы также можете задать имена экспортируемых и импортированных интерфейсов с помощью псевдонимов, например, экспортировать {a как foo}, экспортировать foo как псевдоним a, импортировать foo как b из пути и импортировать b как псевдоним foo.
  • Модули ES6 загружаются при компилятере компилятора кода выходных интерфейсов. Так что динамическая ссылка

Одиннадцать, async и отложить теги сценария

  • Если тег script устанавливает это значение, это означает, что импортированные js должны быть загружены и выполнены асинхронно.Обратите внимание, что этот атрибут применим только к внешне импортированным js
  • Асинхронный скрипт Загрузка и выполнение в случае, если есть async, и не блокирует нагрузки страницы, но не гарантирует заказа, в котором для загрузки, если несколько приоритета выполнения Async, первая хорошая нагрузка JS-файлов, поэтому используйте этот метод для загрузки JS Файл содержит лучше не полагаться на других

Используйте атрибут отсрочки

  • Если это свойство используется, js также будет загружаться и выполняться асинхронно, и он будет выполняться после парсинга документа, так что загрузка страницы не будет заблокирована, а будет выполняться в исходном порядке выполнения, и он может также выполняться для зависимостей.
  • defer определен в html4.0, а async определен в html5.0.

Различные ситуации

  • Если есть только асинхронность, то скрипт выполняется асинхронно после завершения загрузки.
  • Если есть только defer, то скрипт будет выполняться после парсинга страницы.
  • Если таковых нет, то скрипт будет выполняться сразу на странице, останавливая парсинг документа и блокируя загрузку страницы
  • Если и то, и другое совпадает с асинхронным, конечно, эта ситуация обычно используется для совместимости html-версии, если нет асинхронного, то вступит в силу отложенный
  • Тем не менее, рекомендуется помещать тег script непосредственно в нижнюю часть тела.

12. Меняем апи самого массива

  1. pop()Вытолкнуть элемент в конце
  2. push()Вставить элемент в конце
  3. shift()Заголовок всплывает элемент
  4. unshift()Вставить элемент в заголовок
  5. sort([func])Отсортируйте массив, func имеет 2 параметра, а возвращаемое значение меньше 0, тогда параметр 1 располагается перед параметром 2, иначе параметр 2 располагается перед параметром 1
  6. reverse()Поменять местами элементы в массиве
  7. splice(pos,deleteCount,...item)Вернуть измененный массив, удалить элементы deleteCount из pos и вставить элементы в текущую позицию.
  8. copyWithin(pos[, start[, end]])Скопируйте элемент от начала до конца (исключая конец), к индексу, запущенному POS, вернитесь к входящему массиву, неглубокому копию
  9. arr.fill(value[, start[, end]])От начала до конца по умолчанию используется последняя позиция массива, исключая конец, заполняет val и возвращает заполненный массив.

Другие API-интерфейсы массива не изменяют исходный массив

Тринадцать, расположение окна, навигатор

объект местоположения

  • window.location===document.locationhrefа такжеhashЭто имеет смысл только в том, что href переместится на URL-адрес, хэш перейдет к тегу имени привязки (если есть) на текущей странице, и страница не будет перезагружена.
// 这行代码将会使当前页面重定向到http://www.baidu.com
window.location.href = 'http://www.baidu.com'
----------------------------------------------
// 如果使用hash并且配合input输入框,那么当页面刷新之后,鼠标将会自动聚焦到对应id的input输入框,
<input type="text" id="target">
<script>
  window.location.hash = '#target'
</script>

Посмотрите на свойства, которым он владеет

location属性

добавить один здесьoriginАтрибуты,返回URL协议+服务器名称+端口号 (location.origin == location.protocol + '//' + location.host)

  • Вы можете использовать вышеуказанные свойства, чтобы получить указанную часть URL-адреса, или изменить href в хэше, чтобы добиться перемещения и прыжка.
  • Добавьте прослушиватель изменения хэша, чтобы контролировать код, выполняемый при изменении хэша.
window.addEventListener("hashchange", funcRef);
// 或者
window.onhashchange = funcRef;

метод определения местоположения

location方法

  • assign(url), позвонивwindow.location.assignСпособ открыть новую страницу на указанном URLwindow.location.assign('http://www.baidu.com')Baidu на текущей странице открыт, можно откатить обратно
  • replace(url),Открыть указанный URL на текущей странице, без отката
  • reload([Boolean]),Вызов этого метода перезагрузит текущую страницу.Если параметр имеет значение false или не заполнен, страница будет перезагружена оптимальным образом, возможно, с получением ресурсов из кеша.Если параметр имеет значение true, ресурс будет повторно запрошен с сервера

объект навигатора

  • window.navigatorобъект содержитИнформация о браузере, который можно использовать для запроса некоторой информации о приложении, выполняющем текущий скрипт.
document.write("浏览器的代码名:" + navigator.appCodeName + "<br>");
document.write("浏览器的名称:" + navigator.appName + "<br>");
document.write("当前浏览器的语言:" + navigator.browserLanguage + "<br>");
document.write("浏览器的平台和版本信息:" + navigator.appVersion + "<br>");
document.write("浏览器中是否启用 cookie :" + navigator.cookieEnabled + "<br>");
document.write("运行浏览器的操作系统平台 :" + navigator.platform + "<br>");
  • navigator.appCodeNameТолько для чтения, всегда возвращает «Gecko» в любом браузере. Это свойство предназначено только для совместимости.
  • navigator.appNameТолько для чтения, возвращает официальное название браузера. Не ожидайте, что это свойство вернет правильное значение.
  • navigator.appVersionТолько для чтения, возвращает строку, представляющую версию браузера. Не ожидайте, что это свойство вернет правильное значение.
  • navigator.platformДоступно только для чтения, возвращает строку, указывающую системную платформу, на которой расположен браузер.
  • navigator.productТолько для чтения возвращает название продукта текущего браузера (например, «Gecko»).
  • navigator.userAgentТолько для чтения возвращает текущие строки агента пользователя браузера (строка агента пользователя)

Следующая информация печатается в разных браузерах

/*
chrome:
    Mozilla/5.0
    (Macintosh; Intel Mac OS X 10_12_6)
    AppleWebKit/537.36 (KHTML, like Gecko)
    Chrome/61.0.3163.91 Safari/537.36
safari:
    Mozilla/5.0
    (Macintosh; Intel Mac OS X 10_12_6)
    AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0
    Safari/604.1.38
ios11刘海X:
    Mozilla/5.0
    (iPhone; CPU iPhone OS 11_0 like Mac OS X)
    AppleWebKit/604.1.38 (KHTML, like Gecko)
    Version/11.0 Mobile/15A372 Safari/604.1
ipad:
    Mozilla/5.0
    (iPad; CPU OS 9_1 like Mac OS X)
    AppleWebKit/601.1.46 (KHTML, like Gecko)
    Version/9.0 Mobile/13B143 Safari/601.1
galxy sansum:
    Mozilla/5.0
    (Linux; Android 5.0; SM-G900P Build/LRX21T)
    AppleWebKit/537.36 (KHTML, like Gecko)
    Chrome/61.0.3163.91 Mobile Safari/537.36
安装uc浏览器:
    Mozilla/5.0
    (Linux; U; Android 6.0.1; zh-CN; Mi Note 2 Build/MXB48T)
    AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0
    Chrome/40.0.2214.89 UCBrowser/11.4.9.941 Mobile Safari/537.36
winphone:
    Mozilla/5.0
    (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E)
    AppleWebKit/537.36 (KHTML, like Gecko) 
    Chrome/61.0.3163.91 Mobile Safari/537.36
hybrid方法的可能:
    Mozilla/5.0
    (iPhone; CPU iPhone OS 11_0 like Mac OS X)
    AppleWebKit/604.1.38 (KHTML, like Gecko)
    Mobile/15A372 weibo/80011134
*/

Четырнадцать, ajax и fetch

ajax

  • Полное название ajax — асинхронный JavaScript и XML, то есть асинхронные js и xml, что позволяет странице инициировать запрос на получение данных без обновления.
  • использоватьwindow.XMLHttpRequestПримеры сетевого запроса объекта конструктораconst XHR = new XMLHttpRequest()
  • XHR.open(method, url, [ async, [ user, [ password]]])Этот метод используется для отправки запроса.
  • XHR.send(body)Параметр представляет собой содержимое тела запроса, и его формат может быть FormData, ArrayBuffer, Document или сериализованной строкой.После получения ответа данные ответа автоматически заполнят свойства объекта XHR.
  • Может вызываться, когда необходимо установить заголовки запросаXHR.setRequestHeader(header,value)Предоставляется значение типа заголовка запроса, а когда инициировать запрос на публикацию, способ предоставляетсяXHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')Этот заголовок запроса, чтобы изменить значение
  • При прослушивании метода свойства onreadystatechange экземпляра при изменении значения readyState будет запущена функция обратного вызова, соответствующая onreadystatechange.XHR.onreadystatechange = function () { }
  • Состояние запроса readyState имеет 5 значений, соответствующих 5 состояниям запроса, только для чтения.
    • 0 означает, что запрос не был инициализирован и метод open() не вызывался.
    • 1 означает, что соединение с сервером установлено и вызван метод open().
    • 2 означает, что запрос принят, метод send() был вызван, а заголовки и статус доступны.
  • XHR.responseText
  • XHR.responseXMLДанные атрибута XML в форме могут бытьXHR.responseType = 'document'а такжеXHR.overrideMimeType('text/xml')парсить как XML
  • XHR.withCredentialsНедвижимость устанавливается на логическое значение, установив это свойство, чтобы использовать файлы cookie, учетные данные авторизации, как поле поле
  • XHR.timeoutИспользуйте это свойство, чтобы установить время ожидания запроса.
  • XHR.ontimeoutИспользуйте это свойство, чтобы установить функцию обратного вызова тайм-аута запроса, параметром функции является объект события.
  • XHR.abort()Этот метод используется для завершения сетевого запроса.
  • XHR.getAllResponseHeaders()Этот метод используется для получения всех заголовков ответов.
  • XHR.getResponseHeader(name)Этот метод используется для получения указанного заголовка ответа.
  • еще 6 событий в процессе
    • loadstart первый байт, полученный в ответ на триггер
    • Прогресс продолжает стрелять во время приема
    • ошибка произошла ошибка
    • abort завершается вызовом метода abort
    • load получает полные данные, которые могут заменить readystatechange и readyState.
    • loadend срабатывает после завершения связи или прерывания события загрузки из-за ошибки
  • пройти черезXHR.addEventListener(eventname,callback)Метод добавляет соответствующий прослушиватель событий, а его функция обратного вызова получает параметр объекта события.
  • Объект события progress имеет 3 свойства для просмотра информации, связанной с текущим прогрессом, lengthComputable — это логическое значение, указывающее, доступен ли прогресс, position указывает количество полученных байтов, а totalSize указывает общую длину содержимого, которое необходимо передано, то есть количество байтов Content-Length.Обычно используется при передаче контента фрагментами

Просто сделайте запрос

// 最简单的发起一个请求
const XHR = new XMLHttpRequest()
XHR.open('get','http://127.0.0.1:3000/test?key=value')
XHR.send()
XHR.addEventListener('load',(e)=>{
  // 服务端返回的是查询参数
  console.log(XHR.response) // {"key":"value"}
})

Инкапсулировать метод запроса на основе XMLHttpRequest

// 发送的数据
const data = {
  name: 'tom'
}
// 请求配置
const config = {
  type: "post",
  url: "http://127.0.0.1:3000/test",
  data: data,
  dataType: 'application/json',
  success: function (res) {
    console.log(res);
  },
  error: function (e) {
    console.log(e);
  }
}
// 请求构造器
function Ajax(conf) {
  this.type = conf.type || 'get'
  this.url = conf.url || ''
  this.data = conf.data || {}
  this.dataType = conf.dataType || ''
  this.success = conf.success || null
  this.error = conf.error || null
}
// send方法
Ajax.prototype.send = function () {
  if (this.url === '') return
  const XHR = new XMLHttpRequest()
  XHR.addEventListener('load', () => {
    if (XHR.status >= 200 && XHR.status < 300 || XHR.status == 304) {
      typeof this.success === 'function' && this.success(XHR.response)
    }
  })
  XHR.addEventListener('error', (e) => {
    typeof this.error === 'function' && this.error(e)
  })
  if (this.type.toLowerCase() === 'get') {
    XHR.open('get', this.url)
    XHR.send(null)
  } else {
    XHR.open(this.type, this.url)
    XHR.setRequestHeader('Content-Type', this.dataType || 'application/x-www-form-urlencoded')
    let data = this.data
    if (this.dataType === 'application/json') {
      data = JSON.stringify(this.data)
    }
    XHR.send(data)
  }
}
// 发送请求
const ajax = new Ajax(config).send()

Поскольку инкапсуляция модуля сетевого запроса громоздка, он просто инкапсулирован здесь только для справки (.^▽^)

fetch

  • Fetch API предоставляет интерфейс js для замены сетевых запросов в методе XMLHttpRequest.Глобальный метод fetch() более удобен в использовании, чем XHR.
  • Метод выборки принимает 2 параметра, параметр 1 — это URL-адрес запроса или объект запроса, а параметр 2 — необязательный объект конфигурации.
// fetch方法返回一个Promise对象,可用then方法接收结果,用catch方法捕获异常,同Promise使用
// 配置对象具体配置
const config = {
  method: 'GET',      // 请求方法
  headers: {          // 头信息
    'user-agent': 'Mozilla/4.0 MDN Example',
    'content-type': 'application/json'
  },
  body: JSON.stringify({  // 请求的 body 信息,Blob, FormData 等
    data: 1
  }),
  mode: 'cors',             // 请求的模式,cors、 no-cors 或 same-origin
  credentials: 'include',   // omit、same-origin 或 include。为了在当前域名内自动发送 cookie, 必须提供这个选项
  cache: 'no-cache',        // default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached
  redirect: 'follow',       // 可用的 redirect 模式: follow (自动重定向), error (如果产生重定向将自动终止并且抛出一个错误), 或者 manual (手动处理重定向).
  referrer: 'no-referrer',  // no-referrer、client或一个 URL。默认是 client。
  referrerPolicy: 'no-referrer', // 指定 referer HTTP头
  integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=', // 包括请求的  subresource integrity 值
}
// 发起请求
fetch('http://biadu.com' [, config])
  • Затем функция обратного вызова принимает объект ответа параметра, которыми объекты имеют атрибуты 9, метод 8

  • 9 свойств

    • type только для чтения Содержит тип ответа (например, basic, cors)
    • URL-адрес только для чтения URL-адрес, содержащий ответ
    • useFinalURL содержит логическое значение, указывающее, является ли это конечным URL для ответа.
    • статус только для чтения Содержит код состояния ответа
    • ok только для чтения Содержит логическое значение, указывающее, что ответ был успешным (код состояния 200–299).
    • перенаправлено только для чтения Указывает, пришел ли ответ от перенаправления, если да, его список URL-адресов будет иметь несколько
    • statustext только для чтения содержит только информацию о состоянии, согласующуюся с кодом состояния отклика
    • headers только для чтения Содержит объект Headers, связанный с этим ответом
    • bodyUsed Body Только для чтения Содержит логическое значение, указывающее, прочитал ли ответ Body
  • 8 способов

    • clone создает клон объекта Response
    • error возвращает сетевую ошибку, связанную с новым объектом Response
    • перенаправить (url, статус) с другим, чтобы создать новый URL-адрес ответа
    • formData считывает и инкапсулирует поток данных, переносимый в объекте Response, в объект.
    • json берет поток ответа и читает его до конца. Результатом синтаксического анализа является синтаксический анализ тела текста в формате JSON.
    • text предоставляет удобочитаемый «возвратный поток», который возвращает объект USVString, закодированный как UTF-8.

15. Веб-сокет

  • WebSocket — это протокол для полнодуплексной связи по одному TCP-соединению, то есть обе стороны соединения могут одновременно отправлять и получать данные в режиме реального времени.Он может открывать дуплексный двусторонний сеанс связи между пользователем браузер и сервер.
  • WebSocket API предоставляет глобальные методыWebSocket(url[, protocols])Создайте экземпляр, параметр 1, абсолютный URL-адрес другой стороны, чей URL-адрес начинается сws://илиwss://(加密)В начале параметр 2 протоколы — это один протокол или массив строк, содержащий протоколы
// 必须传入绝对URL,可以是任何网站
const s = new WebSocket('ws://www.baidu.com') 
s.readyState    // 0 建立连接 1 已经建立 2 正在关闭 3 连接已关闭或者没有链接成功
s.send('hello') // 发送的数据必须是纯文本
s.onopen = function () {}
s.onerror = function () {}
s.onmessage = function (event) {
  // 当接收到消息时
  console.log(event.data) // 数据是纯字符
}
s.close()   // 关闭连接
s.onclose = function (event) {
  /*
    * event.wasClean 是否明确的关闭 
    * event.code 服务器返回的数值状态码
    * event.reason 字符串,服务器返回的消息
    */
}
  • 10 свойств
    • binaryType возвращает тип двоичных данных, передаваемых соединением через веб-сокет (blob, arraybuffer).
    • bufferedAmount только для чтения Возвращает количество байтов данных, поставленных в очередь методом send(), но еще не отправленных в сеть. Как только все данные в очереди будут отправлены в сеть, значение этого свойства будет сброшено на 0. Однако, если соединение закрывается во время отправки, значение свойства не сбрасывается на 0.
    • extensions только для чтения Возвращает расширения, выбранные сервером. В настоящее время это просто пустая строка или расширенный список переговоров о подключении.
    • onclose используется для указания функции обратного вызова после сбоя соединения.
    • OnMessage используется для указания функции обратного вызова, когда информация получена с сервера
    • onopen используется для указания функции обратного вызова после успешного подключения.
    • протокол подчиненный протокол только для чтения, выбранный сервером
    • readyState читает только текущее состояние ссылки, всего 4
      • 0 установить соединение
      • 1 подключен
      • 2 закрывается
      • 3 Соединение закрыто или соединение не установлено
    • url абсолютный путь только для чтения к WebSocket
  • 2 методы
    • закрыть(код, причина) числовой код состояния необязательный по умолчанию 1005 и необязательная читаемая строка, объясняющая, почему соединение было закрыто.
    • Send (data) отправляет данные на сервер (ArrayBuffer, blob и т. д.)

Шестнадцать, короткий опрос, длинный опрос и WebSocket

короткий опрос

Простая демонстрация

const xhr = new XMLHttpRequest()
// 每秒发送一次短轮询
const id = setInterval(() => {
  xhr.open('GET', 'http://127.0.0.1:3000/test?key=value')
  xhr.addEventListener('load', (e) => {
    if (xhr.status == 200) {
      // 处理数据
      console.log(xhr.response)
      // 如果不需要可以关闭
      clearInterval(id)
    }
  })
  xhr.send()
}, 1000)

долгий опрос

Простая демонстрация

function ajax() {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', 'http://127.0.0.1:3000/test?key=value');
  xhr.addEventListener('load', (e) => {
    if (xhr.status == 200) {
      // 处理数据
      console.log(xhr.response)
      // 如果不需要可以关闭
      if (xhr.response != '') return
      ajax()
    }
  })
  xhr.send();
}

Та же точка

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

разница

  • HTTP Длинный опрос - оставаться на стороне сервера, в то время как опрос HTTP короткое пребывание в боковой стороне браузера
  • Короткий опрос инициирует запрос к серверу через определенные промежутки времени и возвращает результат напрямую, независимо от того, изменились ли данные сервера.Длинный опрос возвращает результат, когда данные сервера изменяются.Если нет изменений в течение определенного периода времени, он будет автоматически отключается по истечении тайм-аута.

轮询对比

Web Socket

  • Чтобы решить проблему отсутствия состояния HTTP, пассивности и опроса, html5 недавно представил протокол веб-сокета.Браузер и сервер должны только выполнить рукопожатие, и они могут установить постоянное соединение и осуществлять двустороннюю связь.
  • Рукопожатие на основе http, происходит шифрование данных, и соединение не разрывается
  • преимущество:
    • Менее контроль над головой, меньшие заголовки пакетов для управления протоколом при обмене данных между клиентом и сервером
    • Улучшенный эффект сжатия: при надлежащей поддержке расширений Websocket может продолжать использовать контекст предыдущего контента, что может значительно улучшить степень сжатия при передаче аналогичных данных.

17. Длинное соединение и короткое соединение

Короткое соединение

  • В HTTP/1.0 по умолчанию используется короткое соединение, то есть каждый раз, когда клиент и сервер выполняют HTTP-операцию, устанавливается соединение, которое разрывается при завершении задачи.
  • Когда веб-страница определенного HTML или другого типа, к которой обращается клиентский браузер, содержит другие веб-ресурсы (такие как файлы JavaScript, файлы изображений, файлы CSS и т. д.), каждый раз, когда встречается такой веб-ресурс, браузер будет заново создавать сеанс HTTP
  • Этапы работы короткого соединения: установить соединение - передача данных - закрыть соединение... установить соединение - передача данных - закрыть соединение
  • Службы HTTP, такие как веб-сайты, обычно используют короткие соединения, которые имеют большой объем параллелизма, но каждому пользователю необходимо использовать короткие соединения без частых операций.

Длинное соединение

  • Начиная с HTTP/1.1, постоянные соединения используются по умолчанию для поддержания характеристик соединения. Используя протокол HTTP с длинным подключением, эта строка кода будет добавлена ​​в заголовок ответа.Connection:keep-alive
  • In the case of using a long connection, when a web page is opened, the TCP connection used to transmit HTTP data between the client and the server will not be closed. When the client accesses the server again, it will continue to use the established связь.
  • Держитесь в живых не навсегда оставаться на связи, он имеет время удержания, может быть установлено в это время для другого серверного программного обеспечения (например, Apache). Достичь длинного подключения требуется, чтобы клиент и сервер поддерживают длительное соединение

Разница между длинным и коротким опросом и длинными и короткими соединениями

  • Длинное соединение и короткое соединение протокола HTTP по сути являются длинным соединением и коротким соединением протокола TCP.
  • Запрашивают ли длинные или короткие соединения заголовки ответа через обе стороныConnection:keep-aliveрешать использовать его, а опрашивать или нет, определяется в соответствии с методом обработки сервера, который не имеет ничего общего с клиентом.
  • Различные реализации, длина соединения достигается протоколом, а длина сервера опроса достигается программированием вручную

18. Хранение

Cookie

  • Файл cookie отправляется сервером клиенту для хранения небольшого количества информации в виде пары ключ-значение {ключ:значение}.

cookie原理

  • Когда клиент запрашивает сервер, если серверу необходимо записать статус пользователя, он использует ответ для выдачи файла cookie браузеру клиента. Браузер клиента сохранит файл cookie. Когда браузер снова запрашивает сервер, браузер отправляет запрошенный URL-адрес вместе с файлом cookie на сервер. Сервер получает статус пользователя, проверяя этот файл cookie.
  • Файлы cookie не являются междоменными, но междоменные не поддерживаются только тогда, когда доменное имя отличается, игнорируя протокол и порт,https://localhost:80/а такжеhttp://localhost:8080/Файл cookie является общим, вы можете установить домен через домен, а путь устанавливает общий путь под доменом.
  • атрибут cookie
    • имя указывает установленное имя файла cookie, то есть ключ, который нельзя повторить или изменить
    • value представляет значение установленного файла cookie
    • домен представляет доменное имя, к которому привязан файл cookie. Текущий домен привязан по умолчанию. Многоуровневые доменные имена не могут обмениваться файлами cookie. Если задано доменное имя, начинающееся с точки, доступны все субдомены..baidu.com,ноa.baidu.comФайл cookie с доступом к своему родительскому домену
    • путь представляет собой путь, который может использовать файл cookie, путь по умолчанию «/», если текущий соответствующий путь и подпути удовлетворены, файл cookie может использоваться совместно.
    • maxAge указывает время истечения срока действия файла cookie в секундах, положительное число означает время истечения срока действия, отрицательное число означает, что срок действия текущего файла cookie истекает при закрытии браузера, а 0 означает удаление файла cookie.
    • secure Указывает, передается ли файл cookie с использованием безопасного протокола, такого как HTTPS и SSL. Он не используется по умолчанию и действителен только для безопасных протоколов, таких как HTTPS. Этот атрибут не может шифровать файл cookie клиента и не может гарантировать абсолютную безопасность.
    • Текущая версия файла cookie версии используется, 0 Следуйте спецификации Cookie Netscape (большинство), 1 указывает на спецификацию RFC2109 следует за помощью W3C (более строгим), по умолчанию 0
    • Браузер для одинакового сайта браузер не могут нести файлы cookie в перекрестных запросах, уменьшите атаки CSRF
    • HttpOnly Если для этого свойства установлено значение true, значение файла cookie не может быть получено с помощью сценария js, который используется для ограничения программного интерфейса, отличного от протокола HTTP, для доступа к клиентскому файлу cookie, что может эффективно предотвращать атаки XSS (межсайтовые атаки). скриптовые атаки, атаки с внедрением кода)
  • Внешний интерфейс читает и записывает файлы cookie через document.cookie.
  • Создание файлов cookie — это бэкэнд.

Session

  • Когда пользователь переходит между веб-страницами в приложении, переменные, хранящиеся в объекте сеанса, не теряются, а сохраняются на протяжении всего сеанса пользователя. Сеанс завершается, когда клиент закрывает сеанс или когда истекает тайм-аут сеанса.

session

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

Разница между файлом cookie и сеансом

  • Данные cookie хранятся в браузере клиента, а данные сеанса хранятся на сервере.
  • куки не очень безопасны, люди могут анализировать локальное хранилище куки и обман куки, принимая во внимание безопасность, следует использовать сеанс. Для аутентификации пользователя в таких случаях обычно используется сеанс
  • Сессия хранится на сервере, и клиент не знает информации, напротив, cookie хранится на клиенте, и сервер может знать информацию.
  • Сессия будет сохраняться на сервере в течение определенного периода времени.При увеличении количества посещений она будет занимать производительность вашего сервера.Учитывая снижение производительности сервера, следует использовать файлы cookie.
  • Объекты хранятся в сеансе, строки хранятся в файлах cookie.
  • Сеанс не может различить путь, тот же пользователь осуществляется во время веб-сайта, и все сеансы могут быть доступны в любом месте, и если параметры пути установлены, то файлы cookie под разными путями на одном сайте не доступны.

localStorage

основное использование

localStorage.setItem("b", "isaac");  //设置b为"isaac"
localStorage.getItem("b");           //获取b的值,为"isaac"
localStorage.key(0);                 //获取第0个数据项的键名,此处即为“b”
localStorage.removeItem("b");        //清除c的值
localStorage.clear();                //清除当前域名下的所有localStorage数据
  • Пока localStorage находится под тем же протоколом, тем же именем хоста и одним и тем же портом, он может читать/изменять одни и те же данные localStorage, которые обычно используются для обмена данными между страницами.
  • доступныйwindow.addEventListener("storage", function(e){}Установите прослушиватель событий localStorage, при изменении содержимого области хранения будет вызываться обратный вызов

sessionStorage

  • sessionStorage используется для локального хранения данных в сеансе, к которым могут получить доступ только страницы в том же сеансе и уничтожить, когда сеанс завершится. Таким образом, sessionStorage — это не постоянное локальное хранилище, а просто хранилище на уровне сеанса.
sessionStorage.setItem(name, num);    //存储数据
sessionStorage.setItem('value2', 119);
sessionStorage.valueOf();             //获取全部数据
sessionStorage.getItem(name);         //获取指定键名数据
sessionStorage.sessionData;           //sessionStorage是js对象,也可以使用key的方式来获取值
sessionStorage.removeItem(name);      //删除指定键名数据
sessionStorage.clear();
  • Использование аналогично localStorage
  • Действует только для текущего веб-сеанса, будет очищен после закрытия страницы или браузера.
  • В основном используется для хранения данных, уникальных для текущей страницы, которые не используются совместно с другими страницами в браузере.

разница

  • хранилище данных
    • Данные файла cookie всегда передаются (даже если не нужны) в однозначном прохождении HTTP-запросов, то есть cookies передаются взад-вперед между браузером и сервером. Данные файла cookie также имеют концепцию пути, которая может ограничить печенье, чтобы принадлежать к определенному пути.
    • sessionStorage и localStorage не отправляют данные на сервер автоматически, а только сохраняют их локально.
  • размер данных хранилища
    • Ограничение размера хранилища также отличается.Данные cookie не могут превышать 4 КБ.В то же время, поскольку каждый http-запрос будет содержать cookie, cookie подходит только для хранения небольших данных, таких как идентификатор сеанса.
    • Хотя sessionStorage и localStorage также имеют ограничения на размер хранилища, они намного больше файлов cookie и могут достигать 5 МБ и более.
  • Срок действия хранения данных
    • SessionStorage: действителен только до тех пор, пока текущее окно браузера не будет закрыто; LocalStorage: Всегда активное окно браузера или выключение было сохранено, локальное хранилище, таким образом, действует как постоянные данные;
    • cookie: действителен только до установленного времени истечения срока действия cookie, даже если окно закрыто или браузер закрыт
  • разная сфера
    • sessionStorage не разделяет разные окна браузера, даже одну и ту же страницу;

storage

jsonp

  • Передавая на дальнем конце имя метода в качестве параметра серверу и обратно после параметров инъекции сервером, клиент обменивается данными на стороне сервера.
  • Из-за использования атрибута src тега script поддерживается только метод get.

Давайте сделаем это

// 前端准备
// 定义回调函数
function fn(arg) {
  // arg为服务端传来的数据
  console.log(`客户端获取的数据:${arg}`)
}
// 创建script标签
const s = document.createElement('script')
// 给script标签的src属性赋值,值为请求url,查询参数callback,需与后端对应
// fn为前端回调函数名
s.src = `http://127.0.0.1:3000/test?callback=fn`
// 向html添加此标签,添加完成之后浏览器自动请求script的src对应的网址
document.getElementsByTagName('head')[0].appendChild(s);
// 等待浏览器收到响应之后,将会自动执行响应内容的代码
----------------------------------------------
// 后端准备
// nestjs(ts)处理
@Controller('test') //api
export class TestController {
  @Get() //get方式请求
  //取url中的查询参数,即?之后的键值对,键与值对应query对象参数的键与值
  callback(@Query() query) {  
    // 返回的数据
    const data = '我是服务端返回的数据';
    // 取查询参数,这里的callback要与前端?之后的键名一致,fn即fn函数名
    const fn = query.callback;
    // 返回结果,格式:函数名(服务器的数据),注意这里需要序列化成字符串,如果参数本身是字符串那么要加引号,前端并不知道data是字符串
    return `${fn}('${data}')`;
  }
}

// express(js)处理,同上
router.get('/test', async (req, res) => {
  const data = '我是服务器返回的数据'
  // req.query为查询参数列表
  const fn = req.query.callback
  // 返回数据
  res.send(`${fn}('${data}')`)
})

содержание ответа

jsonp响应

CORS

  • Cors для совместного использования ресурсов между источниками, который использует дополнительные заголовки HTTP, чтобы сообщить браузерам, что веб-приложениям, работающим в одном источнике (домене), разрешен доступ к указанным ресурсам с разных серверов источника.
  • Сервер и клиент должны одновременно поддерживать междоменный метод cors для выполнения междоменных запросов.Сервер может установитьAccess-Control-Allow-Origin:*Вы можете включить cors, чтобы разрешить междоменные запросы. Используйте подстановочный знак *, чтобы разрешить источникам из всех разных доменов доступ к ресурсам. Вы также можете указать указанное разрешенное имя исходного домена отдельно.
  • При использовании cors cross-domain возможны две ситуации при выполнении запроса:
  • Простой запрос, следующие условия должны быть выполнены
    • Запросы, инициированные с использованием методов get, head и post.
    • Значение Content-Type ограничено одним из следующих трех:
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
    • Несоблюдение этих условий является предварительным запросом.
  • предварительный запрос
    • Использование запросов предварительной проверки позволяет избежать непредсказуемого воздействия междоменных запросов на пользовательские данные для сервера.
    • Предпечатный запрос будет отправлен при выполнении одного из следующих условий.
      • Используйте любой из следующих http-методов:
      • PUT
      • DELETE
      • CONNECT
      • OPTIONS
      • TRACE
      • PATCH
    • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。 Коллекция:
      • Accept
      • Accept-Language
      • Content-Language
      • Content-Type (дополнительные ограничения, о которых следует знать)
      • DPR
      • Downlink
      • Save-Data
      • Viewport-Width
      • Width
    • Значение Content-Type не является одним из следующих:
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • Если одно из вышеуказанных условий выполнено, будет инициирован предварительный запрос. Всего будет инициировано 2 запроса. Первый запрос — это запрос OPTIONS, чтобы определить, поддерживает ли сервер междоменный доступ. Если да, то второй фактический запрос будет инициирован, в противном случае второй запрос не будет отправлен

postMessage

  • postMessage можно использовать для передачи данных между доменами между разными страницами.
  • postMessage(data,origin[, source])data — это данные для отправки, и они могут отправлять только строковую информацию, origin отправляет целевой источник, указывает, какие окна могут получать события сообщения, если для origin установлено значение *, это означает неограниченное количество, source — это ссылка на объект окна окна, которое отправляет сообщение,
<!-- test.html -->
<iframe src="http://127.0.0.1:5501/postMessage.html"
name="postIframe" onload="messageLoad()"></iframe>
<script>
// 定义加载之后执行的函数,给postMessage.html发送数据
function messageLoad() {
  const url = 'http://127.0.0.1:5501/postMessage.html'
  window.postIframe.postMessage('给postMessage的数据', url)
}
// 用于监听postMessage.html的回馈,执行回调
window.addEventListener('message', (event) => {
  console.log(event.data);
})
</script>
----------------------------------------------
<!-- postMessage.html -->
<script>
  // 监听test.html发来的数据,延迟1秒返回数据
  window.addEventListener('message', (event) => {
    setTimeout(() => {
      event.source.postMessage('给test的数据', event.origin)
    },1000)
  })
</script>
  • Несколько важных свойств объекта события
    • Это относится к данным, передаваемым из другого окна через объект сообщения.
    • тип относится к типу отправленного сообщения
    • источник относится к объекту окна, который отправил сообщение
    • origin относится к происхождению окна, отправившего сообщение

window.name

  • из-заwindow.nameЭто глобальный атрибут. Загрузите новую страницу (которая может быть междоменной) в iframe в html, измените значение имени в источнике, на который указывает src, установленный iframe, и измените имя на главной странице. , но его нужно передать в окно iframe.about:blankили страница одинакового происхождения
  • После использования iframe следует удалить, только для значения имени строкового типа и объема данных максимальная поддержка 2MB
<!-- test.html -->
// 封装应该用于获取数据的函数
function foo(url, func) {
  let isFirst = true
  const ifr = document.createElement('iframe')
  loadFunc = () => {
    if (isFirst) {
      // 设置为同源
      ifr.contentWindow.location = 'about:blank'
      isFirst = false
    } else {
      func(ifr.contentWindow.name)
      ifr.contentWindow.close()
      document.body.removeChild(ifr)
    }
  }
  ifr.src = url
  ifr.style.display = 'none'
  document.body.appendChild(ifr)
  // 加载之后的回调
  ifr.onload = loadFunc
}
foo(`http://127.0.0.1:5501/name.html`, (data) => {
  console.log(data) //
})
----------------------------------------------
<!-- name.html -->
const obj = { name: "iframe" }
// 修改name的值,必须为string类型
window.name = JSON.stringify(obj);

document.domain

  • document.domainЗначение соответствует доменному имени текущей страницы
  • http://a.baidu.comа такжеhttp://b.baidu.com
  • домен может быть присвоен только текущему доменному имени или его базовому доменному имени, то есть доменному имени верхнего уровня
<!-- test.html -->
<script>
document.domain = 'baidu.com';
const ifr = document.createElement('iframe');
ifr.src = 'a.baidu.com/test.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
  var doc = ifr.contentDocument || ifr.contentWindow.document;
  // 此处即可操作domain.html的document
  ifr.onload = null;
};
</script>
----------------------------------------------
<!-- domain.html -->
<script>
  // domain.html下设置为与test.html中的domain一致
  document.domain = 'baidu.com';
</script>
  • Главное — манипулировать содержимым другой страницы через iframe, установив для него доменное имя того же происхождения (только его базовое доменное имя).

  • Проксирование к разным источникам путем настройки конфигурационного файла nginx
// nginx.conf配置
server {
  listen 80;  // 监听端口
  server_name  www.baidu.com; // 匹配来源
  location / {  //匹配路径
    // 反向代理到http://127.0.0.1:3000
    proxy_pass http://127.0.0.1:3000;
    // 默认入口文件
    index  index.html index.htm index.jsp;
}
  • Обратный прокси-сервер nginx также может обеспечивать балансировку нагрузки.

Двадцать, setTimeout и setInterval

setTimeout

  • setTimeout — это часть webApi, которая может реализовывать отложенные вызовы, это асинхронная макрозадача, которую можно использовать один раз.
  • setTimeout(func|code, [delay], [arg1], [arg2], ...)Параметр 1 — это функция или строка кода, которая должна быть выполнена, параметр 2 — время задержки выполнения, единица измерения — миллисекунды по умолчанию 0, параметр 3 и последующие параметры — это параметры, которые передаются, когда параметр 1 является функцией, а идентификатор таймера будет возвращаться после звонка.
  • Этот метод выполняется только один раз, вы можете использоватьclearTimeout(id)Очистите таймер, чтобы отменить обратный вызов
  • Посмотрите на задержку в реализации механизмов setTimeout

setTimeout

  • Выше с использованием вложенного цикла для достижения вызова Settimeout вы можете увидеть сроки Settimeout после начала выполнения обратного вызова Settimeout, посмотрите на эффект кода

setTimeout代码演示

  • На приведенном выше рисунке вычисляется интервал между двумя вызовами обратного вызова без учета времени выполнения обратного вызова.Видно, что время от запуска таймера до выполнения обратного вызова действительно является значением, заданным параметром 2, а задержка time не имеет ничего общего со временем выполнения callback-функции. ;
  • Проще говоря, время задержки setTimeout не включает время, занимаемое его собственным обратным вызовом.

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

setInterval

  • SetInterval также является частью webapi, которая в основном используется для выполнения кода в замыкании
  • setInterval(func|code, [delay], [arg1], [arg2], ...), Список параметров установлен на setTimeout, параметр 2 — это время каждого цикла.

setInterval

setInterval代码演示

setInterval代码演示1

В браузере предупреждения, подтверждения и подсказки будут блокировать выполнение основного потока js до тех пор, пока всплывающее окно не исчезнет, ​​но таймер будет продолжать выполняться; таймер не может достичь 0 задержки, а минимальная задержка ограничена 4 мс.

Двадцать один, requestAnimationFrame

  • Перед выходом requestAnimationFrame большинство использует таймер для завершения js-анимации, но поскольку таймер неточен, и каждый раз, когда анимация обновляется, нельзя гарантировать ее синхронизацию с рендерингом браузера, что приведет к тому, что экран негладкий.
  • Поскольку текущая фиксированная скорость обновления основных экранов обычно составляет 60 Гц, что составляет 60 кадров в секунду, а интервал обновления составляет 1000/60 мс, чтобы сделать браузер получить лучший эффект рендеринга, каждый редактор браузера должен соответствовать Скорость обновления экрана. Так что для анимации JS лучшее время обновления должно соответствовать браузеру как можно больше
  • requestAnimationFrame(callback)
  • Время выполнения requestAnimationFrame после очереди микрозадач в механизме цикла событий, до рендеринга браузера и после рендеринга браузера, он войдет в следующий цикл событий (запуск макроса и завершение рендеринга браузера).
  • Если вы используете таймер для операции js-анимации, во-первых, это приведет к тому, что обновление анимации не будет соответствовать времени каждой перерисовки браузера, что приведет к заиканию, а во-вторых, слишком частое обновление анимации приведет к ненужным потерям производительности и невозможно добиться лучшего эффекта
  • Проще говоря, анимация, обновляемая с помощью requestAnimationFrame, синхронизируется с браузером и не пропускает кадры, если только браузер не пропускает кадры или основной поток js не заблокирован и браузер не может нормально отображать. Используйте таймер для обновления анимации. Если частота высока, это повлияет на производительность.И не может достичь лучших результатов, если частота низкая, будет ощущение несогласованности

requestAnimationFrame

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

Двадцать два, инцидент

событие DOM0

  • Событие DOM0 не является стандартом w3c.Модель событий до формирования стандарта DOM — это то, что мы называем DOM уровня 0.
  • Добавление события DOM0 означает назначение функции элементу документа. Когда вызывается функция прослушивания событий, она будет вызываться как метод элемента, который генерирует событие, поэтому это указывает на целевой элемент. Проще говоря, функция обратного вызова непосредственно используется как элемент документа.вызов метода
  • Чтобы удалить событие DOM0, просто присвойте значению события значение null.
document.getElementById("btn").onclick = function () {}
----------------------------------------------
<input type="button" onclick="alert('hi!');">
  • Если метод обратного вызова возвращает false, это предотвратит поведение событий браузера по умолчанию.
  • Событие DOM0 не может получить событие на этапе захвата событий, то есть захват события не может быть запущен, но всплытие может быть запущено в обычном режиме.
  • Поскольку обратный вызов события DOM0 относится к методу элемента документа, невозможно добавить несколько событий с одним и тем же именем, но кажется, что совместимость наилучшая.

События DOM2

  • Поскольку в стандарте DOM уровня 1, запущенном w3c, не определен контент, связанный с событиями, не существует и так называемой модели событий DOM уровня 1.
  • В дополнение к определению некоторых операций, связанных с DOM, модель событий определяется в двухуровневой модели DOM. Модель событий в соответствии с этим стандартом — это то, что мы называем моделью событий двухуровневой модели DOM.
  • DOM уровня 2 определяет распространение события, которое будет проходить через 3 этапа в процессе распространения события:
    1. Этап захвата — это этап захвата событий. Когда событие инициируется в DOM, событие сначала передается от объекта Document по номеру DOM до триггерного узла.Этот процесс является этапом захвата событий, на котором распространяются события можно захватить.
    2. Этап обработки события целевого элемента.На этом этапе событие достигает цели триггера, и для обработки события вызывается обратный вызов.
    3. Фаза для пузырьков, фаза пузырька событий, после завершения обработки целевой элементы, это событие будет пузым, вернуться к документу, наоборот на этом этапе и этапе захвата
  • Выше приведен процесс распространения события после его запуска, который можно понять с помощью следующего рисунка.

事件传递

  • DOM2 регистрирует события, доступ к которым можно получить черезaddEventListener(eventName,callback,isCapturing)
  • пройти черезremoveEventListener(eventName,callback,isCapturing)
  • параметры объекта события функции обратного вызова
  • Атрибуты
    • введите тип произошедшего события
    • Фаза целевого возникновения события, объект, вызвавший событие, и может быть другим currentTarget
    • Обработается событие CENectTarget узема узема, это зарегистрированные элементы этой функции обратного вызова
    • ClientX, Clident Mouse относительно X-координаты браузера и y-координаты
    • screenX, screenY мышь относительно левого верхнего угла дисплея координаты x, y
  • метод
    • stopPropagation() предотвращает дальнейшее распространение текущего события
    • preventDefault() запрещает браузеру выполнять действия по умолчанию, связанные с миром, так же, как DOM0 возвращает false
  • Время запуска
    • документ к целевому узлу, распространение, захват вперед, события захвата, зарегистрированные немедленно, запускают выполнение
    • Достичь целевого узла и инициировать событие (для целевого узла, следует ли сначала захватить или сначала всплывать, захватить событие и порядок регистрации всплывающего события, сначала зарегистрироваться и выполнить первым)
    • Целевой узел распространяется в направлении документа, всплывает вперед и срабатывает сразу после обнаружения зарегистрированного события всплытия.

агент событий

  • Прокси-сервер событий или делегирование событий с помощью механизма всплытия событий использует один родительский узел для обработки ответов нескольких дочерних узлов.Короче говоря, он удаляет события всех дочерних узлов и регистрирует события только для родительского узла, после чего вы можете использовать механизм пузырькового всплытия событий.Механизм пузырьков для обработки ответов дочерних узлов
  • Основываясь на делегировании событий, он может сократить регистрацию событий, сэкономить память и упростить обновление узлов dom в событиях.
<ul id="f">
  <li>a</li>
  <li>b</li>
  <li>c</li>
</ul>
<script>
  const ul = document.querySelector('#f')
  // 点击li时触发事件委托
  ul.addEventListener('click',function foo(event){
    // 处理元素为父元素
    console.dir(event.currentTarget)  // ul#f
    // 触发元素为子元素,event.target为具体触发对象
    console.dir(event.target)         // li
  })
//--------------------------------------------
  // 通过点击添加子元素
  ul.addEventListener('click',function foo(event){
    const child = document.createElement('li')
    child.innerText = '我是新增的子元素'
    event.currentTarget.appendChild(child)
  })
//--------------------------------------------
  // 通过点击删除子元素
  ul.addEventListener('click',function foo(event){
    event.currentTarget.removeChild(event.target)
  })
</script>
----------------------------------------------
<!-- 如果点击span 想知道是哪个li下面的元素 -->
<ul id="f">
  <li>a</li>
  <li>
    <span>b</span>
  </li>
  <li>
    <span>c</span>
  </li>
</ul>
<script>
  const ul = document.querySelector('#f')
  ul.addEventListener('click', function foo(event) {
    let target = event.target
    // 一级级向上寻找直到找到满足条件的元素
    while (target.nodeName.toLowerCase() !== 'li') {
      target.target.parentNode
    }
    console.dir(target) // li
    console.dir(target.parentNode === event.currentTarget) //true
  })
</script>
  • Прокси-сервер события основан на механизме пузырька. Если есть слишком много уровней прокси, и если он заблокирован слоем во время пузыринга, родитель не получит событие.
  • Теоретически делегирование приведет к тому, что браузер будет часто вызывать функцию-обработчик, хотя вполне вероятно, что ее не нужно обрабатывать, поэтому рекомендуется делегировать поблизости
  • Если событие представляет собой множество ситуаций, необходимо хорошо провести логический анализ, чтобы избежать ошибочных суждений.

Суммировать

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

Обобщить не просто, для перепечатки указывайте источник, спасибо!

Если эта статья поможет вам, пожалуйста, поддержите, похвалите ее, чтобы больше людей увидели вашу поддержку, это то, на чем я настаиваю, чтобы написать, если вам нравятся мои статьи, вы также обратите внимание на последующую статью это ~ ψ (`∇') ψ

Другие статьи

js серия

Говоря о новых возможностях ES6

css серия

Гибкая компоновка CSS, часто используемое горизонтальное и вертикальное центрирование

Github, построен,Передняя система знанийПроекты, записывайте и систематизируйте очки знаний, вы можете учиться и делиться ими вместе!

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