• 如何封装JS ----》JS设计模式《------ 封装与信息隐藏


    1. 封装与 信息隐藏之间的关系

        实质是同一个概念的两种表达,信息隐藏式目的,二封装是借以达到目的的技术方法。封装是对象内部的数据表现形式和实现细节,要想访问封装过额对象中的数据,只有使用自己定义的操作方法。通过封装可以强制实施信息的隐藏。

    2. 重新认识创建对象的基本方式

    01.用一个函数来做构造器

    var Book = function (isbn, title, author){
       if(isbn == undefined) throw new Error ('构造器需要一个 isbn');
       this.isbn = isbn;
       this.title = title || 'No title specified';
       this.author = author || 'No  author specified';
    }
    
    Book.prototype.display = function() {
      //  你的代码    
    }

    在构造器中,如果没有提供isbn ,将会抛出一个错误。title和author参数都是可选的,所以要准备默认值以防止他们未被提供。

    似乎这个类符合我们的需要,但是问题是我们无法检验isbn的数据完整性,不完整的数据可能会导致我们的程序出错。所以我们对Book类进行了强化,加强了对isbn的检查。

    代码如下:

    var Book = function(isbn,title,author){
       if(!this.checkIsbn(isbn)) throw new Error ('Book: Invalid Isbn');
       this.isbn = isbn;
       this.title = title || 'No title specified';
       this.author = author|| 'No author specified';
    }
    
    Book.prototype = {
       checkIsbn : function(isbn){
         if(isbn == undefined || typeof isbn != 'String'){
            return false;
          }
    
         isbn = isbn.replace(/-/, ''); //去掉虚线
    
         if(isbn.length != 10 && isbn.length != 13) {
           return false;
          }
    
         var sum = 0;
         if(isbn.length === 10) {
           if(!isbn.match(/^d{9}/)){//保证一至九 数字
               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 {
            if(!isbn.match(/^d{12}/)){
                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() {
         //你的代码
       }
    
    
    }                                

    这段代码 添加了一个checkIsbn方法,用来保障Isbn 是一个具有正确位数和检验的字符串,因为现在该类有了连个方法,所以Book.prototype被设置为一个对象字面量。现在情况有所改善,我们可以在构造器中对数据进行检验。但是别人会将什么样的值直接赋给isbn属性还是没有控制。

    这就需要用到,取值器和赋值器

    var Book = function(isbn,title,author){
         this.setIsbn(isbn);
         this.setTitle(title);
         this.setAuthor(author);
    };
    
    Book.prototype = {
       checkIsbn: function(){
       //检查所用代码
       },
       getIsbn: function(){
         return this.isbn;
        } ,
       setIsbn: function(){
         if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN' );
          this.isbn = isbn;
       },
       getTitle: function() {
         return this.title;
       },
       setTitle: function(){
         this.title = title||'No title specified'
       },
       display: function(){
       //你的代码
       }
       
    
    
    
    
    }        
    

    这种写法对数据有一定的保护作用,也非常容易编写,但是属性是公开的,可以直接设置。

    3. 在实现私有方法和属性之前 我们必须知道原理: 作用域、嵌套函数、闭包

    栗子:

    function foo(){
        var a=10;
         function bar(){
          a*=2;
         }
        bar();
        return a;
    
    }

    在这个栗子中,a定义在函数foo中,但函数bar可以访问它,因为bar也定义在foo中。bar在执行过程中将a设置为a*=2;当bar 在foo 中被调用时能够访问a,这可以理解,但是如果bar 在foo外部被调用呢?

    function foo(){
       var a = 10;
       function bar(){
         a*=2;
         return a;
      }
      return bar;
    
    }
    
    var baz = foo(); // baz 是bar 的一个引用
    baz(); //返回20
    baz(); //返回40
    baz(); //返回80
    
    var test = foo(); // 这是另外一个引用
    test(); // 返回20  因为是另外一个引用

    在这段代码中,所返回的对bar 函数的引用被赋给变量baz。这个函数现在是在foo外部被调用,但依然可以访问a。这是因为javaScript中的作用域是词法性的。 函数是运行在定义他们的作用域中,而不是运行在调用她们的作用域中。只用bar被定义在foo中,就可以访问foo中定义的所有变量,即使foo的执行已经结束;

    这就是一个闭包的例子,在foo返回后,它的作用域被保存下来,但只有它返回的那个函数能够访问这个作用域。---》返回一个内嵌函数是创建闭包的常用手段。

    那么,问题来了,如何用闭包实现私有成员

    借助闭包,我们可以创建只允许特定函数访问的变量,而且这些变量在这些函数的各次调用依然存在。为了创建私有属性,我们需要在构造函数的作用域中定义相关变量。这些变量可以被定义在该作用域中的所欲函数访问,包括特权方法。

    var Book = function(newIsbn,newTitle,newAuthor){
        var isbn , title, author;  //私有属性
        function checkIsbn(isbn){   // 私有方法
         // 检查isbn的代码
       }
    
     // 特权方法
      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(){
        title = newTitle || 'No title specified'
        }
        
       this.setIsbn(newIsbn);
        this.setTitle(newTitle);
        this.setAuthor(newAuthor);
    
    }
    
    Book.prototype = {
         display: function(){
       // 要写的代码
       }  
    }

    这与之前的有什么不一样呢? 在其他的例子中,我们创建和引用对象的属性是总是要用this关键字。而现在我们用var 生明这些变量。这意味着它们只存在于Book构造器中。checkIsbn函数也是同样声明的,因此成了一个私用方法;

    需要访问这些变量和函数的方法 只需声明在Book中即可。被称为 特权方法,因为它们是公用方法,但是却可以访问私用属性和方法。 为了在队形外部能访问这些特权方法,它们前面都加上了this关键字。因为它们定义在Book构造器的作用域中,所以它们可以访问私用属性。

    任何不需要直接访问私用属性的方法否可以在Book.prototype中声明。

    再看下边的代码:

    var Book = (function(){
       var numOfbooks = 0;
       function checkIsbn(isbn){
         // 检查代码
       }
       // 返回一个构造器
       retrun function(newIsbn,newTitle,newAuthor){
          var isbn ,title, author;
         
         this.getIsbn = function(){
           return isbn;
         }
         this.setIsbn = function(newIsbn){
           if(!checkIsbn(newIsbn)) throw new Error('54656');
           isbn = newIsbn
        }
        // 其它get , set 方法类似
       numOfbooks++;
       if(numOfbooks > 50) throw new Error ('book: only 50 instance o f book can be created')
    
       this.setIsbn(newIsbn);
       this.setTitle(newTitle);
        this.setAuthor(newAuthor);
       }
    
    })()
    
    Book.convertToTitleCace = function(inputString){
     //......
    }
    
    Book.prototype = function(){
      display:function(){
        //  ......
      }
    }

    这里的私用成员和特权成员仍然被声明在构造器中,但是构造器却从普通函数变成了一个内嵌函数,并且被作为包含他的函数的返回值赋给变量Book。这就创建了一个闭包。我们可以把静态的私用成员声明在里面。外层函数是一个立即执行函数,返回值是另一个函数,赋值给变量Book ,Book变成了一个构造函数,在实例化时,调用的是内层函数,外层函数只是用来创建一个可以存放静态私用成员的闭包。

    ps: 到此为止,我们应该可以基本理解对象的封装 及 创建私有化成员 。

         如果觉得文章不错,欢迎。。。你懂得。。。

  • 相关阅读:
    lau-表和for while循环
    lua学习笔记——lua数据类型
    javascript 高级程序设计1--14章重点总结
    消息推送之androidpn部署
    extjs底层源码实现继承分析
    cache数据库学习周结
    sql Cursor的用法
    java 报表到excel
    html5之datalist标签
    学习笔记之html5相关内容
  • 原文地址:https://www.cnblogs.com/vali/p/6252054.html
Copyright © 2020-2023  润新知