Очки продвинутых знаний JavaScript

внешний интерфейс JavaScript
Очки продвинутых знаний JavaScript

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

пользовательское событие

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

// 注册事件  不可以添加参数
let eve1 = new Event("myClick")
// 可以添加参数 
let eve2 = new CustomEvent('myClick',params)

// 监听事件
dom.addEventListener("myClick",function () {
    console.log("myClick")
})

// 触发事件
dom.dispatchEvent(eve1)

вар, пусть, константа

разница var let const
Есть ли область действия блока × ✔️ ✔️
Инициировать ли продвижение ✔️ × ×
Можно ли повторить утверждение ✔️ × ×
Можно ли переназначить ✔️ ✔️ ×
Нужно ли устанавливать начальное значение × × ✔️
добавлять ли глобальные свойства ✔️ × ×
Есть ли временная мертвая зона × ✔️ ✔️

Временная мертвая зона: переменная создана (с продвижением переменной), но не инициализирована, и переменная не может быть использована.Прямое использование войдет во временную мертвую зону

Набор, Карта

Setа такжеMapсильные ссылки(инструкции ниже),Можно пройти, например for of/forEach

Set

Позволяет хранить любой тип уникального значения, только значение ключа (ключ) без имени ключа, общий методadd,size,has,deleteПодожди, посмотри

const set1 = new Set()
set1.add(1)
const set2 = new Set([1,2,3])
set2.add('沐华')
console.log(set1) // { 1 }

console.log(set2) // { 1, 2, 3, '沐华' }
console.log(set2.size) // 4
console.log(set2.has('沐华')) // true
set2.delete('沐华')
console.log(set2) // { 1, 2, 3 }

// 用 Set 去重

const set3 = new Set([1, 2, 1, 1, 3, 2])
const arr = [...set3]
console.log(set3) // { 1, 2, 3 }
console.log(arr) // [1, 2, 3]

// 引用类型指针不一样,无法去重
const set4 = new Set([1, { name: '沐华' }, 1, 2, { name: '沐华' }])
console.log(set4) // { 1, { name: '沐华' }, 2, { name: '沐华' } }

// 引用类型指针一样,就可以去重
const obj = { name: '沐华' }
const set5 = new Set([1, obj, 1, 2, obj])
console.log(set5) // { 1, { name: '沐华' }, 2 }

Map

представляет собой набор пар ключ-значение; общие методыset,get,size,has,deleteПодождите, посмотрите, как использовать

const map1 = new Map()
map1.set(0, 1)
map1.set(true, 2)
map1.set(function(){}, 3)
const map2 = new Map([ [0, 1], [true, 2], [{ name: '沐华' }, 3] ])
console.log(map1) // {0 => 1, true => 2, function(){} => 3}
console.log(map2) // {0 => 1, true => 2, {…} => 3}

console.log(map1.size) // 3
console.log(map1.get(true)) // 2
console.log(map1.has(true)) // true
map1.delete(true)
console.log(map1) // {0 => 1, function(){} => 3}

Слабый набор, слабая карта

WeakSetа такжеWeakMapслабые ссылки, даGCБолее дружелюбный, не может пройти

Например: пусть obj = {}

По умолчанию создается сильноссылочный объект.Только при ручном задании obj=null он будет переработан механизмом сборки мусора, если ссылки нет.Если это слабоссылочный объект, то механизм сборки мусора нам автоматически поможет recycle, some В этом случае производительность более выгодна, например, используется для сохранения DOM-узлов, и не так просто вызвать утечку памяти

WeakSet

Члены могут быть только объектами или массивами, методы могут быть толькоadd,has,delete, см. использование

const ws1 = new WeakSet()
let obj = { name: '沐华' }
ws1.add(obj)
ws1.add(function(){})
console.log(ws1) // { function(){}, { name: '沐华' } }
console.log(ws1.has(obj)) // true

ws1.delete(obj)
console.log(ws1.has(obj)) // false

WeakMap

Набор пар ключ-значение, в качестве ключей могут использоваться только объекты (кроме null), а значение может быть произвольным. только методget,set,has,delete, см. использование

const wm1 = new WeakMap()

const o1 = { name: '沐华' },
      o2 = function(){},
      o3 = window

wm1.set(o1, 1) // { { name: '沐华' } : 1 } 这样的键值对
wm1.set(o2, undefined)
wm1.set(o1, o3); // value可以是任意值,包括一个对象或一个函数
wm1.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个WeakMap对象

wm1.get(o1); // 1 获取键值

wm1.has(o1); // true  有这个键名
wm1.has(o2); // true 即使值是undefined

wm1.delete(o1);
wm1.has(o1);   // false

тип данных

базовый тип:Number, String, Boolean, undefined, null, Symbol, BigInt

сложный тип:Object(Function/Array/Date/RegExp/Math/Set/Map...)

Определение типа

typeof

базовый (примитивный) тип

typeof 1 === "number" // true
typeof "a" === "string" // true
typeof true === "boolean" // true
typeof undefined === "undefined" // true
typeof Symbol() === "symbol" // true

тип ссылки

typeof null === "object" // true
typeof {} === "object" // true
typeof [] === "object" // true
typeof function () {} === "function" // true

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

instanceof

Можно ли судить, встречается ли ссылочный тип где-либо в цепочке прототипов типа? Обычно определяют, принадлежит ли переменная экземпляру объекта

console.log(null instanceof Object) // false
console.log({} instanceof Object) // true
console.log([] instanceof Object) // true
console.log(function(){} instanceof Object) // true

toString

let toString = Object.prototype.toString

console.log(toString.call(1)) // [object Number]
console.log(toString.call('1')) // [object String]
console.log(toString.call(true)) // [object Boolean]
console.log(toString.call(undefined)) // [object Undefined]
console.log(toString.call(null)) // [object Null]
console.log(toString.call({})) // [object Object]
console.log(toString.call([])) // [object Array]
console.log(toString.call(function(){})) // [object Function]
console.log(toString.call(new Date)) // [object Date]

это не плохо

другие суждения

// constructor  判断是否是该类型直接继承者
let A = function(){}
let B = new A()
A.constructor === Object // false
A.constructor === Function // true
B.constructor === Function // false
B.constructor === A // true

// 判断数组
console.log(Array.isArray([])) // true
// 判断数字
function isNumber(num) {
  let reg = /^[0-9]+.?[0-9]*$/
  if (reg.test(num)) {
    return true
  }
  return false
}

//封装获取数据类型
function getType(obj){
    let type = typeof obj
    if(type !== 'object'){
        return type
    }
    return Object.prototype.toString.call(obj)
}

// 含隐式类型转换 继续往下看

// 判断数字
function isNumber(num) {  
    return num === +num
}
// 判断字符串
function isString(str) {  
    return str === str+""
}
// 判断布尔值
function isBoolean(bool) {  
    return bool === !!bool
}

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

В JS нет строгих типов данных, поэтому их можно конвертировать друг в друга

  • Преобразование типа дисплея:Number(), String(), Boolean()
  • Неявное преобразование типов: четыре арифметических операции, оценочные операторы,Nativeпередача,JSONметод

показать переход

1. Number()

console.log(Number(1)) // 1
console.log(Number("1")) // 1
console.log(Number("1a")) // NaN
console.log(Number(true)) // 1
console.log(Number(undefined)) // NaN
console.log(Number(null)) // 0
console.log(Number({a:1})) // NaN 原因往下看

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

  • Числовое значение: исходное значение после преобразования
  • Строка: если ее можно преобразовать в значение, преобразовать в соответствующее значение, в противном случае получитьNaN, пустая строка получает 0
  • Логическое значение:trueПревращается в 1, ложь в 0
  • undefined: обратиться кNaN
  • null: повернуть на 0

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

let a = {a:1}
console.log(Number(a)) // NaN
// 原理
a.valueOf() // {a:1}
a.toString() // "[object Object]"
Number("[object Object]") // NaN
  • вызвать сам объектvalueOfметод, если метод возвращает значение примитивного типа (числовое, строковое и логическое), используйте это значение напрямуюNumberметод, не продолжать
  • еслиvalueOfМетод возвращает значение составного типа, а затем вызывает собственное значение объекта.toStringметод, еслиtoStringметод возвращает значение примитивного типа, затем используйтеNumberметод, не продолжать
  • еслиtoStringЕсли метод возвращает значение композитного типа, сообщается об ошибке

2. String()

console.log(String(1)) // "1"
console.log(String("1")) // "1"
console.log(String(true)) // "true"
console.log(String(undefined)) // "undefined"
console.log(String(null)) // "null"
console.log(String({b:1})) // "[object Object]" 原因往下看

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

  • Числовой: преобразовать в соответствующую строку
  • Строка: исходное значение после преобразования
  • Логическое значение:trueк «истине»,falseна "ложный"
  • undefined: перейти к «неопределенному»
  • null: превратить в "ноль"

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

let b = {b:1}
console.log(String(b)) // "[object Object]"
// 原理
b.toString() // "[object Object]"
// b.valueOf() 由于返回的不是复合类型所以没有调valueOf()
String("[object Object]") // "[object Object]"
  • сначала позвониtoStringметод, еслиtoStringметод возвращает значение примитивного типа, затем используйтеStringметод, не продолжать
  • еслиtoStringМетод возвращает значение составного типа, а затем вызываетvalueOfметод, еслиvalueOfметод возвращает значение примитивного типа, затем используйтеStringметод, не продолжать
  • еслиvalueOfЕсли метод возвращает значение составного типа, сообщается об ошибке

3. Boolean()

console.log(Boolean(0)) // flase
console.log(Boolean(-0)) // flase
console.log(Boolean("")) // flase
console.log(Boolean(null)) // flase
console.log(Boolean(undefined)) // flase
console.log(Boolean(NaN)) // flase

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

  • 0
  • -0
  • ""
  • null
  • undefined
  • NaN

Вышеупомянутое равномерно превращается в ложь, а все остальные верны

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

// 四则运算  如把String隐式转换成Number
console.log(+'1' === 1) // true

// 判断语句  如把String隐式转为Boolean
if ('1') console.log(true) // true

// Native调用  如把Object隐式转为String
alert({a:1}) // "[object Object]"
console.log(([][[]]+[])[+!![]]+([]+{})[!+[]+!![]]) // "nb"

// JSON方法 如把String隐式转为Object
console.log(JSON.parse("{a:1}")) // {a:1}

Несколько неявных вопросов о преобразовании

console.log( true+true  ) // 2                   解:true相加是用四则运算隐式转换Number 就是1+1
console.log(  1+{a:1}   ) // "1[object Object]"  解:上面说了Native调用{a:1}为"[object Object]"  数字1+字符串直接拼接
console.log(   []+[]    ) // ""                  解:String([]) =》 [].toString() = "" =》 ""+"" =》 ""
console.log(   []+{}    ) // "[object Object]"   解:"" + String({}) =》 "" + {}.toString() = "" + "[object Object]" =》 "[object Object]"
console.log(   {}+[]    ) // 0                   解:{}当作代码块不执行,[]转为"",结果是 +"" 就是0
console.log(   {}+{}    ) // 谷歌和Node:"[object Object][object Object]"  火狐:NaN

Приоритет оператора, рисунок из MDN

1616464339515.jpg

this

То, на что он указывает, полностью зависит от того, где была вызвана функция, вEs5где this всегда указывает на объект, вызвавший его, а вEs6В функции стрелки нет этой привязки, это указывает на это в области, где определена функция стрелки.

судить об этом

  • Глобальная область видимости, самовыполняющиеся функции и функции без стрелок, передаваемые таймерами, указывают на это.window
  • строгий режим (use strict) при этом указывает наundefined
  • это в конструкторе указывает на текущий экземпляр
  • Это в функции привязки событий указывает на текущий связанный элемент
  • Это в функции стрелки указывает на это в родительской области, где функция стрелки определена

Измените это, чтобы указать на

  • использоватьcall, apply, bind, при вызове и применении измените эту точку, функция будет выполнена немедленно, и привязка не будет
  • сохранить как переменную (let self = this)
  • Используйте стрелочные функции
  • использоватьnewсоздать экземпляр объекта
  • Вызов функции непосредственно в строгом режиме указывает на неопределенность

Жесткая привязка this стрелочной функции не может быть изменена, например, fn.call(window), и после присвоения fn свойству объекта метод this вызывающего объекта по-прежнему остается окном

стрелочная функция

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

Закрытие

Закрытие означает, что функция имеет доступ к переменным во внешней области видимости.Эта функция является замыканием, поэтому все функции JS являются замыканиями, потому что все они являются объектами и связаны с цепочкой областей видимости.

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

  • Внутренняя функция имеет доступ к локальным переменным внешней функции.

недостаток:

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

мелкая копия

Первый уровень заключается в том, что ссылочный тип копирует указатель, а не значение. копировать стек не копирует кучу

// 1. 展开运算符 ...
let obj1 = { a:1, b:{ c:3 } }
let obj2 = { ...obj1 }
obj1.a = 'a'
obj1.b.c = 'c'
console.log(obj1) // { a:'a', b:{ c:'c' } }
console.log(obj2) // { a:1, b:{ c:'c' } }

// 2. Object.assign() 把obj2合并到obj1
Object.assign(obj1, obj2)

// 3. 手写
function clone(target){
    let obj = {}
    for(let key in target){
        obj[key] = target[key]
    }
    return obj
}

// 4. 数组浅拷贝  用Array方法 concat()和slice()
let arr1 = [ 1,2,{ c:3 } ]
let arr2 = arr1.concat()
let arr3 = arr1.slice()

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

Копирование стека также копирует кучу и переоткрывает часть памяти

1. JSON.parse(JSON.stringify())

let obj1 = { a:1, b:{ c:3 } }
let obj2 = JSON.parse(JSON.stringify(obj1))
obj1.a = 'a'
obj1.b.c = 'c'
console.log(obj1) // { a:'a', b:{ c:'c' } }
console.log(obj2) // { a:1, b:{ c:3 } }

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

2. Рукописная рекурсия

Принцип заключается в рекурсивном обходе объекта/массива до тех пор, пока он не будет заполнен базовыми типами, а затем копированием

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

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

Это место для хранения может храниться какkey-valueформа иkeyМожет быть ссылочным типом, выберитеMapэту структуру данных. Проверяем, есть ли на карте клонированные объекты, если есть, возвращаем напрямую, если нет, используем текущий объект как ключ, клонированный объект как хранилище значений и продолжаем клонировать

function clone(target, map = new Map()){
    if (typeof target === 'object') { // 引用类型才继续深拷贝
        let obj = Array.isArray(target) ? [] : {} // 考虑数组
        //防止循环引用
        if (map.get(target)) {
            return map.get(target) // 有拷贝记录就直接返回
        }
        map.set(target,obj) // 没有就存储拷贝记录
        for (let key in target) {
            obj[key] = clone(target[key]) // 递归
        }
        return obj
    } else {
        return target
    }
}

Оптимизировано

  • использоватьWeakMapзаменятьMap, сказано вышеWeakMapслабая ссылка,Mapявляется сильной ссылкой
  • Выберите цикл с лучшей производительностью

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

// 用while来实现一个通用的forEach遍历
function forEach(array, iteratee) {
    let index = -1;
    const length = array.length;
    while (++index < length) {
        iteratee(array[index], index);
    }
    return array;
}
// WeakMap 对象是键/值对集合,键必须是对象,而且是弱引用的,值可以是任意的
function clone(target, map = new WeakMap()){
    // 引用类型才继续深拷贝 
    if (target instanceof Object) {
        const isArray = Array.isArray(target)
        // 克隆对象和数组类型
        let cloneTarget = isArray ? [] : {} 
        
        // 防止循环引用
        if (map.get(target)) {
            // 有拷贝记录就直接返回
            return map.get(target) 
        }
        // 没有就存储拷贝记录
        map.set(target,cloneTarget) 
        
        // 是对象就拿出同级的键集合  返回是数组格式
        const keys = isArray ? undefined : Object.keys(target)
        // value是对象的key或者数组的值 key是下标 
        forEach(keys || target, (value, key) => { 
            if (keys) {
                // 是对象就把下标换成value
                key = value 
            }
            // 递归
            cloneTarget[key] = clone(target[key], map) 
        })
        return cloneTarget
    } else {
        return target
    }
}

new

новый что ты сделал?

  • Создать пустой объект в отдельном пространстве памяти
  • поместите прототип конструктора этого объекта (__proto__) указывает на объект-прототип функцииprototype, и привяжите это
  • Выполнить код в конструкторе
  • Если у конструктора есть возврат, он возвращает значение возврата, если нет, он автоматически возвращает пустой объект, который является этим

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

function Person(name){
    this.name = name
    console.log(this) // { name: '沐华' }
    return { age: 18 }
}
const p = new Person('沐华')
console.log(p) // { age: 18 }

прототип

мы все знаемnewПосле создания нового экземпляра мы можем получить к нему прямой доступ, ничего не делая.toString(),valueOf()Подождите какие-то методы, откуда эти методы взялись?

ответпрототип, давайте сначала посмотрим на картинку

a.jpg

Сравните картинку, давайте посмотрим на несколько строк кода

function Parent(){} // 这就是构造函数
let child = new Parent() // child就是实例

Parent.prototype.getName = function(){ console.log('沐华') } // getName是构造函数的原型对象上的方法
child.getName() // '沐华' 这是继承来的方法
  • prototype: это прототип объекта конструктора. Каждая функция будет иметь этот атрибут. Чтобы подчеркнуть, это функция, а другие объекты не имеют этого атрибута.
  • __proto__: указывает на прототип объекта конструктора. Каждый объект обладает этим свойством. Подчеркнем, это объект. Точно так же, поскольку функции также являются объектами, функции также обладают этим свойством. Однако доступ к прототипу объекта (child.__proto__), рекомендуется использоватьEs6изReflect.getPrototypeOf(child)илиObject.getPrototypeOf(child)метод
  • constructor: это свойство объекта-прототипа, указывающее на конструктор, то есть в коде.Parent.prototype.constructor === Parentэто дляtrueиз

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

Каждый объект имеет_proto_Свойство указывает на объект-прототип, а объект-прототип тоже является объектом, поэтому существуют также_proto_Объект-прототип, который указывает на объект-прототип, слой за слоем, формирует цепочку отношений, которая原型链

Цепочка прототипов также определяет способ наследования в js, когда мы обращаемся к свойству:

  • Сначала получите доступ к свойствам экземпляра объекта, вернитесь, если он найден, пропустите, если нет__proto__перейти к объекту-прототипу
  • Если он найден в объекте-прототипе, он вернется и не будет продолжаться через прототип.__proto__Погляди
  • доObject.prototype, вернуться, если найдено, вернуться, если не найденоundefined, больше не нашел

Верхним объектом цепочки прототипов являетсяObject,ЭтоObjectКто является прототипом конструктора?

Ответ сам по себе, егоconstructorнаправлениеObject, в то время как его_proto_затем указывает наnull

Загрязнение прототипа

Загрязнение прототипа означает, что злоумышленник каким-то образом модифицирует прототип js.

Object.prototype.toString = function () {alert('原生方法被改写,已完成原型污染')};

Как решить проблему загрязнения прототипа

  1. использоватьObject.freeze(obj)Заморозить объект, тогда свойства нельзя будет изменить и он станет нерасширяемым объектом
Object.freeze(Object.prototype)
Object.prototype.toString = 'hello'
console.log(Object.prototype.toString) // ƒ toString() { [native code] }
  1. Вместо буквальной формы используйтеObject.create(null)Создает новый объект без прототипа, поэтому любое расширение прототипа не вступит в силу.
const obj = Object.create(null)
console.log(obj.__proto__) // => undefined
  1. использоватьMapтип данных вместоObjectТипы

    MapОбъекты содержат коллекции пар ключ/значение. Любое значение (объект или примитив) может быть ключом или значением. Таким образом, использование структуры данных карты не будет загрязнять прототип объекта.

    Разница между картой и объектом

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

есть ситуация

JSON.parse('{ a:1, __proto__: { b: 2 }}')

результат не будет перезаписанObject.prototype,потому чтоV8 будет автоматически игнорировать ключ с именем __proto__ в JSON.parse.

наследовать

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

Наследование ES6

class Parent(){
    constructor(props){
        this.name = '沐华'
    }
}
// 继承
class Child extends Parent{
    // props是继承过来的属性, myAttr是自己的属性
    constructor(props, myAttr){
        // 调用父类的构造函数,相当于获得父类的this指向
        super(props)
    }
}
console.log(new Child().name) // 沐华

Хотя сейчас используется ES6class, но на собеседованиях по наследованию ES5 все равно будут спрашивать

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

В ES5 существует множество способов наследования, таких как наследование по цепочке прототипов, наследование композиции, паразитное наследование и т. д. Достаточно понять одно интервью

function Parent(){}
Parent.prototype.getName = function(){ return '沐华' }

function Child(){}
// 方式一
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child // 重新指定 constructor
// 方式二
Child.prototype = Object.create(Parent.prototype,{
    constructor:{
        value: Child,
        writable: true, // 属性能不能修改
        enumerable: true, // 属性能不能枚举(可遍历性),比如在 for in/Object.keys/JSON.stringify
        configurable: true, // 属性能不能修改属性描述对象和能否删除
    }
})

console.log(new Child().getName) // 沐华

объем

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

Es6Раньше имелась только глобальная область действия и область действия функции,Es6Добавлена ​​область действия блока (letа такжеconst)

未标题-111.jpg

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

И этот слой вложенных областей видимости формируется作用域链

Делайте вопросы, понимайте масштаб

function foo(){
  console.log(a);
}
function bar(){
  var a = 3;
  foo();
}
var a = 2;
bar()

множество

Запомните несколько методов, которые изменят исходный массив

pop,push,shift,unshift,reverse,sort,splice,fill,copyWithin

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

Обзор методов массива JS и трудоемкая статистика методов обхода

вывоз мусора

V8 реализует алгоритм GC и использует механизм сборки мусора поколения, поэтому V8 делит память кучи на新生代(вторичный сборщик мусора) и老生代(главный сборщик мусора) две части

Кайнозой

Новое поколение обычно поддерживает только емкость 1~8M, поэтому основнойХраните объекты с более коротким временем жизни

использовать в новом поколенииScavenge GCАлгоритм делит пространство новой генерации на две области: область объекта и свободную область. Как показано на рисунке:

4f9310c7da631fa5a57f871099bfbeaf.webp

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

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

Поскольку пространство в новом поколении невелико, его легко заполнить, поэтому

  • Объекты, пережившие две сборки мусора, будут перемещены в пространство старого поколения.
  • Если доля объектов свободного пространства превышает 25%, чтобы не влиять на выделение памяти, объекты будут перемещены в пространство старого поколения.

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

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

используется в старом поколении标记清除算法а также标记压缩算法. Потому что, если также используется алгоритм Scavenge GC, для копирования больших объектов потребуется больше времени.

пометить как очищенный

Алгоритм маркировки-развертки запускается первым в следующих случаях:

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

Процесс снятия маркировки выглядит следующим образом

  • Начиная с корня (глобальный объект js), пройдитесь по всем объектам в куче, а затем отметьте уцелевшие объекты
  • Уничтожайте неотмеченные объекты после завершения маркировки

Из-за этапа сборки мусора выполнение JS-скрипта будет приостановлено, а выполнение JS-скрипта будет возобновлено после завершения сборки мусора.Такое поведение называется全停顿(stop-the-world)

Например, если объем данных в куче превышает 1G, полная сборка мусора может занять более 1 секунды, на этот период выполнение JS-потока будет приостановлено, что снизит производительность и отзывчивость страницы.

Инкрементные маркеры

Итак, в 2011 году V8 перешел с флага «Остановить мир» на增量标记. Используя алгоритм инкрементной маркировки, сборщик мусора может разбить задачу утилизации на множество небольших задач и выполнять их в середине задач JS, избегая таким образом ситуации зависания приложения.

одновременная маркировка

Затем в 2018 году произошел еще один крупный прорыв в технологии ГХ, т.е.并发标记.Разрешить одновременный запуск JS, позволяя сборщику мусора сканировать и помечать объекты

сжатие тегов

После очистки это вызовет фрагментацию памяти в куче памяти, когда фрагментация превысит определенный предел, она начнется标记压缩算法, переместите оставшиеся объекты в один конец кучи и очистите ненужную память при перемещении всех объектов

цикл событий

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

Прочитав это, вы все еще не понимаете механизм выполнения JavaScript (EventLoop), вы приходите и бьете меня

Обещания, асинхронность, ожидание

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

PromiseКонструктор выполняется синхронно,thenМетоды выполняются асинхронно (микрозадачи)

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

слабость этоawaitЭто заблокирует код.Если ее последующий асинхронный код не будет зависеть от ее результатов, то все равно придется ждать ее завершения, и параллелизм будет потерян.В это время рекомендуется использоватьPromise.all

Посмотрите на пример и, кстати, просмотрите цикл событий

async function fun() {
    console.log(1)
    let a = await 2
    console.log(a)
    console.log(3)
}
console.log(4)
fun()
console.log(5)

Выходной результат:4 1 5 2 3

В сочетании с характеристиками async/await давайте переведем этот вопрос с ES6

function fun(){
    return new Promise(() => {
        console.log(1)
        Promise.resolve(2).then( a => {
            console.log(a)
            console.log(3)
        })
    })
}
console.log(4)
fun()
console.log(5)

Если вы хотите изучить промисы, вы можете прочитать эту статьюОбещание, ты действительно это понимаешь?

Наконец, задайте вопрос: какова связь между async/await и генератором после компиляции?

рукописный код

В этой части много контента, в том числе написанного от руки: анти-шейк, дросселирование, новое, привязка, применение, вызов, instanceof, Promise, Promise.all, Promise.race, AJAX....

Пожалуйста, перейдите, чтобы увидеть мою другую статьюХороший фундамент? 22 высокочастотных рукописных кода JavaScript, которые нужно понять

Эпилог

Много разрозненных заметок, наконец объединенных

сам перечитал, надеюсь поможет

Если что-то не так или отсутствует, поправьте меня

Также попросите лайк, большое спасибо ^_^