浅析JavaScript基本类型与引用类型

对于 JavaScript 类型,可以简单地概括为:相对于强类型语言来说,它是弱(松散)类型的语言;有基本类型和引用类型,他们是区别是一个有固定空间存在于栈内存中,一个没有固定空间保存在堆内存中并且在栈内存中保存了一个指向实现位置的指针。

市面上很多书都有不小的篇幅在讲。这篇文章会讲几个方面,这些方面可能会需要你对 JavaScript 已经有了一些简单的了解,特别是 JavaScript 的类型。如果还不一解,可以随手拿起一本关于 JavaScript 的书翻翻,再来看本文。

一、基本类型与引用类型

1.基本类型:Undefined / Null / Boolean / Number / String
2.引用类型:Object / Array / Function / Date / RegExp / Error / Map / Set …

为什么引用类型没有枚举完呢,因为这里面你了解这么多就够了,至少在我讲的这篇中这些已经足够。其他的可能很少会用到,甚至像 Map 、Set 这样的也不是所有浏览器都支持。

二、JavaScript 类型的判断

在 JavaScript 有两个 operator 可以用以判断类型。他们是 typeof 和 instanceof,不过圈子很小,它们混的可不是那么好,是出了名的不靠谱。少数情况也是对的,很多情况下是不靠谱的。看看就知道了:

代码如下:

// 靠谱的时候:
typeof 'sofish' // string
new String('sofish') instanceof String // true

// 不靠谱的时候:
typeof [] // object
typeof null // object
'sofish' instanceof String // false

呃~ 可能很多初学的 JavaScript 程序员会因此爆粗口。还大部分人在需要用 JS 的时候已经有了 jQuery 等这样的库,他们都做了封装,让你可以方便地检测类型。当然,事实上要检测也不麻烦,因为那句「在 JavaScript 中,一切都是对象」,当然像很多文档中说到的,undefined 其实和 NaN, Infinity 都只是一个全局属性。你大概知道就可以了。但「对象」可以帮到我们:


代码如下:

/* 检测对象类型
 * @param: obj {JavaScript Object}
 * @param: type {String} 以大写开头的 JS 类型名
 * @return: {Boolean}
 */
function is(obj, type)  {
  return Object.prototype.toString.call(obj).slice(8, -1) === type;
}

这样的话,我们就可以利用 is 这个函数来帮我们搞定类型判断了,并且这个简单的函数有很好的兼容性,可以用到你的项目中去。情况如:


代码如下:

is('sofish', 'String') // true
is(null, 'Null') // true
is(new Set(), 'Set') // true

三、JavaScript 类型的转换

在 JavaScript 中,变量(属性)的类型是可以改变的。最常看到的是 String 与 Number 之间的转换。如何把 1 + '2' 变成 12 呢?这里面有必要理解一下 + 号 operator,它是一个数学运算符,同时也是 JavaScript 中的字符串连字符。所以新手会经常会看到一个有趣的现象,当使用 + 号的时候有时计算出来的不是想要的,而用 - 号却总能得到「正确」的答案。


代码如下:

1 + '2' // '12'
1 + (+'2') // 3
1 - '2' // -1

这里面其实就是因为 + 的双重角色导致的。在上面的代码中,可以注意到第二条表达式在 String 前面运用了一个 + 号,强制把它的类转换为 Number。而对于 JavaScript 的类型转换理解,大多数情况下,只要理解 + 具有双重角色就可以了。其他的可以理解类,类似都是可以用赋值/重载来修改的,甚至包括 Error:

代码如下:

var err = new Error();
console.log(err instanceof Error); // true

err = 'sofish';
console.log(err); // 'sofish'

四、JavaScript 引用类型

这一点是本文的一个难点。相于基本类型,引用可以为其添加属性和方法;引用类似的值是一个引用,把一个引用类型的值赋给一个变量,他们所指向的是同一存储在堆内存中的值。变量(属性)可以重载,但复制会是一件很有趣的事情,后面我们会详细来说。

1. 添加属性和方法

下面的代码我们将会看到,假设我们对一个基本类似赋值,它并不会报错,但在获取的时候却是失效的:


代码如下:

var arr = [1,2,3];
arr.hello = 'world';
console.log(arr.hello); // 'world'

var str = 'sofish';
str.hello = 'world';
console.log(str.hello); // undefined

2. 引用类型值的操作

由于引用类型存储在栈内存中的是一个引用,那么当我们指向的同一个原始的值,对值的操作将会影响所有引用;这里有一个例是,重新赋值(并非对值的直接操作)会重新创建一个对象,并不会改变原始值。比如:


代码如下:

var arr = [1,2,3], sofish = arr;
sofish.push('hello world');
console.log(arr); // [1, 2, 3, 'hello world']

// 非相同类型
sofish = ['not a fish']; // 当 sofish 类似改变时,不会改变原始值
console.log(arr);// [1, 2, 3, 'hello world']

3. 引用类型值的复制

对原始值的操作会影响所有引用,而这不一定是我们想要的,有时候我们需要复制一个全新的对象,操作的时候不影响其他引用。而一般情况也,像 Date / Function / RegExp … 都很少有具体的操作,主要是像 Array 和 Object 会有添加项、属性等操作。所以我们主要需要理解的是如何复制 Array 和 Object 对象。

3.1 数组的复制

在 Array 对象中,存在 slice 方法返回一个截取的数组,在 ES5 中 filter 等也返回一个新的数组,那么我们可能利用这个方法来进行复制。


代码如下:

var arr = [1, 2, 3];
var sofish = arr.slice();

// 对新的数组进行操作并不会影响到原始数组
sofish.push('hello world');
console.log(arr); // [1, 2, 3]

3.2 对象的复制

在 Array 的复制中我们使用的是 slice 方法,实际上对于 Array 和 Object 中都可以利用 for ... in 循环来进行遍历并赋值来进行复制。


代码如下:

var obj = { name: 'sofish' }, sofish = {}, p;
for (p in obj) sofish[p] = obj[p];

// 对新的对象操作并不会影响原始值
sofish.say = function() {};
console.log(obj); // { name: 'sofish' }

3.3 Shadow / Deep Copy

像上面的操作,就是我们常说的浅拷贝(Shadow Copy)。不过在 Array 和 Object 都可以有多层(维),像这样的拷贝只考虑到最上面一层的值,在可能存在的值中的 Array 和 Object 都还是指向了原始对象。比如:


代码如下:

var arr = [1, { bio: 'not a fish' } ], sofish = [], p;
for(p in arr) {
  sofish[p] = arr[p];
}

// 对 `sofish` 中包含的对象 `cat` 的操作会影响原始值
sofish[1].bio = 'hackable';
console.log(arr);//  [1, cat: { bio: 'hackable' } ]

那么如何做呢?来一个 copy() 函数解决这个问题:


代码如下:

/* 复制对象
 * @param: obj {JavaScript Object} 原始对象
 * @param: isDeep {Boolean} 是否为深拷贝
 * @return: {JavaScript Object} 返回一个新的对象
 */
function copy(obj, isDeep) {
  var ret = obj.slice ? [] : {}, p, prop;
  // 配合 is 函数使用
  if(!isDeep && is(obj, 'Array')) return obj.slice();
  for(p in obj) {
    if(!obj.hasOwnProperty(p)) continue;
    prop = obj[p];
    ret[p] = (is(prop, 'Object') || is(prop, 'Array')) ?
      copy(prop, isDeep) : prop;
  }
  return ret;
}

这样,我们就可以通过 copy(obj, isDeep) 函数来复制一个 Array 或者 Object 。可以测试一下:


代码如下:

var arr = [1, {bio: 'not a fish'}];
var sofish = copy(arr);

// 浅拷贝对于第一层的操作不影响原始值,但影响第二层
sofish.push('cat');
console.log(arr); //  [1, {bio: 'not a fish'}]
sofish[1].bio = 'hello world';
console.log(arr) //  [1, {bio: 'hello world'}]

// 深拷贝则不会影响原始值
sofish = copy(arr, 1);
sofish[1].bio = 'foo or bar';
console.log(arr); // [1, {bio: 'hello world'}]

到此。你基本上要了解的关于类型的比较难的点,应该是都基本了解了。当然,复制是最麻烦的一个点,除了经常需要操作的 Array 和 Object 来说,还有 Date / Function / RegExp 的复制。

(0)

相关推荐

  • JavaScript中值类型与引用类型实例说明

    复制代码 代码如下: <script type="text/javascript"> var str = 'abced'; var obj = new String(str); function newToString() { return 'hello,world'; } function func(val) { val.toString = newToString; } func(str); //ps:实际只传入了str的值,因此对它的toString修改是无意义的 传

  • JavaScript基本数据类型及值类型和引用类型

    在JavaScript中四种基本的数据类型:数值(整数和实数).字符串型(用""号或''括起来的字符或数值).布尔型(使True或False表示)和空值.在JavaScript的基本类型中的数据可以是常量,也可以变量.由于JavaScript采用弱类型的形式,因而一个数据的变量或常量不必首先作声明,而是在使用或赋值时确定其数据的类型的.当然也可以先声明该数据的类型,它是通过在赋值时自动说明其数据类型的. javascript中的变量 javascript中的变量只是一个占位符,前言已经

  • JavaScript数据类型之基本类型和引用类型的值

    ECMAScript变量包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值是简单的数据段,而引用类型值指那些可能由多个值构成的对象. 在将一个值赋给变量时,解析器必须确定这个值是基本类型还是引用类型.基本类型包括如Undefined.Null.Boolean.Number和String,这5种基本类型数据类型是按值访问的,因此可以操作保存在变量中的实际的值:引用类型类型的值是保存在内存中的对象.与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内

  • 跟我学习javascript的基本类型和引用类型

    一.基本类型和引用类型 基本的数据类型有5个:undefined,boolean,number,string,null typeof null; //"object" typeof undefined; //"undefined" typeof 1; //"number" typeof false //"boolean" typeof "1" //"string" (令人困惑的是,对nu

  • 详解JavaScript基本类型和引用类型

    一.值的类型        早在介绍JS的数据类型的时候就提到过基本类型和引用类型,不过在说两种类型之前,我们先来了解一下变量的值的类型.在ECMAScript中,变量可以存在两种类型的值,即原始值和引用值. (1)原始值        存储在栈中的简单数据段,也就是说,它们的值直接存储在变量访问的位置. (2)引用值        存储在堆中的对象,也就是说,存储在变量处的值是一个指针,指向存储对象的内存处.        为变量赋值时,ECMAScript的解释程序必须判断该值是原始类型,还

  • js中的值类型和引用类型小结 文字说明与实例

    一.拥抱JavaScript 曾经名不经传的JavaScript随着AJAX的流行而身价倍增,现在JavaScript不再仅仅是WEB开发中一个可有可无的辅助工具,甚至有了专门属于它的职位"JavaScript工程师",那怕你仅仅是一名WEB后台开发程序员,你都必须了解JavaScript,至少在一些相关招聘职位要求上你可以看到"熟悉JavaScript优先"的字眼.甚至我还要告诉你,你将可以用JavaScript开发桌面软件,这得益于Adobe AIR的另外一种开

  • JS正则表达式匹配检测各种数值类型(数字验证)

    验证数字的正则表达式集 验证数字:^[0-9]*$ 验证n位的数字:^\d{n}$ 验证至少n位数字:^\d{n,}$ 验证m-n位的数字:^\d{m,n}$ 验证零和非零开头的数字:^(0|[1-9][0-9]*)$ 验证有两位小数的正实数:^[0-9]+(.[0-9]{2})?$ 验证有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$ 验证非零的正整数:^\+?[1-9][0-9]*$ 验证非零的负整数:^\-[1-9][0-9]*$ 验证非负整数(正整数 + 0) ^\d

  • javascript中基本类型和引用类型的区别分析

    基本类型和引用类型 ECMAScript包含两个不同类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段:引用类型值指由多个值构成的对象.当我们把变量赋值给一个变量时,解析器首先要做的就是确认这个值是基本类型值还是引用类型值. 常见的五种基本数据类型是: Undifined.Null.Boolean.Number和String.这五种基本数据类型可以直接操作保存在变量中的实际值. 看下面例子: var a = 10; var b = a; b = 20; console.log(a);

  • JavaScript中值类型和引用类型的区别

    JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和布尔值.此外,JavaScript中还有两个特殊的原始值:null和undefined,它们既不是数字也不是字符串,更不是布尔值.它们通常分别代表了各自特殊类型的唯一成员.JavaScript中除了数字.字符串.布尔值.null和undefined之外的就是对象了. JavaScript中值类型和引用类型的区别 JavaScript中的值类型的值是不可变的: 任何方法都无法改变值类型的值.数字.布尔值.

  • JavaScript给input的value赋值引发的关于基本类型值和引用类型值问题

    在自己做东西时,遇见了一个问题.就拿博客园的首页右边的搜索举例吧,用控制台操作. 现在我需要从另外一个地方将数据传给input,让其在一刷新的时候就显示数据. 这不难啊,于是我按照我的理解做了 代码如下: 此时,id为zzk_q的值应该为  测试  ,即input框内应该显示 测试 .但结果.. 咦,为什么没有变呢,不对啊,又来来回回变着法子试一下,还是不行,当然代码基本还是那样子的.突然想起我以前遇见过这样子的问题,仔细回想当时解决的方法(看样子当时没理解透,只是找到方法就过去了),想起来了,

随机推荐