Первый взгляд на JavaScript глазами Java — разработчика

в 13:27, , рубрики: java, javascript

Первый взгляд на JavaScript глазами Java — разработчика - 1

Java и JavaScript — это не одно и тоже! Теперь вы знаете главную тайну масонов. В этой статье я хочу поделиться с вами мыслями и соображениями о языке JavaScript через глаза Java — разработчика. Проведу сравнение структур данных, типов, функций, объектов, классов и других общих возможностей языков Java и JavaScript.

Фундамент

JavaScript — это интерпретируемый, скриптовый язык для написания сценариев.  Это значит, что тот код, который вы напишите, будет выполняться построчно, от инструкции в инструкции, от скрипта к скрипту. Java — язык компилируемый, это значит, что перед стартом программы на Java, компилятор должен перевести весь код, написанный вами, в специальный машинный код, понятный для JVM - bytecode.

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

  • Перед запуском компиляции вашего кода, написанного на Java, компилятор проведет за вас синтаксический и семантический анализ, и в случае проблем — оповестит вас об этом. Получается, что вы имеете возможность узнать об ошибке еще до запуска приложения. В JS, в силу отсутствия компилятора, такой проверки нет. И ошибки, допущенные вами в момент написания кода будут обнаружены только после запуска скрипта.
  • Т.к. JavaScript выполняется построчно стиль написания функций, вызывающие другие функции, будет отличаться от принципов Чистого кода «дядюшки» Боба, проповедуемого для написания программ на Java. В JS, если из функция foo() необходимо вызвать функцию bar(), вы должны определить bar() до foo(). Если сделать наоборот, как в Java — приложениях, то вызов может закончится неудачно в некоторых старых браузерах.

// Пример на Java
public class JavaVsJS {

    public static void foo(String name) {
        bar(name);
    }

    private static void bar(String name) {
        System.out.println(name);
    }

    public static void main(String[] args) {
        JavaVsJS.foo("Igor Ivanovich");
    }
}

// Пример на JavaScript
var name = "Igor Ivanovich";

// Функция bar() определена до своего вызова в foo()
function bar(name) {
    document.write(name);
}

function foo(name) {
    bar(name);
}

foo(name);

Переменные и их типы

JavaScript — слабо типизированный язык, в отличии от Java. С одной стороны, это дает больше гибкости, с другой — больше возможностей совершить выстрел в ногу. Для объявления переменной в JS достаточно использовать ключевое слово var, после которого указать имя переменной и, если необходимо, значение. На практике же необязательно даже использовать ключевое слово var.

var name_1 = "Igor Ivanovich";
name_2 = "Renat Raphaelevich";

Теперь о типах. В JS для целочисленных и чисел с плавающей точкой нет разных типов. Они объединены в типе number.String, boolean такие же как и в Java. В JS есть тип Object. Если в Java это суперкласс всех классов, то в JS это просто один из типов.

var a_number = 10;     // number    
var b_number = 10.01;  // number

var a_string = "10";   // string
var b_string = "10.1"; // string

var tr = true; //boolean

var person = {name: "Igor", secondName: "Ivanovich"}; //object

Java-разработчика ничего не должно смущать. Теперь давайте попробуем провести операции между объектами разных типов и посмотрим что получится.

10 +1
> 11
10 +"1"
> "101"
true && false
> false
true && 1
> 1
true && 0
> 0
true && "1"
> "1"
false && "1"
> false
false && 0
> false
0.1 + 0.7
> 0.7999999999999999

Некоторые результаты могут немного смутить Java-разработчика. Например, возможность использовать тип boolean таким образом: true && 1 и при этом получать какой-то результат. В Java невозможно провести такую операцию т.к. компилятор выдаст ошибку о том, что нельзя использовать оператор && с типами не boolean.  Теперь обратим внимание на еще одно отличие JS и Java: операторы === и !==. Это операции сравнения, необходимые слабо типизированному языку, такому как JS.  === — вернет true, если сравниваемые объекты равны по значению и их типы совпадают. !==, соответственно, вернет true, если сравниваемые объекты не равны по значению или их типы не совпадают. Рассмотрим несколько примеров:

10 == 10
> true
10 == "10"
> true
10 === "10"
> false
10 != "10"
> false
10 !== "10"
> true
10 !== 10
> false

Функции

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

<!-- Вызов функции на элементе по событию клика на кнопку -->
<input type="button" value="CLICK" onclick="foo();">

//Описание функции foo()
function foo() {
    document.write("Calling foo");
}

При нажатии на кнопку на странице будет напечатано «Calling foo» как результат вызова функции. Теперь о странностях на которые может обратить внимания Java-разработчик. Вернемся к примеру выше, где функция foo() вызывает в себе функцию bar() — которая используется только как внутренняя функция. По идеи мы ожидаем того, что ее можно сделать приватной. Но в JS нет никаких селекторов доступа. Нет возможности сделать поле private простым добавлением этого слова перед объявлением функции. Пойдем дальше. Создадим объект — экземпляр класса со своими полями и одним методом.

function getLocation() {
    if (this.person === "Igor" && this.age > 25) {
        document.write("Your name " + this.person + ", location = Vishnevaia 1");
    } else {
        document.write("Your name " + this.person + ", location = rp Sokoloviy");
    }
}

function Person(person, age) {
    this.person = person;
    this.age = age;
    this.getLocation = getLocation;
}

var igor = new Person("Igor", 26);

igor.getLocation();
document.write("<br />");
getLocation();

Если смотреть на этот код глазами Java-разработчика, то можно отметить,  что функция Person — является конструктором объектов класса Person и определением полей, и методов, входящих в класс. Функция getLocation() является функцией класса Person. Внутри нее мы используем обращение к полям экземпляра класса this.person и this.age. Логично, что эта функция, использующая текущей экземпляр класса Person, должна работать только с ним и последний вызов функции getLocation() не должен работать. Но, в JS это нормально, потому что понятия класса, функции, методов класса размыты. Слабая типизация во всем. Выполнив этот скрипт вы получите следующий вывод в окне браузера:

Your name Igor, location = Vishnevaia 1
Your name undefined, location = rp Sokoloviy

Однако, переписав код следующим образом, определив функцию внутри класса, ее вызов не для экземпляра класса будет недоступен:

function Person(person, age) {
    this.person = person;
    this.age = age;
    this.getLocation = function () {
        if (this.person === "Igor" && this.age > 25) {
           document.write("Your name " + this.person + ", location = Vishnevaia 1");
        } else {
           document.write("Your name " + this.person + ", location = rp Sokoloviy");
        }
    };
}

var igor = new Person("Igor", 26);

igor.getLocation();
document.write("<br />");
getLocation();

Последний вызов приведет к ошибке, т.к. функция getLocation() не определена. Получается что хоть в JS и нет модификаторов доступа, но есть область видимости функций и переменных, управляемая с помощью фигурных скобок. JavaScript прекрасен огромным количеством вариантов совершить выстрел себе в ногу.

Массивы

Когда мы говорим о массивах, то представляем структуру данных, хранящую однотипные элементы, доступ к которым осуществляется по индексу. Это в Java. Когда же дело касается JS и его слабой типизации, то в дело вступает настоящая анархия. В следующим примере мы создаем 4 массива. В первом элементы разных типов, во втором только числа, в третьем boolean, в четвертом boolean и number:

var mix = [3, "Igor Ivanovich", "Renat Raphaelevich", "Sergey Sergeevich", 1, 12.3, true];

var numbers = [1,2,3,4,5];

var booleans = [false, false, true];

var mix2 = [false, 1, 2];

document.write("Type elements in mix: ");
for (element in mix) {
    document.write(typeof mix[element] + " ");
}
document.write("<br /> Type elements in numbers: ");
for (element in numbers) {
    document.write(typeof numbers[element] + " ");
}
document.write("<br /> Type elements in booleans: ");
for (element in booleans) {
    document.write(typeof booleans[element] + " ");
}
document.write("<br /> Type elements in mix2: ");
for (element in mix2) {
    document.write(typeof mix2[element] + " ");
}
var sum = numbers[0] + numbers[1];
document.write("<br /> sum numbers = " + sum);

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

Type elements in mix: number string string string number number boolean 
Type elements in numbers: number number number number number 
Type elements in booleans: boolean boolean boolean 
Type elements in mix2: boolean number number 
sum numbers = 12

Вывод

При первом касании с языком JavaScript у Java — разработчика могут возникнуть все вышеперечисленные замечания и вопросы. При моем первом знакомстве с JS я испытывал не самые веселые эмоции. Скорее это было вот так: «Что за ...?». Многие отличия и не понимания кроются в разнице типизаций двух языков. Я не знаю зачем JS нужна слабая типизация.

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

Да, есть TypeScript, который вроде как является типизированным, но в итоге он же будет переведен все в тот же JS. Лично я не сторонник слабой типизации, но мой коллега, недавно попробовавший JavaScript, почему-то был в восторге от нее. Возможно, это дело вкуса. А как считаете вы, что лучше слабая или сильная типизация?

Автор: Николай Грибанов

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js