zoukankan      html  css  js  c++  java
  • javascript面向对象学习笔记(一)——继承

      最近在学习html5,玩了下canvas,发现js中很多的东西都不太记得了。翻了下笔记后发现还是去图书馆逛逛把,到借阅区找了我一直想看的《javascript design patterns》好好研读了个下午,读罢,顿时有种醍醐顿开的感觉(夸张了..),发现之前对javascript OO方面的认识真的很浅,读了前几章关于OO的介绍后感觉思路清晰很多了,对于js一些基本概念的认识也加深了很多。同时也感概到程序员的想象力之丰富,可以将js模仿如此想传统的OO语言。当然这也更适合我们这些用惯了后台服务器语言开发的人来使用js。

      废话了一段,算是这个笔记的开始把,接下来就开始了。首先还是笔记下js如何模拟继承的。

      (一)js中的继承分为2种,一种是类式继承,另一种是原型式继承。

      1.关于原型链接

      关于继承,不得不先讲下js的原型链,因为这是js实现继承的基础。

      借用下《High Performance JavaScript》的一段代码和UML图,个人觉得这个是理解原型链比较好的例子。

    1   function Book(title, publisher){
    2 this.title = title;
    3 this.publisher = publisher;
    4 }
    5 Book.prototype.sayTitle = function(){
    6 alert(this.title);
    7 };
    8 var book1 = new Book("High Performance JavaScript", "Yahoo! Press");
    9 var book2 = new Book("JavaScript: The Good Parts", "Yahoo! Press");
    10 book1.sayTitle();
    11 book2.sayTitle();

    UML图如下:

      

      代码很简单,从UML图我们可以看到,这正是理解原型链的关键,这里有2个名词要着重提到,一个是原型对象, 一个是prototype对象。在js中每个对象都有原型对象的,原型对象是其构造函数的prototype属性所指向的那个对象,这里又有个名词要注意,由于js中处处是对象(当然除了3种原始类型,不过在需要的情况下,他们也可以被包装成对象的),为了区别,我们把构造函数称为函数对象。并且只有函数对象才拥有prototype属性,这一点我们可以从UML图中看出。

      从例子可以看到,每个实例化对象都有个_proto_这个内部属性,这个属性是指向其构造函数的prototype属性所指向的那个对象。这有什么用呢,我们看下这句代码:

    1  Book.prototype.sayTitle = function(){
    2 alert(this.title);
    3 };

      但我们调用:

    1  book1.sayTitle();

      的时候,由于book1里面没有sayTitle()这个方法,那么怎么办呢?这个时候原型链就有用了,他会沿着book1的原型对象去找sayTitle()这个方法,找到了就调用。

      理解了这一些,理解两种继承就很容易了。

      2.先看下类式继承的例子:

    1 //类式继承
    2 //Person类
    3   function Person(name){
    4 this.name = name;
    5
    6 }
    7 Person.prototype.getName = function(){
    8 alert(this.name);
    9 }
    10
    11 //BOY类,继承Person类
    12   function Boy(name,age){
    13 Person.call(this,name);
    14 this.age = age;
    15 }
    16 //将Person加到Boy的原型链中
    17   Boy.prototype = new Person();
    18 Boy.prototype.constructor = Boy;
    19
    20 Boy.prototype.getAge = function(){
    21 alert(this.age);
    22 }
    23
    24 var boy = new Boy("james","23");
    25 boy.getName();
    26 boy.getAge();

      用惯了java或C#等传统面向对象语言的人来看这些应该会有种亲切感把~,这种继承很符合我们关于传统继承的思想,先写个父类,再让子类来继承,可能16到18行的会有点不和谐对于不熟悉js的人来说,那么我们可以用一个extend方法把这个模拟继承的过程包装起来。

    1   //extend函数,负责将superClass加到subClass的原型链上
    2   function extend(subClass,superClass){
    3 function f(){};
    4 f.prototype = superClass.prototype;
    5 subClass.prototype = new f();
    6 subClass.prototype.constrcuctor = subClass;
    7
    8 }
    这样就可以用
    1   extend(Boy,Person);

      代替掉16到18行,这样基本就和传统的很接近了。

      还有一点要特别注意:就是prototype属性指向的是一个实例化了的对象

      3.接下来介绍原型式继承

      还是先看代码:

    1   //原型式继承
    2   //clone方法,主要是将object对象加到f的原型链上
    3   function clone(object){
    4 function f(){};
    5 f.prototype = object;
    6 return new f();
    7
    8 }
    9
    10 //直接定义一个js对象
    11   var Person ={
    12 name : "james",
    13 getName : function(){
    14 alert(this.name);
    15 }
    16
    17 }
    18
    19
    20 var boy = clone(Person);
    21 //还没改变前读取的是原来Person的默认值;
    22   boy.getName();  //输出James,即原来默认那个值
    23 boy.age = "23";
    24 boy.getAge = function(){
    25 alert(this.age);
    26 }
    27
    28 boy.name = "kobe";  //改变了name的值
    29 boy.getName();    //输出kobe,说明把原来的值覆盖了
    30 boy.getAge();
    31
    32
    33 var girl = clone(Person);
    34 girl.getName();    //输出james,即默认值
    35 girl.name = "tracy"; //改变了name 
    36 girl.getName();    //输出tracy,说明把原来值覆盖了
    37

      从第10行看起,首先我们定义了一个json对象Person,注意我们没用function,就是说这是一个对象了而不是函数对象,然后我们用一个clone方法将其赋给了boy变量,由开头可以知道clone方法其实就是把Person对象加到空对象f的原型链上,然后返回f给boy,原型链已经创建成功即继承已完成。

      但是,这种原型式继承有个比较特别的地方:读写的差异性。即刚开始的时候所有实例化对象都是指向同一份拷贝的,直到他们改变原来的值,这个从21到36行的实验我们可以看出他的差别。很直观,就不多说了。

      4.下面讨论下两种继承的优劣处:

      各有各的优缺点,这里我主要从习惯和性能两点来讨论。

      从习惯来说,类式继承对于用惯了C#等OO语言的人来说真是太舒服了,可以用原来的习惯来开发前台代码,好处自然是大大的。从这点来说,原型式继承就相对来说比较晦涩一点点了,后台人员还要转变思维来习惯,同时对他的读写差异性上理解也容易产生一些偏差。

      从性能来说,原型继承就会好一点,因为实例化对象的时候,在没改变值之前他们都是共享同一份拷贝的,这样可以节约内存开销。

      所以,选择哪一种还是要根据具体的项目需求来决定。

    ps:第一篇笔记终于完成,写了好久,呵呵~,写的过程基本没怎么翻书,都是凭借自己的理解来写的。原来想把知道的东西表达清楚也是要花下心思的...当然难免有什么理解不当,希望各位指出~
  • 相关阅读:
    python递归 及 面向对象初识及编程思想
    python匿名函数 与 内置函数
    python迭代器与生成器(二)
    linux---常用命令(二)
    linux---常用命令(一)
    Linux CentOS服务启动
    常见HTTP状态码
    oncontextmenu
    javascript之with的使用 弊端
    js的with语句使用方法
  • 原文地址:https://www.cnblogs.com/Quains/p/2011917.html
Copyright © 2011-2022 走看看