Закрепить основные точки знаний JS

JavaScript
Закрепить основные точки знаний JS

От работы в индустрии передовых технологий до настоящего момента, когда я чувствую, что добился наибольшего прогресса, было то, когда я планировал сменить работу и начать учиться в прошлом году, особенно когда я прочитал буклет о золотых самородках «Путь фронта». -Конец интервью" от yck. Именно в тот период обучения у меня постепенно появились смутные очертания системы знаний о фронтенде, а также я начал соприкасаться с интересной технологической платформой «Наггетс». Теперь, когда улеглась рабочая пыль, я начал лениться. По дороге на работу я снова начал играть в игры. Когда я возвращаюсь домой вечером, я начинаю играть в игры вместо чтения книг. В свободное время я начните лить воду в группе WeChat QQ group. Но приближаясь к 30 годам, дорога передо мной становится все более размытой. Я знаю, что у меня осталось не так много времени, чтобы наверстать упущенное. Первые три года работы мной потрачены зря, и если я не наверстаю упущенное за эти два года, то могу навсегда остаться на уровне младших и средних программистов. Имейте в виду, что я все еще старший джуниор фронтенд-разработчик, монах наполовину, а не из профессиональной школы, я поощряю себя!

Сяоган учитель

Примитивные типы и ссылочные типы

Типы данных в js делятся на базовые типы и ссылочные типы.Существует шесть основных типов:

  • number
  • string
  • boolean
  • null
  • undefined
  • symbol(эс6)

Ссылочные типы включают объектыobject, множествоarray, функцияfunctionи т. д., вместе именуемые типом объекта:

  • object

stringТипы — это строки.В дополнение к одинарным и двойным кавычкам в es6 были введены новые обратные кавычки ` ` для хранения строк. Расширенная функция обратных кавычек доступна с${…}Встраивайте переменные и выражения в строки. Используйте следующим образом:

let n = 3
let m = () => 4
let str = `m + n = ${m() + n}` // "m + n = 7"

numberЗначения типа включают целые числа, числа с плавающей запятой,NaN,InfinityЖдать. вNaNТип — это единственный тип в js, который не равен самому себе, при возникновении неопределенной математической операции он вернетNaN,Такие как:1 * 'asdf',Number('asdf'). Операции над числами с плавающей запятой могут выглядеть как0.1 + 0.2 !== 0.3Проблема, возникающая из-за точности операций с плавающей запятой, обычно используетсяtoFixed(10)может решить такие проблемы.

boolean,stringа такжеnumberСамо собой разумеется, что в качестве базового типа не должно быть никакой функции для вызова, потому что базовый тип не имеет цепочки прототипов для предоставления методов. Однако эти три типа могут вызыватьtoStringметоды прототипа объекта. Не верю?

true.toString() // 'true'
`asdf`.toString() // 'asdf'
NaN.toString() // 'NaN'

Вы можете сказать, почему тогда числа1нельзя назватьtoStringметод? На самом деле, это не невозможно назвать:

1 .toString()
1..toString()
(1).toString()

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

Почему примитивные типы могут напрямую вызывать методы ссылочных типов? На самом деле, когда движок js анализирует приведенный выше оператор, он анализирует эти три основных типа какобъект-оболочка(Это следующееnew String()), а объект-оболочка — это ссылочный тип, который можно вызыватьObject.prototypeспособ выше. Примерный процесс выглядит следующим образом:

'asdf'.toString()  ->  new String('asdf').toString()  -> 'asdf'

nullСпециальное значение, означающее «нет», «нуль» или «значение неизвестно».

undefinedозначает «не назначен». за исключением случаев, когда переменная была объявлена ​​неназначеннойundefined, если свойство объекта не существуетundefined. Так что по возможности следует избегатьvar a = undefined; var o = {b: undefined}Этот способ написания вместо использованияvar a = null; var o = {b: null}, по умолчанию "не назначено"undefinedслучаи различают.

SymbolЗначение представляет собой уникальный идентификатор. Можно использоватьSymbol()Создание функции:

var a = Symbol('asdf')
var b = Symbol('asdf')
a === b // false

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

var a = Symbol.for('asdf')
var b = Symbol.for('asdf')
a === b // true

Его также можно использовать как свойство объекта, но в данный момент его нельзя использовать.for...inПройдено:

let id = Symbol('id')
let obj = {
  [id]: 'ksadf2sdf3lsdflsdjf090sld',
  a: 'a',
  b: 'b'
}
for(let key in obj){ console.log(key) } // a b
obj[id] // "ksadf2sdf3lsdflsdjf090sld"

Также имеется множество встроенныхSymbol,Такие какSymbol.toPrimitive Symbol.iteratorЖдать. Когда происходит приведение ссылочного типа к примитивному типу, встроенныйSymbol.toPrimitiveФункция, конечно же, может и вручную добавлять объектыSymbol.toPrimitiveФункция, чтобы переопределить поведение по умолчанию актеров.

objectЭто ссылочный тип.Разница между ссылочным типом и базовым типом заключается в том, что исходный тип хранит значение, а ссылочный тип хранит указатель на реальный адрес памяти объекта. В js объекты включаютArray Object Function RegExp MathЖдать.

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

var o = {
  a: 'a',
  b: 'b'
}
var o2 = o // 变量o2复制了变量o的指针,现在他们都指向同一个内存地址,现在开始他们的增删改其实是在同一个内存地址上的操作
o2.c = 'c' // (增)现在o.c也是'c'
delete o2.b // (删)现在o.b也不存在了
o2.a = 'a2' // (改)现在o.a也是'a2'
o2 = 'o2' // 现在变量o2被赋值'o2',已经和原来的内存地址断绝了关系,但变量 o 仍然指向老地址

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

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

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'

можно увидеть, кромеnullДругие основные типы суждений нормальны,typeof(null) === 'object'проверенная временем ошибка в самой первой версии JSnullИнформация о хранении в памяти000начало, и000начинается с будет оцениваться какobjectТипы. Хотя код внутренней оценки типа был изменен, эту ошибку необходимо сохранить в версии, потому что изменение этой ошибки вызовет ошибки на многих веб-сайтах.

typeofдля ссылочных типов, кроме возврата функцииfunction, все остальное возвращаетсяobject. Но массив в нашей разработке надо вернутьarrayтипа, такtypeofНе очень подходит для ссылочных типов. Обычно используется эталонный тип оценкиinstanceof:

var obj = {}
var arr = []
var fun = () => {}
typeof obj // 'object'
typeof arr // 'object'
typeof fun // 'function'
obj instanceof Object // true
arr instanceof Array // true
fun instanceof Function // true

можно увидетьinstanceofОператор может правильно определить тип ссылочного типа.instanceofПо сути, он оценивает конструктор справаprototypeЕсли объект существует в цепочке прототипов слева, возвращает true, если это так. Итак, будь то массивы, объекты или функции,... instanceof Objectоба возвращаютсяtrue.

Наконец, есть всемогущий метод типа суждения:Object.prototype.toString.call(...), можете попробовать сами.

принуждение

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

Сравнение на равенство базовых типов — совпадают ли значения, а сравнение на равенство объектов — совпадают ли адреса памяти. Вот интересное сравнение:

[] == [] // ?
[] == ![] // ?

для[] {} function (){}Для таких ссылочных типов, которые не присвоены переменным, они допустимы только в текущем операторе и не равны никаким другим объектам. Потому что просто нет возможности найти указатель на их адрес памяти. так[] == []даfalse.

для[] == ![], потому что это связано с принуждением, так что это намного сложнее. Если вы хотите узнать больше о преобразовании типа принуждения, вы можете увидеть меняэта статья.

В JS всего три случая преобразования типов:toNumber,toString,toBoolean. В нормальных условиях правила преобразования следующие:

Примитивное значение/тип тип цели: число результат
null number 0
symbol number бросать неправильно
string number '1'=>1 '1a'=>NaN, с нецифрами какNaN
множество number []=>0 ['1']=>1 ['1', '2']=>NaN
object/function/undefined number NaN
Примитивное значение/тип тип цели: строка результат
number string 1=>'1'
array string [1, 2]=>'1,2'
логическое значение/функция/символ string Исходное значение заключено в кавычки, например:'true' 'Sumbol()'
object string {}=>'[object Object]'
Примитивное значение/тип тип цели: логический результат
number boolean Кроме0,NaNдляfalse, остальныеtrue
string boolean за исключением пустой строкиfalse, остальныеtrue
null/undefined boolean false
тип ссылки boolean true

теперь, чтобы раскрыть[] == ![]вернутьtrueПравда:

[] == ![] // true
/*
 * 首先,布尔操作符!优先级更高,所以被转变为:[] == false
 * 其次,操作数存在布尔值false,将布尔值转为数字:[] == 0
 * 再次,操作数[]是对象,转为原始类型(先调用valueOf(),得到的还是[],再调用toString(),得到空字符串''):'' == 0
 * 最后,字符串和数字比较,转为数字:0 == 0
*/
NaN == NaN // false     NaN不等于任何值
null == undefined // true
null == 0 // false
undefined == 0 // false

объем

Область видимости в js — это лексическая область видимости, которая определяетсякогда функция объявленаопределяется местом. Лексическая область действия относится к набору правил доступа для идентификаторов функций, сгенерированных на этапе компиляции. В конце концов, область видимости js — это просто «пустое пространство», в котором нет реальных переменных, но которые определяют правила доступа к переменным. (Лексическая область видимости подтверждается на этапе компиляции, которая отличается от лексической области видимости. Динамическая область видимости подтверждается при выполнении функции. У js нет динамической области видимости, но у js нет динамической области видимости.thisОчень похоже на динамическую область видимости, о которой будет сказано позже. Языки также делятся на статические языки и динамические языки, К статическим языкам относятся языки, типы данных которых определяются на этапе компиляции, такие как java, и динамические языки, такие как javascript, типы данных которых определяется на этапе исполнения. )

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

event loop

js является однопоточным, все задачи нужно ставить в очередь, а следующая задача будет выполняться после завершения предыдущей задачи. Если первая задача занимает много времени, вторая задача должна ждать вечно. Однако IO-устройства (устройства ввода и вывода) очень медленные (например, Ajax-операции для чтения данных из сети), и js не может дождаться завершения IO-устройств, прежде чем продолжить выполнение следующей задачи, что теряет смысл этого язык. Таким образом, задачи js делятся на синхронные задачи и асинхронные задачи.

  1. Все задачи синхронизации выполняются в основном потоке, образуя «стек выполнения» (то есть стек на рисунке ниже);
  2. Все асинхронные задачи будут временно приостановлены, а дождавшись результата операции, его функция обратного вызова войдет в «очередь задач» (task queue) для ожидания в очереди;
  3. Когда все задачи синхронизации в стеке выполнения будут завершены, будет прочитана первая функция обратного вызова в очереди задач, и функция обратного вызова будет помещена в стек выполнения для начала выполнения;
  4. Основной поток продолжает повторять третий шаг в цикле, который является рабочим механизмом «цикла событий».

На приведенном выше рисунке при выполнении основного потока генерируются куча и стек. Куча используется для хранения ссылочных типов, таких как объекты массива. Код в стеке вызывает различные внешние API, которые добавляются в "задачу". очередь». события (щелчок, загрузка, выполнение). Пока код в стеке выполняется, основной поток будет читать «очередь задач» и по очереди выполнять функции обратного вызова, соответствующие этим событиям.

В очереди задаватся две асинхронные задачи, один макрос, в том числеscript setTimeout setIntervalи т. д., другой — микрозадачи, в том числеPromise process.nextTick MutationObserverЖдать. Всякий раз, когда запускается js-скрипт, он будет выполняться первымscriptКогда задача синхронизации в стеке выполнения будет завершена, первая задача в микрозадаче будет выполнена и помещена в стек выполнения для выполнения.Когда стек выполнения пуст, микрозадача будет прочитана и выполнена снова, и цикл повторится пока Список микрозадач не опустеет. Когда список микрозадач пуст, первая задача в макрозадаче будет прочитана и помещена в стек выполнения для выполнения. микрозадача пуста и так далее.

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

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

let fn, bar; // 1、进入全局上下文环境
bar = function(x) {
  let b = 5;
  fn(x + b); // 3、进入fn函数上下文环境
};
fn = function(y) {
  let c = 5;
  console.log(y + c); //4、fn出栈,bar出栈
};
bar(10); // 2、进入bar函数上下文环境

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

понимать выполнение функции

Цель этой статьи выведет ваше понимание процесса выполнения функции на более высокий уровень!

Процесс выполнения функции разделен на два этапа, первый этап — создание этапа среды контекста выполнения, а второй этап — этап выполнения кода:

  • Создание этапа контекста выполнения(Происходит, когда функция вызывается && до выполнения кода в теле функции).

  1. Чтобы создать переменный объект, этот процесс: создастargumentsОбъект, инициализировать переменные параметра функции ---> отметьте, чтобы создать текущую контекстную средуfunctionобъявление функции (так называемый подъем объявления функции) ---> проверка для создания текущего контекстаvarобъявление переменных (так называемый подъем переменных),let constутверждение;

  1. Установите цепочку областей видимости и определите правила поиска переменных в текущем контексте;
  2. Конечноthisуказатель на объект;
  • этап выполнения кода:
  1. Выполните код в теле функции, на этом этапе будет завершено назначение переменной, ссылка на функцию и выполнение другого кода.

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

Переменные объекты более подробно описаны в справочникеэта статья

это указывает на

let fn = function(){
  alert(this.name)
}
let obj = {
  name: '',
  fn
}
fn() // 方法1
obj.fn() // 方法2
fn.call(obj) // 方法3
let instance = new fn() // 方法4
  1. Вызов функции непосредственно в методе 1fn(), такой способ вызова похож на голого командира,thisнаправлениеwindow(в строгом режиме даundefined).
  2. Метод 2 — точечный вызовobj.fn(),В настоящее времяthisнаправлениеobjобъект. точка вызоваthisОтносится к объекту, предшествующему точке.
  3. Использование в методе 3callфункция положитьfnсерединаthisуказывает на первый параметр, вотobj. то есть использоватьcall,apply,bindфункция можетthisПеременная указывает на первый параметр.
  4. используется в методе 4newсоздал экземпляр объектаinstance, тогдаfnсерединаthisуказывает на экземплярinstance.

Что делать, если несколько правил выполняются одновременно? На самом деле приоритет вышеперечисленных четырех правил возрастает:

fn() < obj.fn() < fn.call(obj) < new fn()

первый,newВызов имеет наивысший приоритет, пока естьnewключевые слова,thisуказывает на сам экземпляр; тогда, если неnewключевые слова, естьcall、apply、bindфункция, тоthisуказывает на первый параметр, то если нетnew、call、apply、bind,Толькоobj.foo()Этот метод вызова точки,thisНаведите на объект перед точкой, наконец, полированный командирfoo()Этот способ вызова,thisнаправлениеwindow(в строгом режиме даundefined).

В es6 были добавлены стрелочные функции, и самая большая особенность стрелочных функций заключается в том, что у них нет собственныхthis、arguments、super、new.target, а стрелочная функция не имеет объекта-прототипаprototypeнельзя использовать в качестве конструктора (newФункция стрелки сообщит об ошибке).потому что у меня нет своегоthis, поэтому в стрелочной функцииthisНа самом деле, это относится к содержащей функцииthis. Будь то точечный вызов илиcallвызов, не может изменить функцию стрелкиthis.

Закрытие

Долгое время я придерживался поверхностного понимания замыканий как «функции, определенной внутри функции». На самом деле это лишь одно из необходимых условий образования замыкания. Только когда я прочитал определение замыкания в первом томе Кайла «JavaScript, которого вы не знаете», я вдруг понял:

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

let single = (function(){
  let count = 0
  return {
    plus(){
      count++
      return count
    },
    minus(){
      count--
      return count
    }
  }
})()
single.plus() // 1
single.minus() // 0

Это одноэлементный шаблон, этот шаблон возвращает объект и присваивает его переменнойsingle,Переменнаяsingleсодержит две функцииplusа такжеminus, и обе функции используют переменные в своей лексической области видимости.count. при нормальных обстоятельствахcountи контекст выполнения, в котором он находится, будет уничтожен в конце выполнения функции, но из-заcountвсе еще используется внешней средой, поэтому в конце выполнения функцииcountИ контекст выполнения, в котором он находится, не будет уничтожен, что приведет к замыканию. каждый звонокsingle.plus()илиsingle.minus(), это изменитcountПеременные изменяются, и эти две функции поддерживают ссылку на лексическую область видимости, в которой они расположены.

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

Взгляните на классический Amway:

// 方法1
for (var i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 1000)
}
// 方法2
for (let i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 1000)
}

В методе 1 в цикле задаются пять таймеров, через одну секунду будет выполнена функция обратного вызова в таймере и будет напечатана переменная.iценность . Излишне говорить, что секундой позжеiувеличилось до 6, поэтому таймер печатает 6 пять раз. (Переменная в текущей области не найдена в таймереi, поэтому по цепочке областей видимости в глобальной области видимостиi)

Способ 2, из-за es6letсоздает локальную область, поэтому цикл устанавливает пять областей, а переменные в пяти областяхiРаспределение 1-5, и в каждой области установлен таймер, и переменная печатается на одну секунду позжеiценность . Через одну секунду таймеры находят переменные из соответствующих родительских областей.iсоставляет 1-5. Это новый способ использования замыканий для разрешения исключений переменных в циклах.

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

Почти все объекты в js имеют специальный[[Prototype]]Встроенное свойство, используемое для указания объекта-прототипа объекта, это свойство по сути является ссылкой на другие объекты. Частная собственность обычно отображается в браузере.__proto__, по факту[[Prototype]]реализация браузера. если есть объектvar obj = {}, то можно пройтиobj.__proto__доступ к своему объекту-прототипуObject.prototype,Прямо сейчасobj.__proto__ === Object.prototype. объект имеет[[Prototype]]Указывает на объект-прототип, сам объект-прототип тоже является объектом и имеет свой собственный[[Prototype]]Указание на другие объекты-прототипы, которые соединены последовательно, образуя цепочку прототипов.

var obj = [1, 2, 3]
obj.__proto__ === Array.prototype // true
Number.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true
obj.toString()

Видно, что в приведенном выше примере естьobjприбытьnullЦепочка прототипов выглядит следующим образом:

obj----__proto__---->Array.prototype----__proto__---->Object.prototype----__proto__---->null

Последняя строка в приведенном выше примере вызываетobj.toString()метод, движок js ищет по этой цепочке прототиповtoStringметод. js первым вobjНайти на самом объектеtoStringметод; не найден, продолжаем поиск по цепочке прототиповArray.prototypeесть наtoString; не найдено, продолжайте цепочку прототипов сObject.prototypeНайти на. в конце концовObject.prototypeнайти наtoStringметод, так слезно названный метод. Это самая основная роль цепочки прототипов. Цепочка прототипов по-прежнему является сутью реализации наследования в js, которая будет обсуждаться в следующем разделе.

Выше я сказал "в jsПочти всеобъекты имеют особое[[Prototype]]встроенные свойства», почему не все из них? Потому что js не может создавать встроенные свойства[[Prototype]]Объект:

var o = Object.create(null)
o.__proto__ // undefined

Object.createэто метод es5, который уже поддерживается всеми браузерами. Этот метод создает и возвращает новый объект и присваивает объект-прототип нового объекта в качестве первого параметра. В приведенном выше примереObject.create(null)Создал новый объект и присвоил объект-прототип объекта объектуnull. объект в это времяoне является встроенным свойством[[Prototype]]из (не знаю, почемуo.__proto__нетnull, надеюсь, что большие ребята, которые знают, прокомментируют и пояснят, буду очень признателен).

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

Наследование js реализовано через цепочку прототипов, за подробностями обращайтесь к моемуэта статья, здесь я говорю только о "поведенческом делегировании", с которым вы, возможно, незнакомы. Делегирование поведения — это альтернатива наследованию, рекомендованная Кайлом, автором серии статей "JavaScript, которого вы не знаете". Этот режим в основном используетsetPrototypeOfМетод связывает встроенный прототип [[Protytype]] объекта с другим объектом для достижения цели наследования.

let SuperType = {
  initSuper(name) {
    this.name = name
    this.color = [1,2,3]
  },
  sayName() {
    alert(this.name)
  }
}
let SubType = {
  initSub(age) {
    this.age = age
  },
  sayAge() {
    alert(this.age)
  }
}
Object.setPrototypeOf(SubType,SuperType)
SubType.initSub('17')
SubType.initSuper('gim')
SubType.sayAge() // '17'
SubType.sayName() // 'gim'

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

kyle大佬倡导的行为委托