Список вопросов для интервью на этой неделе:
- Что такое закрытие? Какова роль закрытия?
- Методы достижения Promise.all
- Какие существуют методы асинхронной загрузки js-скриптов?
- Пожалуйста, реализуйте функцию flattenDeep для выравнивания вложенных массивов.
- Каковы характеристики итерируемых объектов?
15. Что такое замыкание? Какова роль замыканий?
Что такое закрытие?
Замыкания – это функции, которые имеют доступ к переменным в области видимости другой функции. Самый распространенный способ создания замыкания — создать функцию внутри другой функции.
создать замыкание
function foo() {
var a = 2;
return function fn() {
console.log(a);
}
}
let func = foo();
func(); //输出2
Замыкания позволяют функции продолжать доступ к лексической области видимости, в которой она была определена. Благодаря fn внутренняя область видимости foo не будет уничтожена после выполнения foo().
Независимо от того, каким образом внутренняя функция передается за пределы лексической области, в которой она находится, она будет содержать ссылку на область исходного определения, и замыкание будет использоваться везде, где функция выполняется. Такие как:
function foo() {
var a = 2;
function inner() {
console.log(a);
}
outer(inner);
}
function outer(fn){
fn(); //闭包
}
foo();
Роль замыканий
-
Возможность доступа к лексической области, в которой была определена функция (предотвращает ее повторное использование).
-
частная переменная
function base() {
let x = 10; //私有变量
return {
getX: function() {
return x;
}
}
}
let obj = base();
console.log(obj.getX()); //10
- Объем ложного блока
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(j){
return function () {
console.log(j);
}
})(i);
}
a[6](); // 6
- Создать модуль
function coolModule() {
let name = 'Yvette';
let age = 20;
function sayName() {
console.log(name);
}
function sayAge() {
console.log(age);
}
return {
sayName,
sayAge
}
}
let info = coolModule();
info.sayName(); //'Yvette'
Шаблон модуля имеет два обязательных условия (из «JavaScript, которого вы не знаете»)
- Должна быть внешняя объемлющая функция, которая должна быть вызвана хотя бы один раз (каждый вызов создает новый экземпляр модуля)
- Охватывающая функция должна возвращать как минимумОдинВнутренние функции, чтобы внутренние функции могли формировать замыкания в приватных областях и могли получать доступ или изменять приватное состояние.
Недостатки закрытия
Закрытие приведет к тому, что переменные функции будут храниться в памяти, слишком много закрытий может привести к утечке памяти.
16. Реализуйте метод Promise.all
Перед реализацией метода Promise.all мы должны сначала узнать функции и характеристики Promise.all, потому что мы сможем написать реализацию, когда поймем функции и характеристики Promise.all.
Обещание.все функции
Promise.all(iterable)
Возвращает новый экземпляр Promise. Этот экземпляр находится вiterable
все параметрыpromise
Всеfulfilled
или параметр не содержитpromise
, государство становитсяfulfilled
; если параметрpromise
есть сбойrejected
, обратный вызов этого экземпляра не удался, причина сбоя - первый сбойpromise
результат возврата.
let p = Promise.all([p1, p2, p3]);
Состояние p определяется p1,p2,p3, разделенными на следующие два случая:
(1) Только состояния p1, p2 и p3 становятсяfulfilled
, состояние p станетfulfilled
, в это время возвращаемые значения p1, p2 и p3 образуют массив, который передается функции обратного вызова p.
(2) До тех пор, пока один из p1, p2 и p3rejected
, состояние p становитсяrejected
, возвращаемое значение первого отклоненного экземпляра будет передано функции обратного вызова p.
Возможности Promise.all
Возвращаемое значение Promise.all является экземпляром обещания.
- Если переданный параметр является пустым итерируемым объектом,
Promise.all
МогуСинхронизироватьвозвращает завершенный статусpromise
- Если переданный параметр не содержит обещаний,
Promise.all
Могуасинхронныйвозвращает завершенный статусpromise
- В других случаях
Promise.all
вернутьОбработка (в ожидании)состояниеpromise
.
Состояние обещания, возвращенного Promise.all
- Если все обещания в переданных параметрах выполняются,
Promise.all
вернутьpromise
Становится полным асинхронно. - Если один из переданных параметров
promise
потерпеть неудачу,Promise.all
Асинхронно отправляет результат сбоя в функцию обратного вызова состояния сбоя, независимо от другихpromise
Готово - При любых условиях,
Promise.all
вернутьpromise
Результатом статуса завершения является массив
Реализация Promise.all
Рассмотрим только случай, когда входящий параметр является массивом
/** 仅考虑 promises 传入的是数组的情况时 */
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++ ) {
//考虑到 i 可能是 thenable 对象也可能是普通值
Promise.resolve(promises[i]).then(data => {
result[i] = data;
if (++index === promises.length) {
//所有的 promises 状态都是 fulfilled,promise.all返回的实例才变成 fulfilled 态
resolve(result);
}
}, err => {
reject(err);
return;
});
}
}
});
}
Можно протестировать с кодом на MDN
Рассмотрим итерируемые объекты
Promise.all = function (promises) {
/** promises 是一个可迭代对象,省略对参数类型的判断 */
return new Promise((resolve, reject) => {
if (promises.length === 0) {
//如果传入的参数是空的可迭代对象
return resolve([]);
} else {
let result = [];
let index = 0;
let j = 0;
for (let value of promises) {
(function (i) {
Promise.resolve(value).then(data => {
result[i] = data; //保证顺序
index++;
if (index === j) {
//此时的j是length.
resolve(result);
}
}, err => {
//某个promise失败
reject(err);
return;
});
})(j)
j++; //length
}
}
});
}
Тестовый код:
let p2 = Promise.all({
a: 1,
[Symbol.iterator]() {
let index = 0;
return {
next() {
index++;
if (index == 1) {
return {
value: new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
}), done: false
}
} else if (index == 2) {
return {
value: new Promise((resolve, reject) => {
resolve(222);
}), done: false
}
} else if(index === 3) {
return {
value: 3, done: false
}
}else {
return { done: true }
}
}
}
}
});
setTimeout(() => {
console.log(p2)
}, 200);
17. Какие существуют методы асинхронной загрузки js-скриптов?
<script>
добавлено на ярлыкasync
(html5) илиdefer
(html4), сценарий будет загружаться асинхронно.
<script src="../XXX.js" defer></script>
defer
а такжеasync
Разница в следующем:
-
defer
Дождаться, пока вся страница нормально отрисуется в памяти (полностью сгенерируется DOM-структура и исполнятся другие скрипты), и выполнить перед window.onload; -
async
После завершения загрузки механизм рендеринга прервет рендеринг и после выполнения этого скрипта продолжит рендеринг. - Если есть несколько
defer
Скрипты загружаются в том порядке, в котором они появляются на странице - несколько
async
Скрипты не гарантируют порядок загрузки
динамически созданныйscript
Этикетка
динамически созданныйscript
,настраиватьsrc
Он не начнет загрузку, но при добавлении в документ JS-файл начнет скачиваться.
let script = document.createElement('script');
script.src = 'XXX.js';
// 添加到html文件中才会开始下载
document.body.append(script);
XHR загружает JS асинхронно
let xhr = new XMLHttpRequest();
xhr.open("get", "js/xxx.js",true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
eval(xhr.responseText);
}
}
18. Пожалуйста, реализуйте функцию flattenDeep для выравнивания вложенных массивов.
Использование Array.prototype.flat
ES6 добавляет новые экземпляры массиваflat
метод для «выравнивания» вложенного массива в одномерный массив. Этот метод возвращает новый массив и не влияет на исходный массив.
flat
По умолчанию будет "сведен" только один слой. Если вы хотите "сгладить" многоуровневый вложенный массив, вам нужно датьflat
Передайте целое число, представляющее количество слоев, которые вы хотите свести.
function flattenDeep(arr, deepLength) {
return arr.flat(deepLength);
}
console.log(flattenDeep([1, [2, [3, [4]], 5]], 3));
Когда переданное целое число больше, чем количество вложенных уровней массива, массив будет сведен в одномерный массив.Максимальное число, которое может представлять JS, равноMath.pow(2, 53) - 1
, поэтому мы можем определитьflattenDeep
функция
function flattenDeep(arr) {
//当然,大多时候我们并不会有这么多层级的嵌套
return arr.flat(Math.pow(2,53) - 1);
}
console.log(flattenDeep([1, [2, [3, [4]], 5]]));
Использование сокращения и объединения
function flattenDeep(arr){
return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
console.log(flattenDeep([1, [2, [3, [4]], 5]]));
Используйте стек для бесконечного удаления вложенных многоуровневых вложенных массивов
function flattenDeep(input) {
const stack = [...input];
const res = [];
while (stack.length) {
// 使用 pop 从 stack 中取出并移除值
const next = stack.pop();
if (Array.isArray(next)) {
// 使用 push 送回内层数组中的元素,不会改动原始输入 original input
stack.push(...next);
} else {
res.push(next);
}
}
// 使用 reverse 恢复原数组的顺序
return res.reverse();
}
console.log(flattenDeep([1, [2, [3, [4]], 5]]));
19. Каковы характеристики итерируемых объектов
ES6 указывает, что по умолчаниюIterator
Интерфейс развернут в структуре данныхSymbol.iterator
Атрибуты, с другой точки зрения, также могут рассматриваться как структура данных, если ониSymbol.iterator
Атрибуты(Symbol.iterator
Метод соответствует функции генерации обходчика, которая возвращает объект обходчика), то его можно считать итерируемым.
Особенности итерируемых объектов
- имеют
Symbol.iterator
Атрибуты,Symbol.iterator()
Возвращает объект итератора - можно использовать
for ... of
сделать петлю
let arry = [1, 2, 3, 4];
let iter = arry[Symbol.iterator]();
console.log(iter.next()); //{ value: 1, done: false }
console.log(iter.next()); //{ value: 2, done: false }
console.log(iter.next()); //{ value: 3, done: false }
Родной имеетIterator
Структура данных интерфейса:
- Array
- Map
- Set
- String
- TypedArray
- объект аргументов функции
- Объект NodeList
Настройка итерируемого объекта
Мы сказали выше, что объект может иметь только правильноеSymbol.iterator
свойство, то оно является итерируемым, поэтому мы можем добавлять объекты, добавляяSymbol.iterator
Сделайте его итерируемым.
let obj = {
name: "Yvette",
age: 18,
job: 'engineer',
*[Symbol.iterator]() {
const self = this;
const keys = Object.keys(self);
for (let index = 0; index < keys.length; index++) {
yield self[keys[index]];//yield表达式仅能使用在 Generator 函数中
}
}
};
for (var key of obj) {
console.log(key); //Yvette 18 engineer
}
Справочная статья:
[1] MDN Promise.all
[2] Promise
[3] Iterator
Спасибо за вашу готовность потратить свое драгоценное время на чтение этой статьи. Если эта статья дала вам небольшую помощь или вдохновение, пожалуйста, не скупитесь на лайки и звезды. Вы, безусловно, самая большая движущая сила для меня, чтобы двигаться вперед .GitHub.com/Иветт Л.А. Ю/Б…