9月阅读周·JavaScript权威指南:类型、值和变量,对象篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读八个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》。
当前阅读周书籍:《JavaScript权威指南》。
全局对象
当JavaScript解释器启动时(或者任何Web浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性:
- 全局属性,比如undefined、Infinity和NaN。
- 全局函数,比如isNaN()、parseInt()和eval()。
- 构造函数,比如Date()、RegExp()、String()、Object()和Array()。
- 全局对象,比如Math和JSON。
全局对象的初始属性并不是保留字,但它们应该当做保留字来对待。可以通过别名"Global"来找到这些全局对象。对于客户端JavaScript来讲,Window对象定义了一些额外的全局属性。
在代码的最顶级——不在任何函数内的JavaScript代码——可以使用JavaScript关键字this来引用全局对象:
var global=this;//定义一个引用全局对象的全局变量
在客户端JavaScript中,在其表示的浏览器窗口中的所有JavaScript代码中,Window对象充当了全局对象。这个全局Window对象有一个属性window引用其自身,它可以代替this来引用全局对象。Window对象定义了核心全局属性,但它也针对Web浏览器和客户端JavaScript定义了一少部分其他全局属性。
当初次创建的时候,全局对象定义了JavaScript中所有的预定义全局值。这个特殊对象同样包含了为程序定义的全局值。如果代码声明了一个全局变量,这个全局变量就是全局对象的一个属性。
包装对象
JavaScript对象是一种复合值:它是属性或已命名值的集合。通过“.”符号来引用属性值。当属性值是一个函数的时候,称其为方法。通过o.m()来调用对象o中的方法。
我们看到字符串也同样具有属性和方法:
var s="hello world!";//一个字符串
var word=s.substring(s.indexOf("")+1,s.length);//使用字符串的属性
字符串既然不是对象,为什么它会有属性呢?只要引用了字符串s的属性,JavaScript就会将字符串值通过调用new String(s)的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会销毁(其实在实现上并不一定创建或销毁这个临时对象,然而整个过程看起来是这样)。
同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象,这些方法的调用均是来自于这个临时对象。null和undefined没有包装对象:访问它们的属性会造成一个类型错误。
看如下代码,思考它们的执行结果:
var s="test";//创建一个字符串
s.len=4;//给它设置一个属性
var t=s.len;//查询这个属性
当运行这段代码时,t的值是undefined。第二行代码创建一个临时字符串对象,并给其len属性赋值为4,随即销毁这个对象。第三行通过原始的(没有被修改过)字符串值创建一个新字符串对象,尝试读取其len属性,这个属性自然不存在,表达式求值结果为undefined。这段代码说明了在读取字符串、数字和布尔值的属性值(或方法)的时候,表现的像对象一样。但如果你试图给其属性赋值,则会忽略这个操作:修改只是发生在临时对象身上,而这个临时对象并未继续保留下来。
存取字符串、数字或布尔值的属性时创建的临时对象称做包装对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。通常,包装对象只是被看做是一种实现细节,而不用特别关注。由于字符串、数字和布尔值的属性都是只读的,并且不能给它们定义新属性,因此你需要明白它们是有别于对象的。
需要注意的是,可通过String(),Number()或Boolean()构造函数来显式创建包装对象:
var s="test",n=1,b=true;//一个字符串、数字和布尔值
var S=new String(s);//一个字符串对象
var N=new Number(n);//一个数值对象
var B=new Boolean(b);//一个布尔对象
JavaScript会在必要时将包装对象转换成原始值,因此上段代码中的对象S、N和B常常——但不总是——表现的和值s、n和b一样。“==”等于运算符将原始值和其包装对象视为相等,但“===”全等运算符将它们视为不等。通过typeof运算符可以看到原始值和其包装对象的不同。
不可变的原始值和可变的对象引用
JavaScript中的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。原始值是不可更改的:任何方法都无法更改(或“突变”)一个原始值。对数字和布尔值来说显然如此——改变数字的值本身就说不通,而对字符串来说就不那么明显了,因为字符串看起来像由字符组成的数组,我们期望可以通过指定索引来修改字符串中的字符。实际上,JavaScript是禁止这样做的。字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。例如:
var s="hello";//定义一个由小写字母组成的文本
s.toUpperCase();//返回"HELLO",但并没有改变s的值
s
//=>"hello":原始字符串的值并未改变
原始值的比较是值的比较:只有在它们的值相等时它们才相等。这对数字、布尔值、null和undefined来说听起来有点儿难懂,并没有其他办法来比较它们。同样,对于字符串来说则并不明显:如果比较两个单独的字符串,当且仅当它们的长度相等且每个索引的字符都相等时,JavaScript才认为它们相等。
对象和原始值不同,首先,它们是可变的——它们的值是可修改的:
var o={x:1};//定义一个对象
o.x=2;//通过修改对象属性值来更改对象
o.y=3;//再次更改这个对象,给它增加一个新属性
var a=[1,2,3]//数组也是可修改的
a[0]=0;//更改数组的一个元素
a[3]=4;//给数组增加一个新元素
对象的比较并非值的比较:即使两个对象包含同样的属性及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。
var o={x:1},p={x:1};//具有相同属性的两个对象
o===p//=>false:两个单独的对象永不相等
var a=[],b=[];//两个单独的空数组
a===b//=>false:两个单独的数组永不相等
我们通常将对象称为引用类型(reference type),以此来和JavaScript的基本类型区分开来。依照术语的叫法,对象值都是引用(reference),对象的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等。
var a=[];//定义一个引用空数组的变量a
var b=a;//变量b引用同一个数组
b[0]=1;//通过变量b来修改引用的数组
a[0]//=>1:变量a也会修改
a===b//=>true:a和b引用同一个数组,因此它们相等
就像你刚看到的如上代码,将对象(或数组)赋值给一个变量,仅仅是赋值的引用值:对象本身并没有复制一次。如果你想得到一个对象或数组的副本,则必须显式复制对象的每个属性或数组的每个元素。下面这个例子则是通过循环来完成数组复制:
function equalArrays(a,b){
if(a.length!=b.length)return false;//两个长度不同的数组不相等
for(var i=0;i<a.length;i++)//循环遍历所有元素
if(a[i]!==b[i])return false;//如果有任意元素不等,则数组不相等
return true;//否则它们相等
}
总结
对象类型——对象、数组和函数。有一类非常重要的对象——全局对象。全局对象(global object)在JavaScript中有着重要的用途:全局对象的属性是全局定义的符号,JavaScript程序可以直接使用。
取字符串、数字或布尔值的属性时创建的临时对象称做包装对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)