一、异步函数始终返回一个promise
来看下面的例子
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!"
二、push()
方法返回新数组的长度
还是先来看例子
function addToList(item, list) {
return list.push(item);
}
const result = addToList("apple", ["banana"]);
console.log(result);
这时候打印出来的是‘2’,这是因为
push()
方法返回新数组的长度。一开始,数组包含一个元素(字符串"banana"
),长度为1。 在数组中添加字符串"apple"
后,长度变为2,并将从addToList
函数返回。 push
方法修改原始数组,如果你想从函数返回数组而不是数组长度,那么应该在push item
之后返回list
。
三、Object.freeze
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 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
在这个例子中,add
函数是一个记忆函数。 通过记忆化,我们可以缓存函数的结果,以加快其执行速度。上述情况,我们创建一个cache
对象,用于存储先前返回过的值。 如果我们使用相同的参数多次调用addFunction
函数,它首先检查缓存中是否已有该值,如果有,则返回缓存值,这将节省执行时间。如果没有,那么它将计算该值,并存储在缓存中。 我们用相同的值三次调用了addFunction
函数: 在第一次调用,num
等于10
时函数的值尚未缓存,if语句num in cache
返回false
,else块的代码被执行:Calculated! 20
,并且其结果被添加到缓存对象,cache
现在看起来像{10:20}
。 第二次,cache
对象包含10
的返回值。 if语句 num in cache
返回true
,From cache! 20
被打印。 第三次,我们将5 * 2
(值为10)传递给函数。 cache
对象包含10
的返回值。 if语句 num in cache
返回true
,From cache! 20
被打印。
五、for in 和for of的区别
通过for-in
循环,我们可以遍历一个对象自有的、继承的、可枚举的、非Symbol的属性。
通过for-of
循环,我们可以迭代可迭代对象(包括 Array
,Map
,Set
,String
,arguments
等)
来看下面的例子
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
, 因此“☕”
,“💻”
,“🍷”
,“🍫”
被打印。
六、默认情况下,如果不给函数传参,参数的值将为undefined
我们来看一下下面这个函数会输出什么
function sayHi(name) {
return `Hi there, ${name}`
}
console.log(sayHi())
运行这个函数,发现输出的结果是Hi there, undefined
默认情况下,如果不给函数传参,参数的值将为undefined
。 上述情况,我们没有给参数name
传值。 name
等于undefined
,并被打印。 在ES6中,我们可以使用默认参数覆盖此默认的undefined
值。 例如: function sayHi(name =“Lydia”){...}
在这种情况下,如果我们没有传递值或者如果我们传递undefined
,name
总是等于字符串Lydia
七、
类是构造函数的语法糖
类是构造函数的语法糖,如果用构造函数的方式来重写Person
类则将是:
function Person() {
this.name = name
}
通过new
来调用构造函数,将会生成构造函数Person
的实例,对实例执行typeof
关键字将返回"object"
,上述情况打印出"object"
。
下面这个例子可以充分说明
class Person {
constructor(name) {
this.name = name
}
}
const member = new Person("John")
console.log(typeof member)
八、箭头函数和普通函数的prototype
我们来看一下,下面这个例子会打印出来什么
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
。
九、Object.entries()
首先来看一个例子,看会打印出什么
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
。
十、... args只能作为最后一个参数
来看下面的例子会输出什么
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
运行之后,我们发现报错了,这是因为
... args
是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。上述示例中,剩余参数是第二个参数,这是不可能的,并会抛出语法错误。
function getItems(fruitList, favoriteFruit, ...args) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange")
上述例子是有效的,将会返回数组:[ 'banana', 'apple', 'orange', 'pear' ]
以上所有内容参考
首页 - 前端面试题宝典《前端面试题宝典》的目标是做全网最专业的的前端面试题库,为初、中级前端工程师提供面试题的刷题及面试技巧指导服务https://fe.ecool.fun/