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 相关文章推荐
iframe自适应宽度、高度 ie6 7 8,firefox 3.86下测试通过
Jul 29 Javascript
你必须知道的JavaScript 变量命名规则详解
May 07 Javascript
JavaSciprt中处理字符串之sup()方法的使用教程
Jun 08 Javascript
分享javascript实现的冒泡排序代码并优化
Jun 05 Javascript
js实现文字超出部分用省略号代替实例代码
Sep 01 Javascript
js实现砖头在页面拖拉效果
Nov 20 Javascript
JavaScript多态与封装实例分析
Jul 27 Javascript
vue 更改连接后台的api示例
Nov 11 Javascript
vue中的v-model原理,与组件自定义v-model详解
Aug 04 Javascript
js实现移动端轮播图滑动切换
Dec 21 Javascript
jQuery实现tab栏切换效果
Dec 22 jQuery
JS实现页面炫酷的时钟特效示例
Aug 14 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
谈谈PHP语法(3)
2006/10/09 PHP
php session处理的定制
2009/03/16 PHP
关于PHP语言构造器介绍
2013/07/08 PHP
PHP编译安装中遇到的两个错误和解决方法
2014/08/20 PHP
jQuery之网页换肤实现代码
2011/04/30 Javascript
Jquery带搜索框的下拉菜单
2013/05/06 Javascript
JavaScript中读取和保存文件实例
2014/05/08 Javascript
通过JS动态创建一个html DOM元素并显示
2014/10/15 Javascript
JavaScript中消除闭包的一般方法介绍
2015/03/16 Javascript
基于Bootstrap实现Material Design风格表单插件 附源码下载
2016/04/18 Javascript
很棒的Bootstrap选项卡切换效果
2016/07/01 Javascript
如何提高数据访问速度
2016/12/26 Javascript
AngularJS双向绑定和依赖反转实例详解
2017/04/15 Javascript
Vue.js鼠标悬浮更换图片功能
2017/05/17 Javascript
JS中通过url动态获取图片大小的方法小结(两种方法)
2018/10/31 Javascript
手写Vue弹窗Modal的实现代码
2019/09/11 Javascript
微信小程序实现底部弹出模态框
2020/11/18 Javascript
Vue实现摇一摇功能(兼容ios13.3以上)
2021/01/26 Vue.js
[22:20]初生之犊-TI4第5名LGD战队纪录片
2014/08/13 DOTA
Python实现控制台输入密码的方法
2015/05/29 Python
python创建列表并给列表赋初始值的方法
2015/07/28 Python
Python创建一个空的dataframe,并循环赋值的方法
2018/11/08 Python
python爬虫 execjs安装配置及使用
2019/07/30 Python
python实现把两个二维array叠加成三维array示例
2019/11/29 Python
tensorflow2.0保存和恢复模型3种方法
2020/02/03 Python
python修改微信和支付宝步数的示例代码
2020/10/12 Python
CSS3让登陆面板3D旋转起来
2016/05/03 HTML / CSS
html5 http的轮询和Websocket原理
2018/10/19 HTML / CSS
北美Newegg打造的全球尖货海购平台:tt海购
2018/09/28 全球购物
俄罗斯最大的灯具网站:Fandeco
2020/03/14 全球购物
C#里面可以避免一个类被其他类继承么?如何?
2013/09/26 面试题
高中运动会广播稿
2014/01/21 职场文书
人事专员岗位说明书
2014/07/29 职场文书
房屋买卖定金协议书
2016/03/21 职场文书
Mysql数据库索引面试题(程序员基础技能)
2021/05/31 MySQL
mybatis 获取无数据的字段不显示的问题
2021/07/15 Java/Android