通过10个例子,深入了解JavaScript

介绍

作为一门灵活的编程语言,JS中有很多缺陷,这很可能被人们所忽略,而产生很大的失误,在本文中,我们将通过10个容易出错的示例来学习JS语法的一些关键部分,有些甚至是现实世界中的错误,而某些示例则直接来自于JS的基本语法。

示例1 Object Destructing

看以下代码,回答问题:

const { var1: a, var2, var3 = 10 } = { var1: 2, var2: NaN, var3: 5 };

function testFn(){
let sum = 0;
for(const ele of arguments){
if(ele !== ele) break;
else sum += ele;
}
return sum;
}

const res = testFn(a,var2,var3);
console.log(res);

该代码的输出是什么?

(1) 12 (2) NaN (3) 2 (4) 7

语法说明

不同形式的Object Destructing
可以从ES5的argument变量中检索输入参数(可以将代码改为箭头函数,看看会发生什么)
NaN 是唯一不等于它本身的值

答案 (3)
示例2 逻辑运算符

看以下代码,回答问题:

function testFn(){
const a = {} && -1 || 0;
console.log(a / 0);
}

testFn();

该代码将会输出什么?

(1) false (2) Error (3) NaN (4)-Infinity

语法说明

与其他编程语言不同,JS中的逻辑运算符&&或是||返回最后检查的值,而不是简单的布尔值
在JS 中 :'',null,NaN,-0,0,+0,false都是Falsy值
在JS中将一个非零数字除以0会产生-InfinityInfinity代替抛出错误

答案(4)
示例3 一元运算符和强制转换

看以下代码,回答问题:

function testFn(){
const a = "3.14";
const b = -a - + - + 3.14;
const c = a + + - + 3.14;
console.log(b + c);
}

testFn();

该代码将会输出什么?
(1) 3.14 (2) “03.14-3.14” (3) NaN (4)-3.14

语法说明

一元运算符的关联性是从右到左
一元运算符+,-可用于字符串到数字的强制转换
+运算符和数字之间使用的运算符会将数字强制为字符串值

答案(2)

###示例4 Promise 流控制

看以下代码,回答问题:

function testFn(){
const p = new Promise((rsv,rjt) => {
console.log("1");
rsv();
}).then(res => {
console.log("2");
setTimeout(() => {
console.log("3");
}, 0);
});
console.log("4");
setTimeout(() => {
console.log("5");
}, 0);
console.log("6");
}

testFn();

该代码将会输出什么?

(1) > "1" > "4" > "6" > "2" > "5" > "3"

(2) > "4" > "6" > "1" > "5" > "2" > "3"

(3) > "1" > "4" > "6" > "2" > "3" > "5"

(4) > "1" > "4" > "6" > "5" > "2" > "3"

语法说明

Promise将保留在调用堆栈的初始位置
setTimeout()将放置在调用堆栈的末尾

答案(1)
示例5 异步函数错误处理

看以下代码,回答问题:

function testFn(fn){
try{
fn();
}catch(err) {
console.log("Error caught: ", err.message);
}
}

function innerFn(){
throw Error("Error from Synchronous Fn");
}

async function innerAsyncFn(){
throw Error('Error from Asynchronous Fn');
}

testFn(innerFn);
testFn(innerAsyncFn);

以下哪个是输出的内容?

(1)
Error caught: Error from Synchronous Fn
Error caught: Error from Asynchronous Fn

(2)
Error caught: Error from Synchronous Fn
Uncaught (in promise) Error: Error from Asynchronous Fn

(3)
Uncaught Error: Error from Synchronous Fn
Uncaught (in promise) Error: Error from Asynchronous Fn

(4)
Uncaught Error: Error from Synchronous Fn
Error caught: Error from Asynchronous Fn

语法说明

从异步函数引发的错误将被包装到一个被拒绝的promise中,它等效于 return new Promise.reject(Error('msg')) 被拒绝的Promise将在Promise.catch()中进行处理.
从同步函数引发的错误将被视为Uncaught Error,如果try{}catch(e){}在当前函数范围内未被处理,则将引发该错误到函数调用的外部范围

答案(2)
示例6 异步函数作为范围

看以下代码,回答问题:


async function testFn(){
console.log('start');
const sampleArr = [1, 2, 3];
for(const ele of sampleArr){
const val = await ele;
console.log(val);
}
console.log('end');
}

testFn();

以下哪个是输出的值?

(1)
'start'
1
2
3
'end'
(2)
'start'
'end'
1
2
3

如果使用以下做法呢?

async function testFn(){
console.log('start');
const sampleArr = [1, 2, 3];
sampleArr.forEach(async ele => {
const val = await ele;
console.log(val);
});
console.log('end');
}

testFn();

语法说明

asyncawait可以成对使用(尽管async可以是多个await),这意味着await关键字将仅使其对应async函数的执行同步。如果将异步函数作为参数传递给第三方函数处理程序,那么该第三方函数处理程序在当前作用域中将变为黑色,并且将会失去对异步函数的控制。
在第二段代码中,将async箭头函数传递给.forEach()函数,因此await仅在异步箭头函数的作用域内起作用。但是在第一段代码中,for循环没有作用域范围,这意味着 await可以和该函数的作用域一起使用。

答案 第一段(1) 第二段(2)
示例7 闭包和this的绑定
var obj1 = {
name: "John Wick",
gender: "male",
myFn: function(val){
var innerFn = function(val2){
console.log(val, val2, this.name);
}.bind(this);

return innerFn;
}
};

var obj2 = {
name: "Trinity",
gender: "female"
};

obj2.testFn = obj1.myFn;
obj2.testFn("Hello")("World");

看看以下哪个是输出结果

(1) undefined “World” “John Wick”
(2) “Hello” “World” “John Wick”
(3) undefined “World” “Trinity”
(4) “Hello” “World” “Trinity”

语法说明

函数是一个对象,JS中的对象分配是引用分配。this关键字始终指向引用当前函数的对象
val内部变量innerFn引用外部作用域的变量val。此引用是从一个作用域范围到外部范围的引用。
.bind(this)强制绑定innerFn到其外部范围的调用函数

答案 (4)
示例8 …展开运算符
const object1 = {a: 1, b: 2, c: 3};
const object2 = Object.create(object1,{
d: {
value: 4,
enumerable: true
}
});
object2.e = 5;
const newObj = {
...object2
};
console.log("new obj: ", newObj);

//Array spreading
const array1 = ['a', 'b', 'c'];
const array2 = Object.create(array1,{
3: {
value: 'd',
enumerable: true
}
});

const newArr = [...array2];
console.log("new arr:", newArr);

以下哪个是输出的结果?

(1)
new obj: {d: 4, e: 5}
new arr: ["a", "b", "c"] (2)
new obj: {a: 1, b: 2, c: 3, d: 4, e: 5}
new arr: ["a", "b", "c", "d"] (3)
new obj: {a: 1, b: 2, c: 3, d: 4, e: 5}
new arr: ["a", "b", "c"] (4)
new obj: {d: 4, e: 5}
new arr: ["a", "b", "c", "d"]


语法说明

...扩展操作符在对象上使用仅迭代对象自身的属性,类似于for (const prop in Object.keys(obj))。另一方面,for ( const prop in obj) 将仅迭代原型链中的所有属性。
...在数组上使用时将遍历数组对象的原型链中的所有可迭代的元素,类似于for (const ele of arr)

答案 (1)
示例9 null和对象
{
const valArr = [null || 0, {}, null];
function testFn(val) {
switch (typeof val){
case "object":
console.log("object");
break;
case "number":
console.log("number");
break;
case "null":
console.log("null");
break;
default:
break;
}
};
valArr.forEach(testFn);
}

以下哪个是输出的结果?

(1)
“number”
“object”
“null”
(2)
“null”
“object”
“object”
(3)
“number”
“object”
“object”
(4)
“null”
“object”
“null”

语法说明

null 在JS中也是一个对象
||操作返回最后的检查值0 即使0是一个falsy值

答案 (3)
示例10 对象克隆

const obj1 = {
name: "John Wick",
gender: "male"
};
const obj2 = Object.assign({}, obj1);
const obj3 = obj1;
const obj4 = {
...obj1
};
console.log(obj1 === obj2);
console.log(obj1 === obj3);
console.log(obj1 === obj4);

以下哪个是输出的内容?

(1)
true
true
false
(2)
false
true
true
(3)
false
false
false
(4)
false
true
false

语法说明

=用于对象分配的是分配对象引用(浅拷贝),而不是将对象真正复制到新对象(深拷贝)。要使用深拷贝,就要用Object.assign()...运算符

=== 逻辑运算符在对象上使用时将检查两个对象是否都引用相同的对象内存。

答案 (4)

作者:Maddix

相关推荐

Django cookie 与 session

Django cookie 与 session

vue-drag-chart 拖动/缩放图表组件的实例代码

JavaScript创建表格的方法

Javascript摸拟自由落体与上抛运动原理与实现方法详解