In the past two days, a former colleague always asked me about Javascript object-oriented things, so I simply wrote an article for him to read. This article mainly wants to explain Javascript object-oriented programming from a holistic perspective. (The writing is quite hasty, there should be inaccurate or errors. Please criticize and correct me)
In addition, this article is mainly based on ECMAScript 5 and aims to introduce new technologies. For compatibility stuff, see the last section.
First exploration
We know that the definition of variables in Javascript is basically as follows:
var name = 'Chen Hao';;
var email = 'haoel(@)';
var website = '';
If you want to write it with an object, it will look like this:
var chenhao = {
name :'Chen Hao',
email : 'haoel(@)',
website : ''
};
So, I can access it like this:
//In the form of a member
;
;
;
//In the form of hash map
chenhao["name"];
chenhao["email"];
chenhao["website"];
Regarding functions, we know that Javascript functions are like this:
So, we can do this:
I believe these things are relatively simple, and everyone understands them. You can see that the javascript object function is declared directly, assigned directly, and used directly. The dynamic language of runtime.
Another relatively standardized way of writing is:
//We can see that it uses function to do class.
var Person = function(name, email, website){
= name;
= email;
= website;
= function(){
var hello = "Hello, I'm "+ + ", \n" +
"my email is: " + + ", \n" +
"my website is: " + ;
alert(hello);
};
};
var chenhao = new Person("Chen Hao", "haoel@",
"");
();
By the way, it is very simple to delete the properties of an object:
1 delete chenhao['email']
In the examples above, we can see several points like this:
Javascript data and member encapsulation is simple. No class is entirely an object operation. Pure dynamic!
This pointer in Javascript function is critical, if not, it is a local variable or a local function.
Javascript object member functions can be temporarily declared when used and a global function can be assigned directly.
Javascript member functions can be modified on instances, which means that the behavior of the same function name in different instances does not necessarily be the same.
Properties Configuration –
Let's look at the following code first:
Let’s talk about what these attribute configurations mean.
writable: Whether the value of this property can be changed.
configurable: Can the configuration of this property be changed?
enumerable: Whether this property can be traversed or listed in the for...in loop.
value: property value.
get()/set(_value): get and set accessor.
Get/Set Accessories
Regarding the get/set accessor, it means using get/set to replace value (it cannot be used with value). The example is as follows:
Let's look at a more practical example - using existing attributes (age) to construct new attributes (birth_year) through get and set:
( chenhao,
'birth_year',
{
get: function() {
var d = new Date();
var y = ();
return ( y - );
},
set: function(year) {
var d = new Date();
var y = ();
= y - year;
}
}
);
alert(chenhao.birth_year);
chenhao.birth_year = 2000;
alert();
It seems a bit troublesome to do this. You said, why don’t I write it like this:
Yes, you can do this, but with defineProperty() you can do these things:
1) Set attribute configurations such as writable, configurable, enumerable, etc.
2) Dynamically add attributes to an object. For example: some HTML DOM objects.
View object properties configuration
If you view and manage these configurations of objects, there is a program that can output the properties and configuration of objects:
call, apply, bind and this
This pointer about Javascript is very similar to C++/Java. Let's take a look at an example: (This example is very simple, I won't say much)
Let’s take a look at call and apply. The difference between these two functions is that the parameters look different, and the other is that the performance is different, and the performance of apply is much worse. (For performance, you can go to JSPerf to have a look)
But after bind, this pointer may be different, but because Javascript is dynamic. As shown in the following example
Inheritance and Overload
Through the above examples, we can actually inherit through (). Please see the code below. Student inherits from Object.
Using the above example, we can see that the properties in Person have not been really copied into the Student, but we can access them. This is because Javascript implements this mechanism with delegates. Actually, this is Prototype, Person is Student's Prototype.
When our code needs a property, the Javascript engine will first look at whether the current object has this property. If not, it will look for whether its Prototype object has this property and continue until it is found or not until there is no Prototype object.
To prove this, we can use() to test it:
So, you can also call the parent object's function in the child object's function, just like Base::func() in C++. So, we can use the parent class code by overloading the method hello, as shown below:
This one is very powerful.
combination
The thing above does not meet our requirements yet, and we may hope that these objects can be truly combined. Why combine? Because we all know that this is the most important thing in OO design. However, this does not support Javascript particularly well, and we can still do something.
First, we need to define a Composition function: (target is the object, source is the source object). The following code is still very simple, which is to take out the properties in the source one by one and define it into the target.
With this function, we can play this:
Prototype and Inheritance
Let’s talk about Prototype first. Let's look at the following routine first. This routine does not need to be explained. It is very similar to the function pointer in C language. There are many things like this in C language.
So, can we encapsulate these things? We need to use prototype. See the following example:
This is how to use prototype, which is the most important content in the JavaScript language. There are too many articles on the Internet to introduce this thing. To put it bluntly, prototype is to extend an object, and its characteristic is to return a new instance by "copying" an existing instance, rather than creating a new instance. The instances that are being copied are what we call "prototype", which is customizable (of course, there is no real copy here, it is actually just a delegation). In the example above, we extend the instance Cal to have an operation property and a calculated method.
In this way, we can achieve inheritance through this feature. Remember the Person we were in the front. The following example is to create a Student to inherit Person.
compatibility
The above codes may not necessarily run in all browsers, because the above codes follow the ECMAScript 5 specifications. For the browser compatibility list of ECMAScript 5, you can see the "ES5 Browser Compatibility Table" here.
All the code in this article has been tested in the latest version of Chrome.
Here are some functions that can be used in ES5-incompatible browsers:
()function
defineProperty() function
keys() function
() function
bind function
refer to
W3CSchool
MDN (Mozilla Developer Network)
MSDN (Microsoft Software Development Network)
Understanding Javascript OOP.
In addition, this article is mainly based on ECMAScript 5 and aims to introduce new technologies. For compatibility stuff, see the last section.
First exploration
We know that the definition of variables in Javascript is basically as follows:
Copy the codeThe code is as follows:
var name = 'Chen Hao';;
var email = 'haoel(@)';
var website = '';
If you want to write it with an object, it will look like this:
Copy the codeThe code is as follows:
var chenhao = {
name :'Chen Hao',
email : 'haoel(@)',
website : ''
};
So, I can access it like this:
Copy the codeThe code is as follows:
//In the form of a member
;
;
;
//In the form of hash map
chenhao["name"];
chenhao["email"];
chenhao["website"];
Regarding functions, we know that Javascript functions are like this:
Copy the codeThe code is as follows:
var doSomething = function(){
alert('Hello World.');
};
alert('Hello World.');
};
So, we can do this:
Copy the codeThe code is as follows:
var sayHello = function(){
var hello = "Hello, I'm "+
+ ", my email is: " +
+ ", my website is: " + ;
alert(hello);
};
//Direct assignment, here it looks like the function pointer of C/C++
= sayHello;
();
var hello = "Hello, I'm "+
+ ", my email is: " +
+ ", my website is: " + ;
alert(hello);
};
//Direct assignment, here it looks like the function pointer of C/C++
= sayHello;
();
I believe these things are relatively simple, and everyone understands them. You can see that the javascript object function is declared directly, assigned directly, and used directly. The dynamic language of runtime.
Another relatively standardized way of writing is:
Copy the codeThe code is as follows:
//We can see that it uses function to do class.
var Person = function(name, email, website){
= name;
= email;
= website;
= function(){
var hello = "Hello, I'm "+ + ", \n" +
"my email is: " + + ", \n" +
"my website is: " + ;
alert(hello);
};
};
var chenhao = new Person("Chen Hao", "haoel@",
"");
();
By the way, it is very simple to delete the properties of an object:
1 delete chenhao['email']
In the examples above, we can see several points like this:
Javascript data and member encapsulation is simple. No class is entirely an object operation. Pure dynamic!
This pointer in Javascript function is critical, if not, it is a local variable or a local function.
Javascript object member functions can be temporarily declared when used and a global function can be assigned directly.
Javascript member functions can be modified on instances, which means that the behavior of the same function name in different instances does not necessarily be the same.
Properties Configuration –
Let's look at the following code first:
Copy the codeThe code is as follows:
//Create an object
var chenhao = (null);
//Set a property
( chenhao,
'name', { value: 'Chen Hao',
writable: true,
configurable: true,
enumerable: true });
//Set multiple attributes
( chenhao,
{
'email' : { value: 'haoel@',
writable: true,
configurable: true,
enumerable: true },
'website': { value: '',
writable: true,
configurable: true,
enumerable: true }
}
);
var chenhao = (null);
//Set a property
( chenhao,
'name', { value: 'Chen Hao',
writable: true,
configurable: true,
enumerable: true });
//Set multiple attributes
( chenhao,
{
'email' : { value: 'haoel@',
writable: true,
configurable: true,
enumerable: true },
'website': { value: '',
writable: true,
configurable: true,
enumerable: true }
}
);
Let’s talk about what these attribute configurations mean.
writable: Whether the value of this property can be changed.
configurable: Can the configuration of this property be changed?
enumerable: Whether this property can be traversed or listed in the for...in loop.
value: property value.
get()/set(_value): get and set accessor.
Get/Set Accessories
Regarding the get/set accessor, it means using get/set to replace value (it cannot be used with value). The example is as follows:
Copy the codeThe code is as follows:
var age = 0;
( chenhao,
'age', {
get: function() {return age+1;},
set: function(value) {age = value;}
enumerable : true,
configurable : true
}
);
= 100; //Call set
alert(); //Call get and output 101 (+1 in get);
( chenhao,
'age', {
get: function() {return age+1;},
set: function(value) {age = value;}
enumerable : true,
configurable : true
}
);
= 100; //Call set
alert(); //Call get and output 101 (+1 in get);
Let's look at a more practical example - using existing attributes (age) to construct new attributes (birth_year) through get and set:
Copy the codeThe code is as follows:
( chenhao,
'birth_year',
{
get: function() {
var d = new Date();
var y = ();
return ( y - );
},
set: function(year) {
var d = new Date();
var y = ();
= y - year;
}
}
);
alert(chenhao.birth_year);
chenhao.birth_year = 2000;
alert();
It seems a bit troublesome to do this. You said, why don’t I write it like this:
Copy the codeThe code is as follows:
var chenhao = {
name: "Chen Hao",
email: "haoel@",
website: "",
age: 100,
get birth_year() {
var d = new Date();
var y = ();
return ( y - );
},
set birth_year(year) {
var d = new Date();
var y = ();
= y - year;
}
};
alert(chenhao.birth_year);
chenhao.birth_year = 2000;
alert();
name: "Chen Hao",
email: "haoel@",
website: "",
age: 100,
get birth_year() {
var d = new Date();
var y = ();
return ( y - );
},
set birth_year(year) {
var d = new Date();
var y = ();
= y - year;
}
};
alert(chenhao.birth_year);
chenhao.birth_year = 2000;
alert();
Yes, you can do this, but with defineProperty() you can do these things:
1) Set attribute configurations such as writable, configurable, enumerable, etc.
2) Dynamically add attributes to an object. For example: some HTML DOM objects.
View object properties configuration
If you view and manage these configurations of objects, there is a program that can output the properties and configuration of objects:
Copy the codeThe code is as follows:
//List the properties of the object.
function listProperties(obj)
{
var newLine = "<br />";
var names = (obj);
for (var i = 0; i < ; i++) {
var prop = names[i];
(prop + newLine);
// List the object's property configuration (descriptor) to use the getOwnPropertyDescriptor function.
var descriptor = (obj, prop);
for (var attr in descriptor) {
("..." + attr + ': ' + descriptor[attr]);
(newLine);
}
(newLine);
}
}
listProperties(chenhao);
function listProperties(obj)
{
var newLine = "<br />";
var names = (obj);
for (var i = 0; i < ; i++) {
var prop = names[i];
(prop + newLine);
// List the object's property configuration (descriptor) to use the getOwnPropertyDescriptor function.
var descriptor = (obj, prop);
for (var attr in descriptor) {
("..." + attr + ': ' + descriptor[attr]);
(newLine);
}
(newLine);
}
}
listProperties(chenhao);
call, apply, bind and this
This pointer about Javascript is very similar to C++/Java. Let's take a look at an example: (This example is very simple, I won't say much)
Copy the codeThe code is as follows:
function print(text){
( + ' - ' + text+ '<br>');
}
var a = {value: 10, print : print};
var b = {value: 20, print : print};
print('hello');// this => global, output "undefined - hello"
('a');// this => a, output "10 - a"
('b'); // this => b, output "20 - b"
a['print']('a'); // this => a, output "10 - a"
( + ' - ' + text+ '<br>');
}
var a = {value: 10, print : print};
var b = {value: 20, print : print};
print('hello');// this => global, output "undefined - hello"
('a');// this => a, output "10 - a"
('b'); // this => b, output "20 - b"
a['print']('a'); // this => a, output "10 - a"
Let’s take a look at call and apply. The difference between these two functions is that the parameters look different, and the other is that the performance is different, and the performance of apply is much worse. (For performance, you can go to JSPerf to have a look)
Copy the codeThe code is as follows:
(a, 'a'); // this => a, output "10 - a"
(b, 'b'); // this => b, output "20 - b"
(a, ['a']); // this => a, output "10 - a"
(b, ['b']); // this => b, output "20 - b"
(b, 'b'); // this => b, output "20 - b"
(a, ['a']); // this => a, output "10 - a"
(b, ['b']); // this => b, output "20 - b"
But after bind, this pointer may be different, but because Javascript is dynamic. As shown in the following example
Copy the codeThe code is as follows:
var p = (a);
p('a'); // this => a, output "10 - a"
(b, 'b'); // this => a, output "10 - b"
(b, ['b']); // this => a, output "10 - b"
p('a'); // this => a, output "10 - a"
(b, 'b'); // this => a, output "10 - b"
(b, ['b']); // this => a, output "10 - b"
Inheritance and Overload
Through the above examples, we can actually inherit through (). Please see the code below. Student inherits from Object.
Copy the codeThe code is as follows:
var Person = (null);
(
Person,
{
'name' : { value: 'Chen Hao'},
'email' : { value : 'haoel@'},
'website': { value: ''}
}
);
= function () {
var hello = "<p>Hello, I am "+ + ", <br>" +
"my email is: " + + ", <br>" +
"my website is: " + ;
(hello + "<br>");
}
var Student = (Person);
= "1234567"; //Student number
= "Computer Science"; //Direction
//Use Person's properties
( + ' ' + + ' ' + +'<br>');
//How to use Person
();
//Overload the SayHello method
= function (person) {
var hello = "<p>Hello, I am "+ + ", <br>" +
"my email is: " + + ", <br>" +
"my website is: " + + ", <br>" +
"my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
(hello + '<br>');
}
//Call again
();
// Check the Student's properties (only no , dept and overloaded saysHello)
('<p>' + (Student) + '<br>');
(
Person,
{
'name' : { value: 'Chen Hao'},
'email' : { value : 'haoel@'},
'website': { value: ''}
}
);
= function () {
var hello = "<p>Hello, I am "+ + ", <br>" +
"my email is: " + + ", <br>" +
"my website is: " + ;
(hello + "<br>");
}
var Student = (Person);
= "1234567"; //Student number
= "Computer Science"; //Direction
//Use Person's properties
( + ' ' + + ' ' + +'<br>');
//How to use Person
();
//Overload the SayHello method
= function (person) {
var hello = "<p>Hello, I am "+ + ", <br>" +
"my email is: " + + ", <br>" +
"my website is: " + + ", <br>" +
"my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
(hello + '<br>');
}
//Call again
();
// Check the Student's properties (only no , dept and overloaded saysHello)
('<p>' + (Student) + '<br>');
Using the above example, we can see that the properties in Person have not been really copied into the Student, but we can access them. This is because Javascript implements this mechanism with delegates. Actually, this is Prototype, Person is Student's Prototype.
When our code needs a property, the Javascript engine will first look at whether the current object has this property. If not, it will look for whether its Prototype object has this property and continue until it is found or not until there is no Prototype object.
To prove this, we can use() to test it:
Copy the codeThe code is as follows:
= 'aaa';
//Output aaa
('<p>' + + '</p>');
//Output Chen Hao
('<p>' +(Student).name + '</p>');
//Output aaa
('<p>' + + '</p>');
//Output Chen Hao
('<p>' +(Student).name + '</p>');
So, you can also call the parent object's function in the child object's function, just like Base::func() in C++. So, we can use the parent class code by overloading the method hello, as shown below:
Copy the codeThe code is as follows:
//The new version of the overloaded SayHello method
= function (person) {
(this).(this);
var hello = "my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
(hello + '<br>');
}
= function (person) {
(this).(this);
var hello = "my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
(hello + '<br>');
}
This one is very powerful.
combination
The thing above does not meet our requirements yet, and we may hope that these objects can be truly combined. Why combine? Because we all know that this is the most important thing in OO design. However, this does not support Javascript particularly well, and we can still do something.
First, we need to define a Composition function: (target is the object, source is the source object). The following code is still very simple, which is to take out the properties in the source one by one and define it into the target.
Copy the codeThe code is as follows:
function Composition(target, source)
{
var desc = ;
var prop = ;
var def_prop = ;
prop(source).forEach(
function(key) {
def_prop(target, key, desc(source, key))
}
)
return target;
}
{
var desc = ;
var prop = ;
var def_prop = ;
prop(source).forEach(
function(key) {
def_prop(target, key, desc(source, key))
}
)
return target;
}
With this function, we can play this:
Copy the codeThe code is as follows:
//artist
var Artist = (null);
= function() {
return + ' starts singing...';
}
= function() {
return + ' starts painting...';
}
//athlete
var Sporter = (null);
= function() {
return + ' starts running...';
}
= function() {
return + ' starts swimming...';
}
Composition(Person, Artist);
(() + '<br>');
(() + '<br>');
Composition(Person, Sporter);
(() + '<br>');
(() + '<br>');
//Look at what's in Person? (Output: sayHello,sing,paint,swim,run)
('<p>' + (Person) + '<br>');
var Artist = (null);
= function() {
return + ' starts singing...';
}
= function() {
return + ' starts painting...';
}
//athlete
var Sporter = (null);
= function() {
return + ' starts running...';
}
= function() {
return + ' starts swimming...';
}
Composition(Person, Artist);
(() + '<br>');
(() + '<br>');
Composition(Person, Sporter);
(() + '<br>');
(() + '<br>');
//Look at what's in Person? (Output: sayHello,sing,paint,swim,run)
('<p>' + (Person) + '<br>');
Prototype and Inheritance
Let’s talk about Prototype first. Let's look at the following routine first. This routine does not need to be explained. It is very similar to the function pointer in C language. There are many things like this in C language.
Copy the codeThe code is as follows:
var plus = function(x,y){
( x + ' + ' + y + ' = ' + (x+y) + '<br>');
return x + y;
};
var minus = function(x,y){
(x + ' - ' + y + ' = ' + (x-y) + '<br>');
return x - y;
};
var operations = {
'+': plus,
'-': minus
};
var calculate = function(x, y, operation){
return operations[operation](x, y);
};
calculate(12, 4, '+');
calculate(24, 3, '-');
( x + ' + ' + y + ' = ' + (x+y) + '<br>');
return x + y;
};
var minus = function(x,y){
(x + ' - ' + y + ' = ' + (x-y) + '<br>');
return x - y;
};
var operations = {
'+': plus,
'-': minus
};
var calculate = function(x, y, operation){
return operations[operation](x, y);
};
calculate(12, 4, '+');
calculate(24, 3, '-');
So, can we encapsulate these things? We need to use prototype. See the following example:
Copy the codeThe code is as follows:
var Cal = function(x, y){
= x;
= y;
}
= {
'+': function(x, y) { return x+y;},
'-': function(x, y) { return x-y;}
};
= function(operation){
return [operation](, );
};
var c = new Cal(4, 5);
('+');
('-');
= x;
= y;
}
= {
'+': function(x, y) { return x+y;},
'-': function(x, y) { return x-y;}
};
= function(operation){
return [operation](, );
};
var c = new Cal(4, 5);
('+');
('-');
This is how to use prototype, which is the most important content in the JavaScript language. There are too many articles on the Internet to introduce this thing. To put it bluntly, prototype is to extend an object, and its characteristic is to return a new instance by "copying" an existing instance, rather than creating a new instance. The instances that are being copied are what we call "prototype", which is customizable (of course, there is no real copy here, it is actually just a delegation). In the example above, we extend the instance Cal to have an operation property and a calculated method.
In this way, we can achieve inheritance through this feature. Remember the Person we were in the front. The following example is to create a Student to inherit Person.
Copy the codeThe code is as follows:
function Person(name, email, website){
= name;
= email;
= website;
};
= function(){
var hello = "Hello, I am "+ + ", <br>" +
"my email is: " + + ", <br>" +
"my website is: " + ;
return hello;
};
function Student(name, email, website, no, dept){
var proto = ;
proto().(this, name, email, website);
= no;
= dept;
}
// Inherit prototype
= ();
//Reset the constructor
= Student;
//Overload saysHello()
= function(){
var proto = ;
var hello = proto().(this) + '<br>';
hello += "my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
return hello;
};
var me = new Student(
"Chen Hao",
"haoel@",
"",
"12345678",
"Computer Science"
);
(());
= name;
= email;
= website;
};
= function(){
var hello = "Hello, I am "+ + ", <br>" +
"my email is: " + + ", <br>" +
"my website is: " + ;
return hello;
};
function Student(name, email, website, no, dept){
var proto = ;
proto().(this, name, email, website);
= no;
= dept;
}
// Inherit prototype
= ();
//Reset the constructor
= Student;
//Overload saysHello()
= function(){
var proto = ;
var hello = proto().(this) + '<br>';
hello += "my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
return hello;
};
var me = new Student(
"Chen Hao",
"haoel@",
"",
"12345678",
"Computer Science"
);
(());
compatibility
The above codes may not necessarily run in all browsers, because the above codes follow the ECMAScript 5 specifications. For the browser compatibility list of ECMAScript 5, you can see the "ES5 Browser Compatibility Table" here.
All the code in this article has been tested in the latest version of Chrome.
Here are some functions that can be used in ES5-incompatible browsers:
()function
Copy the codeThe code is as follows:
function clone(proto) {
function Dummy() { }
= proto;
= Dummy;
return new Dummy(); //Equivalent to (Person);
}
var me = clone(Person);
function Dummy() { }
= proto;
= Dummy;
return new Dummy(); //Equivalent to (Person);
}
var me = clone(Person);
defineProperty() function
Copy the codeThe code is as follows:
function defineProperty(target, key, descriptor) {
if (){
target[key] = ;
}else {
&& target.__defineGetter__(key, );
&& target.__defineSetter__(key, );
}
return target
}
if (){
target[key] = ;
}else {
&& target.__defineGetter__(key, );
&& target.__defineSetter__(key, );
}
return target
}
keys() function
Copy the codeThe code is as follows:
function keys(object) { var result, key
result = [];
for (key in object){
if ((key)) (key)
}
return result;
}
result = [];
for (key in object){
if ((key)) (key)
}
return result;
}
() function
Copy the codeThe code is as follows:
function proto(object) {
return !object? null
: '__proto__' in object? object.__proto__
: /* not exposed? */
}
return !object? null
: '__proto__' in object? object.__proto__
: /* not exposed? */
}
bind function
Copy the codeThe code is as follows:
var slice = [].slice
function bind(fn, bound_this) { var bound_args
bound_args = (arguments, 2)
return function() { var args
args = bound_args.concat((arguments))
return (bound_this, args) }
}
function bind(fn, bound_this) { var bound_args
bound_args = (arguments, 2)
return function() { var args
args = bound_args.concat((arguments))
return (bound_this, args) }
}
refer to
W3CSchool
MDN (Mozilla Developer Network)
MSDN (Microsoft Software Development Network)
Understanding Javascript OOP.