Глубокое понимание итератора Es6

внешний интерфейс ECMAScript 6

Добавить Автора
переведено изnuggets.capable/post/684490…

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

IteratorМожно сказать, что это очень важная функция ES6, а также краеугольный камень многих других функций.

ЗачемIteratorТакой высокий статус?

начать с переменной

var arr = ['红','绿','蓝'];

Это обычный массив, если я хочу получить каждый элемент его данных, что мне делать?

Это непросто, просто используйте цикл for.Если вы считаете, что цикл низкий, используйте forEach.

ок, сразу получай код

//for 循环
for (var i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

//forEach
arr.forEach(item => {
    console.log(item);
});
// 结果 略

хорошо, нет проблем.

Тогда продолжим, посмотрим на код ниже. Учитывая строку, что, если я хочу вывести каждый символ?

var str = '1234567890';

Что-то в этом не так, это просто смешно.

Вы можете использовать цикл for in или цикл for для обработки в виде массива.

Код сейчас

//for in
for (var s in str) {
    console.log(str[s]);
    //s 是 属性名称【key】
}

//转为数组
for (var i = 0; i < str.length; i++) {
    console.log(str[i]);
}

//或者转换为数组
Array.prototype.slice.call(str);

ноfor inОн не используется для получения данных, он будет проходить через все перечисляемые свойства объекта, включая свои собственные и цепочку прототипов, так что это не гарантируется.

эммм.... нет проблем, продолжим.

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

var map = new Map();
map.set('zhang', '1000w');
map.set('liu', '200w');
map.set('sun', '100w');

использоватьforEachВот и все.

map.forEach((val,key) => {
    console.log(val,key);
})

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

проблема найдена

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

Конечно, есть много способов и способов добиться этого.for 循环,forEach,for inЛа.

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

Кто сказал, что его можно унифицировать, его можно использоватьforEachдля перебора массива иmapОн поддерживается сам по себе, и я могу напрямую преобразовать строку в массив.

хорошо, в этом нет ничего плохого.

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

Есть ли лучший общий метод, который сделает его более удобным и крутым для разработчиков?

Ответ - да,es5когда он не появился, обновите доes6Вот оно.

NB для откусить

Этот способ унифицированного обхода различных структур данныхes6изfor ofцикл.

for ofпетля и древнийfor 循环Очень похожий. Разве это не просто новый синтаксис.

Цитируя предложение из книги г-на Жуана, я думаю, вы поймете его с первого взгляда.

В ES6 появился цикл for...of, заимствованный из языков C++, Java, C# и Python. Как единый способ обхода всех структур данных. Ключ заключается в унификации, а другой - в непосредственном получении значения, упрощении операции и отсутствии необходимости объявлять и поддерживать какие-либо переменные или преобразовывать данные.

оказалосьfor ofтак корова,forВы можете делать то, с чем не может справиться цикл.

Зачемfor ofБлагодаря этой возможности можно обеспечить единый метод сбора данных для различных структур данных.

for ofДействительно ли он такой мощный, и какой механизм стоит за ним?

фактическиfor ofНе очень сильный, он простоES6Просто новый синтаксис.

Не все объекты можно использоватьfor of, только если реализоватьIteratorОбъекты интерфейса можно использовать толькоfor ofдля обхода значения.

такfor ofПросто синтаксический сахар, настоящий геройIterator.

What ? Iterator.....

Дебют главного героя - Iterator iterator

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

На самом деле функция Iterator существовала много лет назад во многих внутренних языках, таких как java, C++, C# и т. д.

Поскольку это интерфейс, как мы должны реализовать этот интерфейс? Как выглядят правила реализации?

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

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

Полученное значение представляет собойobject, содержит два свойства,valueа такжеdone.

valueУказывает конкретное возвращаемое значение,doneЭто логическое значение, указывающее, является ли набор прохождений или если есть последующие данные, доступные данные, доступные данныеtrue, иначе возвратfalse.

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

Посмотрите на реализацию кода

function getIterator(list) {
    var i = 0;
    return {
        next: function() {
            var done = (i >= list.length);
            var value = !done ? list[i++] : undefined;
            return {
                done: done,
                value: value
            };
        }
    };
}
var it = getIterator(['a', 'b', 'c']);
console.log(it.next());// {value: "a", done: false}
console.log(it.next());// {value: "b", done: false}
console.log(it.next());// {value: "c", done: false}
console.log(it.next());// "{ value: undefined, done: true }"
console.log(it.next());// "{ value: undefined, done: true }"
console.log(it.next());// "{ value: undefined, done: true }"

Приведенный выше код представляет собой смоделированную реализацию, основанную на базовой концепции итераторов.

  • getIteratorметод возвращает объект - итерируемый объект

  • объект имеетnextметод,nextУказатель хранится внутри метода через замыканиеiценность каждого звонкаnextметодiзначение будет+1.

  • затем согласноiЗначение извлекает данные из массива какvalue, а затем по индексу получитьdoneценность .

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

повторяемый объект

Вот примерно разобралисьIteratorи как создать объект итератора. Но он иfor ofЧто это значит?

для рабочего механизма

когдаfor ofПри выполнении движок автоматически вызовет это во время цикла对象上的迭代器方法, который, в свою очередь, выполняет объект итератораnextметод, будетnextВозвращаемое значение присваиваетсяfor ofпеременная для получения определенного значения.

Я думаю, что приведенное выше предложение содержит важную информацию - «Методы итератора для объектов».

Реализовать итерируемые объекты

Как может быть метод итератора для объекта?

ES6В положениях, развернутых до тех пор, пока свойства объектаIteratorИнтерфейс, в виде добавления к объектуSymbol.iteratorсвойство, это свойство указывает на метод итератора, который возвращает специальный объект — объект итератора.

Объект, который развертывает это свойство и реализует метод итератора, называется可迭代对象.

В этот момент объект является итерируемым, то есть его можноfor ofтраверс.

Symbol.iterator — выражение, возвращающее свойство итератора объекта Symbol, которое является предопределенным специальным значением типа Symbol.

взять каштан

Обычные предметы не могут бытьfor ofОбход, прямое потребление сообщит об ошибке.

var obj = {};
for (var k of obj) {
}

obj не является итерируемым объектом.

Тогда давайте сделаем из объекта итерабельный объект и реализуем его по протоколу, то есть по регламенту.

iterableObjразвернуть на объектеSymbol.iteratorсвойство, а затем создать для него метод итератора.Мы уже упоминали правила итераторов выше.

var iterableObj = {
    items: [100,200,300],
    [Symbol.iterator]: function() {
        var self = this;
        var i = 0;
        return {
            next: function () {
                var done = (i >= self.items.length);
                var value = !done ? self.items[i++] : undefined;
                return {
                    done: done,
                    value: value
                };
            }
        };
    }}
        
    //遍历它
    for (var item of iterableObj) {
        console.log(item);//100,200,300
    }

Это так просто, вышеприведенный объект является итерируемым объектом, который может использоватьсяfor ofпотребляется.

Сценарии собственных приложений Iterator

Вернемся к началуfor ofдля обхода строк, массивов, карт мы не разворачивали для нихIteratorинтерфейс, который все еще можно использоватьfor ofтраверс.

Это потому, что вES6Некоторые объекты в этом интерфейсе уже развернули этот интерфейс по умолчанию, и вы можете использовать его без какой-либо обработки.for ofдля обхода значения.

Не верю? Эй, ты такой жесткий, я не хочу, чтобы ты говорил - письмо, я хочу, чтобы я говорил - письмо.

Посмотрите, сможете ли вы получить их итераторы.

数组

//数组
var arr = [100,200,300];

var iteratorObj = arr[Symbol.iterator]("Symbol.iterator");

//得到迭代器方法,返回迭代器对象
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());

字符串

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

var str = 'abc';

var strIteratorObj = str[Symbol.iterator]("Symbol.iterator");//得到迭代器

console.log(strIteratorObj.next());
console.log(strIteratorObj.next());
console.log(strIteratorObj.next());
console.log(strIteratorObj.next());

Или непосредственно на прототипе, чтобы увидеть, можно ли развернуть это свойство.

argumentsмассивный

внутри функцииargumentsпохож на массив, он также поддерживаетfor of, потому что он также развернут внутриIteratorинтерфейс.

Мы все знаем, что объект не развертывает этот интерфейс по умолчанию, поэтомуargumentsЭто свойство находится не в прототипе, а в свойствах самого объекта.

function test() {
    var obj = arguments[Symbol.iterator]("Symbol.iterator");
    console.log(arguments.hasOwnProperty(Symbol.iterator));
    console.log(arguments);
    console.log(obj.next());
}

test(1,2,3);

Подводя итог, можно сказать, что объекты, для которых развернут интерфейс Iterator по умолчанию, в основном включают в себя массивы, строки, наборы, карты и объекты, подобные массивам (такие как объекты аргументов, объекты DOM NodeList.

Проверка кода незначительна, это рутина, особо нечего сказать

Другая функция Iterator

IteratorПомимо предоставления унифицированного метода доступа к данным для различных структур данных, нашли ли вы другое применение?

Это настраиваемость данных, потому что мы можем управлять итератором по желанию.valueстоимость.

Например: сам массив является итерируемым, мы можем переопределить его итератор по умолчанию.

var arr = [100,200,300];

for(var o of arr) {
    console.log(o);
}

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

После нашей трансформации

var arr = [100,200,300];
arr[Symbol.iterator] = function () {
    var self = this;
    var i = 0;
    return {
        next: function () {
            var done = (i >= self.length);
            var value = !done ? self[i++] : undefined;
            return {
               done: done,
               value: value
            };
        }
    };
}

for (var o of arr) {
    console.log(o);
}

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

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

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


[Сообщество] Общественное внимание ВНИМАНИЕ IVWEB Номер для просмотра новейших технологических журналов, вы лучше сегодня, чем вчера!


Распространение

для прерывания

Мы все знаем обычноеforЦикл может быть прерван в любой момент, затемfor ofЯвляется ли это возможным?

Ответ - да,for ofмеханизм учитываетforа такжеforEach.

Итераторы должны бытьnextВ дополнение к методу, есть два дополнительных методаreturnа такжеthrowметод.

еслиfor ofЕсли цикл выйдет раньше, он будет вызван автоматическиreturnМетод следует отметить, чтоreturnМетод должен иметь возвращаемое значение, а возвращаемое значение должно быть единицей.object.

ps: выйдите, бросив исключение, оно будет выполнено первымreturnметод выдает исключение.

var iterableObj = {
    items: [100,200,300],
    [Symbol.iterator]: function () {
        var self = this;
        var i = 0;
        return {
            next: function () {
                var done = (i >= self.items.length);
                var value = !done ? self.items[i++] : undefined;
                return {
                    done: done,
                    value: value
                };
            },
            return() {
                console.log('提前退出');
                return {
                   //必须返回一个对象
                    done: true
                }
            }
        };
    }}
    
    for (var item of iterableObj) {
        console.log(item);
        if(item === 200) {
            break;
    }}
    
    for (var item of iterableObj) {
        console.log(item);
        throw new Error();
    }

Метод ps:throw здесь не упоминается, это не то место, где он используется, увидимся в следующей статье.

больше, чем для

Кромеfor ofПри выполнении он автоматически вызывает объектIteratorметод, тоES6Есть ли другие грамматические формы?

присваивание деструктуризации

При деструктуризации и назначении итерируемого объекта он будет вызываться по умолчаниюSymbol.iteratorметод.

//字符串
var str = '12345';
var [a,b] = str;
console.log(a,b);// 1 2

//map
var map = new Map();
map.set('我','前端');
map.set('是','技术');
map.set('谁','江湖');
map.set('作者','zz_jesse');

var [d,e] = map;
console.log(d,e);
//["我", "前端"] ["是", "技术"]....

Точно так же, если обычный объект будет уничтожен, будет сообщено об ошибке.

Потому что обычные объекты не являются итерируемыми.

var [d, e] = {name: 'zhang'};

Деструктурирование присваивания из пользовательского итерируемого объекта.

var iterableObj = {
    items: ['红','绿','蓝'],
    [Symbol.iterator]: function () {
        var self = this;
        var i = 0;
        return {
            next: function () {
                var done = (i >= self.items.length);
                var value = !done ? self.items[i++] : undefined;
                return {
                    done: done,
                    value: value
                };
            }
        };
    }}
    var [d,e] = iterableObj;
    console.log(d,e);//红 绿

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

спред оператор

Выполнение оператора распространения (...) также вызывает его по умолчаниюSymbol.iteratorметод для преобразования текущего объекта итерации в массив.

//字符串
var str = '1234';
console.log([...str]);//[1, 2, 3, 4]  转换为数组

//map 对象
var map = new Map([[1, 2], [3, 4]]);
[...map] //[[1, 2], [3, 4]

//set 对象
var set = new Set([1, 2, 3]);
[...set]//[1, 2, 3]

Общие обычные объекты не могут быть преобразованы в массивы.

var obj = {name: 'zhang'};
[...obj]//报错

как источник данных

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

Например: Set, Map, Array.from и т. д.

//为了证明,先把一个数组的默认迭代器给覆盖掉

var arr = [100, 200, 300];

arr[Symbol.iterator] = function () {
    var self = this;
    var i = 0;
    return {
        next: function () { 
            var done = (i >= self.length); 
            var value = !done ? self[i++] : undefined;  
            return {        
                done: done,        
                value: value+'前端技术江湖' 
                //注意这里   
            };       
        }
    };
}

for (var o of arr) { 
    console.log(o);
}

// 100前端技术江湖
// 200前端技术江湖
// 300前端技术江湖

перезаписанный

//生成 set  对象
var set = new Set(arr);

//调用 Array.from方法
Array.from(arr);

ключевые слова yield*

За yield* следует проходимая структура, которая также вызывает функцию итератора при выполнении.

let foo = function* () {
    yield 1;
    yield* [2,3,4]; 
    yield 5;
};

Урожайность необходимо подчеркнуть, и она будет подробно описана в следующем разделе.

Определить, является ли объект итеративным

Поскольку правила для итерируемых объектов должны быть развернуты на объектеSymbol.iteratorАтрибут, то мы можем в основном использовать этот атрибут, чтобы определить, является ли объект итерируемым объектом, а затем мы можем узнать, можно ли его использовать.for ofценится.

function isIterable(object) {
    return typeof object[Symbol.iterator] === "function";
}
console.log(isIterable('abcdefg')); // true
console.log(isIterable([1, 2, 3]));// true
console.log(isIterable("Hello"));// true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set()));// true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet()));// false

Суммировать

ES6Появление множества новых структур данных, таких какMap ,Set, поэтому для удобства сбора данных добавлен единый способ получения данных.for of. а такжеfor ofПри выполнении движок автоматически вызовет итератор объекта, чтобы получить значение.

Не все объекты поддерживают этот метод, он должен быть реализованIteratorТолько интерфейсы могут, такие объекты мы называем итерируемыми объектами.

Итератор реализован в соответствии с итерируемым протоколом, и может быть реализован протокол итератора.

В дополнение к унифицированному методу доступа к данным вы также можете настроить содержимое полученных данных, как хотите, до тех пор, пока вам это нужно.

Итератор — это метод, который возвращает объект итератора.

Iterables развернутIteratorОбъект интерфейса с правильными методами итератора.

Он используется во многих местах в ES6.Iterator, Обычно вы можете уделять больше внимания наблюдению, думать на один шаг больше.

и конец и начало

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

Есть ли более элегантный способ сделать это?

Да, то есть - Генератор - Генератор.

let obj = {
    *[Symbol.iterator]( "Symbol.iterator") {
        yield 'hello';
        yield 'world';
    }
};

for (let x of obj) {
    console.log(x);
}
// "hello"
// "world"

Видно, что он очень лаконичен и может генерировать итератор без лишнего кода.

Помимо того, что он является синтаксическим сахаром для генерации итераторов, он обладает и более магическими возможностями.

Такая забавная способность обязательно будет разбита, но сегодня уже поздно, поздно ложиться спать вредит моему телу, на этот раз я сначала это исправлю.Iterator.

в следующий разGenerator.

упражняться

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

  1. Напишите объект Iterator.

  2. Настройте итерируемый объект.

  3. скажи, что ты правIteratorпонимание, под кратким выводом.

использованная литература

[1] Голод 6. Ruan Yifeng.com/#docs/ITER A… Голод 6. Ruan Yifeng.com/#docs/ITER A…

[2] developer.Mozilla.org/this-cn/docs/… developer.Mozilla.org/this-cn/docs/…

[3] блог woo woo woo.cn на.com/smallmatch… блог woo woo woo.cn на.com/smallmatch…