javascript实现私有变量

js中对象的属性按照私有程度来说三种,第一种完全暴露型(fully exposed)只能提供公用成员,第二种方法使用下划线来表示方法或者属性的私用性。第三种是真正私用的成员,这些成员只能通过一些特权方法访问,可以通过闭包或者利用es6的语法实现

完全暴露

   var Book = function(isbn, title, author){
      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(){
      ...
   }

这里的isbn如果在构造函数中没有定义,就会抛出错误,但我们也许需要对这些数据有更多的验证,那么就来到以下这种

    var Book = function(isbn, title, author){
        this.setIsbn(isbn);
        this.setTitle(title);
        this.setAuthor(author);
    }
    Book.prototype = {
        checkIsbn: function(isbn){
           return isbn == undefined;
        },
        setIsbn: function(isbn){
            if(!this.checkIsbn) throw new Error('..');
            this.isbn = isbn;
        },
        setTitle: function(title){
            this.title = title || 'No title specified';
        },
        getTitle: function(){
            return this.title;
        },
        ...
    }
    var book = new Book('a', 'book1', 'sysuzhyupeng');
    book.getTitle();   // 'book1'
    book.title = 'book2';
    book.getTitle();   // 'book2' 被改变了

尽管这样的set、get方法的设置很像一些面向对象语言如java,但没有使用private关键字导致了title、author这些属性还是公开的,这样对内部数据就无法进行保护,其他一起协作的人可能无意改动了这些属性

用命名规范区别私用成员

下划线是一种命名规范,它表明一个属性(或方法)仅供对象内部使用,直接访问它或者设置它可能导致意想不到的后果

   var Book = function(isbn, title, author){
        this.setIsbn(isbn);
        this.setTitle(title);
        this.setAuthor(author);
    }
    Book.prototype = {
        _checkIsbn: function(isbn){
           return isbn == undefined;
        },
        setIsbn: function(isbn){
            if(!this._checkIsbn) throw new Error('..');
            this._isbn = isbn;
        },
        setTitle: function(title){
            this._title = title || 'No title specified';
        },
        getTitle: function(){
            return this._title;
        },
        ...
    }
    var book = new Book('a', 'book1', 'sysuzhyupeng');
    book.getTitle();   // 'book1'
    //然而我还是可以偷偷改变title
    book._title = 'book2';
    book.getTitle();   // 'book2'

缺点显而易见。

使用闭包实现私有变量

在js中,只有函数具有作用域,在一些强类型语言如java中的{}就能创建块级作用域,这在js中并不存在。那么私有属性本质而言就是你希望对象外部无法访问的变量,所以实现这种需求而求助于作用域是合情合理的

   var Book = function(newIsbn, newTitle, newAuthor){
       //private attributes
       var isbn, title, author;
       //private method
       function checkIsbn(isbn){
           ...
       }
       //privileged methods
       this.getTitle = function(){
          //返回变量title,而不是this.title
          return title;
       }
       this.setTitle = function(newTitle){
          title = newTitle || 'No title specified'
       }
       //Constructor code
       this.setTitle(newTitle);
   }
   //Public, non-privileged methods
   Book.prototype = {
       display: function(){
           ...
       }
   }
   var book = new Book('a', 'book1', 'sysuzhyupeng');
   book.getTitle();  // 'book1'
   book.title       // undefined,只能通过上面的方法访问

原本我们用对象的属性,而现在使用var而不是this声明这些变量,这意味着它们只存在于Book构造器中。那么我们使用get方法取这些变量的时候,形成了闭包。 需要访问这些私有变量的方法声明在Book中即可,这些方法称为特权方法,任何不需要直接访问私有属性的方法都可以像原来那样在Book.prototype中声明。用这种对象创建方式真正实现了私有变量,但有一些缺点。完全暴露型所有方法都在原型对象中,不管new了多次,生成多少对象实例,对于方法的记录内存中只需要一份,所以这里的闭包方法性能不好。其次,这个方法也不适用于派生子类,因为js的原型继承围绕原型,可以参考我的另一篇文章原型继承和应用,一旦实现继承,子类对象根本拿不到父类中的私有变量,导致了“继承破坏封装”

通过es6实现私有变量

在es5中就已经可以通过definedProperty方法中的访问器,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

   var book = {
      //不能是title,必须与title不同,这里使用常用命名规范,在前面加了下划线
      _title: 'book1'
   }
   Object.defineProperty(book, 'title', {
      get: function(){
         //同样不能是title,使用_title
         return this._title;
      },
      set: function(value){
         value == 'book2' ?
         this._title = value : '' 
      }
   })
   book.title   // 'book1'
   book.title = 'book3'; 
   book.title  // 'book1'
   book.title = 'book2';
   book.title  // 'book2'

不方便的是这个是在创建了对象实例之后再define的,在es6中

   class Book {
       constructor(){
          this._title = 'book1';
       }
       get title(){
          return this._title;
       }
       set title(value){
          value == 'book2' ?
          this._title = value : '' 
       }
   }
   var book = new Book();
   book.title = 'book3';
   book.title  // 'book1'
   book.title = 'book2';
   book.title  // 'book2' 

es6中同样是构造另一个属性_title,而在读写title多了一层拦截层,然后偷梁换柱成了_title,从而实现了私有变量。如果兼容了es6,这个方法就是目前看来最好的解决方案

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值