JavaScript内核基础:数据类型、变量与运算符详解

作者:代码拾荒

时间:2026-10-26

浏览:606

本文深入解析JavaScript内核基础,涵盖基本数据类型(字符串、数值、布尔值)与对象类型(对象、数组、函数)的区别。详细阐述变量作用域机制及基本类型与引用类型的赋值差异,并对比==与===运算符在类型转换与值比较上的不同规则,帮助开发者避免常见陷阱。

文章目录

JavaScript内核基础概述

本章重点介绍JavaScript中的数据类型(包括基本类型与引用类型)、变量(涉及作用域概念)以及操作符(主要针对那些字面含义模糊但实际常用的操作符)。鉴于JavaScript中“一切皆对象”的特性,掌握这些基础概念后,读者将能更轻松地理解作用域、调用对象、闭包和柯里化(currying)等进阶概念。



数据类型

拥有其他编程语言经验的读者应清楚,在C或Java等语言中,数据具有明确的类型。例如,表示用户名的属性为字符串,雇员年龄为数字,而UI开关按钮的数据模型为布尔值。数字类型还可细分为浮点数、整型数,整型数又可分为长整型和短整型。总之,这些概念均指代语言中数据值的类型。



JavaScript的数据类型分为两类:基本数据类型和对象类型。其中,对象类型包含对象、数组以及函数(事实上,函数和数组本质上也是对象,这将在后续章节详细阐述)。



1.1.1 基本数据类型

JavaScript数据类型详解

JavaScript包含三种基本数据类型:字符串(String)、数值(Number)和布尔值(Boolean)。以下是一些简单的示例:



代码如下:

var str = "Hello, world";//字符串
var i = 10;//整型数
var f = 2.3;//浮点数
var b = true;//布尔值

我们可以分别查看变量的值及其类型:


代码如下:

 
print(str);
print(i);
print(f);
print(b);
print(typeof str);
print(typeof i);
print(typeof f);
print(typeof b);

请注意,此处使用的print()函数是Rhino解释器顶层对象的方法,用于打印字符串。通常情况下,在客户端开发中,程序员多使用alert()执行类似操作,alert()是浏览器中JavaScript解释器顶层对象(window)的一个方法。



Hello, world


10


2.3


true



string


number


number


Boolean


在JavaScript中,所有的数字,无论是整型还是浮点型,均属于“数字”基本类型。typeof是一个一元操作符,将在本章的小节中专门讲解。



1.1.2 对象类型

这里提到的“对象”并非指具体的对象实例,而是指一种类型。我们将在第三章详细讨论对象,此处的对象类型包括:对象(属性的集合,即键值对的散列表)、数组(有序的列表)以及函数(包含可执行的代码)。



对象类型是一种复合数据类型,其基本元素由基本数据类型组成,当然不限于基本类型。例如,对象类型中的值可以是其他对象类型的实例。我们通过例子来说明:



代码如下:

var str = "Hello, world";
var obj = new Object();
obj.str = str;
obj.num = 2.3;
var array = new Array("foo", "bar", "zoo");
var func = function(){
print("I am a function here");
}

可以看到,对象具有属性,如obj.str和obj.num,这些属性的值可以是基本类型。事实上,它们还可以更复杂,我们来看看它们的类型:


代码如下:

 
print(typeof obj);
print(typeof array);
print(typeof func);
//将打印出
object
object
function

读者可能会对print(typeof array)打印出object感到奇怪。事实上,对象和数组的界限并不那么明显(事实上它们属于同一类型),但它们的行为却非常不同。本书的后续章节将这两种重要的数据类型分别介绍。



2.1.3 两者之间的转换

基本类型与对象类型的转换机制

类似于Java中基本数据类型的自动装箱和拆箱,JavaScript也有类似的动作。基本数据类型在进行一些运算时,会临时包装一个对象,运算完成后,又自动释放该对象。我们可以通过几个例子来说明:



代码如下:

var str = "JavaScript Kernal";
print(str.length);//打印17

str是一个字符串,通过typeof运算符可知其type为“string”,而:



代码如下:

var str2 = new String("JavaScript Kernal");
print(typeof str2);

可知,str2的type为“object”,即这两者并不相同。那么为什么可以使用str.length来获取str的长度呢?事实上,当使用str.length时,JavaScript会自动包装一个临时的String对象,内容为str的内容,然后获取该对象的length属性,最后,这个临时的对象将被释放。



而将对象转换为基本类型则是通过这样的方式:通过调用对象的valueOf()方法来取得对象的值,如果与上下文的类型匹配,则使用该值。如果valueOf取不到值的话,则需要调用对象的toString()方法,而如果上下文为数值型,则又需要将此字符串转换为数值。由于JavaScript是弱类型的,所以JavaScript引擎需要根据上下文来“猜测”对象的类型,这就使得JavaScript的效率比编译型的语言要差一些。



valueOf()的作用是将一个对象的值转换成一种合乎上下文需求的基本类型,toString()则名副其实,可以打印出对象对应的字符串,当然前提是你已经“重载”了Object的toString()方法。



事实上,这种转换规则会导致很多问题。例如,所有的非空对象在布尔值环境下,都会被转成true,比如:


代码如下:

 
function convertTest(){
if(new Boolean(false) && new Object() &&
new String("") && new Array()){
print("convert to boolean")
} 
} 
convertTest();//convert to Boolean

初学者容易被JavaScript中的类型转换规则搞晕,很多情况下会觉得那种写法看着非常别扭。其实只需要掌握了规则,这些古怪的写法会大大提高代码的性能,我们通过例子来学习这些规则:


代码如下:

 
var x = 3;
var y = x + "2";// => 32
var z = x + 2;// => 5
print(y);
print(z);

通常可以在JS代码中发现这样的代码:



代码如下:

if(datamodel.item){
//do something...
}else{
datamodel.item = new Item();
}

这种写法事实上具有更深层次的含义:



应该注意到,datamodel.item是一个对象(字符串、数字等),而if需要一个boolean型的表达式,所以这里进行了类型转换。在JavaScript中,如果上下文需要boolean型的值,则引擎会自动将对象转换为boolean类型。转换规则为,如果该对象非空,则转换为true,否则为false。因此我们可以采取这种简写的形式。



而在传统的编程语言(强类型)中,我们则需要:



代码如下:

if(datamodel.item != null){
//do something...
}else{
datamodel.item = new Item();
}


2.1.4类型的判断

变量与变量作用域

前面讲到JavaScript特性的时候,我们说过,JavaScript是一个弱类型的语言。但是有时我们需要知道变量在运行时的类型,比如,一个函数的参数预期为另一个函数:



代码如下:

function handleMessage(message, handle){
return handle(message);
}

当调用handleMessage的函数传递的handle不是一个函数时,JavaScript引擎会报错,因此我们有必要在调用之前进行判断:



代码如下:

function handleMessage(message, handle){
if(typeof handle == "function"){
return handle(message);
}else{
throw new Error("the 2nd argument should be a function");
} 
}

但是,typeof并不总是有效的,比如下面这种情况:



代码如下:

var obj = {};
var array = ["one", "two", "three", "four"];
print(typeof obj);//object
print(typeof array); //object

运行结果显示,对象obj和数组array的typeof值均为“object”,这样我们就无法准确判断了。这时候,可以通过调用instanceof来进行进一步的判断:



print(obj instanceof Array);//false


print(array instanceof Array);//true


第一行代码返回false,第二行则返回true。因此,我们可以将typeof操作符和instanceof操作符结合起来进行判断。



2.2 变量

变量,即通过一个名字将一个值关联起来,以后通过变量就可以引用到该值。例如:



代码如下:

var str = "Hello, World";
var num = 2.345;

当我们下一次要引用“Hello, World”这个串进行某项操作时,我们只需要使用变量str即可。同样,我们可以用10*num来表示10*2.345。变量的作用就是将值“存储”在这个变量上。



2.2.1基本类型和引用类型



在上一小节,我们介绍了JavaScript中的数据类型,其中基本类型如数字、布尔值,它们在内存中都有固定的大小,我们通过变量来直接访问基本类型的数据。而对于引用类型,如对象、数组和函数,由于它们的大小在原则上是不受任何限制的,故我们通过对其引用的访问来访问它们本身,引用本身是一个地址,即指向真实存储复杂对象的位置。



基本类型和引用类型的区别是比较明显的,我们来看几个例子:



代码如下:

var x = 1;//数字x,基本类型
var y = x;//数字y,基本类型
print(x);
print(y);
x = 2;//修改x的值
print(x);//x的值变为2
print(y);//y的值不会变化

运行结果如下:



1



1



2



1



这样的运行结果应该在你的意料之内,没有什么特别之处。我们再来看看引用类型的例子,由于数组的长度非固定,可以动态增删,因此数组为引用类型:



代码如下:

var array = [1,2,3,4,5];
var arrayRef = array;
array.push(6);
print(arrayRef);

引用指向的是地址,也就是说,引用不会指向引用本身,而是指向该引用所对应的实际对象。因此通过修改array指向的数组,则arrayRef指向的是同一个对象,因此运行效果如下:



1,2,3,4,5,6



2.2.2变量的作用域



变量被定义的区域即为其作用域。全局变量具有全局作用域;局部变量,比如声明在函数内部的变量则具有局部作用域,在函数的外部是不能直接访问的。例如:



代码如下:

var variable = "out";
function func(){
var variable = "in";
print(variable);//打印“in”
} 
func();
print(variable);//打印“out”

应该注意的是,在函数内var关键字是必须的。如果使用了变量而没有写var关键字,则默认的操作是对全局对象的。例如:



代码如下:

var variable = "out";
function func(){
variable = "in";//注意此variable前没有var关键字
print(variable);
} 
func();
print(variable);//全局的变量variable被修改

由于函数func中使用variable而没有关键字var,则默认是对全局对象variable属性做的操作(修改variable的值为in),因此此段代码会打印:



in



in





2.3运算符

JavaScript运算符解析

运算符通常是容易被忽略的一个内容,但是一些比较古怪的语法现象仍然可能需要用到运算符的结合率或者其作用来进行解释。JavaScript中,运算符是一定需要注意的地方,有很多具有JS编程经验的人仍然免不了被搞得晕头转向。



我们在这一节主要讲解这样几个运算符:



2.3.1中括号运算符([])



[]运算符可用在数组对象和对象上,从数组中按下标取值:



代码如下:

var array = ["one", "two", "three", "four"];
array[0]

而[]同样可以作用于对象。一般而言,对象中的属性的值是通过点(.)运算符来取值,如:



代码如下:

var object = {
field : "self",
printInfo : function(){
print(this.field);
} 
} 
object.field;
object.printInfo();

但是考虑到这样一种情况,我们在遍历一个对象的时候,对其中的属性的键(key)是一无所知的,我们怎么通过点(.)来访问呢?这时候我们就可以使用[]运算符:



代码如下:

for(var key in object){
print(key + ":" + object[key]);
}

运行结果如下:



field:slef


printInfo:function (){


print(this.field);


}

2.3.2点运算符(.)



点运算符的左边为一个对象(属性的集合),右边为属性名。应该注意的是,右边的值除了作为左边的对象的属性外,同时还可能是它自己的右边的值的对象:



代码如下:

var object = {
field : "self",
printInfo : function(){
print(this.field);
}, 
outter:{
inner : "inner text",
printInnerText : function(){
print(this.inner);
} 
} 
} 
object.outter.printInnerText();

这个例子中,outter作为object的属性,同时又是printInnerText()的对象。



2.3.3 == 和 === 以及 != 和 !==



运算符==读作相等,而运算符===则读作等同。这两种运算符操作都是在JavaScript代码中经常见到的,但是意义则不完全相同。简而言之,相等操作符会对两边的操作数做类型转换,而等同则不会。我们还是通过例子来说明:



print(1 == true);


print(1 === true);


print("" == false);


print("" === false);



print(null == undefined);


print(null === undefined);


运行结果如下:



代码如下:

true
false
true
false
true
false

相等和等同运算符的规则分别如下:



相等运算符



如果操作数具有相同的类型,则判断其等同性,如果两个操作数的值相等,则返回true(相等),否则返回false(不相等)。



如果操作数的类型不同,则按照这样的情况来判断:



◆ null和undefined相等



◆ 其中一个是数字,另一个是字符串,则将字符串转换为数字,在做比较



◆ 其中一个是true,先转换成1(false则转换为0)在做比较



◆ 如果一个值是对象,另一个是数字/字符串,则将对象转换为原始值(通过toString()或者valueOf()方法)



◆ 其他情况,则直接返回false



等同运算符



如果操作数的类型不同,则不进行值的判断,直接返回false



如果操作数的类型相同,分下列情况来判断:



◆ 都是数字的情况,如果值相同,则两者等同(有一个例外,就是NaN,NaN与其本身也不相等),否则不等同



◆ 都是字符串的情况,与其他程序设计语言一样,如果串的值不等,则不等同,否则等同



◆ 都是布尔值,且值均为true/false,则等同,否则不等同



◆ 如果两个操作数引用同一个对象(数组,函数),则两者完全等同,否则不等同



◆ 如果两个操作数均为null/undefined,则等同,否则不等同



例如:



代码如下:

var obj = {
id : "self",
name : "object"
}; 
var oa = obj;
var ob = obj;
print(oa == ob);
print(oa === ob);

会返回:



true



true



再来看一个对象的例子:



代码如下:

var obj1 = {
id : "self",
name : "object",
toString : function(){
return "object 1";
} 
} 
var obj2 = "object 1";
print(obj1 == obj2);
print(obj1 === obj2);

返回值为:



true



false



obj1是一个对象,而obj2是一个结构与之完全不同的字符串。而如果用相等操作符来判断,则两者是完全相同的,因为obj1重载了顶层对象的toString()方法。



而!=不等和!==不等同,则与==/!==相反。因此,在JavaScript中,使用相等/等同,不等/不等同的时候,一定要注意类型的转换,这里推荐使用等同/不等同来进行判断,这样可以避免一些难以调试的bug。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章