首页 > WEB开发 > 前端开发 > JavaScript高级特性(面向对象)
2014
10-11

JavaScript高级特性(面向对象)

JavaScript高级特性主要研究如下内容:

  • 函数:
  • 闭包(☆):重要概念,理解
  • 对象:
  • 原型(☆):重点是应用
  • 原型链:模拟继承(并非真正的继承,JS没有extends关键字)

一、函数

JS中定义函数的三种方式:

  • function 函数名(参数){}
  • var 函数名 = function(参数){ }
  • var 函数名 = new Function(参数, 函数体);

说明:JS是弱类型语言,因此变量名和函数名无区别,语法上可以将一个函数赋值给一个变量。

1、arguments对象

作用:用来模拟函数重载的效果

1)Java语言中的函数重载带来了如下好处:

  • 节省了函数的命名空间:如果没有重载,同样的逻辑要定义多个函数名。
  • 降低了编码难度:会自动匹配调用的重载函数

2)JS中没有函数重载机制,当在JavaScript中定义了多个同名函数时:

function add(a,b){
   return a+b;
}
function add(a,b,c){
   return a+b+c;
}
alert(add(2,3));  //输出:NaN
alert(add(2,3,4));  //输出:9

会打印NaN和9,说明JS中不存在函数重载机制,当定义了多个同名的函数时,只有最后一个时起作用的(覆盖前者)。

3)模拟重载:利用JS提供的arguments对象,可以模拟Java中的函数重载。

function add(){
	if(arguments.length==2){
	   return arguments[0] + arguments[1];
	}else if(arguments.length==3){
	   return arguments[0] + arguments[1] + arguments[2];
	}
}
alert(add(2,3));  //输出:5
alert(add(2,3,4));   //输出:9

总结:

① JavaScript中没有函数重载,但是可以模拟函数重载的效果。(利用arguments对象)

② arguments对象是一个数组,封装了函数调用时传递的实参。

2、变量作用域

JS中的作用域有两种:全局域和函数域,分别对应着全局变量和局部变量。注:JS中不存在块级作用域,即{}。在某个代码块中定义的变量(如if或for语句),它在代码块外是可见的。

重点:JS中变量作用域的两种特殊情况(不建议在程序中这样搞)

1)定义局部变量时不使用var关键字:该局部变量被定义为全局变量

var a = "a";
function fn(){
  b = "b"; //不使用var关键字,b为全局变量

  alert(a); //输出:a
  alert(b); //输出:b
}
fn();
alert(a); //输出:a
alert(b); //输出:b

2)当全局变量与局部变量同名时,在函数域中只能访问局部变量

var a = "a";
function fn(){
  alert(a);  //输出:undefined(只能访问局部变量a)
  var a = "b";  //局部变量与全局变量同名
  alert(a);  //输出:b
}
fn();
alert(a);  //输出:a

函数域始终优先于全局域,所以局部变量a会覆盖与它同名的全局变量。

3、特殊函数

1)匿名函数

匿名函数的两种用法:

  • 可以将匿名函数作为参数传递给其他函数。这样,接收方函数就能利用所传递的函数来完成某些事情 —> 匿名回调函数
  • 可以定义某个匿名函数来执行某些一次性任务 –> 自调函数

2)回调函数(☆)

当将函数A传递给函数B,并由B来执行A时,A就成了一个回调函数(callback function)。如果A还是一个匿名函数,就称之为匿名回调函数。

function add(a, b){
	return a() + b();
}
var one = function(){return 1;}
var two = function(){return 2;}
alert(add(one,two));		//输出:3

//可以直接使用匿名函数来替代one()和two(),以作为目标函数的参数
alert(add(function(){return 1;}, function(){return 2;}));

在这个例子中,函数one和two都是回调函数。回调函数的优点:可以将一个函数调用操作委托给另一个函数(这意味着可以节省一些代码编写工作)。回调函数也有助于提升性能。(重要)

Demo1:利用回调函数优化代码(能看懂即可)

-------------优化前代码:---------------
//将参数分别乘2返回
function two(a, b, c){
	var i, arr = [];
	for(i = 0;i < 3; i++){
		arr[i] = arguments[i] * 2;
	}
	return arr;
}

function addone(a){
	return a + 1;
}

//将三个数据在两个函数之间传递
var myarr = [];
myarr = two(10, 20, 30);
for(var i = 0; i < 3; i++){
	myarr[i] = addone(myarr[i]);
}
alert(myarr);		//output	[21, 41, 61]

----------------优化后代码-----------------
//利用回调函数优化后的代码如下(修改two()函数)
function two(a, b, c, callback){
   var i, arr = [];
   for(int i = 0, i < 3, i ++){
      arr[i] = callback(arguments[i] * 2);
   }
   return arr;
}
myarr = two(10,20,30,addone);
alert(myarr);

//还可以使用匿名函数来替代addone函数
myarr = two(10,20,30,function(a){return a + 1;});

3)自调函数

定义:在定义函数后自行调用

(
   function(){
      alert("javascript");
   }
)();

-------------------

(
   function(name){
      alert(name + "i love you!");
   }
)("xt");

上面定义了两个自调函数(两对括号):将匿名函数的定义放进一对括号中,然后外面再跟一对括号即可(表示立即调用)。

4)内部(私有)函数

私有函数与Java中的内部类很相似。

function a(param){
	function b(input){
		return input * 2;
	};
	return "The result is " + b(param);
}
alert(a(2));		//输出:The result is 4

当调用全局函数a( ) 时,内部函数b( ) 也会在其内部被调用。由于b( ) 是内部函数,它在a ( ) 以外的地方是不可见的,所以也将b 称之为私有函数。

4、预定义函数(全局函数)

20. JavaScript高级特性3091

二、闭包(closure)

定义:闭包就是能够读取其他函数内部变量的函数。由于在JS语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

如下面代码中的b就是一个闭包:

var b;
function a(){
   var a = "a";
   b = function(){
      return a + "b";
   };
   return a;
}
// 测试
alert(a()); //输出:a
alert(b()); //输出:ab

分析:在a ( ) 函数中定义了 b ( ) 函数,所以b ( ) 函数可以访问a ( ) 函数的作用域;将 b( )函数升级到全局函数,但依然保留可以对a ( ) 函数作用域的访问权(☆)。

极力推荐一篇深入学习闭包的好文:http://kb.cnblogs.com/page/105708/

扩展:循环中的闭包结构(了解)

Demo1:

function f(){
	var a = [];
	var i;
	for(i = 0; i < 3; i++){
		a[i] = function(){
			return i;
		}
	}
	return a;
}

var fun = f();

alert(fun[0]());		//output	3
alert(fun[1]());		//output	3
alert(fun[2]());		//output	3

按照预期,最终结果应该输出 [0 , 1 , 2 ],但是却是[ 3 , 3 , 3 ]。

分析:在函数 f ( ) 中,我们通过循环,创建了三个闭包,它们都指向了共同的局部变量 i 。但是,闭包并不会记录它们的值,它们所拥有的只是一个 i 的连接(即引用),因此只能返回i 的当前值,这显然不是程序的本意。

Demo2:对Demo1的改进

------------------使用自调函数改写闭包结构---------------------------
function f(){
	var a = [];
	var i;
	for(i = 0; i < 3; i++){
		a[i] = (function(x){
			return x;
		})(i);
	}
	return a;
}

var fun = f();

alert(fun[0]);		//output	0
alert(fun[1]);		//output	1
alert(fun[2]);		//output	2

----------------不使用自调函数-----------------------

function f(){
	function n(x){
		return x;
	}
	var a = [];
	var i;
	for(i = 0; i < 3; i++){
		a[i] = n(i);
	}
	return a;
}

var fun = f();

alert(fun[0]);		//output	0
alert(fun[1]);		//output	1
alert(fun[2]);		//output	2

第一种情况分析:在该自调函数中,i 就被赋值给了局部变量x ,这样一来,每次迭代中的x 就会拥有各自不同的值了。

三、对象

1、定义对象的三种方式

1)new一个:new Object()
var a = new Object();
a.x = 1; a.y = 2;

2)JSON:{key:value,key:value…}
var b = { x : 1, y : 2 };

3)定义类型:function 对象名(){this.属性名=属性值;}
function Point(x, y){
this.x = x;
this.y = y;
}
var p = new Point(1,2);

说明:

1)在JS中,a、b、p称为普通对象,Point称为函数对象。在使用函数对象的属性或方法之前,需要先new一下。

2)Object是JS中所有对象的父级对象,这意味着所有对象都继承于Object对象。

3)创建一个空对象:var obj= {}; 或 var obj = new Object();

2、对象操作(普通对象)

对象操作

对象操作的Demo:

//创建一个空对象
var demo= {};

//为demo对象增加属性和方法
demo.name = "javascript";
demo.value = "helloworld";
demo.sayName = function(){return "hello " + demo.name;};

//测试
alert(demo.name);		//output	javascript
alert(demo.sayName());		//output	hello javascript

//删除demo对象的name属性
delete demo.name;

//测试
alert(demo.sayName());		//output	hello undefined

3、JS内建对象(核心对象)

1)数据封装类对象:Object、Array、Boolean、Number、String等。

注:String对象与基本的字符串类型之间的区别:

var str = “hello”; //typeof string

var obj = new String(“world”); //typeof object

2)工具类对象:Math、Date、RegExp等

3)错误类对象

这些对象具体的用法见《JavaScript》一文。

四、原型(prototype)

是什么:在JS中,函数本身就是一个对象,原型其实就是函数对象的一个属性(prototype)。原型是在函数对象创建的时候自动创建的,不能手动创建。

作用:利用函数对象的这个属性,我们可以为函数对象增加方法或属性。

Demo:创建函数对象,并利用prototype属性增加属性和方法

// 创建一个新的函数对象:
function Person(){
    this.name = "Flyne";
    this.say= function(){
        return "I am " + this.name;
    }
}
var p = new Person();

alert(p.say());	//输出:I am Flyne

第一种方式:与顺序无关(可以在new前,也可以在new后)

Person.prototype.age = 24;
Person.prototype.homepage = "www.flyne.org";
Person.prototype.getInfo = function(){
    return "age:" + this.age + ", homepage:" + this.homepage;
}
alert(p.getInfo()); //输出:age:24, homepage:www.flyne.org

第二种方式:与顺序有关,必须定义在new之前

Hero.prototype = {
	price : 100,
	rating : 3,
	getInfo : function(){
	   return "Rating: " + this.rating + " , Price: " + this.price;
	}
};
var p = new Person();
alert(p.getInfo()); //输出:age:24, homepage:www.flyne.org

注:如果对象的自身属性与原型上的属性同名时,默认调用的是函数对象本身的属性,说明函数对象本身的的优先级要高于原型上的属性,但是通过原型增加的属性的确是存在的。下面的代码说明了这一点:

Person.prototype.name = "baidu";
alert(p.name); //输出:Flyne

delete p.name; //p.name指向的是函数对象本身的属性和方法

alert(p.name); //输出:baidu

如果是方法,也是类似的。

扩展:利用原型扩展JS核心对象的属性和方法(了解,不建议自行扩展):

//String.prototype.trim=function(){}:在String的原型上添加了一个trim()方法
String.prototype.trim=function(){
	return this.replace(/(^\s*)|(\s*$)/g,"");
};
var str = "  abc   ";
alert("-"+str.trim()+"-");  //输出:abc

五、原型链

作用:实现继承效果。JS并不支持“继承”,也就是说,JS中没有继承的语法,但在JS中可以模拟继承。

是什么:由上节可知,每个函数对象都有一个prototype属性,它的值可以是一个对象,也可以是null(☆)。如果它的值是一个对象,则这个对象也一定有自己的原型,这样就形成了一条链,我们称之为原型链。如下面的Demo,函数对象B的原型为a对象:

function A(){
	this.a = "a";
	this.sayA = function(){
		alert("this is a.");
	}
}
function B(){
}

var a = new A();
B.prototype = a;

原型链的作用是用来实现继承(说模拟更恰当),如下:

// 测试
var b = new B();
alert(b.a); //输出:a
b.sayA(); //输出:this is a

当函数对象B的原型指向a对象后,就实现了继承对象a的效果。下面继续测试:

function A(){}
A.prototype = {
	a : "a",
	sayA : function(){
		alert("this is a.");
	}
}

function B(){}
B.prototype = A.prototype;
B.prototype.b = "b";
B.prototype.sayB = function(){
	alert("this is b.");
}

var b = new B();

alert(b.b); //输出:b
b.sayB(); //输出:this is b

alert(b.a); //输出:a
b.sayA(); //输出:this is a

var a = new A();

alert(a.a); //输出:a
a.sayA(); //输出:this is a

alert(a.b); //输出:b
a.sayB(); //输出:this is b

分析(☆):定义完函数对象A后,将函数对象A的原型指向了一个JSON对象。然后将函数对象B的原型也指向该JSON对象(B.prototype = A.prototype),并为该JSON对象增加属性和方法。

由于函数对象A、B的原型均指向该JSON对象,因此new出来的A对象和B对象均继承了该JSON对象的属性和方法。不难理解程序输出的结果。


JavaScript高级特性(面向对象)》有 1 条评论

  1. 卡机 说:

    辛苦了! 很适合我这种小菜

留下一个回复

你的email不会被公开。