欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

js数据类型的区别和使用讲解

程序员文章站 2022-06-29 11:13:42
javascript的数据类型可以分为两种:原始类型和引用类型 原始类型也称为基本类型或简单类型,因为其占据空间固定,是简单的数据段,为了便于提升变量查询速度,将其存储在栈(st...

javascript的数据类型可以分为两种:原始类型和引用类型

原始类型也称为基本类型或简单类型,因为其占据空间固定,是简单的数据段,为了便于提升变量查询速度,将其存储在栈(stack)中(按值访问)。

引用类型由于其值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此其存储在堆(heap)中,存储在变量处的值是一个指针,指向存储对象的内存处(按址访问)

[注意]对于引用类型的值,可以为其添加属性和方法,也可以改变和删除其属性和方法;但基本类型不可以添加属性和方法

基本数据类型: undefined、null、string、number、boolean。(按值访问,可操作保存在变量中的实际的值。基本类型值指的是简单的数据段。)

引用类型: object、array、regexp、date、function(当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。引用类型值指那些可能为多个值构成的对象。)

区别

基本类型值不允许添加属性和方法,引用类型可以

//基本类型
let str = 'bbj';
str.age = 10;
str.say = function () {
    console.log(112);
};
console.log(str);
console.log(str.age);
console.log(str.say);

//bbj
//undefined
//undefined
//引用类型
let str1 = new object();
str1.age = 18;
str1.say1 = function () {
    console.log(this.age);
}

console.log(str1.age);
str1.say1();

//18
//18

2. 在复制变量值时,基本类型会在变量对象上创建一个新值,再复制给新变量。此后,两个变量的任何操作都不会影响到对方;而引用类型是将存储在变量对象的值复制一份给新变量,但是两个变量的值都指向存储在堆中的一个对象,也就是说,其实他们引用了同一个对象,改变其中一个变量就会影响到另一个变量。

//基本类型
let str = 'bbj';
let str1 = str;
str = 'ddj';
console.log(str);
console.log(str1);

//ddj
//bbj
//引用类型

//1.对其中一个变量直接赋值不会影响到另一个变量(并未操作引用的对象)
let str = ['bbj', 'ddj'];
let str1 = str;
str = ['bbj', 'ddj', 'ggj'];
console.log(str);
console.log(str1);

// [ 'bbj', 'ddj', 'ggj' ]
// [ 'bbj', 'ddj' ]

//2.使用push(操作了引用的对象)
let str = ['bbj', 'ddj'];
let str1 = str;
str.push('aaj');
console.log(str);
console.log(str1);

//[ 'bbj', 'ddj', 'aaj' ]
//[ 'bbj', 'ddj', 'aaj' ]

//3.应该是str赋值的时候相当于重新创建变量
let str = ['bbj', 'ddj'];
let str1 = str;
str = ['bbj', 'ddj', 'ggj'];
str.push('aaj');
console.log(str);
console.log(str1);

//[ 'bbj', 'ddj', 'ggj', 'aaj' ]
//[ 'bbj', 'ddj' ]

3.参数的传递

ecmascript所有的函数的参数都是按值传递的。函数外部的值赋值给函数内部的参数,与一个变量复制到另一个变量一样。基本类型值的传递和基本类型一样,引用类型的传递和引用类型的复制一样。

let num = 10;
function add (num) {
    num += 10;
    return num; 
}
console.log(num);
console.log(add(num));

// 10
// 20
let person = {};
function setname (obj) {
    obj.name = 'bbj';
    obj = new object();
    obj.name = 'ddj';
    return obj;
}
setname(person);
console.log(person.name);
console.log(setname(person).name);

// bbj
// ddj

即使在函数内部修改了参数的值,但原始的引用(person对象,存储在堆上)仍保持不变。具体传递的obj不是指针而是指针引用的对象(副本copy)。实际上,当在函数内部重写obj时,这个变量的引用的就是一个局部对象了,而这个局部对象会在函数执行完毕后立即被销毁。

扩展:值传递与引用传递

值传递和引用传递,属于函数调用时参数的求值策略(evaluation strategy),这是对调用函数时,求值和传值的方式的描述,而非传递的内容的类型(内容指:是值类型还是引用类型,是值还是指针)。值类型/引用类型,是用于区分两种内存分配方式,值类型在调用栈上分配,引用类型在堆上分配。一个描述内存分配方式,一个描述参数求值策略,两者之间无任何依赖或约束关系。

区别 值传递 引用传递
根本区别 会创建副本(copy) 不创建副本
所以 函数中无法改变原始对象 函数中可以改变原始对象

对于值传递,无论是值类型还是引用类型,都会在调用栈上创建一个副本,不同是,对于值类型而言,这个副本就是整个原始值的复制。而对于引用类型而言,由于引用类型的实例在堆中,在栈上只有它的一个引用(一般情况下是指针),其副本也只是这个引用的复制,而不是整个原始对象的复制。

这便引出了值类型和引用类型(这不是在说值传递)的最大区别:值类型用做参数会被复制,但是很多人误以为这个区别是值类型的特性。其实这是值传递带来的效果,和值类型本身没有关系。只是最终结果是这样。

4.两种类型的比较

let a = '123';
let b = '123';
console.log(a == b);
console.log(a === b);

// true
// true

let aa = {};
let bb = {};
console.log(aa == bb);
console.log(aa === bb);

// false
// false

undefined

undefined类型只有一个值,即特殊的undefined。在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined。不过,一般建议尽量给变量初始化,但是在早期的js版本中是没有规定undefined这个值的,所以在有些框架中为了兼容旧版,会给window对象添加undefined值。

出现场景

[1]已声明未赋值的变量

[2]获取对象不存在的属性

[3]无返回值的函数的执行结果

[4]函数的参数没有传入

[5]void(expression)

null

null类型只有一个值,就是null。逻辑角度看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null时会返回object的原因。

如果定义的变量将用于保存对象,最好将该变量初始化为null。这样一来,只要直接检测null值就可以知道相应的变量是否已经保存了一个对象的引用了。实际上undefined值是派生自null值的,所以undefined == null

尽管null和undefined有这样的关系,但它们的用途完全不同。无论在什么情况下都没有必要把一个变量的值显式地设置为undefined,可是同样的规则对null却不适用。换句话说,只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null值。这样做不仅可以体现null作为空对象指针的惯例,而且也有助于进一步区分null和undefined。

[注意]null是空对象指针,而[]是空数组,{}是空对象,三者不相同

出现场景

对象不存在时

boolean

该类型只有两个字面值:true和false。这两个值与数字值不是一回事,因此true不一定等于1,而false也不一定等于0。

虽然boolean类型的字面值只有两个,但javascript中所有类型的值都有与这两个boolean值等价的值。要将一个值转换为其对应的boolean值,可以调用类型转换函数boolean()

各种数据类型及其对象的转换规则

数据类型 转换为true的值 转换为false的值
boolean true false
string 任何非空的字符串 “”(空字符串)
number 任何非0数值(包括无穷大) 0和nan
object 任何对象 null
undefined 不适用 undefined

!!一般用来将后面的表达式强制转换为布尔类型的数据(boolean),也就是只能是true或者false;

两个感叹号的作用就在于,如果明确设置了变量的值(非null/undifined/0/”“等值),结果就会根据变量的实际值来返回,如果没有设置,结果就会返回false。

出现场景

[1]条件语句导致执行的隐士类型转换

[2]字面量或变量定义

number

javascript只有一种数字类型,既可以表示32位的整数,还可以表示64位的浮点数

这种类型用来表示整数和浮点数值,还有一种特殊的数值,即nan(非数值 not a number)。这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如,在其他语言中,任何数值除以0都会导致错误,从而停止代码执行。但在javascript中,任何数值除以0会返回nan,因此不会影响其他代码的执行。

nan本身有两个非同寻常的特点。首先,任何涉及nan的操作(例如nan/10)都会返回nan,这个特点在多步计算中有可能导致问题。其次,nan与任何值都不相等,包括nan本身

isnan()函数,用于判断是否是一个非数字类型。如果传入的参数是一个非数字类型,那么返回true;否则返回false;

isnan()函数,传入一个参数,函数会先将参数转换为数值。

如果参数类型为对象类型,会先调用对象的valueof()方法, 再确定该方法返回的值是否可以转换为数值类型。如果不能,再调用对象的tostring()方法,再确定返回值。

string

string类型有些特殊,因为字符串具有可变的大小,所以显然它不能被直接存储在具有固定大小的变量中。由于效率的原因,我们希望js只复制对字符串的引用,而不是字符串的内容。但是另一方面,字符串在许多方面都和基本类型的表现相似,而字符串是不可变的这一事实(即没法改变一个字符串值的内容),因此可以将字符串看成行为与基本类型相似的不可变引用类型

string类型是javascript中唯一没有固定大小的原始类型

字符串的值是不可变的。要改变一个字符串的值,首先要销毁原来的字符串,再用另一个包含新值的字符串去填充该字符串。

boolean、number、string 这三个是javascript中的基本包装类型,也就是这三个其实是一个构造函数,他们是function的实例,是引用类型,至于这里的string与以上说的string是同名,是因为其实上文说的string是指字符串,这里的string指的是string这个构造函数,上面那么写,是为了更好的理解,因为javascript是松散类型的。

其实string只是string的一个实例,类似于c#中的string,和string.

注意,typeof 变量 如果值是”string” 的话,也就是这个变量是字符串,在javascript中,字符串是基本类型,而在c#或java中,字符串是引用类型,但是javascript中的string是引用类型,因为它是javascript中定义好的基本包装类型,在c#中,string跟string其实是一样的。

javascript三个基本数据类型都有相应的对象类;分别为sring,number,boolean类;

javascript可以灵活的将一种类型的值转换为另一种类型;

当我们在对象环境中使用字符串时,即当我们试图访问这个字符串的属性或方法时;

javascript会为这个字符串值内部地创建一个string包装对象;

string对象会暂时代替原始的字符串值,完成我们的访问;

这个被内部创建的string对象是瞬间存在的,它的作用是使我们可以正常访问属性和方法;

string对象在使用过后会被系统丢弃掉;

而原始值并不会被改变;

以上同样适用于数字和布尔值类型;

使用object()函数,任何数字、字符串、布尔值都可以转换为它对应的包装对象;

引用类型

引用类型是一种用于将数据和功能组织在一起的数据结构(也常被成为类),引用类型的值(对象)是引用类型的一个实例。

但是js中没有类的概念,因此引用类型也可以被称为对象定义,因为他们描述的是一类对象所具有的属性和方法。

对象是某个特定引用类型的实例,新对象是使用new操作符后跟一个构造函数来创建的,构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

对象是某个特定引用类型的实例,新对象是使用new操作符后跟一个构造函数来创建的,构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

ecmascript提供了很多原生引用类型(如:object),以便开发人员用以实现常见的计算任务

object

我们看到的大部分引用类型都是object类型的实例,object的实例本身不具备很多功能,单对于在应用程序中存储和传输数据而言,是非常理想的选择。

创建object类型

直接new

语法: new操作符跟obeject构造函数

let obj = new object();

对象字面量

语法:花括号,里面的属性用键值对形式,每个属性用逗号隔开,最后一个属性不用逗号。

let person = {
    name: 'bbj',
    age: 18,
    sex: 'n'
}

访问属性

通过点表示法

let person = new object();
person.name = 'bbj';
方括号
var o = new object();
o.age = 22;
console.log(o["age"]);  //22
//方括号可以用变量来访问属性
var otherage = "age";
console.log(o[otherage]);  //22

array

创建数组

new array()

//创建一个空数组
var arr1 = new array();
//创建一个长度为10的空数组,
var arr2 = new array(10);
//创建一个包含一个字符串good的数组
var arr3 = new array("good");

数组字面量

var cars = ["bmw","benz","ferrari"];
//注意,创建数组不要留空项。浏览器兼容性问题,ie8或以前版本会认为这是有3项,下面这种不建议。
let nums = [1,2,];

访问数组

cars[0];
//下标访问

date

创建对象

new date();

regexg

es通过regexg类型来支持正则表达式,语法:var e = /pattern/flag

其中pattern表示正则表达式,flag表示标识,标识可以一个或多个

flags 说明
g 全局模式,该模式应用于所有的字符串
i 不区分大小写模式,确定匹配项时忽略模式与字符串的大小写
m 多行模式,一行文本到尾后还会查下一行的字符串,如果有的话

创建regexg对象

字面量方式

var p1 = /at/g;  //匹配所有"at" 的实例
var p2 = /[bc]at/i  //匹配第一个bat或cat,不区分大小写
var p3 = /.at/gi  //匹配所有at结尾的组合,不区分大小写

//正则中如果想在字符中包含元字符需要对其进行转义
//这里和p3不同的是对.这个元字符进行转义,用\符号转义
var p4 = /\.at/gi;  //这里的意思是匹配所有的".at",不区分大小写。

使用new regexg构造函数

//regexg() 接受两个参数,一个是正则表达式,一个是标志
var pattern1 = new regexg("at","gi");
//pattern1和pattern2完全等价
var pattern2 = /at/gi;

function

定义函数

//1.函数声明
function getname(){
    var name = 'ry-yuan';
    return name;
}
//2.函数表达式
var getage = function(){
    var age = 100;
    return age;
}
//3.使用function构造函数,前面1-n个是参数,最后一个参数的是函数体,这种不推荐使用。
var sum = new function("num","return num");

函数声明和函数表达式的区别

//函数声明,我们先运行sayhello,但是不会报错,因为函数声明已经在解析时被提到顶部
sayhello();  //hello everyone
function sayhello(){
    console.log("hello everyone");
}


//函数表达式,用var定义函数名.用函数表达式,不会提升,所以先运行saybye就会找不到函数体
saybey();  //报错 typeerror: saybye is not a function
var saybye = function(){
    console.log("bye bye");
}

函数名是指向函数的指针

一个函数在js种就是一个function的实例,函数名就是对实例的引用,一个指针,前面的对象中也有说过。那么一个函数就可以有多个函数名了。

//定义一个函数,函数名是sum
var sum = funtion(num1,num2){
    return num1+num2;
}

//讲sum复制给othersum,那么othersum和sum都指向同一个function对象
othersum = sum;

othersum(100,420);  //520
sum(1300+14); //1314

//对sum设置为null,sum变量就不在指向function对象
sum  = null;

//othersum依然能够使用
othersum(1,9); //10

函数没有重载

上面说了,函数名只是函数的指针,函数名是变量一样,重复复制就会覆盖原来的。

在java语言来说,有不同的参数数量也称为重载,但是js中没这种操作

//函数声明,fn为函数名
function fn(num1, num2){
    return num1+ num2;
}
//再来函数声明,fn为函数名
function fn(num){
    return num;
}

//fn只会指向最后一次声明的函数
fn(1,43);  //1

函数像值一样传递

因为函数名本来就是一个变量,所以函数也可以像值一样被传递

//声明一个函数fn1,它可以接受两个参数,一个是函数,一个是值
function fn1(fn,value){
    return (fn(value));
}
//声明一个fn2,接受一个参数
function fn2(val){
    console.log(val+1000);
}
//fn2当成参数传递给fn1
fn1(fn2,24);  //1024