7 быстрых тестов JavaScript с объяснениями

  • 12 ноября, 15:16
  • 4237
  • 0

В этой статье мы рассмотрим некоторые основы JavaScript. Наслаждайтесь!

1. Объявления

Посмотрите на код ниже и попытайтесь ответить на то, что регистрируется (и почему), не читая объяснения ниже.

// situation 1
console.log(person);
var person = 'John';

// situation 2
console.log(person);
let person = 'Phill';

// situation 3
console.log(person);
const person = 'Frank';

// situation 4
const person = 'Vanessa';
console.log(person);
person = 'Mike';
console.log(person);

// situation 5
var person = 'John';
let person = 'Mike';
console.log(person);

// situation 6
var person = 'John';
if (person) {
  let person = 'Mike';
  console.log(person);
}
console.log(person);

Объяснение

Ситуация 1: Ожидаемый результат здесь будет видеть текст, John написанный в консоли, но, как ни странно, мы видим, что undefined вместо этого регистрируется. Интересно, почему?

Что ж, здесь мы можем увидеть классический JavaScript в действии. Такое поведение называется подъемом. Язык разбивает объявление переменной и присвоение значения на две части. Переменные перемещаются в верхнюю часть, объявленные со значением, установленным undefined(hoisted), независимо от того, где они были первоначально объявлены разработчиком. Это выглядит примерно так:

var person;
console.log(person);
person = 'John';

Ситуация 2: Здесь результатом будет ошибка ссылки.

Uncaught ReferenceError: Cannot access 'person' before initialization

Текст ошибки говорит сам за себя. Поскольку мы использовали ключевое слово let, наша переменная была поднята, но осталась неинициализированной, и выдается ошибка, информирующая нас о том, что мы пытаемся получить доступ к неинициализированной переменной. let было введено в ES6 позволяет нам использовать блок-контекстные переменные, таким образом, помогая нам предотвратить непреднамеренное поведение.

Ситуация 3: Здесь мы имеем ту же ошибку, что и в ситуации 2.

Разница в том, что мы использовали ключевое слово const, что предотвращает повторное назначение нашей переменной после инициализации. Это ключевое слово было также введено в ES6.

Ситуация 4: В этом случае мы можем увидеть, как ключевое слово const полезно и как оно может уберечь нас от непреднамеренного переназначения нашей переменной. В нашем примере сначала мы увидим Vanessa записанное в консоли, а затем ошибку типа.

Uncaught TypeError: Assignment to constant variable

Полезность const переменных растет экспоненциально с нашей кодовой базой.

Ситуация 5: Если переменная уже была определена в определенной области с ключевым словом var, попытка объявить ее снова с ключевым словом let в той же области выдает ошибку.

Итак, в нашем примере ничего не будет зарегистрировано, и мы увидим синтаксическую ошибку.

Uncaught SyntaxError: Identifier 'person' has already been declared

Ситуация 6: у нас есть переменная в области функций первой и переменная в области блоков второй. В этом случае не имеет значения, имеют ли они одинаковое имя / идентификатор.

В консоли мы должны увидеть Mike и John, в таком порядке. Почему?

Поскольку ключевое слово let дает нам переменные в области блока, то они существуют только в той области действия, в которой они созданы, в данном случае в пределах if...else оператора. Внутренняя переменная имеет приоритет над внешней переменной, и именно поэтому мы можем использовать один и тот же идентификатор.

2. Наследование

Рассмотрите следующие классы и попытайтесь ответить, что регистрируется и почему.

class Person {
  constructor() {
    this.sayHello = () => {
      return 'Hello';
    }
  }

  sayBye() {
    return 'Bye';
  }
}

class Student extends Person {
  sayHello() {
    return 'Hello from Student';
  }
}

const student = new Student();
console.log(student.sayHello());

Объяснение

Каждый раз, когда мы создаем новый Student экземпляр, мы устанавливаем sayHello свойство, которое будет function возвращать строку Hello. Это происходит в Person конструкторе класса parent ( ).

И в нашем примере sayHello метод в Student классе определен в цепочке прототипов. Учитывая, что каждый раз, когда мы создаем экземпляр Student класса, мы устанавливаем sayHello свойство для этого экземпляра как function возвращающую строку Hello, мы никогда не используем функцию, определенную в цепочке прототипов, поэтому мы никогда не увидим сообщение, Hello from Student которое регистрируется.

3. Изменчивость объекта

Рассмотрим следующие ситуации и продумайте каждый вывод раздела:

// situation 1
const user = {
  name: 'John',
  surname: 'Doe'
}

user = {
  name: 'Mike'
}

console.log(user);

// situation 2
const user = {
  name: 'John',
  surname: 'Doe'
}

user.name = 'Mike';
console.log(user.name);

// situation 3
const user = {
  name: 'John',
  surname: 'Doe'
}

const anotherUser = user;
anotherUser.name = 'Mike';
console.log(user.name);

// situation 4
const user = {
  name: 'John',
  surname: 'Doe',
  address: {
    street: 'My Street'
  }
}

Object.freeze(user);

user.name = 'Mike';
user.address.street = 'My Different Street';
console.log(user.name);

console.log(user.address.street);

Объяснение

Ситуация 1: Здесь, как мы узнали в предыдущем разделе, мы пытаемся переназначить const недопустимую переменную, поэтому мы получим ошибку типа:

Uncaught TypeError: Assignment to constant variable

Ситуация 2: В этом сценарии у нас другое поведение, хотя мы модифицируем переменную, объявленную с ключевым словом const. Разница в том, что мы меняем свойство объекта, а не его ссылку, и это разрешено для const переменных объекта.

Результатом в консоли должно быть слово Mike.

Ситуация 3: Назначив user к anotherUser переменным мы делимся ссылкой или ячейкой памяти, между ними. Другими словами, оба они будут указывать на один и тот же объект в памяти, поэтому изменение свойства одного объекта будет отражать изменение другого.

Результат в консоли должен быть Mike.

Ситуация 4: Здесь мы используем Object.freeze метод для предоставления функциональности, которой не хватало в предыдущем сценарии (ситуация 3). Используя этот метод, мы можем заморозить наш объект, не позволяя изменять значения его свойств. Но тут есть подвох. Он будет делать только небольшую остановку, то есть не будет защищать глубокие обновления свойств. По этой причине мы можем изменить свойство улицы, в то время как свойство name останется неизменным.

Выводом в консоли должны быть слова John и My Different Street в таком порядке.

4. Arrow Function

const student = {
  school: 'My School',
  fullName: 'John Doe',
  printName: () => {
    console.log(this.fullName);
  },
  printSchool: function () {
    console.log(this.school);
  }
};

student.printName();
student.printSchool();

Объяснение

Вывод в нашей консоли будет undefined и My School в таком порядке.

Вы, вероятно, будете знакомы со следующим синтаксисом:

var me = this;
// or
var self = this;

// ...
// ...
// somewhere deep...
// me.doSomething();

Вы можете рассматривать me или self переменную как родительскую область, доступную для каждой вложенной функции, созданной внутри.

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

5. Destructuring

Проверьте деструктуризацию. Разрешен ли данный синтаксис или будет выдана ошибка?

const rawUser = {
   name: 'John',
   surname: 'Doe',
   email: 'john@doe.com',
   displayName: 'SuperCoolJohn',
   joined: '2016-05-05',
   image: 'path-to-the-image',
   followers: 45
}

let user = {}, userDetails = {};
({ name: user.name, surname: user.surname, ...userDetails } = rawUser);

console.log(user);
console.log(userDetails); 

Объяснение

Приведенный выше синтаксис не выдает ошибку! 

Большинство из нас не привыкли к тому, что правая часть выражения выглядит так ... Я имею в виду, только левая сторона должна содержать точечный синтаксис ... или нет? :)

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

Консольный вывод:

// {name: "John", surname: "Doe"}
// {email: "john@doe.com", displayName: "SuperCoolJohn", joined: "2016-05-05", image: "path-to-the-image", followers: 45}

6. Async / Await

Что будет после вызова следующей непосредственной функции?

(async () => {
  let result = 'Some Data';

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve('Some data retrieved from the server'), 2000);
  });

  result = await promise;
  console.log(result);
})();

Объяснение

Если вы сказали Some data retrieved from the server , вы были правы!

Код приостанавливается, пока не будет выполнено обещание. Через две секунды он продолжает и записывает данный текст. Это означает, что движок JavaScript будет буквально ждать завершения асинхронной операции. async/await  подход нужен, чтобы получить результат обещания. 

7. Return

const multiplyByTwo = (x) => {
    return
    {
        result: x * 2
    };
}
console.log(multiplyByTwo(2));  

Объяснение

Если вы сказали {result: 4}, вы ошиблись. Выход есть - undefined

Приведенный выше код будет возвращен undefined из-за автоматической вставки точки с запятой, которая говорит, что между ключевым словом return и выражением не допускается разделитель строки.

Решение состоит в том, чтобы исправить нашу функцию и написать ее одним из следующих способов:

const multiplyByTwo = (x) => {
    return {
        result: x * 2
    };
}

или же
const multiplyByTwo = (x) => {
  return (
    {
      result: x * 2
    }
  );
}


0 комментариев
Сортировка:
Добавить комментарий