Javascript-questions-1

https://github.com/lydiahallie/javascript-questions

https://github.com/lydiahallie/javascript-questions/blob/master/README-zh_CN.md

this

3.关于this在普通函数和箭头函数中的指向问题

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
const shape = {
radius: 10,
diameter() {
return this.radius * 2;
},
perimeter: () => 2 * Math.PI * this.radius
};

console.log(shape.diameter());
console.log(shape.perimeter());

// 20 and NaN

Note that the value of diameter is a regular function, whereas the value of perimeter is an arrow function.

With arrow functions, the this keyword refers to its current surrounding scope, unlike regular functions! This means that when we call perimeter, it doesn’t refer to the shape object, but to its surrounding scope (window for example).

There is no value radius on that object, which returns undefined.

class static

8.class中的静态方法static

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
return this.newColor;
}

constructor({ newColor = "green" } = {}) {
this.newColor = newColor;
}
}

const freddie = new Chameleon({ newColor: "purple" });
console.log(freddie.colorChange("orange"));

// TypeError

The colorChange function is static. Static methods are designed to live only on the constructor in which they are created, and cannot be passed down to any children. Since freddie is a child, the function is not passed down, and not available on the freddie instance: a TypeError is thrown.

new

12.new的性质

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

const lydia = new Person("Lydia", "Hallie");
const sarah = Person("Sarah", "Smith");

console.log(lydia);
console.log(sarah);

// Person {firstName: "Lydia", lastName: "Hallie"} and undefined

For sarah, we didn’t use the new keyword. When using new, it refers to the new empty object we create. However, if you don’t add new it refers to the global object!

We said that this.firstName equals "Sarah" and this.lastName equals "Smith". What we actually did, is defining global.firstName = 'Sarah' and global.lastName = 'Smith'. sarah itself is left undefined, since we don’t return a value from the Person function.

事件传播

13.事件的执行顺序

Capturing > Target > Bubbling

捕获(capturing)阶段中,事件从祖先元素向下传播到目标元素。当事件达到目标(target)元素后,冒泡(bubbling)才开始。

img

函数传参

17.传递字符串模板作为函数的参数

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
function getPersonInfo(one, two, three) {
console.log(one);
console.log(two);
console.log(three);
}

const person = "Lydia";
const age = 21;

getPersonInfo`${person} is ${age} years old`;

// ["", " is ", " years old"] "Lydia" 21

If you use tagged template literals, the value of the first argument is always an array of the string values. The remaining arguments get the values of the passed expressions!

sessionStorage

22.sessionStorage的访问时间

How long is cool_secret accessible?

1
sessionStorage.setItem('cool_secret', 123)

The data stored in sessionStorage is removed after closing the tab.

If you used localStorage, the data would’ve been there forever, unless for example localStorage.clear() is invoked.

key type is string?

24.key在不同类型的数据中是什么类型?

What’s the output?

1
2
3
4
5
6
7
8
9
const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);

obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);

// true true false true

All object keys (excluding Symbols) are strings under the hood, even if you don’t type it yourself as a string. This is why obj.hasOwnProperty('1') also returns true.

It doesn’t work that way for a set. There is no '1' in our set: set.has('1') returns false. It has the numeric type 1, set.has(1) returns true.

same key

25.key值相同会发生什么?

What’s the output?

1
2
3
4
const obj = { a: "one", b: "two", a: "three" };
console.log(obj);

// { a: "three", b: "two" }

If you have two keys with the same name, the key will be replaced. It will still be in its first position, but with the last specified value.

key is object

29.对象的key是对象会怎样?

What’s the output?

1
2
3
4
5
6
7
8
9
10
const a = {};
const b = { key: "b" };
const c = { key: "c" };

a[b] = 123;
a[c] = 456;

console.log(a[b]);

// 456

Object keys are automatically converted into strings. We are trying to set an object as a key to object a, with the value of 123.

However, when we stringify an object, it becomes "[Object object]". So what we are saying here, is that a["Object object"] = 123. Then, we can try to do the same again. c is another object that we are implicitly stringifying. So then, a["Object object"] = 456.

Then, we log a[b], which is actually a["Object object"]. We just set that to 456, so it returns 456.

event.target

31.事件对象

What is the event.target when clicking the button?

1
2
3
4
5
6
7
<div onclick="console.log('first div')">
<div onclick="console.log('second div')">
<button onclick="console.log('button')">
Click!
</button>
</div>
</div>

event.target 是 button

The deepest nested element that caused the event is the target of the event. You can stop bubbling by event.stopPropagation

打印顺序:button second div first div

在事件传播期间,有三个阶段:捕获、目标和冒泡。默认情况下,事件处理程序在冒泡阶段执行(除非将 useCapture 设置为 true)。它从嵌套最深的元素向外传播。

try catch

38.try catch 的作用域块

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
13
(() => {
let x, y;
try {
throw new Error();
} catch (x) {
(x = 1), (y = 2);
console.log(x);
}
console.log(x);
console.log(y);
})();

// 1 undefined 2

The catch block receives the argument x. This is not the same x as the variable when we pass arguments. This variable xis block-scoped.

Later, we set this block-scoped variable equal to 1, and set the value of the variable y. Now, we log the block-scoped variable x, which is equal to 1.

Outside of the catch block, x is still undefined, and y is 2. When we want to console.log(x) outside of the catchblock, it returns undefined, and y returns 2.

reduce

40.reduce的用法

What’s the output?

1
2
3
4
5
6
7
8
[[0, 1], [2, 3]].reduce(
(acc, cur) => {
return acc.concat(cur);
},
[1, 2]
);

// [1, 2, 0, 1, 2, 3]

[1, 2] is our initial value. This is the value we start with, and the value of the very first acc. During the first round, acc is [1,2], and cur is [0, 1]. We concatenate them, which results in [1, 2, 0, 1].

Then, [1, 2, 0, 1] is acc and [2, 3] is cur. We concatenate them, and get [1, 2, 0, 1, 2, 3]

generator yield

44.generator 和 yield 的用法

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
function* generator(i) {
yield i;
yield i * 2;
}

const gen = generator(10);

console.log(gen.next().value);
console.log(gen.next().value);

// 10 20

Regular functions cannot be stopped mid-way after invocation. However, a generator function can be “stopped” midway, and later continue from where it stopped. Every time a generator function encounters a yield keyword, the function yields the value specified after it. Note that the generator function in that case doesn’t return the value, it yields the value.

First, we initialize the generator function with i equal to 10. We invoke the generator function using the next() method. The first time we invoke the generator function, i is equal to 10. It encounters the first yield keyword: it yields the value of i. The generator is now “paused”, and 10 gets logged.

Then, we invoke the function again with the next() method. It starts to continue where it stopped previously, still with iequal to 10. Now, it encounters the next yield keyword, and yields i * 2. i is equal to 10, so it returns 10 * 2, which is 20. This results in 10, 20.

Promise.race

45.Promise.race 的用法

What does this return?

1
2
3
4
5
6
7
8
9
10
11
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});

const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});

Promise.race([firstPromise, secondPromise]).then(res => console.log(res));

// "two"

When we pass multiple promises to the Promise.race method, it resolves/rejects the first promise that resolves/rejects. To the setTimeout method, we pass a timer: 500ms for the first promise (firstPromise), and 100ms for the second promise (secondPromise). This means that the secondPromise resolves first with the value of 'two'. res now holds the value of 'two', which gets logged.

reference

46.又是一个关于引用的问题,挺有意思的

What’s the output?

1
2
3
4
5
6
7
let person = { name: "Lydia" };
const members = [person];
person = null;

console.log(members);

// [{ name: "Lydia" }]

https://github.com/lydiahallie/javascript-questions#46-whats-the-output

parseInt

49.parseInt的用法

What’s the value of num?

1
2
3
4
const num = parseInt("7*6", 10);
console.log(num)

// 7

Only the first numbers in the string is returned. Based on the radix (the second argument in order to specify what type of number we want to parse it to: base 10, hexadecimal, octal, binary, etc.), the parseInt checks whether the characters in the string are valid. Once it encounters a character that isn’t a valid number in the radix, it stops parsing and ignores the following characters.

* is not a valid number. It only parses "7" into the decimal 7. num now holds the value of 7.

map

50.map的相关用法

What’s the output`?

1
2
3
4
5
6
[1, 2, 3].map(num => {
if (typeof num === "number") return;
return num * 2;
});

// [undefined, undefined, undefined]

When mapping over the array, the value of num is equal to the element it’s currently looping over. In this case, the elements are numbers, so the condition of the if statement typeof num === "number" returns true. The map function creates a new array and inserts the values returned from the function.

However, we don’t return a value. When we don’t return a value from the function, the function returns undefined. For every element in the array, the function block gets called, so for each element we return undefined.

try-catch throw

52.try catch 和 throw 的用法

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function greeting() {
throw "Hello world!";
}

function sayHi() {
try {
const data = greeting();
console.log("It worked!", data);
} catch (e) {
console.log("Oh no an error:", e);
}
}

sayHi();

// Oh no an error! Hello world!

With the throw statement, we can create custom errors. With this statement, you can throw exceptions. An exception can be a string, a number, a boolean or an object. In this case, our exception is the string 'Hello world'.

With the catch statement, we can specify what to do if an exception is thrown in the try block. An exception is thrown: the string 'Hello world'. e is now equal to that string, which we log. This results in 'Oh an error: Hello world'.

new return

53.new 构造函数如果有显式的 return 呢

What’s the output?

1
2
3
4
5
6
7
8
9
function Car() {
this.make = "Lamborghini";
return { make: "Maserati" };
}

const myCar = new Car();
console.log(myCar.make);

// "Maserati"

When you return a property, the value of the property is equal to the returned value, not the value set in the constructor function. We return the string "Maserati", so myCar.make is equal to "Maserati".

delete

55.delete相关

What’s the output?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Dog {
constructor(name) {
this.name = name;
}
}

Dog.prototype.bark = function() {
console.log(`Woof I am ${this.name}`);
};

const pet = new Dog("Mara");

pet.bark();

delete Dog.prototype.bark;

pet.bark();

// "Woof I am Mara", TypeError

We can delete properties from objects using the delete keyword, also on the prototype. By deleting a property on the prototype, it is not available anymore in the prototype chain. In this case, the bark function is not available anymore on the prototype after delete Dog.prototype.bark, yet we still try to access it.

When we try to invoke something that is not a function, a TypeError is thrown. In this case TypeError: pet.bark is not a function, since pet.bark is undefined.

import export

57.修改导入的模块

What’s the output?

1
2
3
// counter.js
let counter = 10;
export default counter;
1
2
3
4
5
6
7
8
// index.js
import myCounter from "./counter";

myCounter += 1;

console.log(myCounter);

// Error

An imported module is read-only: you cannot modify the imported module. Only the module that exports them can change its value.

When we try to increment the value of myCounter, it throws an error: myCounter is read-only and cannot be modified.

delete的返回值

58.delete成功删除或者删除失败的返回值

What’s the output?

1
2
3
4
5
6
7
const name = "Lydia";
age = 21;

console.log(delete name);
console.log(delete age);

// false, true

The delete operator returns a boolean value: true on a successful deletion, else it’ll return false. However, variables declared with the var, const or let keyword cannot be deleted using the delete operator.

The name variable was declared with a const keyword, so its deletion is not successful: false is returned. When we set age equal to 21, we actually added a property called age to the global object. You can successfully delete properties from objects this way, also the global object, so delete age returns true.

… operator

60.运算符…的性质

What’s the output?

1
2
3
4
5
6
const user = { name: "Lydia", age: 21 };
const admin = { admin: true, ...user };

console.log(admin);

// { admin: true, name: "Lydia", age: 21 }

It’s possible to combine objects using the spread operator .... It lets you create copies of the key/value pairs of one object, and add them to another object. In this case, we create copies of the user object, and add them to the admin object. The admin object now contains the copied key/value pairs, which results in { admin: true, name: "Lydia", age: 21 }.

Object.defineProperty

61.关于Object.defineProperty

What’s the output?

1
2
3
4
5
6
7
8
const person = { name: "Lydia" };

Object.defineProperty(person, "age", { value: 21 });

console.log(person);
console.log(Object.keys(person));

// { name: "Lydia", age: 21 }, ["name"]

With the defineProperty method, we can add new properties to an object, or modify existing ones. When we add a property to an object using the defineProperty method, they are by default not enumerable. The Object.keys method returns all enumerable property names from an object, in this case only "name".

Properties added using the defineProperty method are immutable by default. You can override this behavior using the writable, configurable and enumerable properties. This way, the defineProperty method gives you a lot more control over the properties you’re adding to an object.

JSON.stringify

62.关于 json.stringify

What’s the output?

1
2
3
4
5
6
7
8
9
10
const settings = {
username: "lydiahallie",
level: 19,
health: 90
};

const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);

// "{"level":19, "health":90}"

The second argument of JSON.stringify is the replacer. The replacer can either be a function or an array, and lets you control what and how the values should be stringified.

If the replacer is an array, only the property names included in the array will be added to the JSON string. In this case, only the properties with the names "level" and "health" are included, "username" is excluded. data is now equal to "{"level":19, "health":90}".

If the replacer is a function, this function gets called on every property in the object you’re stringifying. The value returned from this function will be the value of the property when it’s added to the JSON string. If the value is undefined, this property is excluded from the JSON string.

reduce

65.reduce 的相关性质

What’s the output?

1
2
3
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));

// 1 2 and undefined 3 and undefined 4

reducer 函数接收4个参数:

  1. Accumulator (acc) (累计器)
  2. Current Value (cur) (当前值)
  3. Current Index (idx) (当前索引)
  4. Source Array (src) (源数组)

reducer 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

reducer 函数还有一个可选参数initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供initialValue,则将使用数组中的第一个元素。

在上述例子,reduce方法接收的第一个参数(Accumulator)是x, 第二个参数(Current Value)是y

在第一次调用时,累加器x1,当前值“y”2,打印出累加器和当前值:12

例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回undefined。 在下一次调用时,累加器为undefined,当前值为“3”, 因此undefined3被打印出。

在第四次调用时,回调函数依然没有返回值。 累加器再次为 undefined ,当前值为“4”。 undefined4被打印出。

import require

67.模块引入机制

What’s the output?

1
2
3
4
5
6
7
8
9
10
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));

// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;

// running sum.js, running index.js, 3

With the import keyword, all imported modules are pre-parsed. This means that the imported modules get run first, the code in the file which imports the module gets executed after.

This is a difference between require() in CommonJS and import! With require(), you can load dependencies on demand while the code is being run. If we would have used require instead of import, running index.js, running sum.js, 3 would have been logged to the console.

generator yield

71.generator和yield相关性质

How can we log the values that are commented out after the console.log statement?

1
2
3
4
5
6
7
8
9
10
11
12
13
function* startGame() {
const answer = yield "Do you love JavaScript?";
if (answer !== "Yes") {
return "Oh wow... Guess we're gone here";
}
return "JavaScript loves you back ❤️";
}

const game = startGame();
console.log(/* 1 */); // Do you love JavaScript?
console.log(/* 2 */); // JavaScript loves you back ❤️

// game.next().value and game.next("Yes").value

A generator function “pauses” its execution when it sees the yield keyword. First, we have to let the function yield the string “Do you love JavaScript?”, which can be done by calling game.next().value.

Every line is executed, until it finds the first yield keyword. There is a yield keyword on the first line within the function: the execution stops with the first yield! This means that the variable answer is not defined yet!

When we call game.next("Yes").value, the previous yield is replaced with the value of the parameters passed to the next() function, "Yes" in this case. The value of the variable answer is now equal to "Yes". The condition of the if-statement returns false, and JavaScript loves you back ❤️ gets logged.

String.raw

72.String.raw 会忽略转义字符

What’s the output?

1
2
3
console.log(String.raw`Hello\nworld`);

// Hello\nworld

String.raw函数是用来获取一个模板字符串的原始字符串的,它返回一个字符串,其中忽略了转义符(\n\v\t等)。但反斜杠可能造成问题,因为你可能会遇到下面这种类似情况:

1
2
const path = `C:\Documents\Projects\table.html`
String.raw`${path}`

这将导致:

1
"C:DocumentsProjects able.html"

直接使用String.raw

1
String.raw`C:\Documents\Projects\table.html`

它会忽略转义字符并打印:C:\Documents\Projects\table.html

上述情况,字符串是Hello\nworld被打印出。

async await

73.async的返回值

What’s the output?

1
2
3
4
5
6
7
8
async function getData() {
return await Promise.resolve("I made it!");
}

const data = getData();
console.log(data);

// Promise {<pending>}

An async function always returns a promise. The await still has to wait for the promise to resolve: a pending promise gets returned when we call getData() in order to set data equal to it.

If we wanted to get access to the resolved value "I made it", we could have used the .then() method on data:

1
data.then(res => console.log(res))

This would’ve logged "I made it!"

拓展:注意这里的打印顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
async function getData() {
console.log(await Promise.resolve("I made it!1"))
const a = await Promise.resolve("I made it!2");
console.log(a);
return await Promise.resolve("I made it!3");
}

const data = getData();
console.log(data);

// Promise { <pending> }
// I made it!1
// I made it!2

记忆函数

78.闭包和缓存

What is the output?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const add = () => {
const cache = {};
return num => {
if (num in cache) {
return `From cache! ${cache[num]}`;
} else {
const result = num + 10;
cache[num] = result;
return `Calculated! ${result}`;
}
};
};

const addFunction = add();
console.log(addFunction(10));
console.log(addFunction(10));
console.log(addFunction(5 * 2));

// Calculated! 20 From cache! 20 From cache! 20

The add function is a memoized function. With memoization, we can cache the results of a function in order to speed up its execution. In this case, we create a cache object that stores the previously returned values.

If we call the addFunction function again with the same argument, it first checks whether it has already gotten that value in its cache. If that’s the case, the caches value will be returned, which saves on execution time. Else, if it’s not cached, it will calculate the value and store it afterwards.

We call the addFunction function three times with the same value: on the first invocation, the value of the function when num is equal to 10 isn’t cached yet. The condition of the if-statement num in cache returns false, and the else block gets executed: Calculated! 20 gets logged, and the value of the result gets added to the cache object. cache now looks like { 10: 20 }.

The second time, the cache object contains the value that gets returned for 10. The condition of the if-statement num in cache returns true, and 'From cache! 20' gets logged.

The third time, we pass 5 * 2 to the function which gets evaluated to 10. The cache object contains the value that gets returned for 10. The condition of the if-statement num in cache returns true, and 'From cache! 20' gets logged.

import * export

89.import * 和 export 多个

What’s the output?

1
2
3
4
5
6
7
8
9
// module.js 
export default () => "Hello world"
export const name = "Lydia"

// index.js
import * as data from "./module"

console.log(data)
// { default: function default(), name: "Lydia" }

With the import * as name syntax, we import all exports from the module.js file into the index.js file as a new object called data is created. In the module.js file, there are two exports: the default export, and a named export. The default export is a function which returns the string "Hello World", and the named export is a variable called name which has the value of the string "Lydia".

The data object has a default property for the default export, other properties have the names of the named exports and their corresponding values.

prototype

92.常规函数和箭头函数的prototype

What’s the output?

1
2
3
4
5
6
7
8
9
10
function giveLydiaPizza() {
return "Here is pizza!"
}

const giveLydiaChocolate = () => "Here's chocolate... now go hit the gym already."

console.log(giveLydiaPizza.prototype)
console.log(giveLydiaChocolate.prototype)

// { constructor: ...} undefined

Regular functions, such as the giveLydiaPizza function, have a prototype property, which is an object (prototype object) with a constructor property. Arrow functions however, such as the giveLydiaChocolate function, do not have this prototype property. undefined gets returned when trying to access the prototype property using giveLydiaChocolate.prototype.

Object.entries

93.Object.entries的性质

What’s the output?

1
2
3
4
5
6
7
8
9
const person = {
name: "Lydia",
age: 21
}

for (const [x, y] of Object.entries(person)) {
console.log(x, y)
}
// name Lydia and age 21

Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,上述情况返回一个二维数组,数组每个元素是一个包含键和值的数组:

1
[['name''Lydia'],['age'21]]

使用for-of循环,我们可以迭代数组中的每个元素,上述情况是子数组。 我们可以使用const [x,y]for-of循环中解构子数组。 x等于子数组中的第一个元素,y等于子数组中的第二个元素。

第一个子阵列是[“name”,“Lydia”],其中x等于name,而y等于Lydia。 第二个子阵列是[“age”,21],其中x等于age,而y等于21

扩展运算符 …

94.剩余参数应该放到最后

What’s the output?

1
2
3
4
5
6
7
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}

getItems(["banana", "apple"], "pear", "orange")

// Uncaught SyntaxError: Rest parameter must be last formal parameter

... args是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。上述示例中,剩余参数是第二个参数,这是不可能的,并会抛出语法错误。

1
2
3
4
5
function getItems(fruitList, favoriteFruit, ...args) {
return [...fruitList, ...args, favoriteFruit]
}

getItems(["banana", "apple"], "pear", "orange")

上述例子是有效的,将会返回数组:[ 'banana', 'apple', 'orange', 'pear' ]

Promise.resolve async await

102:Promise.resolve方式和 async await 方式的不同

What’s the value of output?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const myPromise = () => Promise.resolve('I have resolved!')

function firstFunction() {
myPromise().then(res => console.log(res))
console.log('second')
}

async function secondFunction() {
console.log(await myPromise())
console.log('second')
}

firstFunction()
secondFunction()

// second, I have resolved! and I have resolved!, second

With a promise, we basically say I want to execute this function, but I’ll put it aside for now while it’s running since this might take a while. Only when a certain value is resolved (or rejected), and when the call stack is empty, I want to use this value.

We can get this value with both .then and the await keyword in an async function. Although we can get a promise’s value with both .then and await, they work a bit differently.

In the firstFunction, we (sort of) put the myPromise function aside while it was running, but continued running the other code, which is console.log('second') in this case. Then, the function resolved with the string I have resolved, which then got logged after it saw that the callstack was empty.

With the await keyword in secondFunction, we literally pause the execution of an async function until the value has been resolved before moving to the next line.

This means that it waited for the myPromise to resolve with the value I have resolved, and only once that happened, we moved to the next line: second got logged.

The + operator

103:加号运算符的机制

What’s the value of output?

1
2
3
4
5
6
7
8
9
10
11
const set = new Set()

set.add(1)
set.add("Lydia")
set.add({ name: "Lydia" })

for (let item of set) {
console.log(item + 2)
}

// 3, Lydia2, [Object object]2

The + operator is not only used for adding numerical values, but we can also use it to concatenate strings. Whenever the JavaScript engine sees that one or more values are not a number, it coerces the number into a string.

The first one is 1, which is a numerical value. 1 + 2 returns the number 3.

However, the second one is a string "Lydia". "Lydia" is a string and 2 is a number: 2 gets coerced into a string. "Lydia" and "2" get concatenated, which results in the string "Lydia2".

{ name: "Lydia" } is an object. Neither a number nor an object is a string, so it stringifies both. Whenever we stringify a regular object, it becomes "[Object object]". "[Object object]" concatenated with "2" becomes "[Object object]2".

access properties

106:访问属性 [] 和 . 的不同

What’s its value?

1
2
3
4
5
6
7
8
9
10
11
12
13
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
}

const colors = ["pink", "red", "blue"]

console.log(colorConfig.colors[1])

// TypeError

In JavaScript, we have two ways to access properties on an object: bracket notation, or dot notation. In this example, we use dot notation (colorConfig.colors) instead of bracket notation (colorConfig["colors"]).

With dot notation, JavaScript tries to find the property on the object with that exact name. In this example, JavaScript tries to find a property called colors on the colorConfig object. There is no proprety called colors, so this returns undefined. Then, we try to access the value of the first element by using [1]. We cannot do this on a value that’s undefined, so it throws a TypeError: Cannot read property '1' of undefined.

JavaScript interprets (or unboxes) statements. When we use bracket notation, it sees the first opening bracket [ and keeps going until it finds the closing bracket ]. Only then, it will evaluate the statement. If we would’ve used colorConfig[colors[1]], it would have returned the value of the red property on the colorConfig object.

modifies the original array

108:数组的这些方法哪个不改变原数组?

Which of these methods modifies the original array?

1
2
3
4
5
6
7
8
9
10
const emojis = ['✨', '🥑', '😍']

emojis.map(x => x + '✨')
emojis.filter(x => x !== '🥑')
emojis.find(x => x !== '🥑')
emojis.reduce((acc, cur) => acc + '✨')
emojis.slice(1, 2, '✨')
emojis.splice(1, 2, '✨')

// splice

With splice method, we modify the original array by deleting, replacing or adding elements. In this case, we removed 2 items from index 1 (we removed '🥑' and '😍') and added the ✨ emoji instead.

map, filter and slice return a new array, find returns an element, and reduce returns a reduced value.

reference

109:复制数组的一个值然后改变会怎样呢?

What’s the output?

1
2
3
4
5
6
7
8
const food = ['🍕', '🍫', '🥑', '🍔']
const info = { favoriteFood: food[0] }

info.favoriteFood = '🍝'

console.log(food)

// ['🍕', '🍫', '🥑', '🍔']

We set the value of the favoriteFood property on the info object equal to the string with the pizza emoji, '🍕'. A string is a primitive data type. In JavaScript, primitive data types act by reference

In JavaScript, primitive data types (everything that’s not an object) interact by value. In this case, we set the value of the favoriteFood property on the info object equal to the value of the first element in the food array, the string with the pizza emoji in this case ('🍕'). A string is a primitive data type, and interact by value (see my blogpost if you’re interested in learning more)

Then, we change the value of the favoriteFood property on the info object. The food array hasn’t changed, since the value of favoriteFood was merely a copy of the value of the first element in the array, and doesn’t have a reference to the same spot in memory as the element on food[0]. When we log food, it’s still the original array, ['🍕', '🍫', '🥑', '🍔'].