Страницы

Поиск по вопросам

Показаны сообщения с ярлыком ecmascript-6. Показать все сообщения
Показаны сообщения с ярлыком ecmascript-6. Показать все сообщения

пятница, 13 марта 2020 г.

JavaScript 6 classes

#javascript #ecmascript_6


Как можно избегать префиксов "this." перед переменными в классе, если их много?
Пример:

class Person {
  constructor() {
    this.name = "a";
    this.lastName = "b";
    this.age = 20;
    this.hair = "black";
  }
}

    


Ответы

Ответ 1



Запиши все проперти вначале в вспомогательный объект, а затем присвой их this. class Person { constructor() { let props = { name : "a", lastName : "b", age : 20, hair : "black" } for (let prop in props) { this[prop] = props[prop]; } } } Но это все бред, это только сделает код тяжелее. Не бойся использовать this, или же применяй defineProperties, но от него код станет еще толще. Так же есть экспериментальный метод Object.assing. Object.assing(this, { name : "a", lastName : "b", age : 20, hair : "black" }); Но насколько я знаю он поддерживается лишь Firefox, а для прочих браузеров существует лишь в виде полифила или трансплайера и по сути представляет из себя инкапсуляцию способа, что я привел вначале.

четверг, 5 марта 2020 г.

Как вызвать функцию класса внутри js класса [закрыт]

#javascript #классы #ecmascript_6


        
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он соответствовал тематике «Stack Overflow на русском».
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Например: 

 class Car(){
    weight(){
      alert('100kg');
    }
    getWeight(){
      this.weight()   //не работает
   }
}


Как это сделать правильно?
    


Ответы

Ответ 1



Этот вопрос следует закрыть, т.к. проблема вызвана опечаткой class Car { weight(){ alert('100kg'); } getWeight(){ this.weight() //не работает } } (new Car()).getWeight();

Ответ 2



class Car { weight() { console.log('100kg'); } getWeight(){ this.weight(); } } var car = new Car; car.getWeight();

среда, 4 марта 2020 г.

Как проверить поддержку браузером spread operator'а?

#javascript #ecmascript_6


Возможно ли в JavaScript проверить, поддерживает ли браузер spread operator? Будет
ли в этом браузере работать следующая конструкция?

var array = [...arg];

    


Ответы

Ответ 1



в данном случае поможет eval и try..catch при попытке выполнить код с использованием spread оператора в браузере, который его не поддерживает будет кинуто исключение об ошибке синтаксиса, поэтому проверяющая функция может выглядеть так: function checkSpread() { try { return eval('[...[]]==""'); } catch (e) { return false; } } console.log(checkSpread()); Текущий сниппет, например в IE11 вернет false, а в Chrome - true Вместо eval так же можно использовать конструктор Function function checkSpread() { try { var func = new Function('return [...[]]'); return func() == ''; } catch (e) { return false; } } console.log(checkSpread());

воскресенье, 9 февраля 2020 г.

EcmaScript Спецификация или язык программирования?

#javascript #ecmascript_6 #ecmascript


Русская версия


  ECMAScript — это встраиваемый расширяемый не имеющий средств
  ввода-вывода язык программирования, используемый в качестве основы для
  построения других скриптовых языков. Стандартизирован международной
  организацией ECMA в спецификации ECMA-262. Расширения языка:
  JavaScript, JScript и ActionScript.


Английская версия


  ECMAScript (or ES) is a trademarked scripting-language specification
  standardized by Ecma International in ECMA-262 and ISO/IEC 16262.



Так всё-таки EcmaScript это спецификация или язык программирования? Получается в
Вики ошибка или что?

Текст написан про EcmaScript в русской вики


  Язык возник на основе нескольких технологий, самыми известными из
  которых являются языки JavaScript и JScript. Разработка первой
  редакции спецификации началась в ноябре 1996 года.

Получается язык как они говорят EcmaScript возник на основе нескольких технологий
аля: JavaScript и JScript. То есть слияние этих всех технологий или языков? Или наоборот
на примере JavaScript(a): Это EcmaScript + DOM, BOM и еще что-нибудь? Я правильно понимаю?


Да я копаю в основы и тд. Уж лучше так медленней понимать и знать что да как, чем
после 20 минут JavaScript(a) идти во фреймворки и  пытаться понять как там всё работает -_- 
    


Ответы

Ответ 1



Для разбора откроем саму спецификацию (последний черновик) Название явно указывает: ECMAScript® 2019 Language Specification Спецификация языка ECMAScript® 2019 Далее во введении можно увидеть следующее: Этот Ecma стандарт определяет язык ECMAScript 2019. Это десятая редакция спецификации языка ECMAScript. ECMAScript основан на нескольких технологиях, таких как JavaScript (Netscape) и JScript (Microsoft). Настоящий стандарт определяет язык программирования общего назначения ECMAScript 2019. Далее идет секция соответствия, в которой указывается определенные пункты, которым должна соответствовать реализация ECMAScript. Из всего выше сказанного можно сделать вывод: Есть языковая спецификация Спецификация описывает язык ECMAScript Есть конкретные реализации этой спецификации, например JavaScript

Ответ 2



Спецификация языка - это документ, который описывает язык. Чтобы язык начал свое существование, он должен быть описан. В самой спецификации ECMAScript во введении написано: This Ecma Standard defines the ECMAScript 2015 Language. Так что никакого противоречия нет. В приведенной вами цитате Язык возник на основе нескольких технологий, самыми известными из которых являются языки JavaScript и JScript. язык программирования определяется как вид информационной технологии.

четверг, 6 февраля 2020 г.

Redux странный синтаксис

#ecmascript_6 #redux


Недавно в redux видел генераторы экшенов со странным js синтаксисом, не могу найти
и понять что это

{type: ACTION_ADD, id}


Обтяните почему тут id без пары, это же обект. Такие примеры есть в оф документации
по redux. Спасибо
    


Ответы

Ответ 1



Добро пожаловать в удивительный мир ECMAScript 6 (и 7). Это новая синтаксическая конструкция называется Property Shorthand и призвана упростить фарширование объекта значениями из пачки переменных. Если перевести этот синтаксический сахар на привычный ECMAScript 5, получится следующее: {type: ACTION_ADD, id: id} то есть предполагается, что ранее объявлена переменная id.

пятница, 24 января 2020 г.

Использование в TypeScript классов .Net

#javascript #cpp #net #typescript #ecmascript_6


Есть идея иметь доступ из TypeScript к  методам и свойствам классов .Net.

У меня есть опыт вызова методов .Net объектов из натива https://habrahabr.ru/users/serginio1/topics/

И в частности Кроссплатформенное использование классов .Net из неуправляемого кода.
Или аналог IDispatch на Linux
   

Есть идея вызвать через Native Client Messaging System  

Там мы имеем взаимодействие JavaScript и натива через сообщения

В JavaScript мы можем подписаться на события

function handleMessage(message) {
  // In the example, we simply log the data that's received in the message.
  var logEl = document.getElementById('log');
  logEl.textContent += message.data;
}


И отправлять сообщения

var dictionary = {
  key:guid,
  MethodName: name,
  param_array: args
}
nacl_module.postMessage(dictionary);


На стороне натива

Можно принмать сообщения

virtual void HandleMessage(const pp::Var& var) {
  if (var.is_dictionary()) {
    pp::VarDictionary dictionary(var);
    // Use the dictionary
    pp::VarArray keys = dictionary.GetKeys();
    // ...
  } else {
    // ...
  }
}


Так и отправлять

pp::VarDictionary dictionary;
dictionary.Set(pp::Var("command"), pp::Var(next_command));
dictionary.Set(pp::Var("param_int"), pp::Var(123));
pp::VarArray an_array;
an_array.Set(0, pp::Var("string0"));
an_array.Set(1, pp::Var("string1"))
dictionary.Set(pp::Var("param_array"), an_array);
PostMessage(dictionary);


Все это можно сделать через Proxy 

class NetWrapper{

    public currentCount: any;

    public static dictionary = {};

    public proxy: any;

    public static wm = new WeakMap(); // Слабые ссылки на объекты
    public static refArray = new Array(); // ссылки на .Net объекты   
    constructor(private netRef:int) { this.SetProxy(); }


     private GetPromise(name: PropertyKey,args)
    {

        let key = Guid.newGuid();
        let promise = new Promise((resolve, reject) => {
            let item = new Item(resolve, args, name);
            this.dictionary[key] = item;

           var dictionary = {
           netRef:netRef,
           key:key,
           MethodName: name,
           param_array: args
         }
          nacl_module.postMessage(dictionary);
        });


        return promise;
    }


    private SetProxy(): void {

        let self=this;
        this.proxy = new Proxy({}, {

            get: function(rcvr: any, name: PropertyKey)
            {

               // Проблемы с определением, что свойство возвращает ссылку на метод
               // поэтому будем вызвать все как методы
               // if (этоСвойство)
               //     return self.GetPromise(name, []);

               return (...args) => {
                   return self.GetPromise(name,args)

                };


            }


        });
    }




// Пример вызова метода .Net

function handleMessage(message) {
  // In the example, we simply log the data that's received in the message.
  let data=message.data;
  let result=data.result;
  let item = this.dictionary[data.key];

  delete this.dictionary[key];

  if (data.resultIsObject)
   {

    let ref=result;
    result=new NetWrapper(ref);
    refArray.push(ref);
    wm.set(result,ref);
  }

  item.resolve(result);

}


Так как в в JavaScript нет финализаторов, то можно использовать WeakMap 

То есть можно было бы выгрузить значения из wm и сравнить их с текущим массивом ссылок
refArray. Если в WeakMap ссылок нет значит можно освободить ссылки и на стороне .Net

Но вроде как 


  Из-за того, что ссылки являются слабыми, ключи WeakMap не перечисляемы
  (то есть нет метода, который возвращает список ключей). Иначе список
  бы зависел от состояния сбора мусора, представляя индетерминизм. Если
  Вы хотите иметь список ключей, Вам следует поддерживать его
  самостоятельно.


Использование такое

  let HttpClient=await NetWrap.GetType("System.Net.Http.HttpClient","System.Net.Http.dll");
     let HttpClientHandler = await NetWrap.GetType("System.Net.Http.HttpClientHandler","System.Net.Http.dll");

    let client=await NetWrap.new(HttpClient);
    let responst= await (await client.GetStringAsync("https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx")).Result();
 //Result вызываем как функцию


Можно для асинхронных методов result внутри Net сделать асинхронным

Буду рад любым советам. И может это никому и не нужно и не стоит вообще писать в стол?

Правда нашел ссылку на возможность финализатора https://www.npmjs.com/package/finalize

var finalize = require('finalize');
 
var obj = { x: 1337 };
finalize(obj, function () {
  console.log(this.x); // this will print '1337' 
});
global.gc();
// nothing will happen, var obj above holds obj alive 
obj = null;
global.gc();
// the previous line should trigger the callback above 


Кроме того для интегации мне посрветовали cef / JavaScriptIntegration 

Можно и без асинхронности  вызова обычных методов обойтись.

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

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

let url= "https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx");
let response= await client.async.GetStringAsync(url);
let response= client.GetStringAsync(url).@Result;

    


Ответы

Ответ 1



Сделал и работает на Angular 2 и TypeScript let Net = NetObject.NetWrapper; let del = NetObject.FlagDeleteObject; //let SB = Net.Новый("System.Text.StringBuilder"); let StringBuilder = Net.Тип("System.Text.StringBuilder"); let SB = new StringBuilder(); this.AddComment("SB", SB); var res = SB.Append("Первая строка"); res(del); var ToStr = SB.ToString(); this.AddComment("Tostring=", ToStr); SB.Capacity=40; res = SB._Capacity; this.AddComment("_Capacity=", res); SB(del); let Тестовый = Net.Тип("TestDllForCoreClr.Тестовый", "TestDllForCoreClr"); var TO = new Тестовый("Свойство из Конструктора"); var resAsync = await TO.async.GetStringAsync(); this.ResultExecute.push(new ResTest("GetStringAsync", resAsync)); res = TO.ДженерикМетод(1, 2, 5); this.AddComment("ДженерикМетод с выводом типов ", res); let Int32 = Net.GetType("System.Int32"); res = TO.ДженерикМетод([Int32, Int32], 1, 2, 5); this.AddComment("ДженерикМетод с аргeментами типов ", res); var Dictionary2 = Net.GetType("System.Collections.Generic.Dictionary`2", "System.Collections"); var DyctionaryIS = Net.GetGenericType(Dictionary2, "System.Int32", "System.String"); var D = new DyctionaryIS(); res = TO.ДженерикМетод2(D, 99, "Hello"); this.AddComment("ДженерикМетод2 с выводом типов ", res); res = TO.ДженерикМетод2([Int32, "System.String"],D, 99, "Hello"); this.AddComment("ДженерикМетод2 с аргументами ", res); var task = TO.GetStringAsync(); res = await Net.async.ReturnParam(task); this.AddComment("выполнение полученной задачи асинхронно ", res); var array = Net.GetNetArray(1, "Привет", 4.4); for (let item of array) this.AddComment("Элемент ", item); var EO = TO.ПолучитьExpandoObject(); this.AddComment("Свойство ExpandoObject Имя ",EO._Имя); this.AddComment("Свойство ExpandoObject Число ", EO._Число); var делегат = EO._ВСтроку; this.AddComment("Результат вызова делегата Встроку ", делегат()); this.AddComment("Результат вызова как Метода ", EO.ВСтроку()); NetObject.DeleteNetObjets(Int32, task, D, DyctionaryIS, Dictionary2, TO, Тестовый, StringBuilder, array, EO, делегат); this.AddComment("Количество элементов в хранилище ", Net.КоличествоЭлементовВХранилище()); this.AddComment("Первый удаленный ", Net.FirstDeleted()); Отличие от C# это Для получения свойства нужно добавить "_" res = SB._Capacity; Для вызова асинхронного метода нужно добавить ключевое слово async var resAsync = await TO.async.GetStringAsync(); Для вызова дженерик метода, если нельзя вывести типы по параметрам то аргументы указываем в массиве res = TO.ДженерикМетод2([Int32, "System.String"],D, 99, "Hello"); Поддержку событий и передачу объектов JS добавлю позже. Буду рад любым предложениям и критике.   Какие примеры лучше дать для статьи? Выложил в итоге статью с примерами CEF, ES6, Angular 2, TypeScript использование классов .Net Core для расширения возможностей И еще CEF, Angular 2 использование событий классов .Net Core И еще CEF, ES6, Angular 2, WebPack 2 .Net Core десктопное приложение без серверной части

Ответ 2



Кроме того на RSDN мне посоветовали CEF удобное C++ API еще ссылка cef / JavaScriptIntegration Можно и без асинхронности вызова обычных методов обойтись. При этом можно зарегистрировать различные обработчики для асинхронных методов, для событий В итоге можно вызывать асинхронные методы например добавив кллючевое слово async, а для свойств можно использовать @ в начале свойства Кроме того можно переопределить new let HttpClient= NetWrap.GetType("System.Net.Http.HttpClient","System.Net.Http.dll"); let HttpClientHandler = NetWrap.GetType("System.Net.Http.HttpClientHandler","System.Net.Http.dll"); let client= new HttpClient(HttpClientHandler); let url= "https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx"); let response= await client.async.GetStringAsync(url); let response= client.GetStringAsync(url).@Result; для ref параметров можно использовать класс обертку class RefParam{ isRefparam=true; constructor(public Value:any); } Кроме того можно реализовать enumerate для доступа через for(let prop in proxy) Если кому интересно, то выложил статью CEF,Es6,Angular 2,TypeScript использование классов .Net Core для расширения возможностей

Как использовать (запускать) class в цикле

#javascript #классы #ecmascript_6



  Внесу некоторые изменения в вопрос, опишу поподробнее....


Коллеги, есть такой вот  пример. В котором я создаю class CreateBlockInfo.
не думаю что надо объяснять, что именно происходит в данном классе.



let info = document.querySelectorAll('.info');
class CreateBlockInfo {
  constructor(elem) {
    this.fragment = document.createDocumentFragment();
    this.element = document.createElement(elem);
  }
  Append(parent) {
    this.fragment.appendChild(this.element);
    parent.appendChild(this.fragment)
  }
}

//Потом создаю экземпляр класса  runCreateBlockInfo
let runCreateBlockInfo = new CreateBlockInfo('span');
// и вот далее я пытаюсь запустить экземпляр класса в цикле
// я хочу в каждый элемент info  вставить созданный span
for (let i = 0; i < info.length; i++) {
  runCreateBlockInfo.Append(info[i]);
}

// рузультат такой, только в последний элемент info вставляется span 

console.log(info[0].childNodes);
console.log(info[1].childNodes);
console.log(info[2].childNodes);

Я прекрасно понимаю, что если в цикл создавать новый объект for (let i = 0; i < info.length; i++) { let runCreateBlockInfo = new CreateBlockInfo(); runCreateBlockInfo.Append(info[i]); } То я получу, то чего хочу. Но, тут есть одно НО... Простите за выражение, нутром чую, что это не верно. Так как я создаю один и тот же Объект несколько раз. А мне кажется, что надо один раз проинициализировать новый объект. А потом использовать его сколько душе угодно. Коллеги, вопрос - верно ли мои умозаключения выше и если да, как выйти из ситуации??? Или может я не верное рассуждаю? Помогите разобраться.


Ответы

Ответ 1



Метод .appendChild не создает новый элемент. Если элемент уже есть на странице, то он перенесется из одного места, в другое. Чтобы решить это, можно создавать элемент span каждый раз, либо клонировать тот, который создан в конструкторе, например: let info = document.querySelectorAll('.info'); class CreateBlockInfo { constructor(elem) { this.fragment = document.createDocumentFragment(); this.element = document.createElement(elem); } Append(parent) { this.fragment.appendChild(this.element.cloneNode()); parent.appendChild(this.fragment) } } //Потом создаю экземпляр класса runCreateBlockInfo let runCreateBlockInfo = new CreateBlockInfo('span'); // и вот далее я пытаюсь запустить экземпляр класса в цикле // я хочу в каждый элемент info вставить созданный span for (let i = 0; i < info.length; i++) { runCreateBlockInfo.Append(info[i]); } // рузультат такой, только в последний элемент info вставляется span console.log(info[0].childNodes); console.log(info[1].childNodes); console.log(info[2].childNodes); span::before { content: "span" }



среда, 22 января 2020 г.

Какой синтаксис лучше использовать при написании React-компонентов: ES5 или ES6?

#javascript #reactjs #ecmascript_6


Не так давно начал изучать React JS и у меня появился вопрос: какой синтаксис лучше
всего использовать при написании React-компонентов: ES5 или ES6? И какое будет преимущество
в зависимости от выбора?
    


Ответы

Ответ 1



ES6. Документация написана в ES6, ES6 имеет не мало чего нового, и на многое из ES6 заточен React. Так же не забывай про сборщик (webpack, например) и будет тебе счастье

JSDoc. Как правильно описать код для правильного перехода в IDE?

#javascript #nodejs #ecmascript_6 #jsdoc


Как правильно описать код с помощью JSDoc? Например, у меня есть компонент базы данных

/**
     * A namespace.
     * @namespace components
     * @class Db
     */
    class Db {
      /**
       *
       * @param {Function} cb
       * @returns {*}
       */
      connect(cb) {

        return mongoose.connect(this.url, (err) => {
          cb()
         });
      }

    }


И есть IoC контейнер, который возвращает экземпляр этого класса:

/**
 * @type {components.Db}
 */    
let db = diContainer.resolve('db');


Но, при обращении к 

db.connect()


IDE не видит функцию connect, т.е. моя IDE не хочет "прыгать" к классу Db.

Подскажите, как описать класс и переменную db, чтобы они были связаны с помощью JSDoc
для IDE?

Пробовал в следующих IDE: WebStorm, PhpStorm.
    


Ответы

Ответ 1



Для описания параметра типа функция, (метод обратного вызова 'callback'), существует специальный тэг. Как оформлять методы обратного вызова (callback) можно почитать тут. Пример кода, с использованием тэга @callback: /** * Callback for adding two numbers. * * @callback addStuffCallback * @param {int} sum - An integer. */ /** * Add two numbers together, then pass the results to a callback function. * * @param {int} x - An integer. * @param {int} y - An integer. * @param {addStuffCallback} callback - A callback to run. */ function addStuff(x, y, callback) { callback(x+y); }

ES6. Клонирование объектов через спред оператор

#ecmascript_6


Есть ли разница в способах клонирования объектов с использованием спред оператора?

const a = { s:1, d:2 };
// этим способом
const {...b} = a;
// и этим способом
const c = {...a};

    


Ответы

Ответ 1



Судя по тестам при необходимости клонирования объекта "как есть" никакой разницы нет. Однако если нужно убрать, добавить или изменить что-то в результирующем объекте тогда разница есть. Если нужно клонировать целевой объект частично тогда наш выбор первый вариант: const first = { a:1, b:2, c:3, d:4 }; const { a, ...second } = first; console.log(second); // => { b:2, c:3, d:4 }; Если нужно клонировать целевой объект и добавить или изменить какие-то поля тогда наш выбор второй вариант: Добавить: const first = { a:1, b:2, c:3, d:4 }; const second = { ...first, e: 5}; console.log(second); // => { a:1, b:2, c:3, d:4, e:5 }; Изменить: const first = { a:1, b:2, c:3, d:4 }; const second = { ...first, d: 5}; console.log(second); // => { a:1, b:2, c:3, d:5 };

This при работе c событиями в ES6

#javascript #ecmascript_6


Здравствуйте решил написать простенький класс таймера используя ES6 синктаксис.

Есть две проблемы:
1.Как вынести переменные tick, min,seconds,minutes в конструктор, так что б они были
видны внутри метода run().
2.При клике на кнопку стоп,таймер должен останавливаться и в cleatinterval должна
попадать ссылка на таймер из конструтора.

Используя ES5 можно вынести контекст в отдельную функцию и с ней работать, как сделать
тоже самое используя новый синтаксис?



class Timer {
	constructor(){
		this.timer;
	}
  
	run(){
		let tick = 0;
		let min = 0
		let seconds = document.getElementById('seconds');
		let minutes = document.getElementById('minutes');
		this.timer = setInterval( () => {   
			tick++;   
			if(tick == 60){
			   min++;
			   tick =  0;
			   if(min < 9){
				min = '0'+min;
			   }
			   minutes.innerHTML = min;
			}
			if( tick < 9){
				tick = '0'+tick;
			} 
		
			seconds.innerHTML = tick;	
		   
			console.log('timer play');
		},1000)
	}

	stop(){
		console.log(this);//Ссылаетcя на btn
		clearInterval(this.timer);//не работает 
		console.log('timer stop');
	}

}

let main_timer = new Timer;

btn.addEventListener('click',main_timer.run);
stop.addEventListener('click',main_timer.stop);



    


Ответы

Ответ 1



constructor(){ this.timer; this.tick; this.min; this.seconds; this.minutes; } run(){ this.tick = 0; this.min = 0; this.seconds = document.getElementById('seconds'); this.minutes = document.getElementById('minutes'); this.seconds.innerHTML = ''; this.minutes.innerHTML = ''; this.timer = setInterval( () => { this.tick++; if(this.tick == 60){ this.min++; this.tick = 0; this.minutes.innerHTML = (this.min < 10)? '0' + this.min : this.min; } this.seconds.innerHTML = (this.tick < 10)? '0' + this.tick : this.tick; console.log('timer play'); }, 1000); } Что до контекста, в котором будут выполняться run и stop: btn.addEventListener('click',main_timer.run.bind(main_timer)); stop.addEventListener('click',main_timer.stop.bind(main_timer)); или btn.addEventListener('click', () => main_timer.run()); stop.addEventListener('click', () => main_timer.stop()); или class Timer { ... runHandler() { return this.run.bind(this); } stopHandler() { return this.stop.bind(this); } btn.addEventListener('click', main_timer.runHandler()); stop.addEventListener('click', main_timer.stopHandler());

ES6 синтаксис. Как правильно записать?

#javascript #ecmascript_6 #async_await




const handleFiles = () => {
  return async handle => {
    let result;
    result = 'hello';
    return result;
  };
};
handleFiles().then(result => console.log(result)).catch(console.log(e));




Почему не срабатывает данный скрипт?


  Uncaught TypeError: handleFiles(...).then is not a function

    


Ответы

Ответ 1



Проблема в том, что handleFiles возвращает функцию, а не Promise, у которого есть метод then. Для решению нужно либо вызвать возвращенную функцию, либо возвращать результат вызова: const handleFiles = () => { return async handle => { let result; result = 'hello'; return result; }; }; handleFiles()().then(result => console.log(result)).catch(e => console.log(e)); const handleFiles2 = () => { return (async handle => { let result; result = 'hello'; return result; })(); }; handleFiles2().then(result => console.log(result)).catch(e => console.log(e));

Ответ 2



Вот как будет выглядеть ваша промисифицированная функция const handleFiles = (str) => { return new Promise((resolve, reject) => { if(str){ let result = str; resolve(result); } else { reject('Укажите аргумент'); } }); }; В resolve передаете аргумент для then, а в reject аргумент для catch Реализация: handleFiles('1').then(result => console.log(result)).catch(e => console.log(e));

Ответ 3



let handleFiles = new Promise((resolve, reject) => { let result; result = 'hello'; resolve(result); if (false) { reject('error'); } }); handleFiles.then(result => console.log(result), error => console.log(eror));

суббота, 11 января 2020 г.

Присвоить значение выше прокси

#javascript #ecmascript_6


В следующем коде прокси с get-перехватчиком в прототип (__proto__) объекта. Соответственно,
по схеме чтения свойств в js, до перехватчика очередь доходит только в том случае,
если в непосредственно в объекте такого свойства нет. После присваивания свойство появляется
в объекте и до прокси дело не доходит. Это так задумано.



var x = Object.create(new Proxy({}, {
  get(obj, key) {
    if (typeof key !== 'symbol') {
      console.log('Reading a nonexisting property: ' + key);
    }
  }
}));

var temp;
console.log(1, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

temp = x.a;
console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

x.a = 12;
console.log(4, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp);
.as-console-wrapper.as-console-wrapper { max-height: 100vh }




Теперь я в этот код добавляю перехватчик set:



var x = Object.create(new Proxy({}, {
  get(obj, key) {
    if (typeof key !== 'symbol') {
      console.log('Reading a nonexisting property: ' + key);
    }
  },
  set(obj, key, val, receiver) {
    console.log('Assigning a property: ' + key);
    Reflect.set(obj, key, val); // Inside of proxy, not outside
    //Reflect.set(receiver, key, val); // Infinite recursion
    return true;
  }
}));

var temp;
console.log(1, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

temp = x.a;
console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

x.a = 12;
console.log(4, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp);
.as-console-wrapper.as-console-wrapper { max-height: 100vh }




Проблема в том, что не получается записать свойство в сам объект - свойство записывается
либо в тот объект, который находится внутри прокси, либо всё уходит в рекурсию метода
set. Как это можно исправить? Т. е. результат должен быть такой же, как в предыдущем
сниппете, только с появлением строки Assigning a property: a.

PS: Этот вопрос на английском.
    


Ответы

Ответ 1



Додумался до такого способа, но выглядит каким-то костылём: function directSet(obj, key, val) { var proto = Object.getPrototypeOf(obj); Object.setPrototypeOf(obj, null); var res = Reflect.set(obj, key, val); Object.setPrototypeOf(obj, proto); return res; } var x = Object.create(new Proxy({}, { get(obj, key) { if (typeof key !== 'symbol') { console.log('Reading a nonexisting property: ' + key); } }, set(obj, key, val, receiver) { console.log('Assigning a property: ' + key); console.log(directSet(receiver, key, val)); return true; } })); var temp; console.log(1, Object.prototype.hasOwnProperty.call(x, 'a')); temp = x.a; console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp); temp = x.a; console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp); x.a = 12; console.log(4, Object.prototype.hasOwnProperty.call(x, 'a')); temp = x.a; console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp); .as-console-wrapper.as-console-wrapper { max-height: 100vh }

Ответ 2



Перевод ответа от @Bergi Для создания свойства надо воспользоваться Object.defineProperty (или Reflect.defineProperty). Просто попытка установить свойство присваиванием или Reflect.set будет вызывать проход по цепочке прототипов и попадать в перехватчик set, что приведёт к бесконечной рекурсии. new Proxy({}, { get(target, key) { if (typeof key !== 'symbol') { console.log('Reading a nonexisting property: ' + key); } }, set(target, key, val, receiver) { console.log('Assigning a property: ' + key); return Reflect.defineProperty(receiver, key, { value: val, writable: true, enumerable: true, configurable: true }); } }); Полный пример: var x = Object.create(new Proxy({}, { get(obj, key) { if (typeof key !== 'symbol') { console.log('Reading a nonexisting property: ' + key); } }, set(obj, key, val, receiver) { console.log('Assigning a nonexisting property: ' + key); return Reflect.defineProperty(receiver, key, { value: val, writable: true, enumerable: true, configurable: true }); } })); var temp; console.log(1, Object.prototype.hasOwnProperty.call(x, 'a')); temp = x.a; // get trap console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp); temp = x.a; // get trap console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp); x.a = 12; // set trap creates a property and sets it console.log(4, Object.prototype.hasOwnProperty.call(x, 'a')); temp = x.a; // direct read - no traps console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp); x.a = 42; // direct write - no traps console.log(6, Object.prototype.hasOwnProperty.call(x, 'a')); temp = x.a; // direct read - no traps console.log(7, Object.prototype.hasOwnProperty.call(x, 'a'), temp); .as-console-wrapper.as-console-wrapper { max-height: 100vh }

Как использовать новые геттер и сеттер?

#javascript #typescript #ecmascript_6


Помогите пожалуйста отрефакторить код. Я написал компонент, который представляет
из себя форму для отправки вопросов. Так же он выводит заданные на настоящий момент
вопросы (пока в консоль, но это не принципиально). Вместо базы данных для учебных целей
использую localstorage.

Итак, вот мой сервис, которые работает с "базой данных":

@Injectable()
export class QuestionsService {

    private questions: Question[];

  constructor() { 
    this.questions = localStorage.questions ? JSON.parse(localStorage.questions) : [];
  }

  getQuestions() {
    console.log('qqq');
    return this.questions;
  };  

  setQuestions(question): void {
    this.questions.push(question);
    localStorage.questions = JSON.stringify(this.questions);
  };    

}


Вот описанный тип:

export class Question {
    text: string;
    speakerId: number;
    dateHuman: string;
    dateUnix: string;
}


Вот компонент:

export class QuestionComponent implements OnInit {

  private questions: Question[] = [];

  constructor(private questionsService: QuestionsService) { }

  ngOnInit() {   
    this.getQuestions();
    console.log(this.questions);
  }   

  private sendQuestion(): void {   
    this.questionsService.setQuestions({
      "text": "42256yregd",
      "speakerId": 23,
      "dateHuman": "223 oct",
      "dateUnix": "22222"
    });
  };

  private getQuestions(): void {
    this.questionsService.getQuestions().map(question => {
      this.questions.push(question);
     });   
  };   

}


Эта система работает. Но мне хотелось бы использовать новые модные геттеры и сеттеры
из стандарта es6. Вот моя попытка:

Сервис:

    private questions: Question[];

  constructor() { 
    this.questions = localStorage.questions ? JSON.parse(localStorage.questions) : [];
  }

  get questions() {
    console.log('qqq');
    return this.questions;
  };  

  set questions(question): void {
    this.questions.push(question);
    localStorage.questions = JSON.stringify(this.questions);
  };


компонент:

....
..

  private sendQuestion(): void {   
    this.questionsService.questions = {
      "text": "42256yregd",
      "speakerId": 23,
      "dateHuman": "223 oct",
      "dateUnix": "22222"
    };
  };

  private getQuestions(): void {
    this.questionsService.questions().map(question => {
      this.questions.push(question);
     });   
  };  

....
....


Но консоль выводит ошибку:


  Duplicate identifier 'questions'.

    


Ответы

Ответ 1



Проблема у вас вот в чем: private questions: Question[]; // раз get questions() { // два set questions(question): void { У вас объявлено два свойства с одним именем. Так нельзя делать, о чем вам и говорит tsc. Эта проблема решается добавлением префикса к приватному свойству: private _questions: Question[]; get questions() { set questions(question): void {

Ответ 2



Здесь get questions() { ... return this.questions; }; вы через замыкание рекурсивно вызываете геттер questions. До возвращения значения дело не доходит, потому как превышается максимальный стек рекурсивного вызова функции. Интерпретатор понимает запись this.questions не как "обратись к свойству questions объекта this", а как "получи значение свойства questions объекта this". А получение значения происходит при помощи обращения к геттеру, который в данном случае имеет то же имя, что и само свойство questions, что и приводит к неверному результату (к "бесконечной" рекурсии). В конечном итоге вам выдается предупреждение об этом. Но. Так было бы, если бы интерпретатор самостоятельно не пресекал подобное поведение. На самом деле до рекурсивного вызова дело даже не доходит, поскольку интерпретатор (компилятор) сразу обнаруживает, что у вас в коде коллизия имен - имя свойства совпадает с именем геттера. В случае с сеттером, обращаясь внутри него к this.questions вы, опять же, обращаетесь не напрямую к свойству questions, а, из-за наличия геттера, сначала именно к нему [к геттеру]. Соответственно, по логике, и переопределяете вы тоже не свойство, а геттер. Но, повторюсь, судя по ошибке, которую вы получили, подобное поведение все-таки как-то чем-то перехватывается. В ином случае мы бы, возможно, довольно долго гадали, в чем же дело. Ну или просто получили бы ошибку вроде описанной выше. Решение проблемы В любом случае, ваша проблема решается путем разграничения имени свойства и его accessor'ов (геттеров и сеттеров). В идеальном случае стоит делать само свойство приватным, а геттер и сеттер - публичными. Но, поскольку ограничения доступа, как такового, EcmaScript не предусматривает (даже его 6-ая версия), то придется полагаться на добропорядочность тех, кто использует ваш код. А именно, дать свойству имя _questions, а getter'у и setter'у - просто questions. Таким образом вы убиваете сразу двух зайцев: избавляетесь от нежелательного поведения и явно указываете, что само свойство является приватным (ну или защищенным). Обращу ваше внимание, что, по-факту, вы можете использовать любое имя, а вариант с нижним подчеркиванием я привел в качестве наиболее правильно (по ряду причин, описанных выше). Исходя из всего сказанного, ваши геттер и сеттер должны выглядеть как-то так (соответственно): get questions() { console.log('qqq'); return this._questions; // добавлено нижнее подчеркивание }; set questions(question): void { this._questions.push(question); localStorage.questions = JSON.stringify(this.questions); };

Для чего используются методы изменения значений полей класса, если можно изменить значение напрямую?

#javascript #ооп #ecmascript_6


Разбираюсь потихоньку в javascript. Есть класс Weather в нем несколько свойств объявлено,
вроде этого. И в нем функция(метод) для изменения этого самого свойства.

class Weather {
 constructor() {
   this.location;
   // ну и т.д.
 }

 changeLocation(location) {
   this.location = location;
 }
}


В примере это выглядит как

const weather = new Weather();
const location = 'London, UK';

weather.changeLocation(location);


Мне не совсем понятно зачем здесь функция(метод)? Ведь можно просто

const weather = new Weather();
const location = 'London, UK';

weather.location = location;


Не проще ли так ? И не надо дергать функцию(метод). Как правильно? Как это принято
делать?
    


Ответы

Ответ 1



Хорошей практикой является изменение свойств объекта через функцию. Это нужно для того, что ты или другой разработчик (если проект командный) не смог просто взять и поменять это свойство на что угодно. В случае с функцией ты сможешь добавлять всякие проверки на корректность нового значения. class Weather { constructor() { this.location; } changeLocation(location) { if (typeof location !== 'string') { throw new Error('Invalid location value'); } this.location = location; } } const invalidLocation = [1,2,3, {test: 3}]; // невалидный тип const validLocation = 'London, UK'; //валидный тип const weather1 = new Weather(); weather1.location = invalidLocation; // таким кодом ты сможешь сломать свое приложение, но с сточки зрения кода все ок) const weather2 = new Weather(); weather2.changeLocation(invalidLocation); // получишь ошибку и это свойство не будет присваиваться const weather3 = new Weather(); weather3.changeLocation(validLocation); // ошибки не будет и код отработает корректно

пятница, 10 января 2020 г.

Почему браузер дважды обращается к Symbol.unscopables?

#javascript #ecmascript_6




with(new Proxy({}, {
  has() { return true },
  get(obj, key, proxy) { return console.log(String(key)) } })
) {
  a--
}




Вывод в Chrome:

Symbol(Symbol.unscopables)
a
Symbol(Symbol.unscopables)


Вывод в Firefox:

Symbol(Symbol.unscopables)
Symbol(Symbol.unscopables)
a


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

Логично, что конструкция a-- должна записать значение в то же место, откуда прочитала.
Нет, это не так.

А двойное чтение из Symbol.unscopables как бы намекает, что это не так и у меня есть
возможность отдать нечто для чтения, но сказать, что в мой объект этого записывать не надо?

Неужели это так и задумано? Что на эту тему говорит стандарт?

Собственно, в Хроме и FF такое почти прокатывает - чтение и запись связаны с разными
объектами, однако работает по-разному:

Update: Safari 10 читает Symbol.unscopables только один раз.



var a, b, flag = true

with (a = { x: 7 })
  with (b = { x: 4, get [Symbol.unscopables]() { return { x: flag=!flag } } })
    x++

                 // Chrome   FF       Safari   Edge
console.log(a)   // {x:5}    {x:7}    {x:7}    {x:5}
console.log(b)   // {x:4}    {x:8}    {x:5}    {x:4}




PS: Этот вопрос по-английски.
    


Ответы

Ответ 1



Согласно спецификации, @@unscopables в этом случае должно читаться только один раз. Детали (к сожаления по английски) в https://mail.mozilla.org/pipermail/es-discuss/2017-February/047725.html Следовательно Chrome и Firefox не следуют спецификации, а Safari следует. Смотрите также https://bugzilla.mozilla.org/show_bug.cgi?id=1341061 и https://bugs.chromium.org/p/v8/issues/detail?id=5992

Ответ 2



Обратимся к спецификации, в таблице 1 находится описание символа @@unscopables An object valued property whose own property names are property names that are excluded from the with environment bindings of the associated object. Объект, в котором названия свойств совпадают со свойствами исключенными из with связывания. Значение этого свойства проверяется в функции HasBindings(N) Let envRec be the object Environment Record for which the method was invoked. Let bindings be the binding object for envRec. Let foundBinding be HasProperty(bindings, N) ReturnIfAbrupt(foundBinding). If foundBinding is false, return false. If the withEnvironment flag of envRec is false, return true. Let unscopables be Get(bindings, @@unscopables). ReturnIfAbrupt(unscopables). If Type(unscopables) is Object, then Let blocked be ToBoolean(Get(unscopables, N)). ReturnIfAbrupt(blocked). If blocked is true, return false. Return true. Здесь нас интересуют пункты начиная с 6. Если проверяемое свойство N находится в блоке with, оно всегда проверяется в объекте unscopables. Теперь перейдем к постфиксному декременту. В алгоритме есть два ключевых момента: вызов GetValue(lhs) - для получения текущего значения вызов PutValue(lhs, newValue) - для установки нового значения Каждая из этих функций в итоге вызывает описанную выше HasBinding. В обновленном пример добавлена функция set и в логах видно, что второй раз вызывается как раз перед ней. var value = 10; with(new Proxy({}, { has(o, k) { console.log('has', String(k)); return true; // k !== 'console'; }, get(obj, key, proxy) { console.log('get', String(key)); if (key === Symbol.unscopables) { return { a: false, console: true } } return value; }, set(o, k, v, proxy) { console.log('set', k, v); if (k == 'a') value = v; } })) { a--; } Что характерно firefox сначала получает все связанные поля, и лишь затем получает значения свойств. Далее можно разобрать второй пример: var a, b, flag = true var valueA = 7, valueB = 4; with(a = { get x() { console.log('get A', flag); return valueA; }, set x(v) { console.log('set A', flag); valueA = v; } }) with(b = { get x() { console.log('get B', flag); return valueB; }, set x(v) { console.log('set B', flag); valueB = v; }, get [Symbol.unscopables]() { var t = { x: flag = !flag }; console.log('Symbol.unscopables', t); return t; } }) x++ // Chrome FF console.log(valueA) // {x:5} {x:7} console.log(valueB) // {x:4} {x:8} Что тут происходит? постфиксная операция, которая внутри себя вызывает Put и Get getter Symbol.unscopables - который определяет находится ли свойство x в текущем with или нет. Значение флага меняется на противоположное. Как можно заметить по логам, сначала значения флага возвращается false, что в соответствии со спецификацией указывает на то, что данное имя находится в текущем скопе. Поэтому значение берется из объекта b. Далее флаг выставляется в true и при вычслении в каком скопе находится x в котороый надо положить, получаем, что он находится не текущем скопе, поэтому значение сохраняется в объекте a. Как видно - Chrome работает в соответствии со спецификацией. Что касается firefox. Как можно заметить, firefox не инвертирует значение возвращенное в объекте unscopables, как это указано в спецификации, поэтому значение сначала берется из внешнего with и присваивается значению из внутреннего. По результатам проверок, можно сделать вывод, что firefox сначала получает свойство куда записывать, и лишь затем свойство откуда брать значение. Из-за этого в примере, когда значение flag меняется на каждый вызов, происходит расхождение с работой Chrome и EDGE.

пятница, 20 декабря 2019 г.

Регулярное выражение для поиска мобильного телефона в тексте

#javascript #регулярные_выражения #nodejs #ecmascript_6


Подскажите регулярное выражение для поиска номера мобильного телефона в тексте.

Номера телефонов вводятся разными пользователями и в совершенно непредсказуемом формате,
например могут участвовать -+()_ пробелы и еще не известно на что хватит фантазии. 

Примеры телефонов:

8 900 000-00-00

+7 (900) 000 00-00

+7 9001112233

89001112233

8(900) 111-2233

и т.д
    


Ответы

Ответ 1



Думаю, примерно так: /(\+7|8)[- _]*\(?[- _]*(\d{3}[- _]*\)?([- _]*\d){7}|\d\d[- _]*\d\d[- _]*\)?([- _]*\d){6})/g console.log( "8 900 000-00-00\ +7 (900) 000 00-00\ +7 9001112233\ 89001112233\ 8(900) 111-2233\ 8 (1234) 12 12 12\ 8 (12-34) 12 12 12\ 8 ( 123 ) 412 12 12".match( /(\+7|8)[- _]*\(?[- _]*(\d{3}[- _]*\)?([- _]*\d){7}|\d\d[- _]*\d\d[- _]*\)?([- _]*\d){6})/g )) .as-console-wrapper.as-console-wrapper { max-height: 100vh }

Ответ 2



Как вариант: /(?:\+|\d)[\d\-\(\) ]{9,}\d/g Начинается с: "+" или цифры Потом может содержать: цифры, "-", "(", ")", пробел. Повторяющиеся 9 и больше раз Заканчивается цифрой https://regex101.com/r/poJz8C/5

Ответ 3



/(?:\+|\d)[\d\-\(\) ]{9,}\d/g так лучше

Ответ 4



[0-9|\ |\-|\(|\)|\+]{10,17}[0-9|\ |\-|\(|\)]

пятница, 13 декабря 2019 г.

`use strict` не переводит функцию в строгий режим?

#javascript #ecmascript_6


Если написать use strict в косых апострофах, то функция не переходит в строгий режим.
Верно ли это поведение?



function f() { // Нет строгого режима - всё хорошо
  return 010;
}

console.log(f());






function f() { // Почему тут нет строгого режима?
  `use strict`;
  return 010;
}

console.log(f());






function f() { // Строгий режим, поэтому ошибка
  'use strict';
  return 010;
}

console.log(f());






function f() { // Строгий режим, поэтому ошибка
  "use strict";
  return 010;
}

console.log(f());



    


Ответы

Ответ 1



В соответствии с текущим стандартом это верно. Из стандарта Директивы и "use strict" A Use Strict Directive is an ExpressionStatement in a Directive Prologue whose StringLiteral is either the exact code unit sequences "use strict" or 'use strict'. A Use Strict Directive may not contain an EscapeSequence or LineContinuation. StringLiteral определена как StringLiteral:: " DoubleStringCharactersopt " ' SingleStringCharactersopt ' То что в косых апострофах называется Template Literal Lexical Components. К StringLiteral это не относится.

Ответ 2



А так в документации написано: A Use Strict Directive is an ExpressionStatement in a Directive Prologue whose StringLiteral is either the exact character sequences "use strict" or 'use strict'. A Use Strict Directive may not contain an EscapeSequence or LineContinuation. P.S. Вопрос на инглише https://stackoverflow.com/q/5214391/5441700

вторник, 10 декабря 2019 г.

Что такое thenable объект?

#javascript #ecmascript_6 #терминология #promise


Что такое thenable и как он используется?
    


Ответы

Ответ 1



Thenable этот тот же Promise, например, потому что он имеет метод then в виде Promise.prototype.then(). Как подметил vp_arth: henable не обязан быть полноценным Promise. Так, например, Promise.resolve(value) работает по такой логике: if the value is a thenable (i.e. has a "then" method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value Если value это промис, то возвращаемый промис(возвращает Promise.resolve) будет следовать логике промиса в аргументе value. Пример: // тут then будет выполнять then для new Promise Promise.resolve( new Promise(resolve => setTimeout(resolve, 1000, 'Выполнился через секунду')) ).then(value => console.log(value)) Иначе будет возвращен промис с уже выполненным значением: // тут передается промис с выполненым(resolve) значением Promise.resolve('Выполнился через секунду').then(value => console.log(value))

Ответ 2



Javascript включает реализацию спецификации Promises/A+ Согласно этой спецификации термин thenable означает следующее: “thenable” is an object or function that defines a then method. “thenable” – это объект или функция, в которых определен метод then.

Ответ 3



Promise.resolve(value): if the value is a thenable (i.e. has a "then" method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value Если у промиса есть then, то вся цепочка выполнится. Если then нет, то сразу вернётся значение. // сначала выполнится вся цепочка промиса, а потом вернётся результат var promise1 = Promise.resolve(new Promise((resolve, reject) => { resolve([1, 2, 3]); }).then(res => res)); promise1.then(function(value) { console.log(value); // выведет: Array [1, 2, 3] }); // сразу получаем результат var promise2 = Promise.resolve([1, 2, 3]); promise2.then(function(value) { console.log(value); // выведет: Array [1, 2, 3] });

суббота, 7 декабря 2019 г.

Разница в поведении между let и var в цикле for

#javascript #ecmascript_6


Разница в объявлении счетчика цикла через let и var в том что через var переменная-счетчик
видна за пределами цикла в функции, через let - только в пределах цикла.
Объясните пожалуйста почему в этих случаях разные выводы

for(var i = 0; i < 10; i++) setTimeout(() => console.log(i), 1000)


тут получаем десять десяток.

for(let i = 0; i < 10; i++) setTimeout(() => console.log(i), 1000)


а тут от нуля до десяти.

С первым примером все понятно, а почему во втором так? i ведь создается один раз
и прокручивается до начала выполнения первого setInterval до десятки.
Одним словом - почему в эти двух примерах разные результаты?
    


Ответы

Ответ 1



https://learn.javascript.ru/let-const При использовании в цикле, для каждой итерации создаётся своя переменная. Переменная var – одна на все итерации цикла и видна даже после цикла. С переменной let – всё по-другому. Каждому повторению цикла соответствует своя независимая переменная let. Если внутри цикла есть вложенные объявления функций, то в замыкании каждой будет та переменная, которая была при соответствующей итерации.

Ответ 2



Все дело в области видимости переменной. Оператор let объявляет переменную которая приурочена к блочной области видимости. Что такое блочная область видимости? Вот пример let a = 10; { let a = 20; console.log(a); // 20 } console.log(a); // 10 Как видно внутри блока и вне его ES6 создает совершенно разные переменные. Переменная созданная оператором let видна только в своей области видимости и вне ее области видимости к ней обратиться не возможно. Вот что произойдет если то же самое попробовать с оператором var. var a = 10; { var a = 20; console.log(a); // 20 } console.log(a); // 20 Как видишь оператор var не полностью соответствует правилам блочной видимости. Можно твой пример можно упростить для большей ясности for(let i = 0; i < 10; i++) setTimeout(() => console.log(i), 1000) 1) Можно представить следующим образом for(let i = 0; i < 10; i++){ setTimeout( function(){console.log(i);}, 1000 ) } 2) И разбить по строкам { let i = 0; setTimeout( function(){console.log(i);}, 1000 ) } { let i = 1; setTimeout( function(){console.log(i);}, 1000 ) } { let i = 2; setTimeout( function(){console.log(i);}, 1000 ) } /** И так 10 раз .... После 1000 миллисекунд запуститься наша тайемр-функция, и поскольку в каждом блоке благодаря оператору let создавалась свая переменная то в таймер-функиции будет та переменная которая была создана в том же блоке.**/ Но с оператором var все обстоит иначе. { var i = 0; setTimeout( function(){console.log(i);}, 1000 ) } { var i = 1; setTimeout( function(){console.log(i);}, 1000 ) } { var i = 2; setTimeout( function(){console.log(i);}, 1000 ) } /** И так 10 раз ... Поскольку во всех блоках будет одна и та же переменная то после того как через 1000 миллисекунд запущенная таймер-функция прочитает последнее значение переменной i - которое у нас будет 10.**/ Как то вот так у нас обстоят дела с javascript. Больше о блочной области видимости можешь прочитать здесь и здесь.