JS小知识 重学数据(类型)、变量、内存

1,937 阅读8分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

2021-10-8 更新 读了大佬的一篇文章 发现自己写得太不全面了!

感兴趣的朋友们可以去看一下!文章讲述了0.1+0.2!===0.3、隐式转换、精准判断变量类型等有趣且重要的知识点

复习了一些JS最基础的知识——关于

  • 数据类型的分类、判断🎉 以及一些拓展的基础知识

  • 数据、变量、内存的理解🤔

    • 数据存储的底层知识😴

    • 为变量赋值时 赋给它的是?🧐

    • 调用函数时传入的实参是值还是引用地址?😐

这里的内容虽然简单 但是第一次学习时很难理解透咯

偏偏这些知识在后期学习原型链等深入的知识时会很有帮助!

所以~还是要好好巩固下基础

如果有啥不全面/出现错误的地方 欢迎各位大佬的讨论/指正❤️ 夯实基础冲冲冲!

数据类型的分类、判断

1.数据类型的分类、判断🎉

何为数据?👓

首先明确下 啥是数据?

数据是在内存中可读、可传递的 保存了特定信息的东西 本质是二进制的机器码(给机器看的)我们程序员看到的肯定就是高级语言咯 我们写的代码中包含的数据经过编译让电脑可以理解,从而执行各种操作。

一切皆为数据 函数啊,对象啊啥的都是数据

在内存中 我们所有操作的目标都是数据 比如——

  • 算术运算

  • 逻辑运算

  • 赋值运算

  • 运行函数

那么既然要做这些个操作,就会涉及到数据类型们的使用(嘿哟!终于把它们引出来了)来看看这些数据类型的分类,看看怎么判断数据类型们!

数据类型的分类和判断🔐

大家都知道,数据是分为——基本类型、对象引用类型两大种的 我们来列出来看看

基本(值)数据

  • Number 数值类型

  • String 字符串类型

  • Boolean 布尔值类型

  • undefined

  • null

10-8补充

  • Symbol 实例是唯一且不可改变的数据类型

  • BigInt ES10中被提出 用于操作超过最大安全数字(Number.MAX_SAFE_INTEGER 1.111...X 2^52)的数字

image.png

以上五种类型是要怎么判断呢?大家心中应该都有答案了吧——使用 typeof

是 但不完全是 因为null是不能这样判断der~

数据类型判断方法
数值型typeof
字符串型typeof
布尔型typeof
undefinedtypeof或者===
null===
Symboltypeof

这里多一嘴 === 代表严格全等 不会给等式两边自动做数据类型的转换

==会自动为等式两边做隐式类型转换

所以严谨起见 我们追求的是全等 都用=== (我知道大家都知道 我就提一嘴🤐)

var a = 666;
var b = 'bill';
var c = true;
var d;
var e = null;
console.log(typeof a);// number
console.log(typeof b);// string
console.log(typeof c);// boolean
console.log(typeof d, d === undefined);// undefined true
console.log(typeof e, e === null);// object(所以不能用typeof判断null咯) true

这里要注意 typeof a的返回值是字符串嗷

var a = 666;
var d = undefined; 
console.log(typeof a, typeof a === 'number');// number true
console.log(d === undefined, typeof d === undefined, typeof d === 'undefined');// true false true

对象(引用)数据

  • Object 对象

以下二位都属于特殊的对象

  • Array 数组

  • Function 函数

数据类型判断方法
Objecttypeof/instanceof
Arrayinstanceof
Functiontypeof
var a = {};
var b = new Array(6);
var c = () => {};

console.log(typeof a, a instanceof Object);// object true
console.log(typeof b, b instanceof Array);// object(所以数组不能这么判断~) true
console.log(typeof c, c instanceof Function);// function true

【模拟下面试】undefined null那些事儿✨

面试官:“前面聊了数据类型 那么undefined和null有啥区别啊?”

这时候就可以展开讲一讲了~

简单来说

  • undefined代表定义未赋值

  • null代表定义并赋值了 但是赋了一个null

那么为啥要赋个null嘞 也就是这个null能干啥呢?(又延伸出一个点😂)

var obj = null;// 01 表示我们将要将给obj这个变量赋值为对象~(数组、对象、函数都ok)
obj = ['bill', 21];
obj = null;// 02 让obj指向的对象成为垃圾对象(回头垃圾回收器会过来把它收走 防止内存的消耗~) 

如上👆

  • 01 初始赋值 表明将要赋值obj为对象
  • 02 结束使用obj之后 让对象成为垃圾对象

2.数据、变量、内存的理解🤔

数据存储的底层知识😴

了解数据存储的底层知识之前,先要了解 变量、内存 这两个概念 它们是息息相关的~

  • 变量

一个变量对应一小块内存 变量的值保存在内存中!

  • 内存

内存条通电后产生的存储空间(临时的 断电后就木有了~)

内存条就是这个绿绿的东西🧐

image.png

一块内存包含两方面的数据

  • 内部存储的数据(存储于堆空间中——堆空间用于存储对象 占用空间较大)

  • 地址值数据(存储于栈空间中——栈空间用于存储变量 占用空间较小)

举个例子

var obj = {name:'Tom'}

实际上就是等号右边的对象的内存内容(也就是它的地址值)赋给变量obj

👇左面的那一条就是模拟的栈~右面即为堆 image-20210921101846936.png

这里让我想起了之前学习Java的时候 研究面向对象中JVM底部的运行机制时的学习——

这个是当时学习的笔记XD 面向对象基础的笔记

下图中 cat就是变量(位于栈中) 指向了 new Cat() 创建出来的对象(位于堆中)~ 在这里插入图片描述

为变量赋值时 赋给它的是?🧐

来看个例子

// 01 给变量赋值基本数据 —— 保存的就是这个基本数据
var a = 666;
// 02 给变量赋值对象 —— 保存的是 【对象的地址值】 (如上图)
var obj = {name: "Bill"};
// 03 重要!给变量obj1 obj2赋变量a obj【的内存内容】 
// —— 内存空间保存的可以是基本数据(a)也可以是对象的地址(obj)
var obj1 = a;
var obj2 = obj;

很明显地分为了三种情况~

  • 【1】如01一样 可以赋给它基本数据类型

  • 【2】如02一样 可以赋给它对象的地址值

  • 【3】如03一样 赋给它某个对象的内存内容

    • 而这个内存内容可以是基本数据类型
    • 也可以是对象的地址

【高频面试题】调用函数时传入的实参是值还是引用地址?😐即“值传递/引用传递?”

先说结论

其实这里有两种理解——

【1】不管传递的是数组还是对象 都是值传递 —— 更加规范 因为理论上来说引用传递传递的地址值也是一个值~

【2】可能是值传递(传基本数据类型时) 也可能是引用传递(传地址值时)

其实到这里大家应该已经理解了 但是实际应用时呢?来举个例子吧~

举个🌰1号

var a = 3;
function fn(a){
    a = a + 1;
}
fn(a);
console.log(a);

这里的输出是多少呢?

都这么问了肯定是有坑🤣

答案为3

来解释下——

var a = 3;
function fn(a){
    a = a + 1;
    // a(局部的那个a变量(也就是形参变量)所以在外面输出的是全局变量3)=a(全局的那个a变量 值为3)+1
    // 这里的局部变量为4 而外面输出的是全局变量啦!
    console.log(a);// 4
}
fn(a);//这里传递的不是a的地址 而是a的值——3
// 调用函数 将实参赋给形参
console.log(a);// 3

在传实参的时候传过去的是a的“值”!

其实吧 这里如果理解了作用域会更好理解 ——

函数作用域里发生的 a = a + 1 (修改值操作) 和我全局作用域有啥关系🤣

举个🌰2号

function fn2(obj){
    obj.name = "change";// 这里修改了地址中对象的name属性
}
var obj = {name: "Bill"};
fn2(obj);// 调用函数 修改了地址中对象的name属性
console.log(obj.name);//change

害估计大家都能猜到栗子是啥了 传入的实参是地址值咯...

这时候函数作用域里面进行的修改值操作就起效果了~

你可能会问了这不耍赖皮么 为啥同样是修改 一个作用范围这么广泛(地址值)一个就只是局部得(原始数据类型)

欸 这个可不一样(又回到了咱们今天讨论的大主题里——变量、内存的关系) 虽然变量是局部的 但是它指向的对象是全局的啊! 我修改的可是全局的对象 只不过你这个变量指向我 所以从中获取的值也跟着改变了~

image.png 对象的值改变了 obj.name的值自然会改变~

image.png

小结

说了这么多 小结一下

其实在调用函数进行传参时,严谨一点说,进行的都是值传递

只不过有些值传递传过去的是基本数据类型,改变的时候需要注意函数作用域的问题,而有些值传过去是地址值,直接对地址值进行改变才不管作用域呢,直接把对象改变😂看上述的例子就明白了 很简单der~但是面试的时候不得多白话两句显得咱想得比较多嘛(确实想了很多喂(#`O′))