Object-oriented JavaScript III (encapsulation and information hiding) _js object-oriented

Source: Internet
Author: User
Tags naming convention
At the same time, we know that in object-oriented high-level languages, creating objects that contain private members is one of the most basic features, providing properties and methods to access private members to hide internal details. Although JS is Object oriented, there is no internal mechanism to directly indicate whether a member is public or private. Or that sentence, depending on the language flexibility of JS, we can create public, private and privileged members, information hiding is our goal to achieve, and encapsulation is our way to achieve this goal. Let's go from an example: Create a class to store the book data and implement it to display the data in a Web page.

1. The simplest is to completely expose the object. Create a class using the constructor, where all of the properties and methods are accessible externally.
Copy Code code as follows:

var book = function (ISBN, title, author) {
if (ISBN = = undefined) {
throw new Error ("book constructor requires a ISBN.");
}
THIS.ISBN = ISBN;
this.title = Title | | "";
This.author = Author | | "";
}
Book.prototype.display = function () {
Return "BOOK:ISBN:" + THIS.ISBN + ", Title:" + This.title + ", Author:" + this.author;
}

The display method relies on whether the ISBN is correct, and if not, you will not be able to get the images and links. With this in mind, each book ISBN must exist, and the title and author of the book are optional. On the surface it seems as if an ISBN parameter is specified to function correctly. But can not guarantee the integrity of the ISBN, based on this we add an ISBN verification, so that the examination of books more robust.
Copy Code code as follows:

var book = function (ISBN, title, author) {
if (!THIS.CHECKISBN (ISBN)) {
throw new Error ("Book:invalid ISBN.");
}
THIS.ISBN = ISBN;
this.title = Title | | "";
This.author = Author | | "";
}
Book.prototype = {
Checkisbn:function (ISBN) {
if (ISBN = = undefined | | typeof ISBN!= "string") return false;
ISBN = isbn.replace ("-", "");
if (isbn.length!= && isbn.length!=) 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 = =) 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 ()!= checksum) return false;
}
return true;
},
Display:function () {
Return "BOOK:ISBN:" + THIS.ISBN + ", Title:" + This.title + ", Author:" + this.author;
}
};

We have added CHECKISBN () to verify the validity of the ISBN, and to ensure that display () works correctly. But the demand has changed, each book may have multiple versions, meaning that the same may have multiple ISBN number exists, need to maintain a separate version of the algorithm to control. While it is possible to check the integrity of the data, but cannot control the external access to internal members (such as the Isbn,title,author assignment), there is no protection of internal data. We continue to improve this solution using interface implementations (providing get accessor/set storage).
Copy Code code as follows:

var publication = new Interface ("publication", ["GETISBN", "SETISBN", "CHECKISBN", "GetTitle", "Settitle", "Getauthor", "" Setauthor "," display "]);
var book = function (ISBN, title, author) {
Implements publication interface
THIS.SETISBN (ISBN);
This.settitle (title);
This.setauthor (author);
}
Book.prototype = {
Getisbn:function () {
return THIS.ISBN;
},
Setisbn:function (ISBN) {
if (!THIS.CHECKISBN (ISBN)) {
throw new Error ("Book:invalid ISBN.");
}
THIS.ISBN = ISBN;
},
Checkisbn:function (ISBN) {
if (ISBN = = undefined | | typeof ISBN!= "string") return false;
ISBN = isbn.replace ("-", "");
if (isbn.length!= && isbn.length!=) 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 = =) 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 ()!= checksum) return false;
}
return true;
},
Gettitle:function () {
return this.title;
},
Settitle:function (title) {
this.title = Title | | "";
},
Getauthor:function () {
return this.author;
},
Setauthor:function (author) {
This.author = Author | | "";
},
Display:function () {
Return "BOOK:ISBN:" + THIS.ISBN + ", Title:" + This.title + ", Author:" + this.author;
}
};

It is now possible to communicate with the outside world through an interface publication. The assignment method is also completed within the constructor, without the need to implement the same validation two times, seemingly perfect completely exposed object scenarios. Although properties can be set through the set storage, these properties are still public and can be directly assigned. But there's nothing I can do about it, and I'm going to optimize it in the second kind of information-hiding solution. Still, this scenario is easy to start with for beginners who don't have a deep understanding of the scope. The only disadvantage is the inability to protect internal data and the extra unnecessary code that is added to the memory.
2. Use a private method of naming rules. is to use underscores to identify private members and to avoid inadvertently assigning private members, essentially the same as fully exposed objects. But this avoids the first scheme that does not intend to assign to private members, but does not avoid intentionally setting private members. It's just that defining a naming convention that requires team members to follow is not a perfect solution to a real internal information hiding.
Copy Code code as follows:

var publication = new Interface ("publication", ["GETISBN", "SETISBN", "GetTitle", "Settitle", "Getauthor", "Setauthor", "" Display "]);
var book = function (ISBN, title, author) {
Implements publication interface
THIS.SETISBN (ISBN);
This.settitle (title);
This.setauthor (author);
}
Book.prototype = {
Getisbn:function () {
return THIS._ISBN;
},
Setisbn:function (ISBN) {
if (!THIS._CHECKISBN (ISBN)) {
throw new Error ("Book:invalid ISBN.");
}
THIS._ISBN = ISBN;
},
_checkisbn:function (ISBN) {
if (ISBN = = undefined | | typeof ISBN!= "string") return false;
ISBN = isbn.replace ("-", "");
if (isbn.length!= && isbn.length!=) 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 = =) 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 ()!= checksum) return false;
}
return true;
},
Gettitle:function () {
return this._title;
},
Settitle:function (title) {
This._title = Title | | "";
},
Getauthor:function () {
return this._author;
},
Setauthor:function (author) {
This._author = Author | | "";
},
Display:function () {
Return "BOOK:ISBN:" + THIS.GETISBN () + ", Title:" + this.gettitle () + ", Author:" + this.getauthor ();
}
};

Note: In addition to the Isbn,title,author property being labeled "_" as a private member, the CHECKISBN () is also identified as a private method.

3. The genuine privatization of members through closures. For friends who are unfamiliar with scopes and nested functions in the concept of closures, you can refer to the article "One of the object-oriented JavaScript (beginner JavaScript)", which is not discussed in detail here.
Copy Code code as follows:

var publication = new Interface ("publication", ["GETISBN", "SETISBN", "GetTitle", "Settitle", "Getauthor", "Setauthor", "" Display "]);
var book = function (NEWISBN, Newtitle, Newauthor) {
Private attribute
var ISBN, title, author;
Private method
Function Checkisbn (ISBN) {
if (ISBN = = undefined | | typeof ISBN!= "string") return false;
ISBN = isbn.replace ("-", "");
if (isbn.length!= && isbn.length!=) 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 = =) 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 ()!= checksum) return false;
}
return true;
}
Previleged method
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 | | "";
},
This.getAuthor:function () {
return author;
},
This.setAuthor:function (Newauthor) {
Author = Newauthor | | "";
}
Implements publication interface
THIS.SETISBN (NEWISBN);
This.settitle (Newtitle);
This.setauthor (Newauthor);
}
Public methods
Book.prototype = {
Display:function () {
Return "BOOK:ISBN:" + THIS.GETISBN () + ", Title:" + this.gettitle () + ", Author:" + this.getauthor ();
}
};

What is the difference between this scheme and the previous one? First, using VAR to declare three private members in the constructor also declares the private method CHECKISBN (), which is only valid in the constructor. Use the This keyword to declare a privileged method, that is, declaring inside the constructor but having access to private members. Any method that does not require access to a private member is declared in Book.prototype (such as display), which is the key to resolving this problem by declaring a method that requires access to private members as a privileged method. However, this access also has some drawbacks, such as for each instance, to create a copy of the privileged method, is bound to require more memory. We continue to optimize, using static members to solve the problems faced. By the way: static members belong only to the class, and all objects share only one copy (as described in "object-oriented JavaScript two (Implementation interface), see Interface.ensureimplements Method"), and instance methods are for objects.
Copy Code code as follows:

var publication = new Interface ("publication", ["GETISBN", "SETISBN", "GetTitle", "Settitle", "Getauthor", "Setauthor", "" Display "]);
var book = (function () {
private static attribute
var numsofbooks = 0;
private static method
Function Checkisbn (ISBN) {
if (ISBN = = undefined | | typeof ISBN!= "string") return false;
ISBN = isbn.replace ("-", "");
if (isbn.length!= && isbn.length!=) 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 = =) 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 ()!= checksum) return false;
}
return true;
}
Return constructor
return function (NEWISBN, Newtitle, Newauthor) {
Private attribute
var ISBN, title, author;
Previleged method
THIS.GETISBN = function () {
return ISBN;
};
THIS.SETISBN = function (NEWISBN) {
if (! BOOK.CHECKISBN (NEWISBN)) {
throw new Error ("Book:invalid ISBN.");
}
ISBN = NEWISBN;
}
This.gettitle = function () {
return title;
},
This.settitle = function (newtitle) {
title = Newtitle | | "";
},
This.getauthor = function () {
return author;
},
This.setauthor = function (Newauthor) {
Author = Newauthor | | "";
}
book.numsofbooks++;
if (Book.numsofbooks > 50) {
throw new Error ("Book:at most instances of book can be created.");
}
Implements publication interface
THIS.SETISBN (NEWISBN);
This.settitle (Newtitle);
This.setauthor (Newauthor);
};
})();
public static methods
Book.converttotitle = function (title) {
return Title.touppercase ();
}
Public methods
Book.prototype = {
Display:function () {
Return "BOOK:ISBN:" + THIS.GETISBN () + ", Title:" + this.gettitle () + ", Author:" + this.getauthor ();
}
};

This scenario is similar to the previous, using Var and this to create private members and privileged methods. The difference is that the closure is used to return the constructor and declare CHECKISBN as a private static method. One might ask why I create a private static method, and the answer is to make all objects public with a copy of the function. The 50 instances we create here have only one method copy Checkisbn and belong to the class book. Depending on your needs, you can also create public static methods for external invocations (e.g., converttotitle). Here we continue to consider a problem, assuming that later we need to limit the different books, such as <<javascript advanced programming >> Maximum printing capacity of 500,<<.net>> is 1000, That is, a constant that requires a maximum number of printing. Think about it, how do we declare a constant using what we already have? It is not difficult, we think, can take advantage of a private privilege only accessor method can be implemented.
Copy Code code as follows:

var publication = new Interface ("publication", ["GETISBN", "SETISBN", "GetTitle", "Settitle", "Getauthor", "Setauthor", "" Display "]);
var book = (function () {
private static attribute
var numsofbooks = 0;
private static Contant
var Constants = {
"Max_javascript_nums": 500,
"Max_net_nums": 1000
};
private static Previleged method
This.getmaxnums (name) {
Return Constants[name. toUpperCase ()];
}
private static method
Function Checkisbn (ISBN) {
if (ISBN = = undefined | | typeof ISBN!= "string") return false;
ISBN = isbn.replace ("-", "");
if (isbn.length!= && isbn.length!=) 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 = =) 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 ()!= checksum) return false;
}
return true;
}
Return constructor
return function (NEWISBN, Newtitle, Newauthor) {
Private attribute
var ISBN, title, author;
Previleged method
THIS.GETISBN = function () {
return ISBN;
};
THIS.SETISBN = function (NEWISBN) {
if (! BOOK.CHECKISBN (NEWISBN)) {
throw new Error ("Book:invalid ISBN.");
}
ISBN = NEWISBN;
}
This.gettitle = function () {
return title;
},
This.settitle = function (newtitle) {
title = Newtitle | | "";
},
This.getauthor = function () {
return author;
},
This.setauthor = function (Newauthor) {
Author = Newauthor | | "";
}
book.numsofbooks++;
if (Book.numsofbooks > 50) {
throw new Error ("Book:at most instances of book can be created.");
}
Implements publication interface
THIS.SETISBN (NEWISBN);
This.settitle (Newtitle);
This.setauthor (Newauthor);
};
})();
public static methods
Book.converttotitle = function (title) {
return Title.touppercase ();
}
Public methods
Book.prototype = {
Display:function () {
Return "BOOK:ISBN:" + THIS.GETISBN () + ", Title:" + this.gettitle () +
", Author:" + this.getauthor () + ", Maximum:";
},
Showmaxnums:function () {
Return Book.getmaxnums ("Max_javascript_nums");
}
};

The perfect situation is that the program you're encapsulating is just a matter of knowing your interface and not caring how you do it. But the problem is that with the expansion of the project, your packaging content will inevitably increase, when the project handover, for a scope and closures and other concepts unfamiliar to the members, maintenance difficulty will become so great. In some cases should be required to respond to the need to change the source code (here does not necessarily refer to the interface), may be added some details, even if you get your source is not able to do, it is not good to do. Therefore, my suggestion: the encapsulation should not be excessive, the interface must be clear and extensible.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.