`
abruzzi
  • 浏览: 452808 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

JavaScript内核系列 第2章 基本概念

阅读更多

 

第二章 基本概念

本章将聚焦于JavaScript中的基本概念,这些概念与传统语言有比较大的不同,因此单独列出一章来做专门描述,理解本章的概念对书中后续章节的概念,代码的行为等会有很大的帮助,读者不妨花比较大的时间在本章,即使你对JavaScript已经比较熟悉,也建议通读本章。

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

2.1数据类型

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

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

2.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是一个一元的操作符,在本章的另外一个小节会专门讲到。

2.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);
 

 

 

 

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

   

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

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

事实上,这种转换规则会导致很多的问题,比如,所有的非空对象,在布尔值环境下,都会被转成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和数组arraytypeof值均为”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(不相等).

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

l  nullundefined相等

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

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

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

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

 

等同运算符

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

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

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

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

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

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

l  如果两个操作数均为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

 

 

 

附:由于作者本身水平有限,文中难免有纰漏错误等,或者语言本身有不妥当之处,欢迎及时指正,提出建议,参与讨论,谢谢大家!

分享到:
评论
1 楼 abruzzi 2010-04-05  
最后一小节“== 和 === 以及 != 和 !==”写的有点仓促,平时自己也不是非常关注此类细节,体会不是很深,还需要再改进改进!

相关推荐

    JavaScript内核系列.pdf

    ### JavaScript内核系列知识点概述 #### 一、JavaScript发展历程与特性 **JavaScript简史:** JavaScript的历史始于20世纪90年代,随着互联网的发展,静态网页已无法满足日益增长的用户交互需求。1996年,网景公司...

    犀利开发+jQuery内核详解与实践(1)+代码

    第二部分则是“jQuery内核详解与实践”,对jQuery的核心机制进行了深入剖析。 在“犀利开发”部分,作者可能涵盖了诸如高效编写JavaScript代码的方法,如何优化DOM操作,事件处理的最佳实践,以及如何利用jQuery...

    12_JavaScript笔记.pdf

    理解其基本语法、数据类型、运算符和控制结构是掌握JavaScript的第一步,随着学习深入,还可以涉及函数、对象、数组、模块化、异步编程等更高级的概念,进一步提升Web开发能力。对于面试者来说,扎实的JavaScript...

    python入门到高级全栈工程师培训 第3期 附课件代码

    第2章 01 上节课复习 02 arp协议复习 03 字符编码 第3章 01 网络基础和dos命令 02 为何学习linux 03 课程内容介绍 04 操作系统内核与系统调用 05 操作系统安装原理 06 linux操作系统安装 07 初识linux命令 08 ...

    PHP深度分析:101个核心技巧、窍门和问题解决方法(原书第2版).pdf+PHP 内核文档.chm

    首先,书中的"核心技巧"部分涵盖了从基本语法到高级特性的广泛话题。例如,理解PHP的数据类型,如字符串、数组、对象和引用的用法,以及如何有效地操作它们。此外,书中可能会讨论变量作用域、函数与方法的定义与...

    浏览器内核&HTML5.pptx

    - **Chrome**: 使用**Blink**内核(自2013年起由WebKit分叉而来)结合**V8** JavaScript引擎。 - **Internet Explorer (IE)**/**Edge**: 分别使用**Trident**内核和**EdgeHTML**内核。 - **Firefox**: 使用**...

    Android基础教程

    第2章 基本概念 2.1 Android的系统架构 2.1.1 Linux内核 2.1.2 本机库 2.1.3 Android运行时 2.1.4 应用程序框架 2.1.5 应用程序和小部件 2.2 它还活着 2.2.1 进程不等于应用程序 2.2.2 应用程序生命周期 2.3 构建块 ...

    Beginning.Android.4

    **第二章:如何开始** - **安装和配置开发环境**:介绍了如何安装 Eclipse IDE 和 Android SDK,并进行必要的环境配置。 - **创建 Android 项目**:指导读者通过 Eclipse 创建第一个 Android 项目。 - **调试和测试...

    易语言-易语言调用Edge浏览器内核

    在易语言中调用Edge浏览器内核,首先需要理解的是进程通信(Process Communication)的概念。因为浏览器是一个独立的进程,而你的易语言程序是另一个进程,所以你需要通过某种方式让它们之间进行数据交换。这通常...

    PHP仿宝鸡第一人才网(骑士CMS内核)

    【PHP仿宝鸡第一人才网】是一个基于【骑士CMS】内核构建的在线招聘与求职平台。这个项目的主要特点是其美观的界面设计,为用户提供了良好的交互体验。在深入理解这个项目之前,我们需要先了解几个核心概念。 1. **...

    对口高考计算机专业复习计划a4.pdf

    * 第二轮复习:计算机基础知识、操作系统、计算机网络、Office2003、网页设计、多媒体技术 * 第三轮复习:计算机基础知识、操作系统、计算机网络、Office2003、网页设计、多媒体技术 本计划旨在提供一个系统、完整...

    web前台开发手册内容在描述里

    CSS2.0是其第二个主要版本,引入了浮动布局、定位、负边距、透明度等特性,极大地增强了网页设计的灵活性和控制力。在`css2.0中文手册.chm`中,你可以找到关于选择器、盒模型、定位、颜色、字体、边框和背景等所有...

    TP框架内核高仿蓝奏云网盘源码

    【标题】"TP框架内核高仿蓝奏云网盘源码"涉及到的是基于ThinkPHP(简称TP)框架开发的一款类似蓝奏云的网盘系统源代码。这个系统旨在提供一个平台,用户可以上传、存储并分享文件,同时具备网赚功能和VIP服务,能够...

    Chromium OS 技术分析程序全攻略(程式代碼)

    JavaScript的异步编程、DOM操作、事件处理等基本概念和高级特性可能都有所涉及。 4. **实例代码分析**:每个"exx"文件名可能是书中某个章节的示例代码,如ex1可能是第一章的代码,ex10可能是第十章的代码。这些代码...

    代码之美(中文完整版).pdf

    第2章 Subversion中的增量编辑器:像本体一样的接口 2.1 版本控制与目录树的转换 2.2 表达目录树的差异 2.3 增量编辑器接口 2.4 但这是不是艺术? 2.5 像体育比赛一样的抽象 2.6 结论 第3章 我编写过的最漂亮代码 ...

    Linux系统管理技术手册(第二版)

    《Linux系统管理技术手册(第二版)》作为一本全面介绍Linux系统管理知识的手册,不仅覆盖了基本概念和技术要点,还深入探讨了许多高级主题。无论是初学者还是有经验的技术人员,都可以从中获得实用的信息和技巧,...

    基于PHP的八零学院内核网站系统php版源码.zip

    要深入理解并利用这个源码,开发者需要熟悉PHP语法,了解基本的Web开发概念,以及可能涉及的前端技术和数据库管理。同时,阅读和理解源码的注释、结构和逻辑也是必要的。对于初学者,这是一个很好的实践项目,可以...

    Android Apps with Eclipse

    第二章深入探讨了Android应用程序的架构设计。这包括了应用程序组件如Activity、Service、BroadcastReceiver和ContentProvider的功能及相互作用方式。通过学习这一章节,开发者能够更好地理解如何构建模块化和可扩展...

Global site tag (gtag.js) - Google Analytics