当前位置: 移动技术网 > IT编程>开发语言>JavaScript > js数据类型的区别和使用讲解

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

2019年03月20日  | 移动技术网IT编程  | 我要评论
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

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网