zoukankan      html  css  js  c++  java
  • JavaScript实现类的private、protected、public、static以及继承

    基础知识

    JavaScript中的类

    JavaScript实际上是一种弱类型语言,与C++和Java等语言不同。因此,在JavaScript中。没有强调类(class)这一概念,但实际运用中,类还是非常重要的,比方写一款游戏。假设我们不停地调用函数来完毕创建角色,移动角色的话,那会是什么样的呢?可能会出现非常多的反复代码,因此我们须要一个类来统一这些代码。所谓的类,就是把程序中的代码分类,比方说游戏中的关于角色的代码算作一类,游戏背景算作一类,游戏特效又是一类。这样一来,我们对类进行操作。就不会使代码显得非常凌乱。冗杂。尽管Js是弱类型语言,可是也提供了类这一概率。


    定义Js中的类,实际上用的是function。总所周知,这个语法事实上是用来定义函数的。不同于定义函数的是,我们能够在function中通过this.xxx的方式来定义属性和方法。

    比方说:

    function People () {
        this.name = "Yorhom";
    
        this.getName = function () {
            return this.name
        };
    }

    使用的时候使用new

    var yorhom = new People();
    // "Yorhom"
    alert(yorhom.getName());

    能够看到。这样就能够使用到我们定义的类和类中的方法了。
    或许你会问this.xxx仅仅能定义公有属性和方法。那私有属性和方法怎么办呢?这个能够用到js闭包的知识来解决:

    function People () {
        this.name = "Yorhom";
    
        var age = 16;
    
        this.getName = function () {
            return this.name
        };
    
        this.getAge = function () {
            return age;
        };
    }
    
    var yorhom = new People();
    // undefined
    alert(yorhom.age);
    // 16
    alert(yorhom.getAge());

    能够看到,这里的age就是一个私有属性了。

    JavaScript中的prototype

    上面的代码美中不足的地方就是,假设一个类有非常多方法,同一时候用到这个类的地方又有非常多(也就是new出来的对象有非常多),那么用上面的代码就会出现内存占用过剩的问题。问题的根本原因在于,每次实例化一个对象,这个类就会执行构造器里的代码(以People类为例就是function People () {…}执行的代码)。因此每当这个类被实例化的时候,这些方法和属性就会被复制到实例化出来的对象中。这样一来,就会造成“吃”内存的现象。
    于是js中的prototype就诞生了。prototype的作用一般是给一个类加入一系列常量或者方法。 每当一个类被实例化之后。实例化出来的对象会自己主动获取类的prototype中定义的方法和属性。

    仅仅只是这里的获取相似于C++里面的引用,不会在内存里对这些方法和属性进行复制,而是指向这些方法和属性。演示样例:

    function People () {
        this.name = "Yorhom";
    }
    
    People.prototype.getName = function () {
        return this.name;
    };
    
    var yorhom = new People();
    // "Yorhom"
    alert(yorhom.getName());

    这样的方法尽管能够节约内存,可是。美中不足的是,无法定义私有属性。

    类的继承

    Javascript没有提供继承的函数。所以仅仅有自己写了。

    这里借用lufylegend.js中的继承方法向大家展示怎样实现继承:

    function base (d, b, a) {
        var p = null, o = d.constructor.prototype, h = {};
    
        for (p in o) {
            h[p] = 1;
        }
        for (p in b.prototype) {
            if (!h[p]) {
                o[p] = b.prototype[p];
            }
        }
    
        b.apply(d, a);
    }

    这里的base就是继承函数了。继承函数的原理莫过于复制类的方法和属性。因此。仅仅要做到这点,就能够实现类的继承了。能够在上面的代码中看见,我们通过遍历prototype来获取原型链中定义的方法和属性。通过apply调用父类的构造器进行构造器中属性和方法的复制。使用演示样例:

    function People () {
        this.name = "Yorhom";
    }
    
    People.prototype.getName = function () {
        return this.name;
    };
    
    function Student () {
        base(this, People, []);
    }
    
    var yorhom = new Student();
    // "Yorhom"
    alert(yorhom.getName());

    静态属性和方法的定义

    静态属性和方法以及静态类在js中的定义非常easy。先来看静态类:

    var StaticClass = {};

    这么写不是在定义一个Object吗?是的,不错。只是js中的静态类也是能够这样定义的。假设要加入静态类中的方法和属性,就能够这么写:

    var StaticClass = {
        id : 5,
        sayHello : function () {
            alert("Hello"); 
        }
    };

    假设是要向类中加入静态属性或者方法,能够採用这样的写法:

    function People () {
        this.name = "Yorhom";
    }
    
    People.prototype.getName = function () {
        return this.name;
    };
    
    People.TYPE = "people";
    People.sayHello = function () {
        alert("Hello");
    };

    实现一个功能丰富的类

    我们在上文中提到了,节省内存和定义私有属性两者无法兼得,是啊,和“鱼和熊掌不可兼得”是一个道理。在通常的使用过程中,我们须要对这两项进行取舍。可是如今这个年代。哪有不可兼得的呢?鱼和熊掌不能同一时候吃?当然不行……由于吃熊掌是违法的(有待考证)?只是至少鸡和鱼是能够同一时候吃的吧。
    由于js没有实现私有属性的定义。所以这事实上是一个没有头绪的工作,由于在标准的做法中,我们除了闭包能够阻止外部訪问。没有别的办法了。所以这里我们要用点歪门邪道的方法了。

    JavaScript Set/Get訪问器

    什么是set/get訪问器呢?假设你熟悉python,那么你能够理解为@property@xxx.setter,可是简陋的js里也有?当然有,仅仅只是是ES5的标准,能够採用这样的写法:

    Object.defineProperty(this, "name", {
        get : funtion () {
            return name;
        },
    
        set : function (v) {
            name = v;
        }
    });

    详细有什么用呢?大致就是this.name属性在被获取的时候调用get訪问器,在被更改值的时候调用set
    你能够从上面的代码了解大致的写法,只是假设你想深究,能够參考这篇文章:http://blog.csdn.net/teajs/article/details/22738851

    注意以上的这样的使用方法会有兼容性问题。浏览器支持情况例如以下:

    PC端

    Firefox Google Chrome Internet Explorer Opera Safari
    4.0 5 9 11.6 5.1

    移动端

    Firefox Mobile Android IE Mobile Opera Mobile Safari Mobile
    4.0 Yes 9 11.5 Yes

    来自: https://developer.mozilla.org/…/defineProperty#Browser_compatibility

    怎样“歪门邪道”地做到禁止訪问私有和保护属性?

    这是个比較头疼的问题,正如本节开篇所说,我们在常规开发下。仅仅能通过闭包来阻止某变量的訪问。

    可是假设你使用了prototype。那么闭包这条路就走不通了。在这样的情况下,我们的Object.defineProperty就出场了。我们知道,通过这个函数能够设定获取属性时返回的值,也能够设定更改属性时设置的值。

    有了这个函数,我们能够随时跟踪到某个属性是不是在被获取,或者是不是在被更改。我们还须要一个开关,我们在类内部的方法调用时,把这个开关打开,表明是在内部执行。方法调用结束后将开关关闭。表明回到外部执行状态。

    有了这两个状态,我们就能够跟踪privateprotected属性和方法了,一旦他们在开关关闭的时候被使用。就终止这个属性或方法的获取或设置。
    于是乎,大难题就快攻克了。

    开源库件jpp.js

    秉着这个歪门邪道的思想,我把这个功能封装到jpp.js这个库件中,库件的github地址例如以下:
    https://github.com/yuehaowang/jpp.js
    当然这个库件不限于创建一个类。还能够实现函数的重载等。眼下库件还处于开发阶段,欢迎各位提交建议。

    使用jpp.js创建一个类

    var People = jpp.class({
        extends : null,
        private : {
            id : null,
            hobby : null
        },
        protected : {
            money : null,
            phoneNumber : null
        },
        public : {
            firstName : null,
            lastName : null,
            age : null,
            birthday : null,
            occupation : null,
    
            constructor : function (name, id) {
                if (name) {
                    var nameArray = name.split(" ");
    
                    this.firstName = nameArray[0];
                    this.lastName = nameArray[1];
                }
    
                if (id) {
                    this.id = id;
                }
            },
    
            setBirthday : function (date) {
                if (date) {
                    this.birthday = date;
                }
            },
    
            getBirthday : function () {
                return this.birthday;
            },
    
            askForId : function () {
                return this.id;
            },
    
            findHobby : function () {
                return this.hobby;
            }
        },
        static : {
            OCCUPATION_PROGRAMMER : "programmer",
            OCCUPATION_ARTIST : "artist",
            OCCUPATION_MUSICIAN : "musician",
            OCCUPATION_STUDENT : "student"
        }
    });
    
    var peter = new People("Peter Wong", 543232123565);
    peter.occupation = People.OCCUPATION_PROGRAMMER;
    
    peter.setBirthday("19980727");
    
    // result: Peter
    alert(peter.firstName);
    // result: 19990727
    alert(peter.getBirthday());
    // result: 51092028
    alert(peter.askForId());
    // result: null
    alert(peter.findHobby());
    // result: programmer
    alert(peter.occupation);
    // error
    alert(peter.id);

    对上面的代码进行分析:
    使用jpp.class函数创建一个类,函数的參数是一个Object,这个Object可加入的属性例如以下:

    • extends 继承时的父类
    • private 装载私有属性,里面定义的成员外部不可使用且不能继承给子类
    • protected 装载保护属性,里面定义的成员外部不可使用但能够继承给子类
    • public 装载公有属性
    • static 装载静态方法和属性

    在创建类的过程中,在public中加入constructor方法初始化构造器,this.super可訪问父类构造器。

    执行代码,能够看到浏览器正常执行前5个alert。而最后一个执行的时候浏览器报错:

    详细的实现过程有点复杂。只是原理在上文已经详细讲述了。代码能够在github里參看,欢迎各位研究。


    欢迎大家继续关注我的博客

    转载请注明出处:Yorhom’s Game Box

    http://blog.csdn.net/yorhomwang

  • 相关阅读:
    [转]只有tcp6没有tcp问题
    Makefile 中:= ?= += =的区别
    【转】docker images 介绍
    [转]我眼中的 Docker(二)Image
    【转】一个简单的Dockerfile实例
    【转】Prometheus 介绍
    [转]MySQL索引类型按存储类型和逻辑区分
    【转】mysql索引类型
    用Unity制作游戏,你需要深入了解一下IL2CPP
    c++中CreateEvent函数
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7375295.html
Copyright © 2011-2022 走看看