Javascript is a C-like language. Its object-oriented features are strange compared to C++/Java, but it is indeed quite powerful. In the past two days, a former colleague has been asking me about object-oriented programming in Javascript, so I would like to write an article for him to read. This article mainly wants to explain object-oriented programming in Javascript from an overall perspective. Reposted from an article by Cool Shell, it is very well written...
In addition, this article is mainly based on ECMAScript 5 and aims to introduce new technologies. Regarding compatibility, please see the last section.
Preliminary exploration
We know that the definition of variables in Javascript is basically as follows:
var name = 'Chen Hao';;
var email = 'haoel(@)hotmail.com';
var website = 'http:/ /coolshell.cn';
If you want to write it in objects, it will look like this:
var chenhao = {
name :'Chen Hao',
email : 'haoel(@)hotmail.com',
website: 'http://coolshell.cn'
}
So, I can access like this:
//As a member
chenhao.name;
chenhao.email;
chenhao. website;
//In the form of hash map
chenhao["name"];
chenhao["email"];
chenhao["website"];
About functions, we know that Javascript functions are like this of:
var doSomething = function(){
alert('Hello World.');
};
So, we can do this:
var sayHello = function(){
var hello = " Hello, I'm “+ this.name
+ “, my email is: ” + this.email
+ “, my website is: ” + this.website;
alert(hello);
};
//Direct assignment, this is very similar to the function pointer of C/C++
chenhao.Hello = sayHello;
chenhao.Hello();
I believe these things are relatively simple and everyone understands it. You can see that JavaScript object functions are directly declared, assigned values, and used directly. Dynamic language for runtime.
There is also a more standardized way of writing:
//We can see that it uses function as a class.
var Person = function(name, email, website){
this.name = name;
this.email = email;
this.website = website;
this.sayHello = function(){
var hello = "Hello, I'm "+ this.name + ", n" +
"my email is: " + this.email + ", n" +
"my website is: " + this.website ;
alert(hello);
};
};
var chenhao = new Person("Chen Hao", "haoel@hotmail.com",
"http://coolshell.cn");
chenhao.sayHello();
By the way, to delete the attributes of an object, it is very simple:
delete chenhao['email']
From the above examples, we can see the following points:
◆ Javascript's data and member encapsulation is very simple. No class is entirely an object operation. Purely dynamic!
◆ The this pointer in Javascript function is critical. If not, it is a local variable or local function.
◆ Javascript object member functions can be temporarily declared when used, and a global function can be directly assigned to it.
◆ Javascript member functions can be modified on the instance, which means that the behavior of the same function name in different instances may not be the same.
Property configuration – Object.defineProperty
Look at the following code first:
//Create an object
var chenhao = Object.create(null);
//Set a property
Object.defineProperty( chenhao,
'name', { value: 'Chen Hao',
writable: true,
configurable: true,
enumerable: true });
//Set multiple properties
Object.defineProperties(chenhao,
{
'email' : { value: 'haoel@hotmail.com',
writable: true,
configurable: true,
enumerable: true },
'website': { value: 'http: //coolshell.cn',
writable: true,
configurable: true,
enumerable: true }
}
);
Let’s talk about what these attribute configurations mean.
writable: Whether the value of this attribute can be changed.
configurable: Whether the configuration of this attribute can be changed.
enumerable: Whether this property can be traversed in a for...in loop or enumerated in Object.keys.
value: attribute value.
get()/set(_value): get and set accessors.
Get/Set accessor
About the get/set accessor, it means to use get/set to replace value (it cannot be used with value). The example is as follows:
var age = 0;
Object. defineProperty( chenhao,
'age', {
get: function() {return age+1;},
set: function(value) {age = value;}
enumerable : true,
configurable : true
}
);
chenhao.age = 100; //Call set
alert(chenhao.age); //Call get and output 101 (+1 in get);
Let’s look at another update As a practical example - use the existing attribute (age) to construct a new attribute (birth_year) through get and set:
Object.defineProperty(chenhao,
'birth_year',
{
get: function() {
var d = new Date();
var y = d.getFullYear();
return ( y – this.age );
},
set: function(year) {
var d = new Date();
var y = d.getFullYear();
this.age = y – year;
}
}
);
alert(chenhao.birth_year);
chenhao.birth_year = 2000;
alert(chenhao.age);
It seems a bit troublesome to do this. You said, why don’t I write it like this:
var chenhao = {
name: "Chen Hao",
email: "haoel@hotmail.com",
website: "http://coolshell.cn",
age: 100,
get birth_year() {
var d = new Date();
var y = d.getFullYear();
return ( y – this.age );
},
set birth_year(year) {
var d = new Date( );
var y = d.getFullYear();
this.age = y – year;
}
};
alert(chenhao.birth_year);
chenhao.birth_year = 2000;
alert (chenhao.age);
Yes, you can indeed do this, but through defineProperty() you can do these things:
1) Set property configurations such as writable, configurable, enumerable, etc.
2) Dynamically add attributes to an object. For example: some HTML DOM objects.
View the object property configuration
If you view and manage these configurations of the object, there is a program below that can output the properties and configuration of the object:
// List the properties of the object.
function listProperties(obj)
{
var newLine = ”
“;
var names = Object.getOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
var prop = names[i] ;
document.write(prop + newLine);
// List the object’s property configuration (descriptor) using the getOwnPropertyDescriptor function.
var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
for (var attr in descriptor) {
document.write(“…” + attr + ': ' + descriptor[attr]);
document. write(newLine);
}
document.write(newLine);
}
}
listProperties(chenhao);
call, apply, bind and this
About the this pointer of Javascript, and C++/ Java is very similar. Let's take a look at an example: (This example is very simple, I won't say more)
function print(text){
document.write(this.value + ' – ' + text+ '
');
}
var a = {value: 10, print : print};
var b = {value: 20, print : print};
print('hello');// this => global, output " undefined – hello”
a.print('a');// this => a, output “10 – a”
b.print('b'); // this => b, output “ 20 – b”
a['print']('a'); // this => a, output “10 – a”
Let’s look at call and apply again. The difference between these two functions is the parameters The appearance is different, and the other is that the performance is different. The performance of apply is much worse. (For performance, you can go to JSPerf and take a look)
print.call(a, 'a'); // this => a, output “10 – a”
print.call(b, 'b '); // this => b, output “20 – b”
print.apply(a, ['a’]); // this => a, output “10 – a”
print. apply(b, ['b']); // this => b, output "20 – b"
But after bind, the this pointer may be different, but because Javascript is dynamic. As in the following example
var p = print.bind(a);
p('a'); // this => a, output “10 – a”
p.call(b, 'b' ); // this => a, output “10 – b”
p.apply(b, ['b’]); // this => a, output “10 – b”
Inheritance and Replication
Through the above examples, we can actually inherit through Object.create(). Please see the code below. Student inherits from Object.
var Person = Object.create(null);
Object.defineProperties
(
Person,
{
'name' : { value: 'Chen Hao'},
'email' : { value : 'haoel@hotmail .com'},
'website': { value: 'http://coolshell.cn'}
}
);
Person.sayHello = function () {
var hello = “
Hello, I am “+ this.name + “,
” +
“my email is: ” + this.email + “,
” +
“my website is: ” + this.website;
document. write(hello + “
”);
}
var Student = Object.create(Person);
Student.no = “1234567″; //Student number
Student.dept = “Computer Science”; / /Department
//Using the attributes of Person
document.write(Student.name + ' ' + Student.email + ' ' + Student.website +'
');
//Methods of using Person
Student.sayHello();
//Overload the SayHello method
Student.sayHello = function (person) {
var hello = “
Hello, I am “+ this.name + “,
” +
“my email is: ” + this.email + “,
” +
“my website is: ” + this.website + “,
” +
“my student no is: ” + this. no + “, < ;br>” +
“my departent is: ” + this. dept;
document.write(hello + '
');
}
//Call again
Student.sayHello();
// View the properties of Student (only no, dept and overloaded sayHello)
document.write('
' + Object.keys(Student) + '
');
Common to the above example, We can see that the attributes in Person are not actually copied to Student, but we can access them. This is because Javascript implements this mechanism using delegates. In fact, this is the prototype, and Person is the prototype of Student.
When our code needs an attribute, the Javascript engine will first check whether the current object has this attribute. If not, it will search whether its Prototype object has this attribute, and continue until it is found. Or until there are no Prototype objects.
To prove this, we can use Object.getPrototypeOf() to check:
Student.name = 'aaa';
//Output aaa
document.write('
' + Student.name + '
');' +Object.getPrototypeOf(Student).name + '
');So, you You can also call the function of the parent object in the function of the child object, just like Base::func() in C++. Therefore, when we overload the hello method, we can use the code of the parent class, as shown below:
//New version of the overloaded SayHello method
Student.sayHello = function (person) {
Object.getPrototypeOf(this).sayHello .call(this);
var hello = “my student no is: ” + this. no + “,
” +
“my departent is: ” + this. dept;
document.write(hello + '
');
}
This is very powerful.
Combination
The above thing does not meet our requirements. We may hope that these objects can be truly combined. Why combination? Because we all know that this is the most important thing in OO design. However, this does not support Javascript particularly well, so we can still get it done.
First, we need to define a Composition function: (target is the object that acts on it, source is the source object). The following code is very simple. It just takes out the attributes in the source one by one and defines them in the target. On Fulling composition (target, source) {
var desc = object.get.getownpropertydescriptor; opt = object.defineProperty;
Prop (Source). Foreach (
function(key) {
def_prop(target, key, desc(source, key))
}
)
return target;
}
With this function, we can play here:
//Artist
var Artist = Object.create(null);Artist.sing = function() {
return this.name + ' starts singing…';}
Artist.paint = function() {return this.name + ' starts painting…';
}
//Athletes
var Sporter = Object.create(null);
Sporter.run = function() {
return this.name + ' starts running…';
}
Sporter.swim = function() {
return this.name + ' starts swimming…';
Composition(Person, Artist);
document.write(Person.sing() + '
' );
document.write(Person.paint() + '
');
Composition(Person, Sporter);
document.write(Person.run() + '
');
document.write(Person.swim() + '
');
//See what's in Person? (Output: sayHello,sing,paint,swim,run)
document.write('
' + Object.keys(Person) + '
');
Let’s do it first Let’s talk about Prototype. Let's take a look at the following routine first. This routine does not need explanation. It is very similar to the function pointer in C language. There are many such things in C language.
document.write( x + ' + ' + y + ' = ' + (x+y) + '
');
return x + y;
document.write(x + ' – ' + y + ' = ' + (x-y) + '
');
};
var operations = {
'+': plus,
};
var calculate = function(x, y, operation){
return operations[operation](x, y);
calculate(12, 4, '+');
calculate(24, 3, '-');
var Cal = function(x, y){
this.y = y;
}
Cal.prototype.operations = {
'+': function(x, y) { return x+y;},
'-': function(x, y) { return x-y;}
};
Cal.prototype.calculate = function(operation){
return this.operations[operation](this.x, this.y);
};
var c = new Cal(4, 5);
c.calculate('+');
c.calculate('-');
This is the usage of prototype. Prototype is the most important content in the JavaScript language. There are too many articles on the Internet about 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 instead of creating a new instance. The copied instance is what we call a "prototype", and this prototype is customizable (of course, there is no real copy here, it is actually just a delegation). In the above example, we extended the instance Cal to have an operations attribute and a calculate method.
In this way, we can implement inheritance through this feature. Remember our first Person, the following example is to create a Student to inherit Person.
function Person(name, email, website){
this.name = name;
this.email = email;
this.website = website;
};
Person.prototype.sayHello = function(){
var hello = “Hello, I am “+ this.name + “,
” +
“my email is: ” + this.email + “,
” +
“my website is: ” + this .website;
return hello;
};
function Student(name, email, website, no, dept){
var proto = Object.getPrototypeOf;
proto(Student.prototype).constructor.call(this, name, email, website);
this.no = no;
this.dept = dept;
}
//Inherit prototype
Student.prototype = Object.create(Person.prototype);
//Reset constructor
Student.prototype.constructor = Student;
//Overload sayHello()
Student.prototype.sayHello = function(){
var proto = Object.getPrototypeOf;
var hello = proto(Student.prototype).sayHello.call (this) + '
';
hello += “my student no is: ” + this. no + “,
” +
“my departent is: ” + this. dept;
return hello ;
};
var me = new Student(
"gtfx",
"gtfx0209@gmail.com",
"http://dyygtfx.com",
"12345678″,
"Computer Science"
);
document.write(me.sayHello());
Compatibility
The above codes may not be able to run in all browsers, because the above codes follow the specifications of ECMAScript 5. About ECMAScript 5 For the browser compatibility list, you can see the "ES5 Browser Compatibility List" here.
All the code in this article has been tested in the latest version of Chrome.
The following are some functions that can be used in browsers that are not ES5 compatible:
Object.create() function
function clone(proto) {
function Dummy() { }
Dummy.prototype = proto;
Dummy.prototype.constructor = Dummy;
return new Dummy(); //Equivalent to Object.create(Person);
}
var me = clone(Person);
defineProperty() function
function defineProperty(target, key, descriptor) {
if (descriptor.value){
target[key] = descriptor.value;
}else {
descriptor.get && target.__defineGetter__(key, descriptor. get);
descriptor.set && target.__defineSetter__(key, descriptor.set);
}
return target
}
keys() function
function keys(object) { var result, key
result = [];
for (key in object){
if (object.hasOwnProperty(key)) result.push(key)
}
return result;
}
Object.getPrototypeOf() function
function proto(object) {
return !object? null
: '__proto__' in object? object.__proto__
: /* not exposed? */ object.constructor.prototype
}
bind function
var slice = [].slice
function bind(fn, bound_this) { var bound_args
bound_args = slice.call(arguments, 2)
return function() { var args
args = bound_args.concat(slice .call(arguments))
return fn.apply(bound_this, args) }
}
The above is the content of the JavaScript object-oriented tutorial. For more related content, please pay attention to the PHP Chinese website (www.php.cn )!