javascript实现私有变量

来源:互联网 发布:jqueryuicore.min.js 编辑:程序博客网 时间:2024/05/16 09:32

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 0
原创粉丝点击