Hi!这里是JustHappy ,既然点开了这个JS硬气功,那就一起愉快的学习吧!我想大家都有在面试中碰到过JS变量类型之类相关的问题吧,今天我们来练一练这一块的,希望可以帮助大家和我自己在面试中把嘴皮子磨硬🪨🪨🪨
变量声明 && 变量类型 🐶🐮
var、let、const
let
和const
是ES6(ECMAScript 2015)引入的,旨在提供更好的作用域控制和变量声明的安全性。在现代JavaScript开发中,推荐使用let
和const
来代替var
,以编写更清晰、更易于维护的代码。
为什么大家都不继续使用var?
var
声明的变量会被提升到它们所在的作用域的顶部 (这怎么形容呢?就相当于在你代码的第一行前面声明了变量),但是它们的赋值不会提升
这是啥意思呢?我们来看看下面这几小段代码,大家也可以在自己的设备上跑一下
- 我们尝试单独输出一个未被声明的变量
arduino
console.log(myVal)
-
显而易见,这行代码是会报错的
-
但是如果我们在这行代码的下面使用var去声明这个变量,再在下面输出就会有以下情况
ini
console.log(myVar); // 输出:undefined,而不是报错,因为变量声明被提升,但赋值没有提升
var myVar = 5;
console.log(myVar); // 输出:5
-
我们都知道代码是顺序执行的,按道理说第一个
console.log(myVal)
应该会报错,但是却输出了undefined
,这说明了该变量在这一行处于一个"被声明但是为被赋值的状态",而以上现象就是因为var变量的声明 被提升、但赋值并没有被提升 -
这会导致什么问题呢?
- 这可能导致在变量赋值之前就引用了未声明的变量,导致程序出现错误。
let和const
我们在使用let和const的时候是不存在变量提升这一问题,这是因为let和const声明变量会经历所谓的"暂时性死区"(Temporal Dead Zone,TDZ)
- 下面两段代码也证明了这一点
ini
console.log(letVal); // 抛出 ReferenceError: bar is not defined
let letVal = 'aaa';
ini
console.log(constVal); // 抛出 ReferenceError: qux is not defined
const constVal = 'bbb';
你真的了解变量类型吗?
值类型 or 引用类型(即简单类型 和 复杂类型)
值类型 又或者说是 简单类型 吧,在JavaScript中有
我们先来看以下两段代码
ini
// 值类型特征演示
let a = 10;
let b = a;
b = 20;
console.log(a); // 10
// 引用类型特征演示
let c = { name: "Jack" };
let d = c;
d.name = "Tom";
console.log(c.name); // Tom
很直观的可以看到值类型演示中原来的值没有被改变,但是在引用类型中却被改变了
所以这是为什么呢?
不只是JavaScript,几乎是所有的编程语言都有这个特性,这和数据在内存中的存储有关
在JavaScript中,值类型和引用类型在内存中的存储方式有显著的区别,这主要体现在它们存储的位置和访问方式上:
值类型(简单类型)
-
存储位置:值类型的数据直接存储在栈(stack)内存中。
-
存储方式:当值类型的数据被创建时,它们会直接被复制到栈中。例如,当你声明一个变量并赋予一个值时,这个值就会被存储在栈中。
引用类型(复杂类型)
-
存储位置:引用类型的数据存储在堆(heap)内存中。
-
存储方式:引用类型的变量实际上存储的是指向堆中数据的指针(引用)。当引用类型的数据被创建时,它们会被存储在堆中,而变量则保存了指向这些数据的引用。
那么对于一引用数据类型(复杂类型),我们用一个新的变量拷贝完再修改的话,如何使得原本的数据不被修改呢。于是乎我们来聊聊深拷贝 和浅拷贝
深拷贝和浅拷贝
由上我们知道,只有引用类型(复杂数据类型)需要进行深拷贝,所以在此之前我们需要掌握如何判断一个数据是否是引用类型,所以我们来看看typeof()
有关typeof()
typeof()
可以直接返回简单数据类型的确切类型 ,而对复杂数据类型来说,则返回object ,对于函数则返回function,以下是返回结果示例
javascript
// 对于简单数据类型
console.log(typeof 10); // number
console.log(typeof "Jack"); // string
console.log(typeof true); // boolean
console.log(typeof Symbol()); // symbol
console.log(typeof undefined); // undefined
//对于复杂数据类型
console.log(typeof {}); // object
console.log(typeof []); // object
//对于函数
console.log(typeof function () {}); // function
但是typeof()
有个历史遗留问题,当判断null 的时候也会返回object
javascript
console.log(typeof null); // object
所以我们来实现一个深拷贝吧!!!
实现深拷贝有多种方式,我们这里就写一种最重要的,最能体现编程思想的递归深拷贝吧
ini
const deepClone = (obj) => {
if (typeof obj !== "object" || obj === null) return obj;
// 初始化反回结果
let result;
// 判断是数组还是对象
if (Array.isArray(obj)) {
result = [];
} else {
result = {};
}
for (let key in obj) {
// 保证key不是原型链上的属性
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
};
以下是测试这段代码的示例
css
let e = {
name: "Jack",
sex: "male",
address: {
city: "Beijing",
},
age: 18,
};
let f = deepClone(e);
f.address.city = "Shanghai";
console.log(e); // { name: 'Jack', sex: 'male', address: { city: 'Beijing' }, age: 18 }
console.log(f); // { name: 'Jack', sex: 'male', address: { city: 'Shanghai' }, age: 18 }
有点烦人的变量类型转换
先看看以下几个示例吧
ini
// 有关 "==" 号的类型转换
// 如果比较的两个值类型不同,js会尝试将它们转换为相同的类型。
console.log(3 == "3"); // true
console.log(true == 1); // true
console.log(false == 0); // true
console.log("" == 0); // true
console.log("" == false); // true
console.log(null == undefined); // true
// 但是NaN 不等于任何值,包括它自己
console.log(NaN == NaN); // false
console.log(NaN !== NaN); // true
console.log(NaN === NaN); // false
怎么样,是不是有些懵逼!😭
其实这只需要咱记住就好啦
那就有疑问了!我们该如何判断一个值是否是NaN 呢?该如何合乎逻辑的比较两个NaN呢?
我们可以使用 isNaN() 函数判断是否为NaN
javascript
console.log(isNaN(NaN)); // true
使用 object.is() 函数判断两个值是否严格相等
javascript
console.log(Object.is(NaN, NaN)); // true
如果有什么不足之处,大家尽管在评论区留言哦