javascript 学习笔记之面向对象编程(一):类的实现
~~想是一回事,做是一回事,写出来又是一回事~~一直以来,从事C++更多的是VC++多一些,从面向过程到面向对象的转变,让我对OO的编程思想有些偏爱,将一个客观存在的规律抽象出来总是让人比较兴奋,通过抽象出类的关系,我们可以做更多的事,思路也更加清晰。javascript也是一样,虽然其被定为成一个解释性语言,但随着互联网应用越来越广泛,js的作用将显得举足轻重,各种js框架、插件也蓬勃的出现,各浏览器直面用户的交互也越来越依赖js了,js代码的封装和优化日益重要。
也谈js的OO编程,虽然网上有许多技术大牛已经分享了星光熠熠的博文,但是将自己的理解写出来总是一件让人快乐的事,也算是对自己学习的一次真正的消化,下面就自己一段时间以来js面向对象编程经历和大家分享一下,主要从实现、继承、多态几个方面,如果可以,希望对您有所帮助~~~~
对this对象的理解
js中有个程序执行上下文对象this,也可以叫this指针,它在js对象操作中非常重要,看下面的例子:
function simpleFunction() {
//变量定义
this.varable = "zhangyu";
//方法定义
this.method = function() {
console.log("模块函数中的this指代:" + this);
}
document.onclick = function() {
console.log("模块中文档点击事件函数中的this指代:" + this);
}
//返回模块的this对象
return this;
}
var hh = simpleFunction();
hh.method();
//输出当前文档的全局this对象
console.log("全局this对象:" + this);
//输出window对象
console.log("全局window对象:" + window);
//输出模块返回值
console.log("模块this对象:" + hh.toString());
//输出模块类的对象
console.log("模块类对象实例" + new simpleFunction()); console.log("[this==window]:" + (this === window));
console.log("[hh == window]:" + (hh === window));
console.log("[this == hh]:" + (this === hh));
运行结果如下:
根据结果可以看出,在没有指定上下文的执行环境中,this均指向的是window对象,而文档document的点击事件指定了文档对象上下文,故点击事件中this对象指向的是HTMLDocument对象。
this指针永远指向当前正在运行的对象,如果运行的对象发生了变化,那么this指针也发生了变化,因此一个方法内的this指针并不一定始终指向定义该方法的对象,是动态变化的,熟悉函数对象中包含的apply和call方法的童鞋都知道,一个客观存在的属性(方法)集合可以通过这两个函数动态指定执行上下文,下面我们通过一个简单的例子来看看this只针的动态变化:
//定义两个对象objA和objB
var objA = new Object();
var objB = new Object();
//分别给这连个对象定义一个varable变量
objA.varable = 1;
objB.varable = 2;
//给对象A定义一个函数
objA.getVarable = function() {
console.log(this.varable);
};
//将对象A的函数初始化给对象B
objB.getVarable = objA.getVarable;
//调用对象B中的函数
objB.getVarable();
输出结果如下:
可以看出,当对象objA的函数赋值给了objB后,this指针发生了变化,有指向对象A转变为指向对象B了。
类的定义和实现
javascript中类的定义和函数类似,一般来说函数就是一个类的构造器,只不过在类中,对象的属性和方法需要用this指针来标记,类和函数都是模块的表现形式,都是将一系列属性和方法封装到一个单独的脚本块中,对象的定义可以通过new关键字实现,下面定义一个简单的js类:
//类的构造函数
function ClassA() {
//定义一个局部变量
var localVarable = "localVarable";
//定义一个类的属性
this.varable = "test";
//定义一个类的方法
this.method = function() {
console.log(this.param);
}
}
//定义类的实例
var ObjA = new ClassA();
//调用类中的方法
ObjA.method();
可见在javascript中,类的构造函数中既可以定义成员变量,也可以定义成员函数,这里我们对new关键字的执行过程进行解析:
1. 当浏览器js解析器遇到new操作符后,新建一个空的对象{};
2. 接着开始执行类的构造函数,并将对象this指正指向这个函数对象;
3. 执行构造函数体,初始化类中定义的成员变量和函数,如程序给this对象不存在的属性赋值时,系统会自动给该对象创建属性;
4. 构造函数执行完成,new操作符就返回了初始化后的对象。
这种定义类的方法将变量和函数都定义到构造函数中,一定程度下代码的可读性不强,另外,类的每次实例化都要创建对象成员和函数,在程序空间上造成了资源浪费。而js中,对象都有一个叫prototype的原型对象,通过对这个对象绑定对象的变量和方法,可以使该类对象的所有实例共享使用这些变量和方法,类初始化时只需要创建一次,不需要重复创建,参考下面的代码:
//类的构造函数
function ClassA() {
//定义一个局部变量
var localVarable = "localVarable";
//定义一个类的属性
this.varable = "test";
}
//通过原型链定义一个类的方法
ClassA.prototype.method = function() {
console.log(this.varable);
}
//定义类的实例
var ObjA = new ClassA();
//调用类中的方法
ObjA.method();
这里我们通过prototype定义了类的函数method,通过new关键字实例化的对象自动关联了该方法,并可以进行调用,当然,我们还可以通过prototype给类添加、删除、修改成员或者方法,引用prototype后new关键字的执行过程为:
1. 浏览器遇到new关键字后,新建一个空的对象{},并将this指针指向这个空的对象;
2. 将prototype对象的所有成员都赋值给这个对象;
3. 执行构造函数,初始化类;
4. 构造函数执行完成,new关键字返回这个被初始化了的对象。
了解了这一过程,我们发现,通过prototype定义的属性或者方法必须在类的实例化语句之前,不然会产生“未定义”错误。prototype专门用来设计类的成员,它和类是密不可分的,其还有个非常重要的属性constructor,这个属性石对类构造函数的引用,如:
var ss = (ClassA.prototype.constructor === ClassA);
console.log(ss);
结果为ture。
不管怎么说,这些类的定义与其他一些高级语言都有些差别,即:构造函数是用来初始化类成员的,类的成员函数共享类的成员变量。在javascript中,类的成员和类的函数是互相独立的,它们通过this指针和类的对象联系在一起,类的成员间相互引用必须通过this指针进行,下面对classA进行改写,使其最大程度与其他高级语言定义类的过程一致,如下:
//类的构造函数
function ClassA() {
//inintial the varable;
}
ClassA.prototype.varable = "test";
//通过原型链定义一个类的方法
ClassA.prototype.method = function() {
console.log(this.varable);
}
//定义类的实例
var ObjA = new ClassA();
//调用类中的方法
ObjA.method();
可以看到,类的成员(变量或者方法)都通过prototype对象进行了定义,对prototype对象的赋值可以采用无类型方式,改写如下:
ClassA.prototype = {
//变量定义
varable: "test",
//通过原型链定义一个类的方法
method: function() {
console.log(this.varable);
}
}
这样,类的定义基本和其他语言定义的方式一样了。
实现类的私有成员和方法、静态成员和方法
私有成员和方法:js中变量和函数的定义有其作用域,我们可以通过这个特性来实现js类中的私有成员和方法,即在类的构造函数中定义新的变量和函数,这些成员只能被构造函数内部的公用成员方法共享使用,构造函数外面访问不到,也就是说,prototype对象定义的公用成员方法将不能访问这些私有成员,看下面的例子:
//类的构造函数
function ClassA() {
//类的私有(或者看做私有的变量和方法)
var localParam = 2;
function getLocalParam() {
return localParam;
}
//类的公有变量和方法,可以访问私有成员
this.param = 1;
this.setParam = function() {
this.param = localParam + getLocalParam();
}
}
//通过prototype定义类的公有成员方法
ClassA.prototype.Display = function() {
//输出类的公有成员
console.log(this.param);
//注:此处对类构造函数中的私有成员变量或者方法将不能访问。
}
其中,通过js中变量或方法的作用域将变量localParam和方法getLocalParam看做是类的私有成员,因为它们只能在类的构造函数中使用,同时,这种做法是以牺牲代码可读性来的,但相对应它的贡献,这种尝试还是值得的。
静态成员和方法:在其他编程语言中,静态成员和方法是通过类名直接调用的,js中可以通过直接给类添加属性(非对象属性)的方式实现这种效果,就像下面这样:
//添加静态成员和方法
ClassA.staticParam = 1;
ClassA.setParam = function() {
ClassA.staticParam = 2;
}
这些定义的变量和方法只能通过类名直接访问,由于它们只和类的构造函数本身有关,与类的对象实例没有关系,故该类所有对象将共享该静态成员和方法(非调用关系),且静态成员函数将不能访问类的成员变量,只能根据传递给它的参数进行逻辑处理。另外,js中所有的函数类型对象均是Function类的实例,故我们可以通过Function类给所有的函数类型对象添加静态方法,如下:
//给所有函数类型的对象添加方法
Function.prototype.getContent = function() {
return this.toString();
}
~~接下篇~~
javascript 学习笔记之面向对象编程(一):类的实现的更多相关文章
- C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]
面向对象编程 --句柄类与继承[续] 三.句柄的使用 使用Sales_item对象能够更easy地编写书店应用程序.代码将不必管理Item_base对象的指针,但仍然能够获得通过Sales_item对 ...
- javascript 学习笔记之面向对象编程(二):继承&多态
~~接上篇~~上一篇实现了类的实现以及类成员变量和方法的定义,下面我们来了解下面向对象中两个最重要的特性:继承和多态. 继承 js中同样可以实现类的继承这一面向对象特性,继承父类中的所有成员(变量和属 ...
- python学习笔记(七):面向对象编程、类
一.面向对象编程 面向对象--Object Oriented Programming,简称oop,是一种程序设计思想.在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一 ...
- python自动化测试学习笔记-7面向对象编程,类,继承,实例变量,邮件
面向对象编程(OOP)术语: class TestClass(object): val1 = 100 def __init__(self): self.val2 = 200 ...
- C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域
面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...
- JavaSE学习笔记05面向对象编程01
面向对象编程01 java的核心思想就是OOP 面向过程&面向对象 面向过程思想: 步骤清晰简单,第一步做什么,第二步做什么...... 面向过程适合处理一些较为简单的问题 面向对象思想: 物 ...
- python 学习笔记7 面向对象编程
一.概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强..." ...
- C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承
面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...
- Spark学习笔记11面向对象编程
面向对象编程 11.1 object类 11.1.1定义一个简单的类 11.1.2 field的getter与setter 定义类包含,定义类的field及方法.其格式如下 class Cla ...
随机推荐
- UIView frame, bounds and center
http://stackoverflow.com/questions/5361369/uiview-frame-bounds-and-center Since the question I asked ...
- java-mina(nio 框架)
mina是对nio的具体实现.是目前比较高效和流行的nio框架了. 下面是对使用mina进行通讯的一个简单demo,后面再用mina写一个RPC的简单框架. mina主要包括: (使用的mina版 ...
- Android应用开发学习之列表视图
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 列表视图我们会经常用到,可以通过两种方式来创建列表视图,一种方式是直接使用ListView组件创建,另一种方式是通过 ...
- storm的acker机制理解
转载请注明原创地址http://www.cnblogs.com/dongxiao-yang/p/6142356.html Storm 的拓扑有一些特殊的称为"acker"的任务,这 ...
- Java内存区域 - 深入Java虚拟机读后总结
Java虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,有各自的创建时间和销毁时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动 ...
- 在JSP页面中调用另一个JSP页面中的变量
在jsp学习中,经常需要在一个jsp页面中调用另一个jsp页面中的变量,下面就这几天的学习,总结一下. jsp页面之间的变量调用有多种方法: 1.通过jsp的内置对象—request对象获取参数: ( ...
- Look and say numbers
地址:http://www.codewars.com/kata/53ea07c9247bc3fcaa00084d/train/python There exists a sequence of num ...
- 安装Win7和Ubuntu12.04双系统后,意外删除Ubuntu12.04引导文件,出现error:unknown filesystem;grub rescue>错误的解决方案
很久之前在Win7基础上安装了Ubuntu12.04系统,采用硬盘安装的方法.分了1个10G的硬盘分区F盘用于存放Ubuntu12.04的引导文件,其实完全可以制作一个Ubuntu12.04的U盘启动 ...
- 在android客户端加载html源代码总结
在实际应用中,客户端要从网页上获取数据是常见的事,如果要解析网页上的html文档,那么首先得获取html源码,然后现在一般使用Jsoup来转换成Document文档来进行解析,本文主要讨论如何使用Js ...
- Git学习笔记(一)
刚开始学Git,记录下来,讹误之处还望指教. 安装好git之后,有两个东东,如图: git bash 表示是命令行操作. git GUI 表示是图形化操作.但是这个界面用起来不方便,另一个图形化操作工 ...