JS的那些坑(一)

2022-03-19

本文总结了JS在日常使用和面试中需要注意的知识点。

变量提升

请看以下代码输出什么

1
2
3
4
5
6
showName()
console.log(myname)
var myname = 'hello world'
function showName() {
console.log('函数showName被执行')
}

答案

image.png

答案和我们印象中js自上而下的执行顺序有些出入,这是由于js为了开发者写代码方便而省略掉的变量声明部分,而在编译阶段会将此部分补充完整,而开发者习惯了“简洁版”,并延续了”简洁版”的思路而产生的错觉。
现在,我们把”省去”的声明代码还原,就能发现端倪:

首先看变量的“全貌”:

var myname = 'hello world'

1
2
3
var myname //声明部分

myname = 'hello world' //赋值部分

再看看函数的“全貌”:

1
2
3
4
5
6
7
// 把声明提前
function showName() {
console.log('函数showName被执行')
}

//执行部分
showName()

在这里,以上函数showName是一个完整的函数声明,没有赋值操作,而如下函数声明,是声明与赋值分开的:

1
2
3
4

var showName = function(){
console.log("函数showName被执行")
}

等同于如下操作:

1
2
3
4
5
6
7
// 声明
var showName = undefined

// 赋值
showName = function(){
console.log("函数showName被执行")
}

所谓的变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。

script标签

请看以下代码,将输出什么?

1
2
3
4
5
<script type="text/javascript">
console.log("1")
console.log("</script>")
console.log("2")
</script>

答案:程序报错

包含在<script>标签的内容,将会被浏览器顺序执行,直到遇到</script>;特别注意:不要在代码的任何地方出现“</script>”字符串,只要一出现,浏览器的解释器就认为是代码终止符到了,后续内容将不再解析;应该换为“<\/script>”,这样浏览器会对内容做一个转义,程序就能正常运行了。

undefined

JS整个语言只有六种数据类型:undefined、Null、Boolean、Number、String以及Object,JS不支持自定义类型的机制。

请看以下代码,试问返回什么

1
2
3
4
5

var message

alert(message)
alert(age)

答案显而易见:在进行alert(age)的时候,报错了;那再看看如下代码,请问会输出什么:

1
2
3
4
var message

alert(typeof message)
alert(typeof age)

答案可能很意外,会弹出两次undefined,而且不报错。

可能让人感到很奇怪,但事实却是如此;对未初始化的变量进行typeof操作符会返回undefined的,对未定义的变量进行typeof操作也是会返回undefined;可能背后的逻辑是:无论变量是否定义,都不会真正执行吧

NaN

请看以下代码输出什么?

1
console.log(NaN === NaN)

正确答案是:false

NaN的含义是:“Not a Number”;是一个特殊的值,这个数值表示一个本来返回数值的操作数未返回数值的情况,以替代直接报错。例如:“10 / 0” 在其他语言中直接就报错了,但是JS为了防止程序报错崩溃,加入了NaN

/ 0
1
2
3
4
5
6
7
8
9
10
11

## 函数参数
请看以下代码输出什么?

```js
function sum(num1, num2){
return num1 + num2
}

let a = sum(1)
console.log(a)

会报错吗?
正确答案是:输出NaN

js函数的参数与绝大多数语言中的参数不同,js对函数参数的类型和个数都没有强制限制;在js内部,函数参数的实现是拿一个数组来表示的;因此,哪怕你定义了两个参数,只传进去一个参数,也不影响js的正常运行。
写代码的时候,在js的函数体内部,可以通过arguments对象来访问js的参数数组,从而获取传递给函数的每一个参数。

再来看看以上这道题目,sum函数有俩参数,但是只传了一个参数,程序不会报错,原因不解释了。在sum函数中,程序变为了 1 + arguments[1],由于 arguments[1]为传参,故值为undefined,

+ undefined```为``NaN``(not a number)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

## 函数名

请看以下代码,存在两个相同的函数名,在代码运行后,会输出什么?

```js
function addNum(num) {
return num + 10
}

function addNum(num) {
return num + 20
}

let num = addNum(1)
console.log(num)

正确答案:输出21

其他语言中例如Java,只要定义函数签名或者函数参数类型和数量不同,就可以定义相同的函数名,这是传统意义上的重载,但是JS中没有重载,如果定义了两个以上相同的函数名,JS最终会以最后定义的函数为准。

变量

请看一下代码,最终会输出什么?

1
2
3
4
5
6
7
8
9
10
var num1 = 5
var num2 = num1
num2 = num2 + 1
console.log(num1)


var obj1 = new Object()
var obj2 = obj1
obj2.name = "ryugou"
console.log(obj1.name)

正确答案:

1
2
5
ryugou

js的变量值可分为:基本类型引用类型两种;其中,基本类型值是简单的数据段,包含 underfinedNullBooleanNumberString这五种,其余为引用类型。
这两种类型的值在进行变量间赋值的时候,也会有所不同。对于值是基本类型的变量,在赋值的时候,会在给赋值变量复制一个新值。例如上题中的var num2 = num1 这时,是将num1的值复制一份,填写给num2,相当于:var num2 = 5
但是,如果是引用类型的变量,在赋值的时候,各种变量还会指向原引用。例如代码中的var obj2 = obj1,此时obj2obj1同时指向了同一个object。

更多精彩内容,请关注我的公众号