1.面向对象的编程

1.1.什么是面向对象编程

面向对象编程:即是把能够完成独立完成一部分功能的代码封装在一起,组成一个类。

  举个例子来说:

这里有一把枪, 枪的种类很多,有步枪,机关枪,阻击枪....。但是无论怎么说,这些都是枪的概念,如果把这种抽象的概念剥离出来,就是我们经常说的“类”。那么枪有什么特点呢? 威力大小,型号,长度,子弹型号,能承载子弹的数量,枪口半径......ok! 这些一切的一切都是为了描素枪,把这些抽象出来,就组成了“枪类”的属性。枪又能干什么呢?  瞄准,开火,....这些描素都是枪的功能-----把这些抽象出来,即组成了一个类的方法。

  所以,总体来说,面向对象的编程即是把一个程序模块化,每个模块承载一部分功能,各个模块协同合作,维持程序的正常执行;

  而所谓的类的组成,无外乎三个部分:这个类的名称(对应着例子中的“枪”),这个类的属性(对应着特点),这个类的方法(对应着功能)。

就像我们描素一个人一样,无外乎,描素一个人的特点以及人的能力。所以,现实生活中的人,在程序中也可以抽象成类。

1.2.类,对象,实例的关系

  • 类:是一个抽象概念,是对某一类相似对象的抽象。
  • 对象:是类的一个实例化,因为类是一个抽象的概念,所以,在使用时必须落实到实物的身上。那么,对象就作为载体来完成某项功能。
  • 实例:和对象是一个概念。一般说一个类的实例,指的就是这个类的某个对象。

举个例子来说明三者之间的关系:

//1.Person是一个类的名字,定义的是一个人,对这个人的描述一般就是姓名,年龄。
var Person = function (name, age) {
//Person类的属性
this.name = name;
this.age = age;
}
//Person类的方法
Person.prototype.greet = function () {
console.log('hello');
}
//这是一个类的实例化过程,lisi这里就是Person类的一个对象,也可以说其是Person类的一个实例
var lisi=new Person("lisi",18);

1.3.面向对象的四个特点

  • 封装. 所谓的封装就是把一个类的对象的属性和方法封装在类的内部。封装的好处就是:类与类之间的属性和方法相互独立,互不干扰。
  • 继承. 所谓的继承就是指的是一个类可以派生自另外一个类。比如,图形类,可以派生出三角形,正方形,圆....
  • 重载. 重载就是指一个类的方法可以名字可以相同(JS不支持重载).第二部分会给出解释。
  • 多态.多态指的是父类的方法,子类可以重写该方法。那么,子类在调用该方法时调用的会是子类的方法。

请记住:面向对象,所有的一些都是为了代码的复用。

2.面向对象的四个特点在JS中的实现

2.1.JS中的封装

      JS类的封装即是把类的属性和方法封装在类的内部. 如果只是简单的实现封装,那么可以有多种方法。比如下面的两种

//第一种方法
var Person = function (name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log('hello');
}
}
//第二种方法
var Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function () {
console.log('hello');
}

这两种方法,虽然在使用效果上是一致的,但是在第一种方法中,每次new 一个对象的时候都要为该对象添加一个函数greet----这样就没有做到代码的复用。所以在使用的时候,一般都是使用第二种方式---也就是所谓的组合式创建。所以一般我们也推荐用第二种方式。

2.2.JS中不存在重载

什么是重载呢? 重载的概念来源于强类型预言(C++,java,C#)中。我们先来看一些java中的重载

class Person{  //java语言, 定义一个Person类,该类中存在greet方法的重载。
public String name;
public int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
public void greet(){
System.out.println("I am "+ this.name); }
public void greet(String message){ System.out.println("I am "+ this.name+ "\n This is your"+message);
}
}

    所谓的重载,就是一个同一个方法名在一个类中被出现了多次。那么在该方法被调用的时候,编译器如何区分具体调用哪个方法呢?

      在强类型语言中,编译器先根据函数的名字选择函数,然后在根据调用时,形参和实参的类型,形参的个数和实参的个数是否一致来区分一个函数。

    那么,问题来了....JS中的解释器是符合区分一个函数呢? ok...JS中解释器只是根据函数的名称来选择函数,而函数的形参并不在考虑的范围----因为在编译时无法根据确定形参的类型,更无法确定实参的类型。

    既然,JS不支持重载,那么如果一个函数被重写了,会出现什么情况呢?

var Person = function (name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log('hello');
}
}
var Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function () {
console.log('我被覆盖了');
}
Person.prototype.greet = function (message) {
console.log("我是重写的方法");
} var person=new Person("zhangsan",18); person.greet(); //我是重写的方法

    根据上面的例子,可以看出,无论函数的参数是什么,只要函数同名,那么被调用的肯定是最后一次被写的同名函数。

2.3.JS中的继承

  继承这个概念的来源也是面向对象的编程。JS引荐强类型预言中的继承做到这一点。所以我们要从强类型语言中的继承来类推---JS中为什么要这么设计。

  2.3.1.强类型语言中继承的实现

    在强类型语言中,在假设有两个类 A 、B....A是B的父类。实现如下:

class A{//父类的构造函数
protected int x;
A(int x){
this.x=x;
}
}
class B extends A{
protected int y;
B(int x,int y){//子类的构造函数
super(x); //在子类的构造函数中,第一句话总是先调用父类的构造函数,
//如果不写 则默认调用super();如果父类中不存在无参构造函数,则编译时会报错。
         this.y=y; 
}

  public String getPoint(){
    return "("+this.x+","+this.y+")";  //返回坐标(x,y)
  }

}

从上面的这些我们可以看出什么呢? 就是对象初始化的顺序...先初始化父类,在初始化子类。

初始化的时候顺序为: 父类的属性----》父类的方法-----》子类的属性-----》子类的方法。(我们这里讲的是排除了类中静态数据和方法来说,因为静态数据和方法的初始化,在类第一次被加载的时候就已经初始化完毕)

    下面我们看下,JS中是怎么实现和上述一样的功能的...

var A = function (x) {
this.x = x;
}
var B = function (x, y) {
A.call(this, x); //相当于第一种的super()函数。
this.y = y;
}
//实现继承
function extend(subClass, superClass) {
var prototype = Object.create(superClass);
subClass.prototype = prototype;
subClass.constructor = subClass;
}
extend(B, A);
B.prototype.getPoint = function () {
return '(' + this.x + ',' + this.y + ')';
}

  上面这两段代码,撇开语言的特性来说,他们实现的功能是等效的。只是第一种玩的是思想,第二种玩的是技巧。

    OK!下面我们开始详解JS的设计者为了JS语言能实现继承所做的努力。

2.3.2.JS语言支持继承的原理。

所有的函数均有一个prototype属性。就是这个属性帮我们做到了一些,首先要认识到一点这个属性是一个对象。

    用上面我们创建的一个Person函数详解,那么这个函数的prototype属性如下表示:

    这是这个函数在刚开始被初始化时候的固有形式,后来执行了一句

Person.prototype.greet = function () {
console.log('hello');
}

  在这句执行完毕的时候,Person.prototype变化为

 Person  prototype
constructor 指向Person函数
greet (greet函数)

   解释了这么多,貌似并没有解释继承是怎么实现的是吧....别慌...慢慢来!!!

    来看一下,当一个函数被实例化的时候发生了什么?

var lisi=new Person("lisi",18); //看看Person实例化的对象发生了什么?

  

  到这里,我们看到了吧..当用new创建一个构造函数的对象的时候。这个对象会有一个【【__proto__】】内置属性,指向函数的prototype。------这就是对象lisi传说中的原型对象。

  一个函数只有一个原型(prototype),这个函数在用new调用的时候会把这个原型赋值给当前对象的内置属性。

  当查询一个对象的属性的时候,首先查询对象本身的属性,如果没有找到则根据对象内置属性层层向上查找。

  所以一切的一切都归咎于,只要们修改一个函数的prototype属性,那么就可以实现继承。

下面图解,B继承A的过程。

//1. A类,和B类的构造函数    

var A = function (x) {
this.x = x;
}
var B = function (x, y) {
A.call(this, x); //相当于第一种的super()函数。
this.y = y;
}

//2.修改B的prototype,使其指向A的prototype

//实现继承
function extend(subClass, superClass) {
var prototype = Object.create(superClass);
subClass.prototype = prototype;
subClass.constructor = subClass;
}
extend(B, A);
B.prototype.getPoint = function () {
return '(' + this.x + ',' + this.y + ')';
}

如此,便实现B类继承A类...关键点就在函数的prototype属性上。----在下一篇中会详解函数的prototype。

JS中实现多态

何谓多态?

首先一定要强调一点,只有在继承存在的情况下才可能出现多态! 这是为什么呢..因为多态指的是子类覆盖父类的方法.....子类覆盖父类的方法,这种情况就是所谓的多态。

在java中的多态

public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
B b=new B(1,2);
String result=b.getPoint();
System.out.println(result);
}
}
class A{
protected int x;
A(int x){
this.x=x;
}
public String getPoint(){
return "我是父类";
}
}
class B extends A{
protected int y;
B(int x,int y){
super(x);
this.y=y;
}
    //多态,父类覆盖的方法
public String getPoint(){
return "我是子类";
}
}
//输出结果 :我是子类

  在JS中的多态情况,也是指的的子类的方法覆盖父类的方法。 上面的功能在JS中是这么实现的。

var A = function (x) {
this.x = x;
}
A.prototype.getPoint = function () {
return '我是子类';
}
var B = function (x, y) {
A.call(this, x); //相当于第一种的super()函数。
this.y = y;
}
//实现继承 function extend(subClass, superClass) {
var prototype = Object.create(superClass);
subClass.prototype = prototype;
subClass.constructor = subClass;
}
extend(B, A);
B.prototype.getPoint = function () {
return '我是子类';
}
var b = new B(1, 2);
b.getPoint();
//输出结果 :我是子类

  在上述代码执行完毕后,函数B的结构如图所示

B类在实例化的时候,B类的对象会拥有一个内部属性指向B.prototype.当该实例调用函数的时候,会先在该对象内部查询该函数是否存在,如果不存在则沿着内置属性查询原型对象,即B.prototype。如果找到此属性,则停止查询,否则会接着沿着内置属性所指向的对象,一直找到最上级为止。

JavaScript系列----面向对象的JavaScript(1)的更多相关文章

  1. JavaScript系列----面向对象的JavaScript(2)

    本文中心: 这篇文章比较难懂,所以读起来比较晦涩.所以,我简单列一下提纲: 在第一部分,从函数原型开始谈起,目的是想搞明白,这个属性是什么,为什么存在,在创建对象的的时候起到了什么作用! 在第二部分, ...

  2. 啊金学习javascript系列一之javascript整体印象

    javascript是一门编程语言,这个是第一个观点.是编程语言,那就拥有编程语言的功能.在我理解之中,编程语言是和计算机打交道的语言,就是我们跟计算机说话用的语言,是用来指挥计算机的.人类能够理解语 ...

  3. JavaScript系列:《JavaScript高级程序设计》,chapter2, 在html中使用JavaScript

    2.1.2 延迟脚本     指的是defer属性,且只适用于外部脚本,也就是有defer属性的脚本.     由于各种延迟浏览器对延迟脚本的支持不统一,且在html5之后也不再支持defer属性,所 ...

  4. 深入理解JavaScript系列+ 深入理解javascript之执行上下文

    http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html http://blog.csdn.net/hi_kevin/article/d ...

  5. 深入理解JavaScript系列(结局篇)

    介绍 最近几个月忙得实在是不可开交,终于把<深入理解JavaScript系列>的最后两篇“补全”了,所谓的全是不准确的,因为很多内容都没有写呢,比如高性能.Ajax安全.DOM详解.Jav ...

  6. 深入理解JavaScript系列(41):设计模式之模板方法

    介绍 模板方法(TemplateMethod)定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 模板方法是一种代码复用的 ...

  7. 深入理解JavaScript系列(18):面向对象编程之ECMAScript实现(推荐)

    介绍 本章是关于ECMAScript面向对象实现的第2篇,第1篇我们讨论的是概论和CEMAScript的比较,如果你还没有读第1篇,在进行本章之前,我强烈建议你先读一下第1篇,因为本篇实在太长了(35 ...

  8. 深入理解JavaScript系列(17):面向对象编程之概论

    介绍 在本篇文章,我们考虑在ECMAScript中的面向对象编程的各个方面(虽然以前在许多文章中已经讨论过这个话题).我们将更多地从理论方面看这些问题. 特别是,我们会考虑对象的创建算法,对象(包括基 ...

  9. 深入理解JavaScript系列(13):This? Yes,this!

    介绍 在这篇文章里,我们将讨论跟执行上下文直接相关的更多细节.讨论的主题就是this关键字.实践证明,这个主题很难,在不同执行上下文中this的确定经常会发生问题. 许多程序员习惯的认为,在程序语言中 ...

随机推荐

  1. (1)pygame_第一个窗口程序

    ####可以使用python自带的IDLE交互式开发,也可以借助其他的编辑器,我这里采用的pycharm编辑器 1.导入我们所需要的模块 import pygame,sys   --导入我们需要的模块 ...

  2. Manacher’s Algorithm (神啊)

    (转载自)http://blog.csdn.net/hopeztm/article/details/7932245 这里描述了一个叫Manacher’s Algorithm的算法. 算法首先将输入字符 ...

  3. 关于python中的pickle函数

    8-7参考阅读 - 读文件.写文件.异常处理.文件保存游戏.pickle数据转成文本的过程又被称为"序列化",即将对象状态转换为可保持或传输的格式的过程.对应的,从序列化的格式中解 ...

  4. HDU1142 A Walk Through the Forest(最短路+DAG)

    A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/O ...

  5. SpringMVC 实现文件的上传与下载

    一  配置SpringMVC ,并导入与文件上传下载有关的jar包(在此不再赘述) 二 新建 相应 jsp 和controller FileUpAndDown.jsp <%@ page lang ...

  6. 详解python命名空间和作用域

    1.典型案例 先从几个典型的案例来看下名称空间及作用域对python代码运行的影响,请看下面几个代码实例及其执行结果,是否符合你的预期. 代码1:块作用域 if True: i = 1 print i ...

  7. 自学spring AOP

    本人是一个编程新手也是第一次写博客 这篇文章是我结合网上的资料和一些书籍学的 如果有不对之处请留言告知 本文介绍了AOP的两个知识点 1: 代理 代理有两种 我先写:Java静态代理 1:建立一个接口 ...

  8. canvas画布标签

    最近良师益友整理一些canvas的资料,加强学习了解! 当你创建一个<canvas>元素后,就拥有了它的绘图上下文. 一.简单图形 1.getContext()方法 为了在canvas上绘 ...

  9. js正则验证特殊字符

    js正则验证特殊字符 方案一 var regEn = /[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/im, regCn = /[·!#¥(--):: ...

  10. hack查询地址

    悲催的IE! http://browserhacks.com/