一 简介
1.JavaScript是一种脚本语言,由LiveScript改名而来,Netscape公司为了推广这个脚本语言,利用了java的知名度,两者并没有什么关系。JavaScript是一种基于客户端浏览器的,基于对象、事件驱动式的脚本语言。
2.java和javaScript的关系
(1)完全是两个不同的产品,Java是Sun公司推出的面向对象的程序设计语言;JaaScript是Netscape公司的产品,目的是为了拓展Netscape浏览器功能。javaScript是一种可以嵌入Web页面中的解释性语言。
(2)Java语言最小的程序单位是类定义,JavaScript中充斥着大量函数。
(3)Java是强类型变量语言,所有的变量必须先声明,才可以使用,所有的变量都有其固定的数据类型;JavaScript是弱类型变量语言,变量在使用前无需声明,由解释器在运行时检查其数据类型。
3.在实际的使用过程中,还有另一种脚本语言:JScript,由Microsoft公司开发。由于早期JScript和javaScript差异较大,
程序员要为两种浏览器分别编写脚本,后来就诞生了ECMAScript,这是一个国际标准化的JavaScript版本,现在的主流浏览器都会支持这个版
本。
二 变量
1.全局变量和局部变量
在JavaScript中同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var声明一次。这种变量类型不固定的语言称为动态语言,与之对应的静态语言,如java,赋值时类型不匹配会报错。
全局变量:(1)在方法外部声明的变量(2)方法内部,没有加var关键字声明的变量
局部变量:方法内部,使用var声明的变量
<script type="text/javascript">
var a=3; //全局变量
function test(){
var a=2; //局部变量
alert(a);
}
test();
</script>
结果:2
如果注释掉var a=2;
<script type="text/javascript">
var a=3; //全局变量
function test(){
// var a=2; //局部变量
alert(a);
}
test();
</script>
结果:3
若将程序改为:
<script type="text/javascript">
function test(){
c=5; //相当于全局变量
}
alert(c);
test();
</script>
结果为:c is not defined
c虽然是全局变量,但是要先执行函数test(),才能执行到c。也就是说,javascript是由上而下执行的。
2.变量提升
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:
function foo() {
var x = 'Hello, ' + y;
alert(x);
var y = 'Bob';
}
foo();
语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是alert显示Hello, undefined,说明变量y的值为undefined。这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。
对于上述foo()函数,JavaScript引擎看到的代码相当于:
function foo() {
var y; // 提升变量y的申明
var x = 'Hello, ' + y;
alert(x);
y = 'Bob';
}
由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量。
3.变量的作用范围
JavaScript的变量没有块范围。
<script type="text/javascript">
function test(o){
var i=0;
if(typeof o=="object"){
//定义变量j,变量j的作用范围是整个函数内,而不是if块内
var j=5;
for(var k=0;k<10;k++){
//k的作用范围是整个函数内,而不是循环体内
document.write(k);
}
}
//即使出了循环体,k的值依然存在
alert(k+"
"+j);
}
test(document);
</script>
结果:10 5
再如:
<script type="text/javascript">
//定义全局变量
var scope="全局变量";
function test(){
//全局变量被局部变量覆盖,而此时scope局部变量尚未赋值,故此处输出undefined
document.writeln(scope+"<br />");
//定义局部变量,其作用范围为整个函数内
var scope="局部变量";
document.writeln(scope+"<br />");
}
test();
</script>
结果:undefined 局部变量
三 数据类型
1.基本数据类型:Number,Boolean,String,Undefined,Null
Number:整数,小数,NAN,Infinity(正无穷),-Infinity(负无穷)
Undefined:表示变量声明但是未赋值
Null:表示一个空的对象引用
注意:NaN是一个特殊的数值,它是Not a Number三个单词的首字母,表示非数。NaN==NaN返回false。isNaN()函数就专门用来判断某个变量是否为NaN
例:var a=1/0;并不会报错,会返回Infinity
2.复合类型
复合类型是由多个基本数据类型组成的数据体,JavaScript中的复合数据类型有大致3种:Object:对象;Array:数组;Function:函数
对象:JavaScript是基于对象的脚本语言,它提供了大量的内置对象供用户使用。除了Object外,还有如下常用的内置类:
Array Date Error Function Math Number Object String
数组:数组长度可变,同一数组中的元素类型可以互不相同,访问数组元素时不会越界,访问并未赋值的数组元素时,该元素的值为undefined
函数:JS的函数声明中,参数列表不需要数据类型声明,函数返回值也不需要数据类型声明,函数可以单独存在,无需属于任何类,函数必须要用function关键字定义
3.typeof和instanceof
typeof用来判断数据变量的数据类型,instanceof用来判断某个变量是否为指定类的实例。
如:
var a=5;
alert(typeof(a)); //number
var b=[1,2];
alert(b instanceof Array); //true
四 数组
js数组类似于java容器,长度可变,元素类型也可以不同
<script type="text/javascript">
var arr=[1,false];
var result=arr.push(2,true,"abc");
alert(arr);
alert(result);
</script>
结果为:1,false,2,true,abc 5
其中,push()函数会向数组中添加元素,并会返回新数组的长度
<script type="text/javascript">
var arr=[1,false,2,"dfr"];
var obj=arr.pop();
alert(arr);
alert(obj);
</script>
结果:1,false,2 dfr
其中,pop()函数可以从数组的尾部移除一个元素,并返回移除的元素值
shift()函数从头部移除一个元素,unshift()函数从头部插入多个元素,并返回新数组的长度
<script type="text/javascript">
var arr=[1,2,4,6,3];
arr.splice(1, 2,3,4,5);
alert(arr);
</script>
结果:1,3,4,5,6,3
其中,splice()的第一个参数表示起始位置,第二个参数表示截取的个数,第三个参数以后表示要追加的新元素
若是只有两个参数,arr.splice(1,2);结果为:1,6,3
程序若改为:
<script type="text/javascript">
var arr=[1,2,4,6,3];
arr.slice(2,4);
alert(arr);
</script>
结果:1,2,4,6,3
slice()函数截取范围:左闭右开区间,不操作数组本身,返回截取的内容,而splice方法会操作数组本身
修改程序:
<script type="text/javascript">
var arr=[1,2,4,6,3];
var result=arr.slice(2,4);
alert(result);
</script>
结果:4,6
<script type="text/javascript">
var arr1=[1,2,3];
var arr2=[2,3,4];
var result=arr1.concat(arr2);
alert(result);
</script>
结果:1,2,3,2,3,4
同理,concat()合并粘贴,不操作数组本身
<script type="text/javascript">
var arr1=[1,2,3];
var result=arr1.join('-');
alert(arr1)
alert(result);
</script>
结果为:1,2,3 1-2-3
join()方法在每个元素之间加入内容,也不操作数组
sort()方法正序排序,reverse()方法倒序排序
五 strict模式
javaScript在设计之初,为了方便初学者学习,并不强制要求用var申明变量。这个设计错误带来了严重的后果:如果一个变量没有通过var申明就
被使用,那么该变量就自动被申明为全局变量。在同一个页面的不同的JavaScript文件中,如果都不用var申明,恰好都使用了变量i,将造成变量i
互相影响,产生难以调试的错误结果。
为了修补JavaScript这一严重设计缺陷,ECMA在后续规范中推出了strict模式,在strict模式下运行的JavaScript代码,强
制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。启用strict模式的方法是在JavaScript代码的第一行写上:
'use strict';
这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。
六 函数
JavaScript是一种基于对象的脚本语言,JavaScript代码复用的单位是函数。函数可以独立存在,JS中的函数完全可以作为一个类使用,并且它还是该类唯一的构造器。函数本身也是一个对象,它是Function实例。
1.函数的定义
(1)定义命名函数
格式:function 函数名(x){ 函数执行体; }
举例:
<script type="text/javascript">
hello('lly');
function hello(name){
alert(name+",hello");
}
</script>
函数的最大作用是提供代码复用,将需要重复的代码块定义成函数,可以提供更好的代码复用。
上例中可以看出,javascript允许先调用函数,然后再定义该函数。在不同的<script>中,必须要先定义,然后再调用。
实际上采用命名函数的方式,代码的可读性并不好,因为在定义函数时也得到了一个对象。
(2)定义匿名函数
var 函数名=function(x){ 函数执行体; };
程序使用function关键字定义一个函数对象,再将这个对象复制给变量,以后就可以通过该变量来调用这个函数。很多优秀的JS源代码,如jQuery,Prototype.js等等,都是采用这种方式来定义函数的。
这种方法说明,在javascript中,函数就是一种对象,也就是说,函数也是一种数据类型,参数列表相当于函数的入口,return相当于函数的出口。
如:
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
abs(2);
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。
2.函数 方法 对象 类
函数是javascript编程里非常重要的一个概念,当使用javascript定义一个函数后,实际上可以得到以下4项:
函数:像java的方法一样,这个函数可以被调用。
对象:定义一个函数时,系统会创建一个对象,该对象是Function的实例。
方法:定义一个函数时,该函数通常会附加给某个对象,作为该对象的方法。
类:定义函数的同时,也得到了一个与函数同名的类。
如:
<script type="text/javascript">
var hello=function(name){
return name+",hello";
}
alert("hello是否是Function对象:"+(hello instanceof Function)+"
hello是否是Object对象:"+(hello instanceof Object));
alert(hello);
</script>
结果:true true 直接输出函数本身,则会输出函数的源代码。
由上可知,在定义一个函数后,有两种方式来调用函数:
(1)直接调用:总是返回该函数体内最后一条return语句的返回值,若没有return则没有返回值。
(2)使用new关键字调用:这种方式总有一个返回值,返回值就是一个JavaScript对象。
<script type="text/javascript">
function Person(name,age){
//被this修饰的关键字不再是局部变量,而是该函数的实例属性
this.name=name;
this.age=age;
this.info=function(){
document.writeln("my name is:"+this.name);
document.writeln("my age is:"+this.age);
};
}
var p=new Person("lyy",20);
p.info();
</script>
由上可知,js定义的函数可以附加到某个对象上,作为该对象的方法。实际上,如果没有明确指定将函数附加到哪个对象上,则默认将其附加到window对象上,作为window对象的方法。
<script type="text/javascript">
//直接定义一个函数,不指定该函数属于哪个对象
var hello=function(name){
document.writeln(name+'hello');
}
//window调用hello函数
window.hello('lyy');
//定义一个对象
var p={
walk:function(){
document.writeln("walk....");
}
}
p.walk();
</script>
3.函数的实例属性和类属性
js中函数不仅仅是一个函数,还是一个类,该函数还是此类唯一的构造器,调用函数时使用new关键字可以返回一个Object对象,该Object不是函数的返回值,而是函数本身产生的对象。因此在JS中,不仅有局部变量,还有实例属性和类属性。
局部变量:以普通方式声明的变量,包括以var和不加任何前缀声明的变量。
实例属性:以this前缀声明的变量。
类属性:以函数名前缀修饰的变量。
其中,实例属性和类属性是面向对象的概念:实例属性是属于单个对象的,因此必须通过对象来访问;类属性是属于整个类(即函数)本身的,因此必须通过类(函数)来访问。
<script type="text/javascript">
function Person(name,national){
this.name=name;
var a=0;
Person.national=national;
}
var p=new Person("lyy",''China");
document.writeln(p.name+Person.national+p.a)
</script>
结果:lyy China undefined
4.调用函数
JavaScript提供了三种方式来调用函数:
(1)直接调用:以函数附加的对象作为调用者,在函数后括号内传入参数来调用函数,如上面的p.walk();这种方式也是最常见的方式
(2)以call()方法调用函数
直接调用函数比较简单,但是不够灵活,有时在调用函数时还需要动态的传入一个函数引用,为了动态调用函数,就需要使用call方法。
调用者.函数(参数列表)=函数.call(调用者,参数列表)
<script type="text/javascript">
//定义一个each函数
var each=function(array,fn){
for(var index in array){
//以window为调用者来调用fn函数,将index,array[index]作为参数传递给fn函数
fn.call(null,index,array[index]);
}
}
//调用each函数
each([2,3,4,5],function(index,ele){
document.write("第"+index+"个元素是:"+ele);
});
</script>
(3)以apply()方法调用函数
apply()方法和call()方法都可以动态的调用函数,功能基本相似,在用法上有以下区别:
通过call()调用函数时,必须在括号中详细的列出每个参数;通过apply()动态调用函数时,可以在括号中以arguments来代表所有参数。它
只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数,arguments类似Array但它不是一个Array。
function foo(x) {
alert(x); // 10
for (var i=0; i<arguments.length; i++) {
alert(arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);
实际上arguments最常用于判断传入参数的个数。你可能会看到这样的写法:
// foo(a[, b], c)
// 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null:
function foo(a, b, c) {
if (arguments.length === 2) {
// 实际拿到的参数是a和b,c为undefined
c = b; // 把b赋给c
b = null; // b变为默认值
}
// ...
}
要把中间的参数b变为“可选”参数,就只能通过arguments判断,然后重新调整参数并赋值。
5.函数的独立性
在定义函数时可以将函数定义为某个类的方法或某个对象的方法,但是实际上,函数是独立的,并不属于其他类或对象。
<script type="text/javascript">
function Person(name){
this.name=name;
this.info=function(){
alert("name是:"+this.name);
}
}
var p=new Person("lyy");
p.info(); //name是:lyy
var name="lulu";
p.info.call(window); //name是:lulu
</script>
6.函数的参数传递
基本类型:值传递。当通过实参调用函数时,传入函数里的并不是实参本身,而是实参的副本,因此在函数中修改参数值并不会影响到实参。
<script type="text/javascript">
function change(arg1){
arg1=10;
document.write("函数执行中的arg1的值为:"+arg1);
}
var x=5;
document.write("函数调用前的arg1的值为:"+arg1);
change(x);
document.write("函数调用后的arg1的值为:"+arg1);
</script>
函数调用前的arg1的值为:5
函数执行中的arg1的值为:10
函数调用后的arg1的值为:5
复合类型:值传递。
<script type="text/javascript">
function changeAge(person){
person.age=10;
document.write("函数执行中person的age值为:"+person.age);
person=null;
}
var person={age:5};
document.write("函数调用前person的age值为:"+person.age);
changeAge(person);
document.write("函数调用后person的age值为:"+person.age);
document.write("person对象为:"+person);
</script>
函数调用前person的age值为:5
函数执行中person的age值为:10
函数调用后person的age值为:10
person对象为:[object Object]
注意:函数最后一行将person对象直接赋值为null,但是在函数执行结束后,person依然是一个对象,而不是null,这说明person本身
并未传入到changeAge()函数中,传入函数的依然是副本。复合类型的变量本身并未持有对象本身,只是一个引用,该引用指向实际的
javascript对象。当把person复合类型的变量传入changeAge()函数中时,传入的依然是person对象的副本,只是该副本和原
person变量指向同一个javascript对象。
七 对象
1.对象
由于javascript的函数定义不支持继承语法,因此javascript没有完善的继承机制,所以我们习惯上称javascript是基于对象的脚本语言。
javascript中的对象本质上是一个关联数组,或者说更像是java里的Map数据结构,由一组key-value对组成,这里的value,不仅可以是值(包括基本类型和复合类型),此时的值是函数的属性;还可以是函数,此时的函数就是该对象的方法。
当访问对象的属性时,可以使用obj.propName的形式,也可以采用obj[propName]的形式,有时必须采用这种形式,如:
<script type="text/javascript">
function Person(name,age){
this.name=name;
this.age=age;
this.info=function(){
alert('info method!');
}
}
var p=new Person('lyy',20);
for(propName in p){
document.writeln('p对象的'+propName+'属性值为:'+p[propName]);
}
</script>
这里必须用p[propName],因为如果用p.propName,会直接访问p对象的propName属性,但是实际上并没有propName属性。
2.继承和prototype
javascript虽然也支持类、对象的概念,但是并没有继承的概念,只能通过特殊的方式来扩展原有的javascript类。
javascript是一种动态语言,它允许自由的为对象增加属性或方法,当程序为对象的某个不存在的属性赋值时,就可以认为是在为该对象增加属性。
<script type="text/javascript">
function Person(name,age){
//被this修饰的关键字不再是局部变量,而是该函数的实例属性
this.name=name;
this.age=age;
this.info=function(){
document.writeln("my name is:"+this.name);
document.writeln("my age is:"+this.age);
};
}
var p1=new Person("lyy",20);
p1.info();
var p2=new Person("lulu",23);
p2.info();
</script>
上面这种为Person类定义info方法相当不好,主要有两个原因:
性能低下:每次创建Person实例时,程序依次向下执行,每次都会创建一个新的info函数,会造成系统内存泄漏,引起性能下降。
使得info函数中的局部变量产生闭包:闭包会扩大局部变量的作用域,使得局部变量一直存活到函数之外的地方。
<script type="text/javascript">
function Person(){
var locVal="lyy";
this.info=function(){
//此处形成闭包
document.writeln("locVal的值为:"+locVal);
return locVal;
};
}
var p=new Person();
var val=p.info();
//输出val返回值,该返回值就是局部变量locVal
alert(val);
</script>
程序的最后一行可以发现,即使离开了info函数,程序也可以访问到局部变量的值。为了避免这种情况,通常不建议在函数定义中直接为该函数定义方法,而是
建议使用prototype属性。javascript的所有类都有一个prototype属性,当我们为javascript类的prototype属
性增加函数、属性时,可以视为对原有类的扩展。增加了prototype属性的类继承了原有的类,这就是javascript所提供的伪继承机制。
<script type="text/javascript">
function Person(name,age){
//被this修饰的关键字不再是局部变量,而是该函数的实例属性
this.name=name;
this.age=age;
this.info=function(){
document.writeln("my name is:"+this.name);
document.writeln("my age is:"+this.age);
};
}
var p1=new Person("lyy",20);
p1.info();
Person.prototype.walk=function(){
document.writeln(this.name+'慢慢走');
}
var p2=new Person('lulu',23);
p2.info();
p2.walk();
//此时p1也具有了walk方法
p1.walk();
</script>
上面的程序实际上是用prototype为Person类增加了一个walk方法,这样会让所有的Person实例共享一个walk方法,而且该walk
方法还不在Person函数之内,因此不会产生闭包。注意:javascript并没有提供真正的继承,当通过某个类的prototype属性动态的增加
属性或方法时,其实质是对原有类的修改,并不是产生一个新的子类。
<script type="text/javascript">
Array.prototype.indexof=function(obj){
var result=-1;
for(var i=0;i<this.length;i++){
if(this[i]==obj){
result=i;
break;
}
}
return result;
}
var attr=[4,5,7,-2];
alert(arr.indexof(-2));
</script>
3.创建对象
主要有3种方式:
(1)使用new关键字调用构造器创建对象
<script type="text/javascript">
function Person(name,age){
this.name=name;
this.age=age;
this.info=function(){
document.writeln("my name is:"+this.name);
document.writeln("my age is:"+this.age);
};
}
var p1=new Person("lyy",20);
p1.info();
</script>
(2)使用Object直接创建对象
<script type="text/javascript">
var myObj=new Object();
myObj.name="lyy";
myObj.age="20";
myObj.info=function abc(){
document.writeln("my name is:"+this.name);
document.writeln("my age is:"+this.age);
}
myObj.info();
</script>
(3)使用JSON语法创建对象
JSON:JavaScript Object Notation,使用JSON可以避免书写函数,也可以避免使用new关键字,可以直接创建一个javascript对象。
person={
name:'lyy',
age:20,
info:function(){
document.writeln("my name is:"+this.name);
document.writeln("my age is:"+this.age);
}
schools:['小学','中学','大学'],
parents:[{name:'father',age:60,address:'江苏'},{name:'mother',age:58,address:'上海'}]
}