JavaScript 类型浅解

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

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

一、基本类型与引用类型

  • 基本类型:Undefined / Null / Boolean / Number /
    String

  • 引用类型: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 其实和 NaNInfinity 都只是一个全局属性。你大概知道就可以了。但「对象」可以帮到我们:

/* 检测对象类型
* @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 / FunctionRegExp …
都很少有具体的操作,主要是像 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;
// 配合 is 函数使用
if(!isDeep && is(obj, ‘Array‘)) return obj.slice();
for(p in obj) {
var prop = obj[p];
if(!obj.hasOwnProperty(p)) continue;
if(is(prop, ‘Object‘) || is(prop, ‘Array‘)) {
ret[p] = copy(prop, isDeep);
} else {
ret[p] = 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 的复制。

时间: 2024-12-21 09:13:06

JavaScript 类型浅解的相关文章

JavaScript对象类型详解

JavaScript对象类型详解 JavaScrtip有六种数据类型,一种复杂的数据类型(引用类型),即Object对象类型,还有五种简单的数据类型(原始类型):Number.String.Boolean.Undefined和Null.其中,最核心的类型就是对象类型了.同时要注意,简单类型都是不可变的,而对象类型是可变的. 什么是对象 一个对象是一组简单数据类型(有时是引用数据类型)的无序列表,被存储为一系列的名-值对(name-value pairs).这个列表中的每一项被称为 属性(如果是函

javascript事件详解笔记

javascript事件详解笔记: 一.事件流 1.事件流: 描述的是页面中接受事件的顺序,有事件冒泡.事件捕获两种. 2.事件冒泡: 由最具体的元素接收,然后逐级向上传播到最不具体的元素的节点(文档). 3.事件捕获: 最不具体的节点先接收事件,而最具体的节点应该是最后接收事件. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>事件</title&

JavaScript prototype 详解(对prototype 使用的一些讲解)

对JavaScript有一定了解的你,对jquery不陌生吧,那你看jQuery源代码的时候对prototype 也一定有见过,如果对prototype有疑问或者想更深入的去了解与使用它,欢迎你继续往下阅读. 最初的用法是, 为了避免方法在构造器里随机数据被实例化时而产生重复的副本  后来被用在"继承"上面了, 注意, JS语义上是没有继承的, 这里说的是人为的实现.对于下面对JavaScript中类型名称叫做"对象"."函数"."类型

byte和int的浅解

1.byte范围为什么是(-128~127)? --byte是8位 --这里定义2种8位展现形式:1,符号形式(第一眼看符号位,判断正负,然后计算剩下7位的数值).2,存储形式(内存中都是补码存储) --想要知道一个数在内存中如何存储?真值 >> 符号形式 >> 补码 >> 存储形式 --示例1: +8(真值) +8(符号形式):0000 1000,//真值"+8",因为是"+",所以将第一位定义为0,因为值是"8&qu

JavaScript事件详解-zepto的事件实现

zepto的event 可以结合上一篇JavaScript事件详解-原生事件基础(一)综合考虑源码暂且不表,github里还有中文网站都能下到最新版的zepto.整个event模块不长,274行,我们可以看到,整个event模块,事件绑定核心就是on和off,还有一个trigger用来触发,类观察者模式,可以先看看汤姆大叔的深入理解JavaScript系列(32):设计模式之观察者模式,其余皆为实现的处理函数.首先来个demo: $("#btn").on("click&quo

软考之路(1)——浅解网络基础知识

对网络这一块的基础知识理解如下,以图文并茂的形式展出,便于分析和理解.解析与图如下: 物理层: 功能: 提供为建立.维护和拆除物理链路所需的机械.电气.功能和规程的特性: 提供有关在传输介质上传输非结构的位流及物理链路故障检测指示: 为数据链路层提供一个物理连接,以及他们的机械.电气.功能和过程性.如规定使用电缆和接头的类型,传送信号的电压.在这一层,数据还没有被组织,仅作为原始的位流或电气电压处理,单位是位. 物理链路: 这里多次出现物理链路,简单解释物理链路:一条无源的点到点的物理线路段,中

JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 exec方法的返回值 exec方法返回的其实并不是匹配结果字符串,而是一个对象,简单地修改一下execReg函数,来做一个实验就可以印证这一点: function execReg(reg, str) { var result = reg.exec(str); alert(typeof result

JavaScript 对象详解

1 创建对象的方法 最常见的创建对象方法是通过new调用构造函数,此外还有一个方法就是使用Object.create(),将一个原型对象作为参数传递给Object.create也可以创建一个继承了其属性的新对象.但是和使用new创建对象还有一点差别,那就是构造函数不会被调用.所以如果使用这种方法创建一个foo新对象,其foo.f是undefined的: function Foo(z) { this.f = z; } Foo.prototype.add = function(x, y) { ret

面试-1-C#浅解

C#浅解众所周知c#是微软推出的一款完全没面向对象的编程语言,那么对象是什么?在现实生活中人们一提到对象首先想到的就是"情侣"!但是在我们的程序中对象是什么? 在程序中个能够区别于其他事物的独立个体我们称它为对象他是类的一个实例.那么现在问题来了 ,你说对象是类的实例.那类又是什么?好 在程序中类是多个对象的统称他是对象的模版规定了对象所具有的行为(方法)和静态特征(字段).这是有人可能会问"是先有类还是先有对象?"关于这个问题我们就要分情况讨论了第一种情况:在现实