От работы в индустрии передовых технологий до настоящего момента, когда я чувствую, что добился наибольшего прогресса, было то, когда я планировал сменить работу и начать учиться в прошлом году, особенно когда я прочитал буклет о золотых самородках «Путь фронта». -Конец интервью" от yck. Именно в тот период обучения у меня постепенно появились смутные очертания системы знаний о фронтенде, а также я начал соприкасаться с интересной технологической платформой «Наггетс». Теперь, когда улеглась рабочая пыль, я начал лениться. По дороге на работу я снова начал играть в игры. Когда я возвращаюсь домой вечером, я начинаю играть в игры вместо чтения книг. В свободное время я начните лить воду в группе WeChat QQ group. Но приближаясь к 30 годам, дорога передо мной становится все более размытой. Я знаю, что у меня осталось не так много времени, чтобы наверстать упущенное. Первые три года работы мной потрачены зря, и если я не наверстаю упущенное за эти два года, то могу навсегда остаться на уровне младших и средних программистов. Имейте в виду, что я все еще старший джуниор фронтенд-разработчик, монах наполовину, а не из профессиональной школы, я поощряю себя!
Сяоган учитель
- Базовый тип и тип ссылки
- Типовое суждение
- принуждение
- объем
- event loop
- контекст выполнения
- понимать выполнение функции
- это указывает на
- Закрытие
- Прототипы и цепочки прототипов
- js наследование
Примитивные типы и ссылочные типы
Типы данных в 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 делятся на синхронные задачи и асинхронные задачи.
- Все задачи синхронизации выполняются в основном потоке, образуя «стек выполнения» (то есть стек на рисунке ниже);
- Все асинхронные задачи будут временно приостановлены, а дождавшись результата операции, его функция обратного вызова войдет в «очередь задач» (task queue) для ожидания в очереди;
- Когда все задачи синхронизации в стеке выполнения будут завершены, будет прочитана первая функция обратного вызова в очереди задач, и функция обратного вызова будет помещена в стек выполнения для начала выполнения;
- Основной поток продолжает повторять третий шаг в цикле, который является рабочим механизмом «цикла событий».
На приведенном выше рисунке при выполнении основного потока генерируются куча и стек. Куча используется для хранения ссылочных типов, таких как объекты массива. Код в стеке вызывает различные внешние 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函数上下文环境
Каждый раз, когда вызывается функция, в верхней части стека выполнения создается новый контекст выполнения. Нижняя часть стека всегда является глобальным контекстом, а вершина стека — контекстом текущего активного исполняемого кода.
понимать выполнение функции
Цель этой статьи выведет ваше понимание процесса выполнения функции на более высокий уровень!
Процесс выполнения функции разделен на два этапа, первый этап — создание этапа среды контекста выполнения, а второй этап — этап выполнения кода:
- Создание этапа контекста выполнения(Происходит, когда функция вызывается && до выполнения кода в теле функции).
- Чтобы создать переменный объект, этот процесс: создаст
arguments
Объект, инициализировать переменные параметра функции ---> отметьте, чтобы создать текущую контекстную средуfunction
объявление функции (так называемый подъем объявления функции) ---> проверка для создания текущего контекстаvar
объявление переменных (так называемый подъем переменных),let const
утверждение;
- Установите цепочку областей видимости и определите правила поиска переменных в текущем контексте;
- Конечно
this
указатель на объект;
- этап выполнения кода:
- Выполните код в теле функции, на этом этапе будет завершено назначение переменной, ссылка на функцию и выполнение другого кода.
Перед переходом к этапу выполнения свойства в объекте переменных все еще создаются, и доступ к ним недоступен. Однако после входа в фазу выполнения переменный объект создается и преобразуется в активный объект, и к свойствам в нем можно получить доступ, а затем запускаются операции в фазе выполнения. То есть единственная разница между переменным объектом и активным объектом — это разные времена жизни в контексте выполнения.
Переменные объекты более подробно описаны в справочникеэта статья
это указывает на
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
fn()
, такой способ вызова похож на голого командира,this
направлениеwindow
(в строгом режиме даundefined
). - Метод 2 — точечный вызов
obj.fn()
,В настоящее времяthis
направлениеobj
объект. точка вызоваthis
Относится к объекту, предшествующему точке. - Использование в методе 3
call
функция положитьfn
серединаthis
указывает на первый параметр, вотobj
. то есть использоватьcall
,apply
,bind
функция можетthis
Переменная указывает на первый параметр. - используется в методе 4
new
создал экземпляр объекта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
Встроенное происхождение включено, так что метод на родительском объекте может быть непосредственно вызывается на дочернем объекте. Основная цепь, генерируемая поведением, проста и очищает, чем наследование класса, которое ясно и ясно.