写了多年的javascript在看 变量类型和类型检查突然明白了什么?

一个变量引发的提交数据bug

A同学定义了一个提交对象 submitData ={name:‘xx’,sex:‘x’}, 同时这个对象要展示一个页面 给用户确认
姓名:xx ,性别:x ,
然后用户点击确认就提交服务端,同时限制了只能是两个属性,一切很美好,测试通过,这天A同学有事休息了。工作交接给B同学,恰好来了一个需求,需要在确认页面,添加一个展示字段,年龄,但是这个年龄不用提交,B同学一想,好简单,按照惯例,增加代码尽量不修改原来的逻辑,这个时候B同学定义了一个变量 showData = submitData ,在showData 添加字段age,用来展示,然而问题出现了。展示没有问题,但是提交总是不通过?A同学回来问B同学,你是不是改变了数据,B同学说我没有,我是新增的变量?那到底B同学有没有改变原来的值呢?

由于js 的变量赋值比较松散,可能带来极大的便利,也隐藏着些许隐患,上述问题,需要追述到在学习JavaScript 开篇或许当时大家都没有留意,只想着抓紧写代码实现功能,没有思考过js变量存储到哪里了,类型到底是如何有效的使用,多年以后才发现原来js是这么回事

js 中变量只存在两种类型的值,基本数据类型的值(primitive values)和引用数据类型的值(reference values

基本类型

在JavaScript 中主要有6中基本的数据类型:Undefined,Null,Number,String,Boolean,Symbol

基本类型的特点

1、值是不可改变的

看个例子

let person='木子聊javascript';
    person.toUpperCase(); //木子聊JAVASCRIPT
    person.sex='男';
    person.action = function(){ alert('my name is function')};
    console.log(person) ; //木子聊javascript
    console.log(person.sex) ; // undefined
    console.log(person.action) ;// undefined

调用 toUpperCase()是一个字符串的方法,变为大写,但是并没有改变person 原始状态值,返回的是一个新的值;
当给person 添加属性和方法,发现最后的结果是undefined ,也就是说,基本的数据类型的值是不可以改变,这里举例是Sring类型,剩下的五种类型也是同样的道理

2、两个基本类型的比较是值的比较,也就是基本数据类型是按值访问的

这句话也可以解释为,只要值相等就是相等,不管类型是什么,比如:

undefined == null // true

在看一个

 let n = 1;
 let m = true;
 let y = '1';
 console.log(n==m); // true
 console.log(m==y); // true 
 console.log(m==y); // true

定义三个不同类型 Number,Boolean,和String 通过 相等操作符 == 之后发现居然是相等
这是因为 == 比较的最终是值 同时 == 在比较之前会做强制类型转化 ,转化为相同的类型和相对于的值,在进行比较,如:例子中 m 和n 比较 会先将 true 转化为1 在和n的值1比较;

这个有时候并不能,为我们带来准确的结果,因此需要 全等于 ===

 console.log(n===m); // false
 console.log(m===y); // false
 console.log(m===y); // false

加上全等之后发现,不相等了,这是全等的功劳,不会类型转化

===

全等号,不会进行类型转化
比较的时候类型和值必须相同

==

相等符 ,会进行类型转化(强制)
比较的时候 值相同即可

3、基本类型的变量是存放在栈内存(Stack)里的,也就是实际变量中的值可操作

在js中 栈内存(内存的一种)保存 变量符号和值(类型)

基本类型的赋值、复制过程

过程是一动作组成,需要用图像来解释
当定义 let a = 1 发生了什么? 这个时候,内存分配一个块栈内存,来保存 a变量符和类型为Number 为1
当内存块,这个时候赋值完成,随时可用,如图:
在这里插入图片描述
接着 操作

let b =a

这个时候,内存会拷贝一份 a的值 ,同时新建立一块空间也就是一个新当栈内存地址来保存 变量b 和 值,变量b 和a 之后在没联系,形成相对独立的值 如图
在这里插入图片描述
接着操作,改变b的值,a 会不会发生变化呢

let a = 1;
let b = a;
console.log(b) // 1
b = 0
console.log(b) // 0
console.log(a) // 1

看代码知道b的改变不会对 a的影响,因为已经相对独立,内存中怎么表现呢?如图
在这里插入图片描述
这样就很清楚了。至此,看看能不能判断 开篇例子中B同学有没有改变A同学的值,貌似A同学定义的类型并不是 基础类型,如果是那么B同学一定没有办法更改A同学的值,现在情况变得有趣了。

接着看js的另外一个类型

引用类型

除了上面6中基础类型之外剩下的都是引用类型,统称 Object 类型,所有的引用类型都来自Object,具体细化一下举例一下:Array 类型、Date 类型、RegExp 类型、Function 类型 等,都继承Object都特性;

对象是属性和方法的集合。也就是说引用类型可以拥有属性和方法,属性又可以包含基本类型和引用类型

引用类型的特点

1、值是可以改变的
用代码来解释

  // 创建一个对象  包含名字
let person={name:'木子聊javascript'};
// 添加属性
    person.sex='男';
    // 添加方法
    person.action = function(){ alert('my name is function')};
    console.log(person.sex) ; // 男
    console.log(person.action()) ;//  弹出对话框 

代码运行之后发现,person 这个对象 是可以动态增加 属性和方法的,基本类型不具备这个特点

2、两个引用类型的比较是引用的比较,也就是引用数据类型是按引用访问的

引用类型的比较什么描述有点吃力,大概就是说不是值,样子相等,其实不是同宗,看代码

定义连个看样子一摸一摸的家伙,看看是不是相等的

let person ={}
let person1 ={}

用相等符号试试

console.log(person==person1);  // false

不相等,那么全等呢?

console.log(person===person1);  // false

可见长相再像也未必相等,因为这个是引用类型都特点之一,引用类型是保存在堆内存里面两个不同堆对象,而栈内存保存堆只是一个引用地址

3、引用类型的值是保存在堆内存(Heap)中的对象(Object), JavaScript 不能直接操作对象的内存空间(堆内存)也就是只能操作引用而不是对象

堆内存的位置是不可操作的,而栈内存是可以修改值的,因此当要修改堆内存堆时候只能在栈内存修改保存的引用,从而改变堆内存堆状态

引用类型的赋值、复制过程

过程是动作组成,需要用图像来解释
从赋值说起

let a = {}

这个时候在栈内存保存一个变量符 和一个 Object 对象 引用地址,可以叫指针类似当东西,这时候在堆内存有一块内存来存放 实际当对象内容,引用地址指向堆内存地址,记住所有堆应用类型都是Object对象延展 如图:
在这里插入图片描述
给 对象增加一个属性

let a ={
	name:'Li'
}

此时 栈内存是不会有任何变化的,只是通过引用改变堆里面的内容,如图:
在这里插入图片描述
继续加代码,到复制到变量b

let a ={
	name:'Li'
}
let b = a;

此时把a 赋值给b完成复制,此时 在栈内存增加一个存储空间保存 变量b 和 a赋值给b到引用地址,也就是a和b 指向到堆是同一个地址如图:
在这里插入图片描述

继续操作代码,改变一下b这个变量的值

let a ={
	name:'Li'
}
let b = a;
b.name='zhang'
b.age ='18'
console.log(a.name); //zhang
console.log(a.age); // 18

依据代码结果,发现,操作b对象,但是a对象跟着改变,原因就是因为指向了同一个堆地址
在这里插入图片描述

基本解释完了 JavaScript中的两种类型的存储过程,
那么回答开篇的问题是不是很简单了。B同学和A同学操作的是对象一个应用类型,指向的是同一个堆内存地址,因此B同学更改了A同学的数据。知道吗?

类型检测

很多情况数据来源自己并不知道,比如插件带过来的,服务端返回等等,这时候就需要去检测,包括自己写共方法的时候需要的校验

1. typeof

typeof :经常用来检测一个变量是不是最基本的数据类型

let a;
console.log(typeof a); // undefined
let b=null;
console.log(typeof b); // object 

let c = 1;
console.log(typeof c); //number

let d = 'li';
console.log(typeof d); // string

let f = true;
console.log(typeof f); //boolean

let g = Symbol('1');
console.log(typeof g); //symbol

let h = {};
console.log(typeof h); // object

除了最后一个引用对象,基础类型的检测,每一个都有一个对应的 小写字符,返回 Null 这类型比较特殊可以参考 《null 和 undefined 故事》

怎么用呢
比如判断是不是一个Boolean 值,依据typeof 加上 返回的字符串可以轻松搞定

typeof f === 'boolean' // true

if(typeof f === 'boolean'){
	// 干点啥
}else{
	// 不干点啥
}

怎么确定上面代码 b 变量 是null 而不是object ,可以用如下组合方法

!b && typeof b==='object'// true 
2. instanceof

instanceof 用来判断某个构造函数的 prototype 属性所指向的对象是否存在于另外一个要检测对象的原型链上
简单说就是判断一个引用类型的变量具体是不是某种类型的对象

({}) instanceof Object  // true
(function(){}) instanceof Function  // true

instanceof 判断并不是非常准确比如

([]) instanceof Object  // true
([]) instanceof  Array  // true

以后在详细的写,这篇主要记录 基础类型和引用类型,以及基本类型检测

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值