Вопросы по JavaScript. Версия 1
Каким будет вывод?
Вопрос № 1
function sayHi() {
console.log(name)
console.log(age)
var name = "John"
let age = 30
}
sayHi()
- A:
John
иundefined
- B:
John
иError
- C:
Error
- D:
undefined
иError
Ответ
Правильный ответ: D
В функции sayHi
мы сначала определяем переменную name
с помощью ключевого слова var
. Это означает, что name
поднимается в начало функции. name
будет иметь значение undefined
до тех пор, пока выполнение кода не дойдет до строки, где ей присваивается значение John
. Мы еще не определили значение name
, когда пытаемся вывести ее значение в консоль, поэтому получаем undefined
. Переменные, объявленные с помощью ключевых слов let
и const
, также поднимаются в начало области видимости, но в отличие от переменных, объявленных с помощью var
, не инициализируются, т.е. такие переменные поднимаются без значения. Доступ к ним до инициализации невозможен. Это называется временной мертвой зоной
. Когда мы пытаемся обратиться к переменным до их определения, JavaScript
выбрасывает исключение ReferenceError
.
Вопрос № 2
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
- A:
0 1 2
и0 1 2
- B:
0 1 2
и3 3 3
- C:
3 3 3
и0 1 2
- D:
3 3 3
и3 3 3
Ответ
Правильный ответ: C
Из-за очереди событий в JavaScript
функция обратного вызова setTimeout
выполняется после освобождения стека вызовов. Так как переменная i
в первом цикле определяется с помощью ключевого слова var
, она является глобальной. В цикле мы каждый раз увеличиваем значение i
на 1
, используя оператор ++
. К моменту выполнения setTimeout
в первом примере значение i
равняется 3
. Во втором цикле переменная i
определяется с помощью ключевого слова let
. Такие переменные (а также переменные, объявленные с помощью ключевого слова const
) имеют блочную область видимости (блок - это код внутри фигурных скобок - {}
). На каждой итерации i
будет иметь новое значение, и это значение будет замкнуто в области видимости внутри цикла.
Вопрос № 3
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
console.log(shape.diameter())
console.log(shape.perimeter())
- A:
20
и62.83185307179586
- B:
20
иNaN
- C:
20
и63
- D:
NaN
и63
Ответ
Правильный ответ: B
Обратите внимание, что diameter
- это обычная функция, а perimeter
- стрелочная. У стрелочных функций, в отличие от обычных, значение this
указывает на внешнее/лексическое окружение. Это значит, что при вызове метода perimeter
его this
указывает не на объект shape
, а на глобальный объект window
. У этого объекта нет свойства radius
, поэтому возвращается undefined
.
Вопрос № 4
console.log(+true)
console.log(!"John")
- A:
1
иfalse
- B:
0
иtrue
- C:
false
иNaN
- D:
false
иfalse
Ответ
Правильный ответ: A
Унарный плюс (+
) приводит операнд к числу. true
- это 1
, а false
- 0
. Строка John
- это истинное значение. Мы спрашиваем, является ли это значение ложным? Ответ: false
.
Вопрос № 5
let c = { greeting: "Hey!" }
let d
d = c
c.greeting = "Hello!"
console.log(d.greeting)
- A:
Hello!
- B:
Hey!
- C:
undefined
- D:
Error
Ответ
Правильный ответ: A
В JavaScript
все объекты являются "ссылочными" типами данных, т.е. значения объектов передаются по ссылкам. Сначала в переменной c
создается ссылка на объект. Затем мы указываем переменной d
ссылаться на тот же объект, на который ссылается c
. При изменении объекта меняются значения всех указывающих на него ссылок.
Вопрос № 6
let a = 3
let b = new Number(3)
let c = 3
console.log(a == b)
console.log(a === b)
console.log(b === c)
- A:
true false true
- B:
false false true
- C:
true false false
- D:
false true true
Ответ
Правильный ответ: C
new Number
- это встроенная функция-конст руктор. И хотя она выглядит как число, это не настоящее число: у него имеется ряд дополнительных возможностей. На самом деле это объект. Оператор ==
(абстрактное/нестрогое равенство) разрешает преобразование типов данных, он проверяет равенство значений. Оба значения равны 3
, поэтому возвращается true
. При использовании оператора ===
(строговое равенство, оператор идентичности) должны совпадать не только значения, но и типы данных. В данном случае это не так: new Number()
- это не число, а объект. Поэтому два последних сравнения возвращают false
.
Вопрос № 7
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor
return this.newColor
}
constructor({ newColor = "green" } = {}) {
this.newColor = newColor
}
}
const freddie = new Chameleon({ newColor: "pink" })
freddie.colorChange("orange")
- A:
orange
- B:
pink
- C:
green
- D:
Error
Ответ
Правильный ответ: D
Метод colorChange
является статическим. Такие методы не имеют доступа к экземплярам класса. Так как freddie
- это экземпляр, статический метод в нем не доступен. Поэтому выбрасывается исключение TypeError
.
Вопрос № 8
// обратите внимание: код выполняется в нестрогом режиме
let greeting
greetign = {} // опечатка!
console.log(greetign)
- A:
{}
- B:
Error
- C:
undefined
- D:
""
Ответ
Правильный ответ: A
С помощью выражения greetign = {}
мы создаем новый глобальный пустой объект, который выводится в консоль. Когда мы вместо greeting
написали greetign
, интерпретатор выполнил global.greetign = {}
в Node.js
(или window.greetign = {}
в браузере). В строгом режиме ("use strict"
) будет выброшено исключение ReferenceError: greetign is not defined
.
Вопрос № 9
function bark() {
console.log("Woof!")
}
bark.animal = "dog"
console.log(bark.animal)
- A:
dog
- B:
Error
- C:
undefined
- D:
""
Ответ
Правильный ответ: A
В JavaScript
такое возможно, т.к. функции в JS
- это тоже объекты. Точнее, функция — это специальный тип объекта, который можно вызывать (такой объект имеет внутренний слот callable
). Кроме того, функция — это объект со свойствами, вызывать которые нельзя, поскольку они не являются функциями.
Вопрос № 10
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const person = new Person("John", "Smith")
Person.getFullName = function () {
return `${this.firstName} ${this.lastName}`
}
console.log(person.getFullName())
- A:
Error
- B:
""
- C:
John Smith
- D:
undefined undefined
Ответ
Правильный ответ: A
Нельзя добавлять свойства к конструктору как к обычному объекту. Если необходимо добавить свойство или метод всем экземплярам, то следует использовать прототипы. В данном случае выражение Person.prototype.getFullName = function () { return ${this.firstName} ${this.lastName} }
сделает метод getFullName
рабочим. В чем здесь преимущество? Предположим, что мы добавили этот метод к конструктору. Возможно, он нужен не каждому экземпляру класса Person
. Это приведет к лишнему расходованию памяти, т.к. все экземпляры будут иметь указанный метод. Напротив, если мы добавим данный метод к прототипу, у нас будет только одно место в памяти, к которому смогут обращаться все экземпляры. Такие методы называются совместными или распределенными (shared).
Вопрос № 11
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const john = new Person("John", "Smith")
const jane = Person("Jane", "Air")
console.log(john)
console.log(jane)
- A:
Person {firstName: "John", lastName: "Smith"}
иundefined
- B:
Person {firstName: "John", lastName: "Smith"}
иPerson {firstName: "Jane", lastName: "Air"}
- C:
Person {firstName: "John", lastName: "Smith"}
и{}
- D:
Person {firstName: "Smith", lastName: "Smith"}
иError
Ответ
Правильный ответ: A
Мы создаем объект jane
без помощи ключевого слова new
. Использование new
приводит к созданию нового объекта (экземпляра). Без new
создается глобальный объект. Мы указали, что this.firstName
равняется Jane
и this.lastName
равняется Air
. На самом деле, мы определили global.firstName = 'Jane'
и global.lastName = 'Air'
. Значением jane
является undefined
, поскольку мы ничего не возвращаем из функции Person
.
Вопрос № 12
function sum(a, b) {
return a + b
}
console.log(sum(1, "2"))
- A:
NaN
- B:
Error
- C:
"12"
- D:
3
Ответ
Правильный ответ: C
JavaScript
- это динамически типизированный язык: мы не определяем тип данных при объявлении переменных (для этого был придуман TypeScript
). Значения переменных могут быть автоматически преобразованы из одного типа в другой без нашего участия. Это называется "неявным приведением типов". Приведение - это преобразование данных из одного типа в другой. В рассматриваемом примере JavaScript
конвертирова л число 1
в строку, чтобы операция имела смысл и вернула хоть какое-то значение. При сложении числа (1
) и строки ("2"
) число преобразуется в строку. Мы можем объединять строки так: "Hello" + "World"
. Это называется конкатенацией строк. Таким образом, 1 + "2"
возвращает "12"
.
Вопрос № 13
let number = 0
console.log(number++)
console.log(++number)
console.log(number)
- A:
1 1 2
- B:
1 2 2
- C:
0 2 2
- D:
0 1 2
Ответ
Правильный ответ: C
Постфиксный оператор ++
:
- возвращает значение (
0
); - увеличивает (инкрементирует) значение (после чего значением переменной
number
становится1
). Префиксный оператор++
: - инкрементирует значение (теперь
number === 2
); - возвращает значение (
2
). Результат:0 2 2
.
Вопрос № 14
function getPersonInfo(one, two, three) {
console.log(one)
console.log(two)
console.log(three)
}
const person = "John"
const age = 30
getPersonInfo`${person} is ${age} years old`
- A:
John 30 ["", " is ", " years old"]
- B:
["", " is ", " years old"] John 30
- C:
John ["", " is ", " years old"] 30
- D:
undefined
Ответ
Правильный ответ: B
При использовании тегированных шаблонных литералов (tagged template literals) первым значением, возвращаемым функцией, является массив строк. Прочими значениями являются значения, переданные функции в качестве аргументов.
Вопрос № 15
function checkAge(data) {
if (data === { age: 18 }) {
console.log("Ты взрослый!")
} else if (data == { age: 18 }) {
console.log("Ты по-прежнему взрослый.")
} else {
console.log("Хм... У тебя что, нет возраста?")
}
}
checkAge({ age: 18 })
- A:
"Ты взрослый!"
- B:
"Ты по-прежнему взрослый."
- C:
"Хм... У тебя что, нет возраста?"
- D:
undefined
Ответ
Правильный ответ: C
В операциях сравнения примитивы сравниваются по значениям, а объекты - по ссылкам. JavaScript
проверяет, чтобы объекты указывали на одну и ту же область памяти. Сравниваемые объе кты в рассматриваемом примере не такие: объект, переданный в качестве аргумента, указывает на другое место в памяти, нежели объекты, используемые в сравнениях. Поэтому выражения { age: 18 } === { age: 18 }
и { age: 18 } == { age: 18 }
возвращают false
.
Вопрос № 16
function getAge(...args) {
console.log(typeof args)
}
getAge(30)
- A:
number
- B:
array
- C:
object
- D:
NaN
Ответ
Правильный ответ: C
Оператор распространения или расширения (spread, ...
) возвращает массив с аргументами, переданными функции. Массив - это объект, поэтому выражение typeof args
возвращает object
.
Вопрос № 17
function getAge() {
"use strict"
age = 30
console.log(age)
}
getAge()
- A:
30
- B:
undefined
- C:
Error
- D:
NaN
Ответ
Правильный ответ: C
"use strict"
, среди прочего, позволяет предотвратить случайное объявление глобальных переменных. Мы не объявляли переменную age
, поэтому (в строгом режиме) выбрасывается исключение ReferenceError
. В нестрогом режиме ошибки не возникнет, а переменная age
станет свойством глобального объекта window
.
Вопрос № 18
const sum = eval("10*10+5")
console.log(sum)
- A:
105
- B:
"105"
- C:
Error
- D:
"10*10+5"
Ответ
Правильный ответ: A
Функция eval
выполняет код, переданный ей в виде строки. Если это выражение (как в данном случае), то оно вычисляется (оценивается). Выражение 10 * 10 + 5
возвращает число 105
. Использовать eval
не рекомендуется по причинам безопасности.
Вопрос № 19
var num = 8
var num = 10
console.log(num)
- A:
8
- B:
10
- C:
undefined
- D:
Error
Ответ
Правильный ответ: B
С помощью ключевого слова var
можно определять любое количество одноименных переменных. Переменная будет хранить последнее присвоенное ей значение. Однако, такой трюк нельзя проделать с let
и const
, т.к. переменные, объявленные с помощью этих ключевых слов, имеют блочную область видимости.
Вопрос № 20
const obj = { 1: "a", 2: "b", 3: "c" }
const set = new Set([1, 2, 3, 4, 5])
console.log(obj.hasOwnProperty("1"))
console.log(obj.hasOwnProperty(1))
console.log(set.has("1"))
console.log(set.has(1))
- A:
false true false true
- B:
false true true true
- C:
true true false true
- D:
true true true true
Ответ
Правильный ответ: C
Ключи объектов (кроме Symbol
) являются строками, даже если заданы не в виде строк (например, индексы в массиве). Поэтому выражение obj.hasOwnProperty('1')
также возвращает true
. Однако, это не работает с Set
. Значение 1
отсутствует в set
: set.has('1')
возвращает false
, а set.has(1)
- true
.
Вопрос № 21
const obj = { a: "one", b: "two", a: "three" }
console.log(obj)
- A:
{ a: "one", b: "two" }
- B:
{ b: "two", a: "three" }
- C:
{ a: "three", b: "two" }
- D:
Error
Ответ
Правильный ответ: C
Если в объекте имеется два ключа с одинаковыми именами, то первый ключ перезаписывается. Его позиция сохраняется, а значением становится последнее из присвоенных.