Javascript中怎么定义类(私有成员、静态成员)?

本文将从一个简单例子演绎怎么定义一个具有私有成员、静态成员的类。

 

好,看简单原型:

var Book =  function (isbn, title, author) {
     if (isbn === undefined) {
         throw  new Error('Book constructor requires an isbn.');
    }

     this.isbn = isbn;
     this.title = title || 'No title specified';
     this.author = author || 'No author specified';
};
Book.prototype.display =  function () {
    alert("isbn: " +  this.isbn + ", title: " +  this.title + ", author: " +  this.author);

}; 

实例中再简单不过,不过就是定义了一个有3个参数的构造函数和一个diplay实例方法。问题在于:Book的isbn属性虽然在构造中进行了简单判断,但是这还不够。改进如下:

var Book =  function (isbn, title, author) {
     if (!isValidIsbn(isbn)) {
         throw  new Error('The isbn passed in is invalid.');
    }

     this.isbn = isbn;
     this.title = title || 'No title specified';
     this.author = author || 'No author specified';
};
Book.prototype = {
    isValidIsbn:  function (isbn) {
         if(isbn === undefined ||  typeof isbn != 'string') {
             return  false;
        }

        isbn = isbn.replace(/-/, '');  //  Remove dashes.
         if(isbn.length != 10 && isbn.length != 13) {
             return  false;
        }
         var sum = 0;
         if(isbn.length === 10) {  //  10 digit ISBN.
             if(!isbn.match(/^\d{9}/)) {  //  Ensure characters 1 through 9 are digits.
                 return  false;
            }
             for( var i = 0; i < 9; i++) {
                sum += isbn.charAt(i) * (10 - i);
            }
             var checksum = sum % 11;
             if(checksum === 10) checksum = 'X';
             if(isbn.charAt(9) != checksum) {
                 return  false;
            }
        } else {  //  13 digit ISBN.
             if(!isbn.match(/^\d{12}/)) {  //  Ensure characters 1 through 12 are digits.
                 return  false;
            }
             for( var i = 0; i < 12; i++) {
                sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
            }
             var checksum = sum % 10;
             if(isbn.charAt(12) != checksum) {
                 return  false;
            }
        }

         return  true;
    },
    display:  function(){
        alert("isbn: " +  this.isbn + ", title: " +  this.title + ", author: " +  this.author);
    }

}; 

新加入的isValidIsbn实例方法主题来源于Pro Javascript Design Pattern(同时修正了里面的部分错误)。借鉴于主流OOP语言做法,我们继续也为Book类增加访问器。如下:

 

// var Publication = new Interface("Publication", ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);

var Book =  function (isbn, title, author) {
     this.setIsbn(isbn);
     this.setTitle(title);
     this.setAuthor(author);
};
Book.prototype = {
    isValidIsbn:  function (isbn) {
         if (isbn === undefined ||  typeof isbn != 'string') {
             return  false;
        }

        isbn = isbn.replace(/-/, '');  //  Remove dashes.
         if (isbn.length != 10 && isbn.length != 13) {
             return  false;
        }
         var sum = 0;
         if (isbn.length === 10) {  //  10 digit ISBN.
             if (!isbn.match(/^\d{9}/)) {  //  Ensure characters 1 through 9 are digits.
                 return  false;
            }
             for ( var i = 0; i < 9; i++) {
                sum += isbn.charAt(i) * (10 - i);
            }
             var checksum = sum % 11;
             if (checksum === 10) checksum = 'X';
             if (isbn.charAt(9) != checksum) {
                 return  false;
            }
        }  else {  //  13 digit ISBN.
             if (!isbn.match(/^\d{12}/)) {  //  Ensure characters 1 through 12 are digits.
                 return  false;
            }
             for ( var i = 0; i < 12; i++) {
                sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
            }
             var checksum = sum % 10;
             if (isbn.charAt(12) != checksum) {
                 return  false;
            }
        }

         return  true;
    },
    getIsbn:  function () {
         return  this.isbn;
    },
    setIsbn:  function (isbn) {
         if (! this.isValidIsbn(isbn)) {
             throw  new Error('The isbn passed in is invalid.');
        }
         this.isbn = isbn;
    },
    getTitle:  function () {
         return  this.title;
    },
    setTitle:  function (title) {
         this.title = title || 'No title specified';
    },
    getAuthor:  function () {
         return  this.author;
    },
    setAuthor:  function (author) {
         this.author = author || 'No author specified';
    },
    display:  function () {
        alert("isbn: " +  this.getIsbn() + ", title: " +  this.getTitle() + ", author: " +  this.getAuthor());
    }
};   

 

但是,这就够了么?实际上,我们还是可以绕过访问器,直接通过对象的属性来操作对象。比如this.isbn="abc",这显然是invalid的,但是可以赋值成功。我们要做的是让isbn、title、author这些成员私有!


好,先采用业界普遍认同的方法——将伪私有变量已下划线开头:

// var Publication = new Interface("Publication", ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);

var Book =  function (isbn, title, author) {
     this.setIsbn(isbn);
     this.setTitle(title);
     this.setAuthor(author);
};
Book.prototype = {
    _isValidIsbn:  function (isbn) {
         if (isbn === undefined ||  typeof isbn != 'string') {
             return  false;
        }

        isbn = isbn.replace(/-/, '');  //  Remove dashes.
         if (isbn.length != 10 && isbn.length != 13) {
             return  false;
        }
         var sum = 0;
         if (isbn.length === 10) {  //  10 digit ISBN.
             if (!isbn.match(/^\d{9}/)) {  //  Ensure characters 1 through 9 are digits.
                 return  false;
            }
             for ( var i = 0; i < 9; i++) {
                sum += isbn.charAt(i) * (10 - i);
            }
             var checksum = sum % 11;
             if (checksum === 10) checksum = 'X';
             if (isbn.charAt(9) != checksum) {
                 return  false;
            }
        }  else {  //  13 digit ISBN.
             if (!isbn.match(/^\d{12}/)) {  //  Ensure characters 1 through 12 are digits.
                 return  false;
            }
             for ( var i = 0; i < 12; i++) {
                sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
            }
             var checksum = sum % 10;
             if (isbn.charAt(12) != checksum) {
                 return  false;
            }
        }

         return  true;
    },
    getIsbn:  function () {
         return  this._isbn;
    },
    setIsbn:  function (isbn) {
         if (! this._isValidIsbn(isbn)) {
             throw  new Error('The isbn passed in is invalid.');
        }
         this._isbn = isbn;
    },
    getTitle:  function () {
         return  this._title;
    },
    setTitle:  function (title) {
         this._title = title || 'No title specified';
    },
    getAuthor:  function () {
         return  this._author;
    },
    setAuthor:  function (author) {
         this._author = author || 'No author specified';
    },
    display:  function () {
        alert("isbn: " +  this.getIsbn() + ", title: " +  this.getTitle() + ", author: " +  this.getAuthor());
    }

}; 

这样貌似可以,至少老鸟们一般不会再Book实现外部去碰它们。但是,有没有更纯的? 有!请看:

 

var Book =  function (isbn, title, author) {
     var _isbn, _title, _author;

     function isValidIsbn(isbn) {
         if (isbn === undefined ||  typeof isbn != 'string') {
             return  false;
        }

        isbn = isbn.replace(/-/, '');  //  Remove dashes.
         if (isbn.length != 10 && isbn.length != 13) {
             return  false;
        }
         var sum = 0;
         if (isbn.length === 10) {  //  10 digit ISBN.
             if (!isbn.match(/^\d{9}/)) {  //  Ensure characters 1 through 9 are digits.
                 return  false;
            }
             for ( var i = 0; i < 9; i++) {
                sum += isbn.charAt(i) * (10 - i);
            }
             var checksum = sum % 11;
             if (checksum === 10) checksum = 'X';
             if (isbn.charAt(9) != checksum) {
                 return  false;
            }
        }  else {  //  13 digit ISBN.
             if (!isbn.match(/^\d{12}/)) {  //  Ensure characters 1 through 12 are digits.
                 return  false;
            }
             for ( var i = 0; i < 12; i++) {
                sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
            }
             var checksum = sum % 10;
             if (isbn.charAt(12) != checksum) {
                 return  false;
            }
        }

         return  true;
    }

     this.getIsbn =  function () {
         return _isbn;
    }
     this.setIsbn =  function () {
         if (!isValidIsbn(isbn)) {
             throw  new Error('The isbn passed in is invalid.');
        }
        _isbn = isbn;
    };
     this.getTitle =  function () {
         return _title;
    };
     this.setTitle =  function () {
        _title = title || 'No title specified';
    };
     this.getAuthor =  function () {
         return _author;
    };
     this.setAuthor =  function () {
        _author = author || 'No author specified';
    };

     this.setIsbn(isbn);
     this.setTitle(title);
     this.setAuthor(author);
};
Book.prototype.display =  function () {
    alert("isbn: " +  this.getIsbn() + ", title: " +  this.getTitle() + ", author: " +  this.getAuthor());
};    

 

可以看到,3个被期望为私有的属性,先不在绑定在this上了,而是通过var声明在构造器内。显然,它们真的成为私有了,你是无法通过类似于new Book()._isbn来直接访问和改写它们了。然而,这个实现也有一个明显的缺点,那就是对于每个Book实例,它们的访问器都是各有一份,当需要创建多个Book对象实例时,内存就需要被多占用一些了。不过,这对于目前主流的机器来说,可能不是一个问题。

 

 

私有成员的实现到此为止,那静态成员的实现呢?关于静态方法,前面已无声无息的有涉及和实现,这里不再累赘。但是,我这里要讲的是怎么实现涉及到内部私有成员信息的静态成员。比如,我想知道Book被实例了多少次,即想要给Book内部定制一个计数器。先看看一个demo:

var Class = ( function () {
     var constants = {
        UpperBound: 100,
        LowerBound: -100
    };
     var instanceCount = 0;

     var _Class =  function (id, name) {
         var _id, _name;

         this.getId =  function () {
             return _id;
        };
         this.setId =  function (id) {
            _id = id;
        };
         this.getName =  function () {
             return _name;
        };
         this.setName =  function (name) {
            _name = name;
        };

         this.setId(id);
         this.setName(name);
        ++instanceCount;
    };
    _Class.getUpperBound =  function () {
         return constants.UpperBound;
    };
    _Class.getLowerBound =  function () {
         return constants.LowerBound;
    };
    _Class.getInstanceCount =  function () {
         return instanceCount;
    };

     return _Class;
})();

var c1 =  new Class(1, "Zhangsan");
var c2 =  new Class(2, "Lisi");
alert(Class.getLowerBound());

alert(Class.getInstanceCount()); 

Class类共有3个静态方法:getUpperBound()、getLowerBound()、getInstanceCount(),它们均实际为Class类内部私有属性的外部访问器。缘于该私有属性的实现方式和方法的性质,我思考再三,终于自己找到了解决方案!即通过声明内部_Class类,并将它绑定静态方法,以访问闭包内变量,然后将_Class引用抛出。真的是闭包功力无敌!好,终极的Book实现如下:



         var Book = (function () {         

            var numOfBooks = 0;

            function checkIsbn(isbn) {
                if (isbn === undefined || typeof isbn != 'string') {
                    return false;
                }

                isbn = isbn.replace(/-/, ''); // Remove dashes.
                if (isbn.length != 10 && isbn.length != 13) {
                    return false;
                }
                var sum = 0;
                if (isbn.length === 10) { // 10 digit ISBN.
                    if (!isbn.match(/^\d{9}/)) { // Ensure characters 1 through 9 are digits.
                        return false;
                    }
                    for (var i = 0; i < 9; i++) {
                        sum += isbn.charAt(i) * (10 - i);
                    }
                    var checksum = sum % 11;
                    if (checksum === 10) checksum = 'X';
                    if (isbn.charAt(9) != checksum) {
                        return false;
                    }
                } else { // 13 digit ISBN.
                    if (!isbn.match(/^\d{12}/)) { // Ensure characters 1 through 12 are digits.
                        return false;
                    }
                    for (var i = 0; i < 12; i++) {
                        sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
                    }
                    var checksum = sum % 10;
                    if (isbn.charAt(12) != checksum) {
                        return false;
                    }
                }

                return true;
            }

            var _Book = function (newIsbn, newTitle, newAuthor) {
                var isbn, title, author;

                this.getIsbn = function () {
                    return isbn;
                };
                this.setIsbn = function (newIsbn) {
                    if (!checkIsbn(newIsbn)) {
                        throw new Error('Book: Invalid ISBN.');
                    }
                    isbn = newIsbn;
                };
                this.getTitle = function () {
                    return title;
                };
                this.setTitle = function (newTitle) {
                    title = newTitle || 'No title specified';
                };
                this.getAuthor = function () {
                    return author;
                };
                this.setAuthor = function (newAuthor) {
                    author = newAuthor || 'No author specified';
                };

                numOfBooks++;
                if (numOfBooks > 50) {
                    throw new Error('Book: Only 50 instances of Book can be created.');
                }

                this.setIsbn(newIsbn);
                this.setTitle(newTitle);
                this.setAuthor(newAuthor);
            };
            _Book.getBookTotalCount = function () {
                return numOfBooks;
            };

            return _Book;
        })();

// Public static method.
Book.convertToTitleCase = function (inputString) {
    return inputString.toUpperCase();
};

// Public, non-privileged methods.
Book.prototype = {
    display: function () {
        alert("isbn: " + this.getIsbn() + ", title: " + this.getTitle() + ", author: " + this.getAuthor()); 
    }
};

var b1 = new Book("9787533668293", "How Apple works", "Steve Jobs");
var b2 = new Book("7883653519", "Biography Bill Gates", "Raymond");
b1.display();
//b2.display();
//Book.convertToTitleCase("It's only a test.");
alert(Book.getBookTotalCount());

好好学习,这边文章含金量绝对很高哦。全部源码download

转载于:https://www.cnblogs.com/Langzi127/archive/2012/09/08/2677089.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值