一、 前言
我粗略看了下函数的内容,太多与之相关的了,第二个月里函数有关的知识点太多,也不太好拆分。包括我们第一弹讲的作用域,其实也与函数有关,由于没有说到函数,所以,作用域其实也只是个入门。后面讲到函数的时候,会继续深入讲解作用域与执行上下文。按照这种情况,我需要提前进入函数的内容整理了。 那之后的JS课程我就提前预留两章出来。
之后都是每周更新一期,从JS最基础【变量与作用域】到【异步编程,密码学与混淆】。计划历时6个月左右。希望自己能坚持下来, 也希望给准备入行JS逆向的朋友一些帮助
先预告一下【V少JS基础班】的全部内容。看着很少,其实,正儿八经细分下来其实挺多的,第一个月的东西也一点不少。
第一个月【变量 、作用域 、BOM 、DOM 、操作符、数据类型】
第二个月【函数、闭包、原型链、this】
第三个月【面向对象编程、异步编程、nodejs】
第四个月【密码学、各类加密函数】
第五个月【jsdom、vm2、express】
第六个月【基本请求库、前端知识对接】
二、本节涉及知识点
1- 数据类型
2- 操作符
三、重点内容
1- 数据类型:
很重要,认真看
JavaScript中的数据类型分两种: 一种是原始类型, 一种是引用类型。
原始类型(Primitive Types)
原始类型的值是不可变的,存储在栈内存中。它们包括:
number:数字类型,包含整数和浮点数。
string:字符串类型,一系列字符的集合。
boolean:布尔值,表示真或假。
undefined:表示未定义的值,变量声明了但未赋值时的默认值。
null:表示空或无效对象引用。
symbol:用于创建匿名唯一值的类型,常用于对象属性的标识符。
原始类型的值是不可更改的(不可变)。
变量直接存储值本身。
存储在栈内存中。
由于值本身不可改变,操作这些数据不会引发内存重分配。
引用类型(Reference Types)
引用类型包括所有复杂的数据类型,如对象、数组、函数、日期等。它们的值是对象的引用,而不是值本身。
对象(Object):一组键值对的集合,可以包含多个数据类型。
数组(Array):一种特殊类型的对象,用于存储有序的元素集合。
函数(Function):一组可以调用的代码块,用于执行某些任务。
引用类型的值是可变的,可以改变对象或数组的内容。
存储在堆内存中,但栈内存中存储的是对这些对象的引用(内存地址)。
当你将引用类型赋值给另一个变量时,你传递的是引用(内存地址),而不是值本身。
2- 栈内存&堆内存:
栈内存(Stack Memory)
栈内存用于存储原始数据类型(如数字、布尔值、字符串等)和函数调用信息。栈内存具有以下特点:
内存管理: 栈内存的分配和回收非常高效。每次函数调用时,会创建一个新的栈帧,存储局部变量,函数调用结束后,栈帧会被销毁。
存储内容: 栈内存存储的是原始类型的值以及指向堆内存中引用类型的引用(即对象、数组、函数的内存地址)。
内存生命周期: 栈内存中的数据在函数执行时分配,当函数执行完毕时,相关数据会自动销毁。
栈内存示例:
function example() {
let num = 10; // 存储在栈内存中
let str = 'hello'; // 存储在栈内存中
}
堆内存(Heap Memory)
堆内存用于存储引用类型的数据(如对象、数组、函数等)。堆内存的特点包括:
内存管理: 堆内存的分配和回收较为复杂,由垃圾回收机制(GC)负责。当对象不再被引用时,垃圾回收器会回收堆内存。
存储内容: 堆内存存储的是引用类型的值。栈中存储的是指向堆内存中数据的引用(即对象的内存地址)。
内存生命周期: 堆内存中的数据由垃圾回收机制管理,生命周期不固定,可能在堆内存中存在很长时间,直到没有任何引用指向它。
堆内存示例:
function example() {
let obj = { name: 'Alice' }; // 存储在堆内存中
let arr = [1, 2, 3]; // 存储在堆内存中
}
3- 原始类型与引用类型的区别:
赋值方式: 原始类型在赋值时会复制值本身,而引用类型在赋值时会复制引用(即内存地址)。
let x = 5;
let y = x; // y 是 x 的副本
y = 10; // 修改 y 不会影响 x
console.log(x); // 输出 5
console.log(y); // 输出 10
引用类型赋值:
let obj1 = { a: 1 };
let obj2 = obj1; // obj2 引用 obj1
obj2.a = 2; // 修改 obj2 会影响 obj1
console.log(obj1.a); // 输出 2
console.log(obj2.a); // 输出 2
小结:这里的知识点就是深拷贝&浅拷贝。在其他语言里经常会有深拷贝和浅拷贝的知识点。
我们这里就知道:浅拷贝只能拷贝原始类型, 对于引用类型使用浅拷贝,原对象修改,拷贝的对象也会变动。
类型转换的内存与计算开销:
当 JavaScript 执行类型转换时,可能会导致额外的内存分配或计算开销:
原始类型转换:原始类型之间的转换(如 number 转为 string)通常会生成一个新的值。例如,当数字 1 被转换成字符串 ‘1’ 时,JavaScript 会在堆内存中为新生成的字符串分配内存。
let num = 1;
let str = num + ''; // 1 转为字符串,创建新的字符串对象
引用类型转换:引用类型的转换(如将对象转换为基本数据类型)通常会导致临时对象的创建,增加了计算和内存开销。
let result = 1 + '1'; // 隐式类型转换,数字 1 被转为字符串 '1'
console.log(result); // 输出 "11"
总结:
栈内存:存储原始类型的数据和函数调用信息,分配和回收非常高效。
堆内存:存储引用类型的数据,分配和回收较慢,需要垃圾回收机制来管理。
原始类型:存储值本身,分配在栈内存。
引用类型:存储内存地址(引用),分配在堆内存,栈内存存储引用。
类型转换:会增加计算和内存开销,特别是当涉及到创建新的对象时。
通过合理管理数据类型和避免不必要的类型转换,你可以提高 JavaScript 程序的效率,并更好地管理内存资源。
解析: 讲了很多我不知道大家理解了多少。 上面的总结大家认真看了应该都会有所感悟。那我这里有个点再跟大家强调一下。
就是类型装换。 我们不论原始类型,还是引用类型。 只要涉及到类型转换,大多都与堆内存有关。 或许有人会疑惑,原始类型不是应该在栈内存吗。
这里也很好理解。 栈内存的特点是,高效,系统自动分配,自动回收。 而且内存的大小与系统直接相关。那么如果原始类型的值过大,会导致栈溢出。
所以,为了避免性能产生的问题。 在类型转换的时候,大多会在堆内存中处理。这里也间接说明了类型转换是一个消耗性能的操作。
特征 | 栈内存 | 堆内存 |
---|---|---|
存储内容 | 原始类型数据(number、string、boolean)、局部变量 | 引用类型数据(对象、数组、函数等) |
分配方式 | 操作系统自动分配,遵循 LIFO(后进先出)规则 | 由程序员手动分配或垃圾回收自动管理 |
内存回收 | 自动回收,函数执行完毕后自动销毁 | 需要垃圾回收机制来清理不再引用的对象 |
生命周期 | 与函数的生命周期一致,函数执行完后自动销毁 | 与对象的引用计数相关,引用为 0 时被回收 |
大小限制 | 内存较小,由操作系统设置 | 内存较大,受操作系统限制较小 |
访问速度 | 较快,结构简单 | 较慢,需要通过引用访问数据 |
分配和释放速度 | 分配和释放都很快 | 分配和释放较慢,受垃圾回收影响 |
例子 | let a = 10; | let person = { name: ‘John’ }; |
4- 类型转换(显示转换):
在 JavaScript 中,显示转换(Explicit Type Conversion)是指通过明确的代码将一种数据类型转换为另一种数据类型。这种转换是由开发者主动进行的,不像隐式转换那样由 JavaScript 引擎自动完成。
常见的显示转换方式有以下几种:
1. 字符串转换(String Conversion)
你可以使用 String() 函数或者 .toString() 方法将其他类型转换为字符串。
let num = 42;
let str = String(num); // 显式转换为字符串
console.log(str); // 输出 "42"
let bool = true;
let strBool = String(bool); // 显式转换为字符串
console.log(strBool); // 输出 "true"
数据类型 | 转换结果 |
---|---|
Number | 数字直接转换为字符串(例如 123 → “123”) |
Boolean | true → “true”, false → “false” |
Object | 对象通常转换为 “[object Object]”,除非有自定义 toString() 方法 |
Array | 数组元素通过逗号连接成字符串(例如 [1,2,3] → “1,2,3”) |
Function | 转换为函数的代码(例如 function() {…}) |
null | 转换为字符串 “null” |
undefined | 转换为字符串 “undefined” |
Date | 转换为日期的字符串表示(例如 “Mon Dec 25 2024”) |
空字符串 “” | 不变,仍然是 “” |
Symbol | 报错:TypeError |
细节1:
Symbol不可以被String()方法强转。 但是Symbol 类型提供了一个专门的方法 toString(),它可以将 Symbol 转换为字符串,返回该 Symbol 的字符串表示(包含描述信息)。
let sym = Symbol("description");
console.log(sym.toString()); // "Symbol(description)"
sym.toString() 返回 Symbol 对象的字符串表示形式,格式为 "Symbol(description)",其中 description 是创建 Symbol 时传入的可选描述。
细节2:
Object 转换为字符串:
JavaScript 对象本身并不直接具备字符串表示形式。因此,String() 方法依赖 toString() 方法来生成字符串表示。默认情况下,Object.prototype.toString() 返回的是一个固定格式的字符串 “[object Object]”,表示该对象的类型(对象)。
但是你可以通过为对象定义 toString() 方法来定制它的字符串表示。
细节3:
这个涉及到隐式转换和显示转换的区别:
显示转换:你显式地调用 String()、Number() 等方法进行转换。在这种情况下,String() 会优先调用 toString(),然后才会回退到 valueOf()。
隐式转换:通常发生在 JavaScript 执行一些操作时(比如加法、布尔值测试等),这时 JavaScript 自动调用相关类型的转换方法(如 toString() 或 valueOf())。隐式转换的规则会首先尝试 valueOf(),如果不行,才会尝试 toString()。
2. 数字转换(Number Conversion)
你可以使用 Number() 函数将其他类型转换为数字,或者使用 parseInt() 和 parseFloat() 转换为整数或浮动数值。
let str = "42";
let num = Number(str); // 显式转换为数字
console.log(num); // 输出 42
let bool = true;
let numBool = Number(bool); // 显式转换为数字 (true -> 1, false -> 0)
console.log(numBool); // 输出 1
let invalidStr = "abc";
let numInvalid = Number(invalidStr); // 转换无效字符串为 NaN
console.log(numInvalid); // 输出 NaN
另外,parseInt() 和 parseFloat() 用于将字符串转换为整数或浮点数:
let strInt = "42";
let intNum = parseInt(strInt); // 显式转换为整数
console.log(intNum); // 输出 42
let strFloat = "3.14";
let floatNum = parseFloat(strFloat); // 显式转换为浮动数
console.log(floatNum); // 输出 3.14
数据类型 | 转换结果 |
---|---|
字符串 (String) | 如果是有效数字字符串,转换为该数字(例如 “123” → 123);否则返回 NaN(例如 “abc” → NaN) |
布尔值 (Boolean) | true → 1,false → 0 |
对象 (Object) | 一般对象转换为 NaN;Date 转换为对应的时间戳(毫秒数) |
null | 转换为 0 |
undefined | 转换为 NaN |
数组 (Array) | 数组先转换为字符串,再转换为数字;空数组 [] 转换为 0 |
Symbol | 报错:TypeError,无法转换为数字 |
函数 (Function) | 转换为 NaN |
NaN 和 Infinity | NaN → NaN,Infinity → Infinity |
3. 布尔值转换(Boolean Conversion)
Boolean() 函数可以将其他类型转换为布尔值。JavaScript 中有一些"假值"(falsy values),例如 0、“”、null、undefined、NaN 等,它们会被转换为 false,其余的值都转换为 true。
let str = "hello";
let boolStr = Boolean(str); // 显式转换为布尔值
console.log(boolStr); // 输出 true
let num = 0;
let boolNum = Boolean(num); // 显式转换为布尔值
console.log(boolNum); // 输出 false
let obj = {};
let boolObj = Boolean(obj); // 非空对象转为 true
console.log(boolObj); // 输出 true
数据类型 | true 转换的结果 | false 转换的结果 |
---|---|---|
数字 (Number) | 1 | 0 |
字符串 (String) | “true” | “false” |
对象 (Object) | Boolean {true} | Boolean {false} |
数组 (Array) | Boolean {true} | Boolean {false} |
null | false | false |
undefined | false | false |
NaN | false | false |
空字符串 (“”) | false | false |
数字 0 | false | false |
其他数据类型(如对象、非空字符串、非零数字等) | true | false |
4. 转换为其他类型(其他方法)
有时你可能需要将一个对象转换为特定的类型,例如将对象转换为字符串。这可以通过 .toString() 方法完成。
let obj = { name: "Alice", age: 25 };
let objStr = obj.toString(); // 转换为字符串
console.log(objStr); // 输出 "[object Object]"
你还可以通过 JSON.stringify() 方法将对象转换为 JSON 字符串:
let obj = { name: "Alice", age: 25 };
let objJson = JSON.stringify(obj); // 转换为 JSON 字符串
console.log(objJson); // 输出 '{"name":"Alice","age":25}'
5- NaN,null和undefined区别:
null、undefined 和 NaN 都在 JavaScript 中代表“无效”或“未定义”的值,但它们有不同的含义和用途。这里是它们的区别:
1. null
含义:null 表示“空”或“无”。它通常用于指示一个有意为空的值。
用途:可以在代码中明确地将一个变量设置为 null,表示这个变量目前没有有效值,但预期会有值。
类型:null 是一个特殊的对象类型,但 typeof null 返回 'object',这是 JavaScript 的一个历史遗留问题。
示例:
javascript
复制代码
let obj = null; // 明确表示 obj 目前为空
2. undefined
含义:undefined 表示“未定义”。通常,当变量声明了但没有赋值时,默认值就是 undefined。
用途:表示一个变量或属性尚未被初始化。如果尝试访问不存在的对象属性,也会得到 undefined。
类型:undefined 是它自己的类型。typeof undefined 返回 'undefined'。
示例:
javascript
复制代码
let value;
console.log(value); // undefined,因为 value 没有赋值
let obj = {};
console.log(obj.property); // undefined,因为 property 属性不存在
3. NaN
含义:NaN 表示“不是一个数字”(Not a Number)。通常出现在数值运算出错的情况下,例如试图将无法解析为数值的内容转换为数字。
用途:用于表示无效的数值计算结果。例如,0 / 0、parseInt('abc') 等会返回 NaN。
类型:NaN 是 number 类型的一部分。typeof NaN 返回 'number'。
示例:
javascript
复制代码
console.log(0 / 0); // NaN
console.log(Number('abc')); // NaN
主要区别总结
特性 null undefined NaN
含义 明确为空 未定义 非数值
产生原因 有意地表示“没有值” 未赋值或属性不存在 数值运算错误
类型 object undefined number
典型场景 空对象、空值 未初始化变量、缺失属性 无效数学计算
特殊性比较
null == undefined 返回 true,因为它们都表示“没有值”。
null === undefined 返回 false,因为它们的类型不同。
自我总结:
nan: 是number类型,表示为非数字, 一般只有在数值运算出错的情况下出现。
undefined: 本质是一个对象, 他就是undefined类型。 但是, 这个对象是没有被定义的。 所以为undefined
null: 他本质是这个值为空, 但是后续可能会赋值, 只是目前为空。 但是type null 结果为对象, 这个其实是js的底层错误,是历史遗留问题
我口语化解释一下:
nan :他是个number类型。 一般都在数学计算中,才会出现nan。
undefined : 变量声明了,但是没有初始化,这时候变量就是undefined。 举例: var a;
此时a就是被声明了,但是a没有被初始化。此时的值就是undefined
null :占位空值。 记住一点,null只能是显示赋值。 意思就是null必须得是人为定义。 举例:
var a = null;我们人为的给了他一个空值。用于占位。
6- 总结:
以上是数据类型部分的知识。其中隐式转换并没有说的很细。 因为隐式转换多半涉及到操作符。 我们看完操作符,再回过头来看这个隐式转换