考题列表
- 什么是 async/await 及其如何工作?
- 展开运算符和Rest运算符有什么区别?
- 什么是默认参数?
- 什么是包装对象(wrapper object)?
- 隐式和显式转换有什么区别?
- 什么是NaN?以及如何检查值是否为 NaN?
- 如何判断值是否为数组?
- 如何在不使用%模运算符的情况下检查一个数字是否是偶数?
- 如何检查对象中是否存在某个属性?
- AJAX 是什么?
51. 什么是 async/await 及其如何工作?
async/await是 JS 中编写异步或非阻塞代码的新方法。它建立在Promises之上,让异步代码的可读性和简洁度都更高。
async/await是 JS 中编写异步或非阻塞代码的新方法。它建立在Promises之上,相对于 Promise 和回调,它的可读性和简洁度都更高。但是,在使用此功能之前,我们必须先学习Promises的基础知识,因为正如我之前所说,它是基于Promise构建的,这意味着幕后使用仍然是Promise。
使用 Promise
function callApi() {
return fetch("url/to/api/endpoint")
.then(resp => resp.json())
.then(data => {
//do something with "data"
}).catch(err => {
//do something with "err"
});
}
使用async/await
在async/await,我们使用 tru/catch 语法来捕获异常。
async function callApi() {
try {
const resp = await fetch("url/to/api/endpoint");
const data = await resp.json();
//do something with "data"
} catch (e) {
//do something with "err"
}
}
注意:使用 async关键声明函数会隐式返回一个Promise。
const giveMeOne = async () => 1;
giveMeOne()
.then((num) => {
console.log(num); // logs 1
});
注意:await关键字只能在async function中使用。在任何非async function的函数中使用await关键字都会抛出错误。await关键字在执行下一行代码之前等待右侧表达式(可能是一个Promise)返回。
const giveMeOne = async () => 1;
function getOne() {
try {
const num = await giveMeOne();
console.log(num);
} catch (e) {
console.log(e);
}
}
// Uncaught SyntaxError: await is only valid in async function
async function getTwo() {
try {
const num1 = await giveMeOne(); // 这行会等待右侧表达式执行完成
const num2 = await giveMeOne();
return num1 + num2;
} catch (e) {
console.log(e);
}
}
await getTwo(); // 2
52. 展开(spread )运算符和 剩余(Rest) 运算符有什么区别?
展开运算符(spread)是三个点(...),可以将一个数组转为用逗号分隔的参数序列。说的通俗易懂点,有点像化骨绵掌,把一个大元素给打散成一个个单独的小元素。
剩余运算符也是用三个点(...)表示,它的样子看起来和展开操作符一样,但是它是用于解构数组和对象。在某种程度上,剩余元素和展开元素相反,展开元素会“展开”数组变成多个元素,剩余元素会收集多个元素和“压缩”成一个单一的元素。
function add(a, b) { return a + b; };
const nums = [5, 6]; const sum = add(...nums); console.log(sum); 在本例中,我们在调用add函数时使用了展开操作符,对nums数组进行展开。所以参数a的值是5 ,b的值是6,所以sum 是11。
function add(...rest) {
return rest.reduce((total,current) => total + current);
};
console.log(add(1, 2)); // 3
console.log(add(1, 2, 3, 4, 5)); // 15
在本例中,我们有一个add函数,它接受任意数量的参数,并将它们全部相加,然后返回总数。
const [first, ...others] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(others); // [2,3,4,5]
这里,我们使用剩余操作符提取所有剩余的数组值,并将它们放入除第一项之外的其他数组中。
53. 什么是默认参数?
默认参数是在 JS 中定义默认变量的一种新方法,它在ES6或ECMAScript 2015版本中可用。
//ES5 Version
function add(a,b){
a = a || 0;
b = b || 0;
return a + b;
}
//ES6 Version
function add(a = 0, b = 0){
return a + b;
}
add(1); // returns 1
我们还可以在默认参数中使用解构。
function getFirst([first, ...rest] = [0, 1]) {
return first;
}
getFirst(); // 0
getFirst([10,20,30]); // 10
function getArr({ nums } = { nums: [1, 2, 3, 4] }){
return nums;
}
getArr(); // [1, 2, 3, 4]
getArr({nums:[5,4,3,2,1]}); // [5,4,3,2,1]
我们还可以使用先定义的参数再定义它们之后的参数。
function doSomethingWithValue(value = "Hello World", callback = () => { console.log(value) }) {
callback();
}
doSomethingWithValue(); //"Hello World"
54. 什么是包装对象(wrapper object)?
我们现在复习一下JS的数据类型,JS数据类型被分为两大类,基本类型和引用类型。
基本类型:Undefined,Null,Boolean,Number,String,Symbol,BigInt
引用类型:Object,Array,Date,RegExp等,说白了就是对象
其中引用类型有方法和属性,但是基本类型是没有的,但我们经常会看到下面的代码:
let name = "marko";
console.log(typeof name); // "string"
console.log(name.toUpperCase()); // "MARKO"
name类型是 string,属于基本类型,所以它没有属性和方法,但是在这个例子中,我们调用了一个toUpperCase()方法,它不会抛出错误,还返回了对象的变量值。
原因是基本类型的值被临时转换或强制转换为对象,因此name变量的行为类似于对象。除null和undefined之外的每个基本类型都有自己包装对象。也就是:String,Number,Boolean,Symbol和BigInt。在这种情况下,name.toUpperCase()在幕后看起来如下:
console.log(new String(name).toUpperCase()); // "MARKO"
在完成访问属性或调用方法之后,新创建的对象将立即被丢弃。
55. 隐式和显式转换有什么区别)?
隐式强制转换是一种将值转换为另一种类型的方法,这个过程是自动完成的,无需我们手动操作。
假设我们下面有一个例子。
console.log(1 + '6'); // 16
console.log(false + true); // 1
console.log(6 * '2'); // 12
第一个console.log语句结果为16。在其他语言中,这会抛出编译时错误,但在 JS 中,1被转换成字符串,然后与+运算符连接。我们没有做任何事情,它是由 JS 自动完成。
第二个console.log语句结果为1,JS 将false转换为boolean 值为 0,,true为1,因此结果为1。
第三个console.log语句结果12,它将'2'转换为一个数字,然后乘以6 * 2,结果是12。
而显式强制是将值转换为另一种类型的方法,我们需要手动转换。
console.log(1 + parseInt('6'));
在本例中,我们使用parseInt函数将'6'转换为number ,然后使用+运算符将1和6相加。
56. 什么是NaN?以及如何检查值是否为NaN?
NaN表示**“非数字”**是 JS 中的一个值,该值是将数字转换或执行为非数字值的运算结果,因此结果为NaN。
let a;
console.log(parseInt('abc')); // NaN
console.log(parseInt(null)); // NaN
console.log(parseInt(undefined)); // NaN
console.log(parseInt(++a)); // NaN
console.log(parseInt({} * 10)); // NaN
console.log(parseInt('abc' - 2)); // NaN
console.log(parseInt(0 / 0)); // NaN
console.log(parseInt('10a' * 10)); // NaN
JS 有一个内置的isNaN方法,用于测试值是否为isNaN值,但是这个函数有一个奇怪的行为。
console.log(isNaN()); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN(String('a'))); // true
console.log(isNaN(() => { })); // true
所有这些console.log语句都返回true,即使我们传递的值不是NaN。
在ES6中,建议使用Number.isNaN方法,因为它确实会检查该值(如果确实是NaN),或者我们可以使自己的辅助函数检查此问题,因为在 JS 中,NaN是唯一的值,它不等于自己。
function checkIfNaN(value) {
return value !== value;
}
57. 如何判断值是否为数组?
我们可以使用Array.isArray方法来检查值是否为数组。当传递给它的参数是数组时,它返回true,否则返回false。
console.log(Array.isArray(5)); // false
console.log(Array.isArray("")); // false
console.log(Array.isArray()); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray({ length: 5 })); // false
console.log(Array.isArray([])); // true
如果环境不支持此方法,则可以使用polyfill实现。
function isArray(value){
return Object.prototype.toString.call(value) === "[object Array]"
}
当然还可以使用传统的方法:
let a = []
if (a instanceof Array) {
console.log('是数组')
} else {
console.log('非数组')
}
58. 如何在不使用%模运算符的情况下检查一个数字是否是偶数?
我们可以对这个问题使用按位&运算符,&对其操作数进行运算,并将其视为二进制值,然后执行与运算。
function isEven(num) {
if (num & 1) {
return false
} else {
return true
}
}
0 二进制数是 0001 二进制数是 0012 二进制数是 0103 二进制数是 0114 二进制数是 1005 二进制数是 1016 二进制数是 1107 二进制数是 111
以此类推...
与运算的规则如下:
a b a & b
0 0 0
0 1 0
1 1 1
因此,当我们执行console.log(5&1)这个表达式时,结果为1。首先,&运算符将两个数字都转换为二进制,因此5变为101,1变为001。
然后,它使用按位怀运算符比较每个位(0和1)。101&001,从表中可以看出,如果a & b为1,所以5&1结果为1。
101 & 001
101
001
001
首先我们比较最左边的1&0,结果是0。
然后我们比较中间的0&0,结果是0。
然后我们比较最后1&1,结果是1。
最后,得到一个二进制数001,对应的十进制数,即1。
由此我们也可以算出console.log(4 & 1) 结果为0。知道4的最后一位是0,而0 & 1 将是0。如果你很难理解这一点,我们可以使用递归函数来解决此问题。
function isEven(num) {
if (num < 0 || num === 1) return false;
if (num == 0) return true;
return isEven(num - 2);
}
59. 如何检查对象中是否存在某个属性?
检查对象中是否存在属性有三种方法。
第一种使用 in 操作符号:
const o = {
"prop" : "bwahahah",
"prop2" : "hweasa"
};
console.log("prop" in o); // true
console.log("prop1" in o); // false
第二种使用 hasOwnProperty 方法,hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
console.log(o.hasOwnProperty("prop2")); // true
console.log(o.hasOwnProperty("prop1")); // false
第三种使用括号符号obj["prop"]。如果属性存在,它将返回该属性的值,否则将返回undefined。
console.log(o["prop"]); // "bwahahah"
console.log(o["prop1"]); // undefined
60. AJAX 是什么?
即异步的 JavaScript 和 XML,是一种用于创建快速动态网页的技术,传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。使用AJAX则不需要加载更新整个网页,实现部分内容更新
用到AJAX的技术:
- HTML - 网页结构
- CSS - 网页的样式
- JavaScript - 操作网页的行为和更新DOM
- XMLHttpRequest API - 用于从服务器发送和获取数据
- PHP,Python,Nodejs - 某些服务器端语言