JavaScript基于对象编程
js面向对象特征介绍
javascript是一种面向(基于)对象的动态脚本语言,是一种基于对象(Object)和事件驱动(EventDirven)并具有安全性能的脚本语言。它具有面向对象语言所特有的各种特性,比如封装、继承及多态等。但对于大多数人说,我们只把javascript做为一个函数式语言,只把它用于一些简单的前端数据输入验证以及实现一些简单的页面动态效果等,我们没能完全把握动态语言的各种特性。在很多优秀的Ajax框架中,比如ExtJS、JQuery等,大量使用了javascript的面向对象特性,要使用ext技术,javascript的高级特性,面向对象语言特性是我们必需完全把握的。
明确概念:
-
javascript是一种基于对象(Object-based)的语言,你遇到的所有东西几乎都是对象。特别说明:基于对象也好,面向对象也好实际上都是以对象的概念来编写程序,从本质上并无区别,所有这两个概念在我(韩顺平)的课程中是一样的。
-
因为javascript中没有class(类),所以有人把类也称为原型对象,因为这两个概念从在编程中发挥的作用看都是一个意思,为了统一叫法,我这里就统一叫类。
如下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> //传统方法比较麻烦 var cat1_name = "小白"; var cat1_age = 3; var cat1_color = "白色"; //...解决方法,把猫的属性集中,创建一种新的数据类型(原型对象/类) //用面向对象的方法来解决上面的问题 //这里就是一个Cat类 function Cat() { } //Cat();//如果你这样用,就是一个函数 var cat1 = new Cat();//类 //这时cat1就是一个对象(实例) cat1.name = "小白"; cat1.age = 3; cat1.color = "白色"; //从上面的代码我们可以看出 //1.js中的对象的属性可以动态的添加 //2.属性没有限制 window.alert(cat1.name+cat1.age+cat1.color+cat1.hobby); </script> </head> <body> </body> </html>
这里,注意几点:
1、若直接这样写Cat();则就是一个函数调用。
2、js中的对象的属性可以动态的添加。
3、属性没有限制。
js面向(基于)对象
计算机语言的发展是向接近人的思维方式演变的,这是一个大趋势。js引入的面向对象的思想。js不是纯面向对象的,我们可以认为它是基于面向对象的。
汇编语言[面向机器],C语言[面向过程],java语言[面向对象],js[支持面向对象]。
类和对象(实例)的关系:
注意:从猫类到猫对象实例,目前有几种说法:1、创建一个对象实例;2、实例化一个对象;3、对象实例化...以后大家听到这些说法,不要迷糊。
当然,上面的猫也可是鱼、狗、人...js支持面向对象编程。
类(原型对象)和对象(实例)的区别和联系:
- 类(原型对象)是抽象的、概念的,代表一类事物,比如人、猫..
- 对象是具体的,实际,代表一个具体事物
- 类(原型对象)是对象实例的模板,对象实例是类的一个个体。
类(原型对象)--如何定义类(原型对象)和对象
- 工厂方法--使用new Object创建对象并添加相关属性;
- 使用构造函数来定义类(原型对象);(目前我们先讲解)
- 使用prototype
- 构造函数及原型混合方式
- 动态原型方式
类(原型对象)--如何定义类(原型对象)
基本语法:
function 类名/原型对象名(){
}
对象的属性,一般是基本数据类型(数,字符串),也可是另外的对象。比如我们前面创建猫对象的age就是猫对象的属性。
创建对象基本语法:
var 对象名=new 类名/原型对象名();
对象--特别说明
1、在js中一切都是对象。如:
function Person(){} var a = new Person(); window.alert(a.constructor);//a对象实例的构造函数 window.alert(typeof a);//a的类型是什么,是object
会依次弹出对话框:
那么,如下代码会弹出什么呢? 类(原型对象)其实也是对象,它实际上是function类的一个实例,通过类我们可以创建自己的对象实例,可以通过下面的代码来说明。
window.alert(Person.constructor);
对话框如下:
就好像Java中如下代码:(我也说不太清楚,模棱两可)
class Cat { private String name; =>Cat.class(字节码文件)->class对象(字节码对象) } Cat cat = new Cat();
再比如:
var b = 123; window.alert(b.constructor); window.alert(typeof b);
依次弹出:
再比如:
var c = "123"; window.alert(c.constructor); window.alert(typeof c);
依次弹出:
思考:如何判断一个对象实例是不是Person类型?
window.alert(a);
以上代码会弹出:
有两种方法来判断一个对象实例是不是Person类型?
1、
if(a instanceof Person) { window.alert("a是Person ok1"); }
2、
if(a.constructor == Person) { window.alert("a是Person ok2"); }
定义变量带var和不带var的区别:
如下例:
//带不带var均是全局变量 var abc = 89; function test() { //在函数里,如果不带var,则表示使用全局的abc变量 //如果你带上var则表示在test()中定义了一个新的abc变量 var abc = 900;//如果改写成abc = 900;那么输出900 } test(); window.alert(abc);//输出结果为89
对象实例--如何访问(使用)对象实例的成员变量
1、对象实例名.属性名;(普通方式)
2、对象实例名["属性名"];(动态)
对象实例名["变量名"]的方式可以实现动态的访问变量,如:
function Person() {}; var p1 = new Person(); p1.name = "李阿昀"; window.alert(p1.name); var val = "na" + "me";//属性名可以动态拼接而成 window.alert(p1[val]);
对象引用问题的说明:
js提供了二种方式进行内存回收机制:被动回收机制与主动回收机制。
被动回收机制在于堆地址被引用的次数,当引用次数为0时,对象回收机制GC就将无引用的堆地址空间进行释放。如下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person() {}; var a=new Person(); a.age=10; a.name="小明"; var b=a; b.name = "小白"; alert(b.age+"名字 "+b.name+" 名字 "+a.name); b = null; alert(a.age+" a名字 "+a.name); alert("b.age = " + b.age); //Uncaught TypeError: Cannot read property 'age' of null a = null; </script> </head> <body> </body> </html>
示意图:
主动回收机制
如下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person() {} var a=new Person(); a.age=10; a.name="小明"; var b=a; b.name = "小白"; alert(b.age+" 1名字 "+b.name); delete a.age; //删除a对象的属性 alert(b.age+" 2名字 "+a.name);// alert(b.age+" 2名字 "+b.name); </script> </head> <body> </body> </html>
delete 对象名.属性名;//这样就会释放对象指向的属性空间
this--问题提出
看一段代码:
function Person(){} var p1=new Person(); p1.name="顺平"; p1.age=60; window.alert(p1.name+" "+p1.age); var p2=new Person(); window.alert(p2.name);
最后一句会输出什么呢?答案是undefined。
在实际编程中,我们可能有这样的需求,当我们创建一个对象后,就希望该对象自动的拥有某些属性[比如:当我们创建一个Person对象后,就希望该对象自动拥有name和age属性,这又怎么办呢?
答案:使用this来解决。如下:
function Person(){ this.name="abc"; this.age=90; } var p1=new Person(); var p2=new Person(); p2.name = "阿昀"; window.alert(p1.name+" "+p2.name);
示意图:
可能大家会这样去思考问题。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> //可能大家会这样去思考问题 function Person() { var name = "abc";//如果这样去使用,name这个属性是私有的,私有的,只能在内部使用 var age = 900; this.name2 = "abc2";//this.name2 表示name2这个属性是公开的。 this.show = function() {//这个就是Person类的一个公开方法(特权方法) window.alert(name+" "+age); show2(); } function show2() {//这是Person类的一个私有方法,只能在Person类中使用 window.alert("show2() "+name+" "+age); } } var p1=new Person(); p1.show(); //p1.show2();//这里会报错! //window.alert(p1.name+" "+p1.age+" "+p1.name2);//输出undefined undefined abc2 </script> </head> <body> </body> </html>
特别说明:
-
js中一般在类中不定义私有变量或私有方法
-
类中的变量和方法定义为公开的都必需使用this来定义
进一步理解this
记住一句话:哪个对象实例调用this所在的函数,那么this就代表哪个对象实例。如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function test1() { alert(this.v); } var v = 90; test1();// <==> window.test1(); 输出90 //window.alert(v);// <==> window.alert(window.v); 输出90 //alert("ok"); <==> window.alert("ok"); </script> </head> <body> </body> </html>
又如:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function test1() { alert(this.v); } var v = 90; function Person() { this.abc = function() { window.alert(this.v); } } var p = new Person(); p.v = "hello"; p.abc();//输出hello window.alert(this.v); //this指代window,所以输出90 </script> </head> <body> </body> </html>
this--注意事项
this不能在类定义的外部使用,只能在类定义的方法中使用。
对象--对象(成员)函数的初步介绍
比如:我们希望对象不但有属性,还希望他有行为。(行为在程序中靠函数完成)
在某些情况下,我们需要定义对象函数。比如人对象:除了有一些属性外(成员变量表示的年龄、姓名...),我们人对象还有一些行为比如:可以说话、跑步...通过学习,我们人还可以做算术题。这时就要用函数才能完成,现在要求对Person对象完善。
1、添加speak函数,输出我是一个好人
2、添加jisuan函数,可以计算从1+...+1000的结果
3、修改jisuan函数,该方法可以接收一个n,计算从1+...+n的结果
代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(name, age) { //这个就是使用传入的实际参数,去初始化属性 this.name = name; this.age = age; //输出自己的名字,这里this.show就是一个公开函数,函数名是show this.show = function() { document.writeln("名字="+this.name); } /* this.jisuan = function() { var res = 0; for (var i = 1; i <= 1000; i++) { res += i; } return res; } */ this.jisuan = function(n) { var res = 0; for (var i = 1; i <= n; i++) { res += i; } return res; } } var p1 = new Person("宋江", 90); var p2 = new Person("林冲", 19); p1.show(); p2.show(); //document.writeln("<br/> res = "+p1.jisuan()); document.writeln("<br/> res = "+p1.jisuan(100)); </script> </head> <body> </body> </html>
对象--成员函数
给一个对象添加(指定)函数的几种方式。
方式一,语法:
function 类名(){ this.属性; } var 对象名 = new 类名(); function 函数名(){ //执行代码 } 对象名.属性名 = 函数名;//这样就相当于把函数赋给对象名.属性名,此时这个属性名就表示一个函数 对象名.属性名();//调用
如下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name="abc"; this.age=900; } function show1(){ window.alert("hello "+this.name); } //创建一个p1对象 var p1 = new Person(); //把show1函数,给p1.abc p1.abc = show1; //注意p1.abc=show1;与p1.abc=show1();之间的区别 p1.abc(); </script> </head> <body> </body> </html>
思考:如果window.alert(p1.abc)会输出什么? 如果window.alert(show1)会输出什么?
若window.alert(p1.abc),则会输出:
function show1(){ window.alert("hello"+this.name); }
若window.alert(show1),则输出同上。
若为以下代码,会输出什么呢?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name="abc1"; this.age=900; } function show1(){ window.alert("hello "+this.name); } var p1 = new Person(); p1.abc = show1; show1(); </script> </head> <body> </body> </html>
答案:照理说,应该输出hello undefined,但是只输出了hello。
若为以下代码,又会输出什么呢?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name="abc1"; this.age=900; } var name="北京"; function show1(){ window.alert("hello "+this.name); } var p1=new Person(); p1.abc=show1; show1(); </script> </head> <body> </body> </html>
答案:很显然输出:hello 北京。
方式二,语法:
function 类名(){ this.属性; } var 对象名 = new 类名(); 对象名.属性名=function(参数列表){ //代码 } 对象名.属性名(实际参数);//调用
如下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name = "小明"; this.age = 900; } var p1 = new Person(); p1.abc = function show1(){ window.alert("hello "+this.name); } p1.abc(); </script> </head> <body> </body> </html>
方式三,前面的几种方法有一个问题:那就是每个对象独占函数代码,如果对象很多则会影响效率。js设计者,给我们提供另一个方法,原型法:这样多个对象可以共享函数。
下面代码中,每个对象实例都会有一个函数,并且每一个函数的地址都不一样,证明之!
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name = "abc"; this.age = 900; this.abc = function(v1,v2){ window.alert(this.name+" "+this.age+" "+v1+" "+v2); } } var p1 = new Person(); var p2 = new Person(); window.alert(p1.abc == p2.abc); </script> </head> <body> </body> </html>
内存示意图如下:
这里再看一个案例。如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Dog(){} var dog1=new Dog(); //动态绑定一个函数给shout属性 dog1.shout=function(){ window.alert("小狗"); } dog1.shout(); var dog2=new Dog(); dog2.shout();//这里报错:Uncaught TypeError: dog2.shout is not a function </script> </head> <body> </body> </html>
好了,我们给出第三种方式吧!
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> //希望所有的对象共享某个函数 function Dog(){} //使用prototype[类]去绑定一个函数给shout Dog.prototype.shout = function() { window.alert("小狗"); } var dog1 = new Dog(); dog1.shout(); var dog2 = new Dog(); dog2.shout();//这里ok window.alert(dog1.shout == dog2.shout); var dog3 = new Dog(); var dog4 = new Dog(); var dog5 = dog4; //window.alert(dog3);//输出[object Object] window.alert("dog3 == dog4 "+(dog3 == dog4)); window.alert("dog5 == dog4 "+(dog5 == dog4)); </script> </head> <body> </body> </html>
内存示意图:
==号的作用
1、当==的两边都是字符串的时候,则比较内容是否相等。
2、如==的两边是数字,则比较数的大小是否相等。
3、如==的两边是对象或是对象函数则比较地址是否相等。
给所有对象添加(指定)函数。如下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name="abc"; this.age=900; this.abc=function(v1,v2){ window.alert(this.name+" "+this.age+" "+v1+" "+v2); } } var p1=new Person(); p1.abc();//输出abc 900 undefine undefine p1.abc("北京","天津");//输出abc 900 北京 天津 var p2=new Person(); p2.abc();//输出abc 900 undefine undefine p2.abc("南京","东京");//输出abc 900 南京 东京 </script> </head> <body> </body> </html>
思考题,看看以下代码,会输出什么,从中我们可以得出什么结论?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function Person(){ this.name="abc"; this.age=900; this.abc=function(v1,v2){ window.alert(this.name+" "+this.age+" "+v1+" "+v2); } } var p1=new Person(); p1.name="中国";//动态添加一个属性 p1.abc("北京","天津");//输出中国 900 北京 天津 var p2=new Person(); p2.abc("南京","东京");//输出abc 900 南京 东京 </script> </head> <body> </body> </html>
对象Object类
Object类是所有javascript类的基类,提供了一种创建自定义对象的简单方式,不需要程序员再定义构造函数。
Object的主要属性:
constructor--对象的构造函数
prototype--获得类的prototype对象,static性质。
主要方法:
hasOwnProperty(property)--是否属于本类定义的属性
isPrototypeOf(object)--是否是指定类的prototype
propertyIsEnumerable(property)--是否可例举的属性
toString()--返回对象对应的字符串
valueOf()--返回对象对应的原始类型值
看下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> //初步体验Object,通过Object类直接创建对象 var p1 = new Object(); p1.name = "sp"; window.alert(p1.constructor); </script> </head> <body> </body> </html>
弹出对话框如下:
再看下例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> var i1 = new Number(10); window.alert(i1.constructor); </script> </head> <body> </body> </html>
弹出对话框如下:
若为如下代码,则弹出对话框同上。
var i2 = 10; window.alert(i2.constructor);
加深对类和对象的认识。
如何给类添加方法(如何给某类型的所有对象添加方法)
代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> var i=new Number(10);//等价于var i=10; //我们可以给类添加方法 Number.prototype.add=function(a){//所有Number类都有add方法 return this+a; } window.alert(i.add(10).add(30));//结果为50 var b = 90; window.alert(b.add(40));//结果为130 </script> </head> <body> </body> </html>
上面的写法我们称为对类的修改或扩展。
思考题:请思考给js的Array对象扩展一个find(val)方法,当一个Array对象调用该方法的时候,如果能找到val则返回其下标,否则返回-1。
代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> //体验一下Array var arr1 = new Array(3); arr1[0] = "George"; arr1[1] = "John"; arr1[2] = "Thoams"; for (var i = 0; i < arr1.length; i++) { document.writeln(arr1[i]+" ") } //使用Array提供的方法,颠倒数据 arr1.reverse(); document.writeln("<br/>"); for (var i = 0; i < arr1.length; i++) { document.writeln(arr1[i]+" ") } //现在我们一起看看如何给所有Array对象添加一个方法find(val); Array.prototype.find = function(val) { //开始遍历数组 for (var i = 0; i < this.length; i++) { if(val == this[i]) { return i; } } return -1; } document.writeln("John下标 = " + arr1.find("ayun")); </script> </head> <body> </body> </html>
什么是闭包?(此知识点在讲封装时再讲。)
闭包(closure)
function a(){ var i=0; function b(){ alert(++i); } return b; } var c=a(); c();
闭包的作用就是在a执行完并返回后,闭包使得javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。
javascript闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁。这样在执行完var c=a()后,变量c实际上是指向了函数b,b中用到了变量i,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b就是说:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个我们通常所谓的“闭包”。
成员函数--细节说明
1、函数的参数列表可以是多个。
参数列表可以是多个,并且数据类型可以是任意的类型,整数、小数、类。
function 函数名(参数列表){ 语句;//函数主体 return 返回值; }
2、函数可以没有返回值,但最多只能有一个返回值。
返回类型可以是任意的数据类型(整数、小数、字符串)也可没有返回值。
function 函数(参数列表){ 语句;//函数主体 return 返回值; }
js中的函数还有以下几点特别说明:
1、js支持参数个数可变的函数
2、js支持创建动态函数,这个用的比较少,了解即可
3、js中不支持函数的重载
例,
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function test(a, b) { window.alert("hello"); } function test(a){ window.alert(a); } function test(a,b){//js中函数无重载,不管参数列表,只认函数名 window.alert(a+" "+b); } test(23); window.test(3,"hello"); </script> </head> <body> </body> </html>
结论:js在调用函数的时候,是根据函数名来调用,如果有多个函数名相同,则以最后一个出现的函数来执行。
再看下面一段代码(关于可变参数):
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function abc(a,b){ return a+b; } function abc(a,b,c){ return a+b+c; } window.alert(abc(1,2));//输出NaN window.alert(abc(1,2,3));//输出6 </script> </head> <body> </body> </html>
最后看一段代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> function abc(){ var s=0; for(var i=0;i<arguments.length;i++){ s+=arguments[i]; } return s; } window.alert(abc(1,2));//输出3 window.alert(abc(7,8,9));//输出24 </script> </head> <body> </body> </html>