JS 基础小题
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter()
shape.perimeter()
// B: 20 and NaN
-
注:对于箭头函数,
this
关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。这意味着当我们调用perimeter
时,this
不是指向shape
对象,而是它的周围作用域(在例子中是window
)。在
window
中没有radius
这个属性,因此返回undefined
。
let a = 3
let b = new Number(3)
let c = 3
console.log(a == b)
console.log(a === b)
console.log(b === c)
//C: true false false
-
new Number()
是一个内建的函数构造器。虽然它看着像是一个 number,但它实际上并不是一个真实的 number:它有一堆额外的功能并且它是一个对象。当我们使用
==
操作符时,它只会检查两者是否拥有相同的值。因为它们的值都是3
,因此返回true
。然后,当我们使用
===
操作符时,两者的值以及类型都应该是相同的。new Number()
是一个对象而不是 number,因此返回false
。
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor
return this.newColor
}
constructor({ newColor = 'green' } = {}) {
this.newColor = newColor
}
}
const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
//TypeError
colorChange
是一个静态方法。静态方法被设计为只能被创建它们的构造器使用(也就是Chameleon
),并且不能传递给实例。因为freddie
是一个实例,静态方法不能被实例使用,因此抛出了TypeError
错误。
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
- 如果使用标记模板字面量,第一个参数的值总是包含字符串的数组。其余的参数获取的是传递的表达式的值!
function checkAge(data) {
if (data === { age: 18 }) {
console.log('You are an adult!')
} else if (data == { age: 18 }) {
console.log('You are still an adult.')
} else {
console.log(`Hmm.. You don't have an age I guess`)
}
}
checkAge({ age: 18 })
//Hmm.. You don't have an age I guess
-
在测试相等性时,基本类型通过它们的值(value)进行比较,而对象通过它们的引用(reference)进行比较。JavaScript 检查对象是否具有对内存中相同位置的引用。
题目中我们正在比较的两个对象不是同一个引用:作为参数传递的对象引用的内存位置,与用于判断相等的对象所引用的内存位置并不同。
这也是
{ age: 18 } === { age: 18 }
和{ age: 18 } == { age: 18 }
都返回false
的原因。
function getAge(...args) {
console.log(typeof args)
}
getAge(21)
//object
- 扩展运算符(
...args
)会返回实参组成的数组。而数组是对象,因此typeof args
返回"object"
。
function getAge() {
'use strict'
age = 21
console.log(age)
}
getAge()
//ReferenceError
- 使用
"use strict"
,你可以确保不会意外地声明全局变量。我们从来没有声明变量age
,因为我们使用"use strict"
,它将抛出一个引用错误。如果我们不使用"use strict"
,它就会工作,因为属性age
会被添加到全局对象中了。
JavaScript 全局执行上下文为你做了两件事:全局对象和 this 关键字。
A: true
B: false
C: it depends
//true
- 基本执行上下文是全局执行上下文:它是代码中随处可访问的内容。
const a = {}
const b = { key: 'b' }
const c = { key: 'c' }
a[b] = 123
a[c] = 456
console.log(a[b])
//{[object Object]: 456}
-
对象的键被自动转换为字符串。我们试图将一个对象
b
设置为对象a
的键,且相应的值为123
。然而,当字符串化一个对象时,它会变成
"[object Object]"
。因此这里说的是,a["[object Object]"] = 123
。然后,我们再一次做了同样的事情,c
是另外一个对象,这里也有隐式字符串化,于是,a["[object Object]"] = 456
。然后,我们打印
a[b]
,也就是a["[object Object]"]
。之前刚设置为456
,因此返回的是456
。 -
在 JavaScript 中,所有对象的 keys 都是字符串(除非对象是 Symbol)。尽管我们可能不会定义它们为字符串,但它们在底层总会被转换为字符串。
-
所有对象的键(不包括 Symbol)在底层都是字符串,即使你自己没有将其作为字符串输入。这就是为什么
obj.hasOwnProperty('1')
也返回true
。
// 当点击按钮时,event.target是什么?
<div onclick="console.log('first div')">
<p onclick="console.log('second p')">
<button onclick="console.log('button')">
Click!
</button>
</p>
</div>
// button
//当您单击该段落时,日志输出是什么?
//p div
- 导致事件的最深嵌套的元素是事件的 target。你可以通过
event.stopPropagation
来停止冒泡。 - 如果我们点击
p
,我们会看到两个日志:p
和div
。在事件传播期间,有三个阶段:捕获、目标和冒泡。默认情况下,事件处理程序在冒泡阶段执行(除非将useCapture
设置为true
)。它从嵌套最深的元素向外传播。
setInterval 方法的返回值是什么?
setInterval(() => console.log('Hi'), 1000)
//一个唯一的id
setInterval
返回一个唯一的 id。此 id 可被用于clearInterval
函数来取消定时。
const num = parseInt("7*6", 10);
//输出7
const num = parseInt("s7*6", 10);
//NaN
-
只返回了字符串中第一个字母. 设定了 进制 后 (也就是第二个参数,指定需要解析的数字是什么进制: 十进制、十六机制、八进制、二进制等等……),
parseInt
检查字符串中的字符是否合法. 一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。*
就是不合法的数字字符。所以只解析到"7"
,并将其解析为十进制的7
.num
的值即为7
.
// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from "./counter";
myCounter += 1;
console.log(myCounter);
//Error
-
引入的模块是 只读 的: 你不能修改引入的模块。只有导出他们的模块才能修改其值。
当我们给
myCounter
增加一个值的时候会抛出一个异常:myCounter
是只读的,不能被修改。
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person);
console.log(Object.keys(person));
//{ name: "Lydia", age: 21 }, ["name"]
-
通过
defineProperty
方法,我们可以给对象添加一个新属性,或者修改已经存在的属性。而我们使用defineProperty
方法给对象添加了一个属性之后,属性默认为 不可枚举(not enumerable).Object.keys
方法仅返回对象中 可枚举(enumerable) 的属性,因此只剩下了"name"
.用
defineProperty
方法添加的属性默认不可变。你可以通过writable
,configurable
和enumerable
属性来改变这一行为。这样的话, 相比于自己添加的属性,defineProperty
方法添加的属性有了更多的控制权。[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
-
reducer
函数接收4个参数:- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
reducer
函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。reducer
函数还有一个可选参数initialValue
, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供initialValue
,则将使用数组中的第一个元素。在上述例子,
reduce
方法接收的第一个参数(Accumulator)是x
, 第二个参数(Current Value)是y
。在第一次调用时,累加器
x
为1
,当前值“y”
为2
,打印出累加器和当前值:1
和2
。例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回
undefined
。 在下一次调用时,累加器为undefined
,当前值为“3”, 因此undefined
和3
被打印出。在第四次调用时,回调函数依然没有返回值。 累加器再次为
undefined
,当前值为“4”。undefined
和4
被打印出。
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
//Promise {<pending>}
-
异步函数始终返回一个promise。
await
仍然需要等待promise的解决:当我们调用getData()
并将其赋值给data
,此时data
为getData
方法返回的一个挂起的promise,该promise并没有解决。如果我们想要访问已解决的值
"I made it!"
,可以在data
上使用.then()
方法:data.then(res => console.log(res))
这样将打印
"I made it!"
const box = { x: 10, y: 20 };
Object.freeze(box);
const shape = box;
shape.x = 100;
console.log(shape)
//{ x: 10, y: 20 }
-
Object.freeze
使得无法添加、删除或修改对象的属性(除非属性的值是另一个对象)。当我们创建变量
shape
并将其设置为等于冻结对象box
时,shape
指向的也是冻结对象。你可以使用Object.isFrozen
检查一个对象是否被冻结,上述情况,Object.isFrozen(shape)
将返回true
。由于
shape
被冻结,并且x
的值不是对象,所以我们不能修改属性x
。x
仍然等于10
,{x:10,y:20}
被打印。注意,上述例子我们对属性
x
进行修改,可能会导致抛出TypeError异常(最常见但不仅限于严格模式下时)。
const myLifeSummedUp = ["☕", "💻", "🍷", "🍫"]
for (let item in myLifeSummedUp) {
console.log(item)
}
for (let item of myLifeSummedUp) {
console.log(item)
}
//: 0 1 2 3 and "☕" "💻" "🍷" "🍫"
-
通过
for-in
循环,我们可以遍历一个对象自有的、继承的、可枚举的、非Symbol的属性。 在数组中,可枚举属性是数组元素的“键”, 即它们的索引。 类似于下面这个对象:{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
其中键则是可枚举属性,因此
0
,1
,2
,3
被记录。通过
for-of
循环,我们可以迭代可迭代对象(包括Array
,Map
,Set
,String
,arguments
等)。当我们迭代数组时,在每次迭代中,不同属性的值将被分配给变量item
, 因此“☕”
,“💻”
,“🍷”
,“🍫”
被打印。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
-
常规函数,例如
giveLydiaPizza
函数,有一个prototype
属性,它是一个带有constructor
属性的对象(原型对象)。 然而,箭头函数,例如giveLydiaChocolate
函数,没有这个prototype
属性。 尝试使用giveLydiaChocolate.prototype
访问prototype
属性时会返回undefined
。
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()
方法返回一个给定对象自身可枚举属性的键值对数组,上述情况返回一个二维数组,数组每个元素是一个包含键和值的数组:[['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
。