Откажись от var в пользу const и let

Существует несколько причин создавать переменную:

  • Создание промежуточной абстракции, к которой надо будет возвращаться несколько раз. Значение переменной в течение ее жизни не изменяется.
  • Создание контейнера для результата, который будет заполнен разными значениями в зависимости от ситуации. Значение переменной в течение ее жизни изменяется.

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

Объявление переменной через var допускает ее изменение в любой момент времени. Подскажи себе и тому кто код будет читать, что переменная не будет изменяться: объяви ее через const. А чтобы не допускать разночтений типа “этот var случаен или преднамерен?” используй let для переменных, которые будут изменяться.

Попробуй оценить код, написанный по правилам изложеным выше:

const editedDoc = resources.find({
  _id: this.props.docId
}).fetch()[0]
if (!editedDoc) {
  return {
    loading: false,
    noDocument: true,
    valueOfFieldInDb: null,
    hasValueForInput: false,
    valueForInput: null
  }
}

// Restore value for input
const prevModel = this.state ? this.state.model : null
const valueOfFieldInDb = getValueByPath(this.props.valuePath, editedDoc)
let valueForInput = null
if (prevModel && prevModel.valueForInput != null) {
  valueForInput = prevModel.valueForInput
} else if (pc.has(this.props.valuePath)) { // if no prev state, take value from persistent storage
  valueForInput = pc.get(this.props.valuePath)
} else if (valueOfFieldInDb != null) { // if neither model or state value, take value from doc
  valueForInput = valueOfFieldInDb
}
assert(valueForInput != null, `Don't expect value to be null`)
return {
  loading: false,
  noDocument: false,
  valueOfFieldInDb: valueOfFieldInDb,
  hasValueForInput: true,
  valueForInput: valueForInput
}  
2 симпатии

var теперь стал рудиментом прошлого (туда ему и дорога) и будет встречаться разве что в legacy коде. let и const действительно хороши и ограждают от множества ошибок. Плюс они сами по себе являются «семантическими». Стало быть не просто декларируют переменную, но и указывают на то как она будет использоваться.

2 симпатии

Я бы поспорил со словом “семантическими” у @xoria, но раз уж оно взято в кавычки, то не буду. Как известно из каждого учебника ФП, любую программу можно написать без единой мутабельной переменной. Мне даже немного обидно, что let означает именно переменную, а не как в функциональных языках. Но, конечно, по сравнению с функциональной, а не блочной, видимостью var, даже мутабельный let это просто квинтэссенция семантики, юзабилити и всего такого:

> for(var i=1; i<=2; i++){console.log(i)}
1
2
undefined
> i
3 

У меня вообще есть такое мнение, как будто разработчики языков допускают одни и те же антисемантические ошибки, кочующие из синтаксиса в синтаксис. Приведу список из всех трёх (одна из них, по понятным причинам, JS не касается, но я всё равно её выложу, мб кто-нибудь мне поможет понять целесообразность этого решения):

№1: объявление константы через const, а не переменной через let mut или var mut (очевидно, определять константы трудоёмче, а не наоборот, что, естественно, приводит к тому, что мало кто хочет использовать константы).

№2: использование оператора мутации C-style = вместо Pascal-style := (выражение a=b кажется обманчиво-симметричным, хотя на самом деле происходит чтение второго операнда и мутация первого).

№3 (которая не встречается в JS): объявление знакового/беззнакового числа (не обязательно целочисленного) как int / unsigned int вместо signed int / int (эта ошибка настолько глубоко въелась в системное программирование, что боюсь, она уже не будет исправлена никогда).

1 симпатия

Встречал рекомендации не использовать не только var, но и let. Сейчас в коде использую только const.

И вообще ниодного let в коде нет? Даже в циклах, например?

В старом коде есть и let, и var. В новом только const вроде. По крайней мере я не помню, чтобы была необходимость в let.

Можно увидеть примеры вашего кода? :)

Весь код в закрытом репозитории. Вот первый попавшийся компонент (не знаю, правда, что это даст):

import React, { Component } from 'react';
import moment from 'moment';
export default class Clock extends Component {
	componentWillMount() {
		const intervalID = setInterval(() => this.setState({
			time: Date.now()
		}), 5000);
		this.setState({
			intervalID
		});
	}
	componentWillUnmount() {
		clearInterval(this.state.intervalID);
	}
	state = {
		intervalID: null,
		time: Date.now()
	}
	render() {
		const msOffset = (this.props.offset || 0) * 1000;
		const formattedTime = moment(this.state.time).utc().add(msOffset).format('hh:mm a');
		return (
			<span>{formattedTime}</span>
		);
	}
1 симпатия

[quote=“dmitry, post:5, topic:1529”]
Даже в циклах, например?
[/quote]А зачем кому-нибудь могут понадобиться циклы? ;-)

@jimmy_ringo, Не знаю, почему вы сослались на меня, но мне нравится ваш код своей декларативностью: он такой же простой, как HTML-страница, тут нечему падать, а единственное хитрое место это setInterval (лямбда понятно что делает, а интервал на 5с всё-таки странный). Ну и default в контексте export тоже неясно что делает, я просто загуглил и вот что нашел на MDN:

Note that it is not possible to use var, let or const with export default.

Оптимизация, работа с типизированными массивами, утилитарные функции.

1 симпатия

Оптимизация. Работа с объектами и массивами на низком уровне (например в утилитарных функциях).

Наверно случайно. По поводу default и const имеется в виду, что нельзя написать так: export default const a = 5; Но при этом внутри экспортируемого класса / функции это делать не запрещено.

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

ОК, спасибо за уточнение.

Просто потому, что я не понял, зачем он там нужен.

@dmitry, в общем с кодом вполне согласен, но вот если такой юзкейс: есть много кода на проекте написанного давно, люди/человек, который его писал, любил делать var внутри if (исходя из условий определенных понятное дело), но вот потом по коду эти переменные используются, если они не undefind, полным ходом - вот в таком коде как то стрёмно рефакторить var to let, ну и в случае если очень надо будет, то довольно весомый оверхед получается - думать что где объявлено.

Все верно. Я бы такой код помечал комментом, что он нерефакторенный. Вопрос в том как вы выстроете стратегию работы с legacy кодом.

@dmitry, а какие еще приимущества использования let вместо var ты бы отметил? например, есть ли прирост производительности в итоге реальный и т.д.?

Ничего кроме ментального фреймверка (того как думать об судьбе переменной по ее объявлению) не могу выделить.

  1. тогда уж просто mut
  2. это же паскаль :-< зачем это ?
  3. в C и в D использую int/uint и нет никаких проблем.

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

sort(массив, little_to_big, stabale, 5); // здесь по сути самый обычный тьюпл, который который передаётся в функцию.
var (массив, little_to_big, stabale) = sort(1,2,3); // а так уже не работает

А зачем тогда разрабы сделали и let и const, и в каком случаи что использовать ?
Дак, это получаеться что var можно закапывать уже ?
И как быть без циклов то ?

Это попытка “исправить” (в кавычках потому что нет понятия “правильный/неправильный”) поведение var.

В частности чтобы реализовать блочную область видимости переменных

if (true) {
   var name = "Dmitry"
}
console.log(name) // undefined из-за лексической области видимости

if (true) {
   const name = "Dmitry"
}
console.log(name) // error из-за блочной области видимости

и создать “неизменяемые” переменные. В кавычках, потому что есть нюансы: cons переменная хранящая ссылочный тип данных позволяет изменение внутри ссылочного типа данных.

// Попытка изменить const переменную выбрасывает ошибку
const name = "Masha"
name = "Dima" // error
// Ссылочный тип данных в const  переменной меняется.
// Для некоторых это может быть неожиданностью
const names = ["Masha"]
names.push("Dmitry") // ok

const - для всего что не будет изменяться дальше по ходу кода. let - для всего остального. По этому принципу написан пример кода в теле поста.

Да, именно об этом и пост.

Актуальноть циклов и их возможности не изменяются из-за использованися let и const. Изменяемые i объявляй через let. Главное помни что i теперь не доступна
вне цикла.

const names = ["Dima", "Masha", "Lena"]
for (let i = 0; i < names.length; i += 1) {
	console.log(names[i])
}
console.log(i) // error
1 симпатия