JavaScript入门之基本概念篇

9 篇文章 0 订阅
2 篇文章 0 订阅

转载自原文:http://abruzzi.iteye.com/blog/632010

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

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

本章主要讲述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)的一个方法。

在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感到奇怪,事实上,对象和数组的界限并不那么明显(事实上它们是属于同一类型的),但是他们的行为却非常不同,本书的后续章节将两个重要的数据类型做了分别介绍。

3.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.因此我们可以采取这种简写的形式。

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

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

4.1.4类型的判断

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

Js代码 
function handleMessage(message, handle){  
    return handle(message);  
}
当调用handleMessage的函数传递的handle不是一个函数则JavaScript引擎会报错,因此我们有必要在调用之前进行判断:
Js代码 
function handleMessage(message, handle){  
    if(typeof handle == "function"){  
       return handle(message);   
    }else{  
       throw new Error("the 2nd argument should be a function");  
    }  
}

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

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

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

Js代码 
    print(obj instanceof Array);//false  
    print(array instanceof Array);//true  

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

二         变量

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

Js代码 
var str = "Hello, World";  
var num = 2.345;

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

1.2.1基本类型和引用类型

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

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

Js代码 
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

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

Js代码 
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变量的作用域

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

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

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

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

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

in
in

运算符

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

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

1.3.1中括号运算符([])

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

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

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

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

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

Js代码 
for(var key in object){  
    print(key + ":" + object[key]);  
}
运行结果如下:
    field:slef  
    printInfo:function (){  
           print(this.field);  
    }  

2.3.2点运算符(.)

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

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

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

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

Js代码 
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  null和undefined相等

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

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

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

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

 

等同运算符

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

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

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

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

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

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

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

比如:

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

会返回:

true
true

再来看一个对象的例子:

Js代码 
<strong>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。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一章 概述 1.1 Javascript简史 在20世纪90年代,也就是早期的WEB站点上,所有的网页内容都是静态的,所谓静态是指,除了点击超链接,你无法通过任何方式同页面进行交互,比 如让页面元素接受事件,修改字体等。人们于是迫切的需要一种方式来打破这个局限,于是到了1996年,网景(Netscape)公司开始研发一种新的语言 Mocha,并将其嵌入到自己的浏览器Netscape中,这种语言可以通过操纵DOM(Document Object Model,文档对象模型)来修改页面,并加入了对鼠标事件的支持。Mocha使用了C的语法,但是设计思想上主要从函数式语言Scheme那里取得了灵 感。当Netscape 2发布的时候,Mocha被改名为LiveScript,当时可能是想让LiveScript为WEB页面注入更多的活力。后来,考虑到这个脚本语言的推 广,网景采取了一种宣传策略,将LiveScript更名为JavaScript,目的是为了跟当时非常流行的面向对象语言Java发生暧昧的关系。这种 策略显然颇具成效,以至于到现在很多初学者还会为JavaScript和Java的关系而感到困惑。 Javascript取得成功了之后,确实为页面注入了活力,微软也紧接着开发自己的浏览器脚本语言,一个是基于BASIC语言的 VBScript,另一个是跟Javascript非常似的Jscript,但是由于Javascript已经深入人心,所以在随后的版本中,微软的 IE几乎是将Javascript作为一个标准来实现。当然,两者仍然有不兼容的地方。1996年后期,网景向欧洲电脑厂商协会(ECMA)提交了 Javascript的设计,以申请标准化,ECMA去掉了其中的一些实现,并提出了ECMAScript-262标准,并确定Javascript的正 式名字为ECMAScript,但是JavaScript的名字已经深入人心,故本书中仍沿用Javascript这个名字。 1.1.1动态网页 WEB页面在刚开始的时候,是不能动态修改其内容的,要改变一个页面的内容,需要先对网站上的静态HTML文件进行修改,然后需要刷新浏览器。后来 出现的JSP,ASP等服务器端语言可以为页面提供动态的内容,但是如果没有JavaScript则无法在服务器返回之后动态的在前端修改页面,也无法有 诸如鼠标移上某页面元素则高亮该元素之的效果,因此JavaScript的出现大大的丰富了页面的表现,提高了用户体验。 而当 AJAX流行起来之后,更多的非常绚丽的WEB应用涌现了,而且呈越来越多的趋势,如Gmail,Google Map,Google Reader,Remember the milk,facebook等等优秀的WEB2.0应用,都大量的使用了JavaScript及其衍生的技术AJAX。 1.1.2浏览器之战 1.1.3标准 1.2 JavaScript语言特性 JavaScript 是一门动态的,弱型,基于原型的脚本语言。在JavaScript中“一切皆对象”,在这一方面,它比其他的OO语言来的更 为彻底,即使作为代码本身载体的function,也是对象,数据与代码的界限在JavaScript中已经相当模糊。虽然它被广泛的应用在WEB客户 端,但是其应用范围远远未局限于此。下面就这几个特点分别介绍: 1.2.1动态性 动态性是指,在一个Javascript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候做赋值操作即可,如下例: Js代码 1. //定义一个对象 2. var obj = new Object(); 3. //动态创建属性name 4. obj.name = "an object"; 5. //动态创建属性sayHi 6. obj.sayHi = function(){ 7. return "Hi"; 8. } 9. obj.sayHi(); //定义一个对象 var obj = new Object(); //动态创建属性name obj.name = "an object"; //动态创建属性sayHi obj.sayHi = function(){ return "Hi"; } obj.sayHi(); 加入我们使用Java语言,代码可能会是这样: Js代码 1. class Obj{ 2. String name; 3. Function sayHi; 4. public Obj(Sting name, Function sayHi){ 5. this.name = name; 6. this.sayHi = sayHi; 7. } 8. } 9. Obj obj = new Obj("an object", new Function()); class Obj{ String name; Function sayHi; public Obj(Sting name, Function sayHi){ this.name = name; this.sayHi = sayHi; } } Obj obj = new Obj("an object", new Function()); 动态性是非常有用的,这个我们在第三章会详细讲解。 1.2.2弱型 与Java,C/C++不同,Javascript是弱型的,它的数据型无需在声明时指定,解释器会根据上下文对变量进行实例化,比如: Js代码 1. //定义一个变量s,并赋值为字符串 2. var s = "text"; 3. print(s); 4. //赋值s为整型 5. s = 12+5; 6. print(s); 7. //赋值s为浮点型 8. s = 6.3; 9. print(s); 10. //赋值s为一个对象 11. s = new Object(); 12. s.name = "object"; 13. print(s.name); //定义一个变量s,并赋值为字符串 var s = "text"; print(s); //赋值s为整型 s = 12+5; print(s); //赋值s为浮点型 s = 6.3; print(s); //赋值s为一个对象 s = new Object(); s.name = "object"; print(s.name); 结果为: text 17 6.3 Object 可见,Javascript的变量更像是一个容器,似与Java语言中的顶层对象Object,它可以是任何型,解释器会根据上下文自动对其造 型。 弱型的好处在于,一个变量可以很大程度的进行复用,比如String型的name字段,在被使用后,可以赋值为另一个Number型的对象,而 无需重新创建一个新的变量。不过,弱型也有其不利的一面,比如在开发面向对象Javascript的时候,没有型的判断将会是比较麻烦的问题,不过 我们可以通过别的途径来解决此问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值