JavaScript内核之基本概念


Posted in Javascript onOctober 21, 2011

本章主要讲述JavaScript中的数据类型(基本类型与引用类型),变量(包括变量的作用域),操作符(主要是一些较为常见,但是不容易从字面上理解的操作符)。由于JavaScript中的“一切皆对象”,在掌握了这些基本的概念之后,读者就可以较为轻松的理解诸如作用域,调用对象,闭包,currying等等较难理解的概念了。

数据类型

有程序设计经验的读者肯定知道,在C或者Java这样的语言中,数据是有类型的,比如用以表示用户名的属性是字符串,而一个雇员的年龄则是一个数字,表示UI上的一个开关按钮的数据模型则为布尔值等等,对数字可能还可以细分为浮点数,整型数,整型数又可能分为长整型和短整型,总而言之,它们都表示语言中的数据的值的类型。

JavaScript中的数据类型分为两种:基本数据类型和对象类型,其中对象类型包含对象,数组,以及函数(事实上,函数,数组等也都是对象,这个在后边的章节详述)。

1.1.1 基本数据类型

在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, Wrold”这个串进行某项操作时,我们只需要使用变量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中,运算符是一定需要注意的地方,有很多具有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。

Javascript 相关文章推荐
javascript禁用Tab键脚本实例
Nov 22 Javascript
require.js的用法详解
Oct 20 Javascript
jquery实现全选和全不选功能效果的实现代码【推荐】
May 05 Javascript
基于jQuery实现仿QQ空间送礼物功能代码
May 24 Javascript
详解jQuery停止动画——stop()方法的使用
Dec 14 Javascript
JS动态遍历json中所有键值对的方法(不知道属性名的情况)
Dec 28 Javascript
JavaScript简单实现合并两个Json对象的方法示例
Oct 16 Javascript
webpack3之loader全解析
Oct 26 Javascript
jQuery实现鼠标移到某个对象时弹出显示层功能
Aug 23 jQuery
对angularJs中ng-style动态改变样式的实例讲解
Sep 30 Javascript
原生js实现公告滚动效果
Jan 10 Javascript
如何为你的JavaScript代码日志着色详解
Apr 08 Javascript
输入框的字数时时统计—关于 onpropertychange 和 oninput 使用
Oct 21 #Javascript
学习JavaScript的最佳方法分享
Oct 21 #Javascript
修复IE9&safari 的sort方法
Oct 21 #Javascript
修复ie8&chrome下window的resize事件多次执行
Oct 20 #Javascript
jquery ajax return没有返回值的解决方法
Oct 20 #Javascript
IE与FireFox中的childNodes区别
Oct 20 #Javascript
IE和Firefox的Javascript兼容性总结[推荐收藏]
Oct 19 #Javascript
You might like
人大复印资料处理程序_输入篇
2006/10/09 PHP
PHP中对各种加密算法、Hash算法的速度测试对比代码
2014/07/08 PHP
Thinkphp集成抖音SDK的实现方法
2020/04/28 PHP
Javascript 构造函数 实例分析
2008/11/26 Javascript
jQuery Tools Dateinput使用介绍
2012/07/14 Javascript
javascript设计模式 接口介绍
2012/07/24 Javascript
jQuery.prototype.init选择器构造函数源码思路分析
2013/02/05 Javascript
一个css与js结合的下拉菜单支持主流浏览器
2014/10/08 Javascript
Css3制作变形与动画效果
2015/07/24 Javascript
JavaScript中数组的合并以及排序实现示例
2015/10/24 Javascript
ES6下React组件的写法示例代码
2017/05/04 Javascript
js is_valid_filename验证文件名的函数
2017/07/19 Javascript
利用Angular2 + Ionic3开发IOS应用实例教程
2018/01/15 Javascript
利用weixin-java-miniapp生成小程序码并直接返回图片文件流的方法
2019/03/29 Javascript
Webpack的Loader和Plugin的区别
2020/11/09 Javascript
flask中使用SQLAlchemy进行辅助开发的代码
2013/02/10 Python
linux环境下安装python虚拟环境及注意事项
2020/01/07 Python
python使用pyecharts库画地图数据可视化的实现
2020/03/25 Python
使用Python防止SQL注入攻击的实现示例
2020/05/21 Python
python利用os模块编写文件复制功能——copy()函数用法
2020/07/13 Python
Python调用百度OCR实现图片文字识别的示例代码
2020/07/17 Python
python PyAUtoGUI库实现自动化控制鼠标键盘
2020/09/09 Python
联想马亚西亚官方网站:Lenovo Malaysia
2018/09/19 全球购物
艺术家策划的室内设计:Curious Egg
2019/03/06 全球购物
自我评价是什么
2014/01/04 职场文书
先进党支部事迹材料
2014/01/13 职场文书
股权投资意向书
2014/04/01 职场文书
国际经济与贸易专业求职信
2014/07/10 职场文书
创先争优活动党员公开承诺书
2014/08/29 职场文书
上党课的心得体会
2014/09/02 职场文书
2014客服代表实习自我鉴定
2014/09/18 职场文书
Python基础详解之邮件处理
2021/04/28 Python
如何用Navicat操作MySQL
2021/05/12 MySQL
php修改word的实例方法
2021/11/17 PHP
十大最强火系宝可梦,喷火龙上榜,第一名有双火属性
2022/03/18 日漫
SQL Server Agent 服务无法启动
2022/04/20 SQL Server