javacript 是弱类型语言,定义的变量可以随意更改值的类型。
数据类型
名词汇总
- 基本数据类型
- 值类型
- 原始数据类型
- 引用数据类型
- 复杂数据类型
类型判断
typeof
1 | typeof xxx得到的值有以下几种类型:undefined boolean number string object function、symbol ,比较简单,不再一一演示了。这里需要注意的有三点: |
instanceof
用于实例和构造函数的对应。例如判断一个变量是否是数组,使用 typeof 无法判断,但可以使用[1, 2] instanceof Array 来判断。因为,[1, 2]是数组,它的构造函数就是 Array。同理:
1 | function Foo(name) { |
内存图
栈:原始数据类型(Undefined,Null,Boolean,Number、String)
堆:引用数据类型(对象、数组和函数)
两种类型的区别是:
存储位置不同;
- 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
- 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
参数传递方式
- 原始类型是按值传递
- 引用类型是按共享传递
JS 中这种设计的原因是:按值传递的类型,复制一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保证了其访问速度。按共享传递的类型,是复制其引用,而不是整个复制其值(C 语言中的指针),保证过大的对象等不会因为不停复制内容而造成内存的浪费。
值类型&引用类型
除了原始类型,ES 还有引用类型,上文提到的 typeof 识别出来的类型中,只有 object 和 function 是引用类型,其他都是值类型。
根据 JavaScript 中的变量类型传递方式,又分为值类型和引用类型,值类型变量包括 Boolean、String、Number、Undefined、Null,引用类型包括了 Object 类的所有,如 Date、Array、Function 等。在参数传递方式上,值类型是按值传递,引用类型是按共享传递。
下面通过一个小题目,来看下两者的主要区别,以及实际开发中需要注意的地方。
自动检测
// 值类型
var a = 10
var b = a
b = 20
console.log(a) // 10
console.log(b) // 20
上述代码中,a b 都是值类型,两者分别修改赋值,相互之间没有任何影响。再看引用类型的例子:
自动检测
// 引用类型
var a = {x: 10, y: 20}
var b = a
b.x = 100
b.y = 200
console.log(a) // {x: 100, y: 200}
console.log(b) // {x: 100, y: 200}
上述代码中,a b 都是引用类型。在执行了 b = a 之后,修改 b 的属性值,a 的也跟着变化。因为 a 和 b 都是引用类型,指向了同一个内存地址,即两者引用的是同一个值,因此 b 修改属性时,a 的值随之改动。
再借助题目进一步讲解一下。
说出下面代码的执行结果,并分析其原因。
自动检测
function foo(a){
a = a * 10;
}
function bar(b){
b.value = ‘new’;
}
var a = 1;
var b = {value: ‘old’};
foo(a);
bar(b);
console.log(a); // 1
console.log(b); // value: new
通过代码执行,会发现:
- a 的值没有发生改变
- 而 b 的值发生了改变
这就是因为 Number 类型的 a 是按值传递的,而 Object 类型的 b 是按共享传递的。
JS 中这种设计的原因是:按值传递的类型,复制一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保证了其访问速度。按共享传递的类型,是复制其引用,而不是整个复制其值(C 语言中的指针),保证过大的对象等不会因为不停复制内容而造成内存的浪费。
引用类型经常会在代码中按照下面的写法使用,或者说容易不知不觉中造成错误!
自动检测
var obj = {
a: 1,
b: [1,2,3]
}
var a = obj.a
var b = obj.b
a = 2
b.push(4)
console.log(obj, a, b)
虽然 obj 本身是个引用类型的变量(对象),但是内部的 a 和 b 一个是值类型一个是引用类型,a 的赋值不会改变 obj.a,但是 b 的操作却会反映到 obj 对象上。
定义变量
es5 - 变量
1 | // 通过 var 关键字来定义变量,变量的值可以随意更改(基本、引用) |
es5 - 常量
1 | // 没有确切定义常量的方式 |
es6
- 通过关键字 let 定义变量
- 没有变量提升
- 不得在声明之前调用 (暂时性死区)
- 通过 let 定义的变量,遇到有大括号的时候,会在这个大括号内,形成这个变量的封闭作用域
1 | // 可以使用str来定义变量 |
- 通过关键字 const 定义常量
- 没有变量提升
- 不得修改值的类型
- 基本数据类型不得重新赋值
- 引用数据类型,不得重新赋值(不包括数组、对象的属性)
- 不得在声明之前调用 (暂时性死区)
- 通过 const 定义的变量,遇到有大括号的时候,会在这个大括号内,形成这个变量的封闭作用域
1 | const obj = { |
| 栈 | 堆 |
| |
| ——— | ——- |
| const obj | o1 ={ } |
|
| name gaocaipeng |
|
|
null&undefined 区别
null 表示一个对象被定义了,值为“空值”;
undefined 表示不存在这个值。
typeof undefined //“undefined”
undefined :是一个表示”无”的原始值或者说表示”缺少值”,就是此处应该有一个值,但是还没有定义。当尝试读取时会返回 undefined;
例如变量被声明了,但没有赋值时,就等于 undefined
typeof null //“object”
null : 是一个对象(空对象, 没有任何属性和方法);
例如作为函数的参数,表示该函数的参数不是对象;
注意:
在验证 null 时,一定要使用 === ,因为 == 无法分别 null 和 undefined
undefined 表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:
1)变量被声明了,但没有赋值时,就等于 undefined。 2) 调用函数时,应该提供的参数没有提供,该参数等于 undefined。
3)对象没有赋值的属性,该属性的值为 undefined。
4)函数没有返回值时,默认返回 undefined。
null 表示”没有对象”,即该处不应该有值。典型用法是:
1) 作为函数的参数,表示该函数的参数不是对象。
2) 作为对象原型链的终点。
变量交换
实现变量交换你有几种方式?
变量借用
1 | var a = 1; |
解构交换
1 | var a = 1; |
运算交换
1 | var a = 1; |
- Post link: https://blog.gaocaipeng.com/2020/03/02/any3xm/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.