This article teaches you what an object is, constructor, 5 modes for creating an object, 5 ways to implement inheritance, shallow copy, etc. This article is accompanied by a free instructional video called Object-oriented Programming. Thank you for your attention – thumbs-up – favorites, the follow-up will continue to update quality content.
JavaScript statements have strong object-oriented programming capabilities
What is the object
Object Oriented Programming (OOP) is the mainstream Programming paradigm. It abstracts all kinds of complex relationships in the real world into objects, and then completes the simulation of the real world through the division of labor and cooperation between objects.
Each object is a function center with a clear division of labor, which can complete tasks such as receiving information, processing data and sending information. Objects can be reused and customized through inheritance mechanisms. Therefore, object-oriented programming is flexible, reusable, highly modular and easy to maintain and develop. Compared with traditional procedural programming composed of a series of functions or instructions, it is more suitable for large-scale software projects with multiple people.
So what exactly is an object? We understand it on two levels.
[1] An object is an abstraction of a single object
A book, a car, a person can all be objects, as can a database, a web page, a connection to a remote server. When objects are abstracted into objects, the relationship between objects becomes the relationship between objects, so that the real situation can be simulated and the object can be programmed.
[2] Object is a container that encapsulates properties and methods.
Properties are the state of the object, and methods are the behavior of the object (to accomplish something). For example, we can abstract an animal as an animal object, using “attributes” to record which animal it is, and “methods” to represent certain behaviors of the animal (running, hunting, resting, etc.).
The constructor
The first step in object-oriented programming is to generate objects. As I said, an object is an abstraction of a single object. You usually need a template that represents common characteristics of a class of objects from which objects are generated.
Typical object-oriented programming languages, such as C++ and Java, have the concept of a class. A class is a template of an object, and an object is an instance of a class. However, the object architecture of the JavaScript language is not based on “classes”, but on constructors and prototype chains.
The JavaScript language uses constructors as templates for objects. A “constructor” is a function specifically used to generate instance objects. It is the template of the object and describes the basic structure of the instance object. A constructor that generates multiple instance objects, all of which have the same structure.
A constructor is an ordinary function, but it has its own characteristics and uses.
var Dog = function(){
this.name = 'huang';
}
Copy the code
In the code above, Dog is the constructor. To distinguish constructor names from normal functions, the first letter is usually capitalized.
Constructors have two characteristics:
- It’s used in the body of the function
this
Keyword that represents the object instance to be generated. - When generating objects, you must use the new command
Constructors can take arguments as needed
function Dog (name){
this.name = name;
}
var d1 = new Dog('huang');
console.log(d1.name);/ / huang
Copy the code
If you forget to use the new operator, this will represent the global object Window
function Dog(){
this.name = name;
}
var d1 = Dog();
//Uncaught TypeError: Cannot read property 'name' of undefined
console.log(d1.name);
Copy the code
In the code above, forgetting to use the new command actually causes D1 to program undefined and the name attribute to become a global variable. Therefore, you should be very careful not to call the constructor directly without using the new command
To ensure that the constructor must be used with the new command, one solution is to use strict mode inside the constructor, with use strict on the first line. That way, if you forget to use the new command, calling the constructor directly will return an error.
function Dog(name){
'use strict';
this.name = name;
}
var d1 = Dog('huang');
Copy the code
Dog in the code above is the constructor, and the use strict command ensures that this function runs in strict mode. Because in strict mode, this inside a function cannot refer to a global object and is equal to undefined by default, the call without new will result in an error (JavaScript does not allow adding attributes to undefined).
instanceof
The operator runtime indicates whether the object is an instance of a particular class
As an alternative, the constructor internally checks whether the new command is used and returns an instance object if it is not
The instanceof operator can be used to identify the type of an object
function Dog(name){
if(! (this instanceof Dog)){
return new Dog(name);
}
this.name = name;
}
var d1 = Dog('huang');
console.log(d1.name);/ / 'huang'
console.log(Dog('huang').name);/ / 'huang'
Copy the code
The constructor in the above code, with or without the new command, yields the same result
The new command
As you can see, if we want to create an object, after we declare the constructor, we must use the new command to instantiate the object. So let’s look at how the new command works
When the new command is used, the functions that follow it perform the following steps
- Creates an empty object as an instance of the object to be returned
- Points the empty object’s prototype to the constructor’s
prototype
attribute - Assigns this empty object to the internal
this
The keyword - Start executing the code inside the constructor
That is, inside the constructor, this refers to a newly generated empty object on which all operations will be performed. A constructor is called a “constructor” because its purpose is to manipulate an empty object (this object) and “construct” it into what it needs to look like.
constructor
Each object is automatically created with a constructor attribute, Contructor, that contains a reference to its constructor. This Constructor property actually inherits from the stereotype object, and constructor is the only property of the stereotype object
function Dog(){}var d1 = new Dog();
console.log(d1.constructor === Person);//true
console.log(d1.__proto__.constructor === Person);//true
Copy the code
Here are the internal properties of Dog, and found that Constructor is an inherited property
While there is such a relationship between object instances and their constructors, you can use Instanceof to check object types.
The return value
The return statement in a function is used to return the value of the call, whereas the return value of the new constructor is a bit special.
If the constructor uses a return statement but does not specify a return value, or if the return value is a primitive value, the return value is ignored and the new object is used as the result of the call
function Fn(){
this.a = 2;
return;
}
var test = new Fn();
console.log(test);//{a:2}
Copy the code
If the constructor explicitly returns an object using a return statement, the value of the calling expression is that object
var obj = {a:1};
function fn(){
this.a = 2;
return obj;
}
var test = new fn();
console.log(test);//{a:1}
Copy the code
The advantage of using constructors is that all objects created using the same constructor have the same properties and methods
function Person(name){
this.name = name;
this.sayName = function(){
console.log(this.name); }}var p1 = new Person('Tom');
var p2 = new Person('Jack');
Copy the code
Constructors allow objects to be configured with the same attributes, but constructors do not eliminate code redundancy. The main problem with using constructors is that each method has to be recreated on each instance. In the example above, each object has its own sayName() method. This also means that if there are 100 object instances, there are 100 functions doing the same thing, just using different data.
function Person(name){
this.name = name;
this.sayName = function(){
console.log(this.name); }}var p1 = new Person('Tom');
var p2 = new Person('Jack');
console.log(p1.sayName === p2.sayName);//false
Copy the code
In the code above, p1 and p2 are two instances of a constructor with the sayName method. Since the sayName method is generated on each instance object, the two instances are generated twice. That is, every time an instance is created, a new sayName method is created. This is unnecessary and wasteful of system resources, so all sayName methods behave the same and should be shared entirely.
The solution to this problem. This is the JavaScript prototype object
A prototype object
When we talk about prototype objects, we talk about the triangulation of prototype objects, instance objects, and constructors
The following two lines of code illustrate their relationship
function Foo(){};
var f1 = new Foo();
Copy the code
The constructor
The function used to initialize the newly created object is the constructor. In this example, the Foo function is the constructor
Instance objects
The objects created by the constructor’s new operation are instance objects, often referred to as object instances. Multiple instance objects can be constructed using a single constructor. The following f1 and F2 are instance objects
function Foo(){};
var f1 = new Foo();
var f2 = new Foo();
console.log(f1 === f2);//false
Copy the code
Prototype object and Prototype
After the instance object is created through the constructor’s new operation, the constructor automatically creates a Prototype property that points to the prototype object of the instance object. Multiple objects instantiated through the same constructor have the same prototype object. In this example, foo. prototype is the prototype object
function Foo(){};
Foo.prototype.a = 1;
var f1 = new Foo;
var f2 = new Foo;
console.log(Foo.prototype.a);/ / 1
console. log(f1.a);/ / 1
console.log(f2.a);/ / 1
Copy the code
The prototype property
The JavaScript inheritance mechanism is designed with the idea that all properties and methods of the prototype object can be shared by the instance object. That is, if properties and methods are defined on the stereotype, then all instance objects can be shared, saving memory and showing the relationship between instance objects.
How to specify a prototype for an object. JavaScript states that each function has a Prototype property that points to an object
function fn(){};
// The fn function has a prototype attribute by default and refers to an object
console.log(typeof fn.prototype);//"Object"
Copy the code
For ordinary functions, this property is basically useless. But in the case of constructors, this property automatically becomes the prototype of the instance object when the instance is generated.
function Person(name){
this.name = name;
}
Person.prototype.age = 18;
var p1 = new Person('the king');
var p2 = new Person('two Kings');
console.log(p1.age);/ / 18
console.log(p2.age);/ / 18
Copy the code
In the code above, the Person constructor’s prototype property is the prototype object for instance objects P1 and p2. An age attribute is added to the prototype object, and as a result, the instance objects share this attribute.
Properties of the stereotype object are not properties of the instance object itself. As soon as the prototype object is modified, the change is immediately reflected in all instance objects
Person.prototype.age = 40;
console.log(p1.age);/ / 40
console.log(p2.age);/ / 40
Copy the code
In the code above, the age property of the prototype object changes to 40, which immediately changes the age property of the two instances. This is because the instance object doesn’t actually have an age property, but instead reads the age property of the prototype object. That is, when the instance object doesn’t have a property or method, it goes to the prototype object to find that property or method. This is what makes prototype objects special.
If the instance object has a property or method, it will not go to the prototype object to find that property or method
p1.age = 35;
console.log(p1.age);/ / 35
console.log(p2.age);/ / 40
console.log(Person.prototype.age) / / 40
Copy the code
In the code above, the age property of instance object P1 is changed to 35, so that it no longer reads the age property of the prototype object, which still has the value of 40.
conclusion
The purpose of a stereotype object is to define properties and methods shared by all instance objects. This is why it is called a prototype object, and instance objects can be considered children of the prototype object
Person.prototype.sayAge = function(){
console.log('My age is'+ this.age);
}
Copy the code
In the code above, the Person. Prototype object defines a sayAge method that can be called on all Person instance objects.
Prototype chain
JavaScript dictates that all objects have their own prototype object. On the one hand, any object can serve as a prototype for other objects. On the other hand, since a prototype object is also an object, it also has its own prototype. Therefore, a prototype chain is formed: the prototype of the object, then the prototype of the prototype……
The prototype property of the Object constructor is called Object. Prototype. That is, all objects inherit the properties of Object.prototype. This is why all objects have valueof and toString methods, because these are inherited from Object.prototype.
Does the object. prototype Object have a prototype? Object. Prototype is null. Null has no properties, no methods, and no prototype of its own. Thus, the end of the prototype chain is NULL.
Object.getPrototypeOf(Object.prototype);//null
Copy the code
Prototype is null. Null has no attributes, so the prototype chain ends there. The object.getProtoTypeof method returns the prototype of the parameter Object, which is described in this lesson on Object methods.
When reading a property of an object, the JavaScript engine looks for the property of the object itself; if it can’t find it, it looks for its prototype; if it still can’t find it, it looks for the prototype. If no Object. Prototype is found up to the top level, undefined is returned. Now, if an object itself and its archetype both define a property of the same name, we can look at attributes of the object itself first, and that’s called Overriding.
Note that looking for a property one level up the entire prototype chain has an impact on performance. The higher the level of the prototype object whose properties are sought, the greater the impact on performance. If you look for an attribute that doesn’t exist, the entire prototype chain will be traversed.
For example, having the prototype property point to an array means that the instance object can call array methods
var MyArray = function () {};
MyArray.prototype = Array.prototype;
MyArray.prototype.constructor = MyArray;
var mine = new MyArray();
mine.push(1.2.3);
mine.length / / 3
mine instanceof Array // true
Copy the code
constructor
By default, the stereotype object only gets the constructor property, pointing to the corresponding constructor of the stereotype object. The other methods are inherited from Object
function Foo(){};
console.log(Foo.prototype.constructor === Foo);//true
Copy the code
Since an instance object can inherit properties from a stereotype, it also has a constructor property that points to the corresponding stereotype object’s constructor
function Foo(){};
var f1 = new Foo();
console.log(f1.constructor === Foo);//true
console.log(f1.constuctor === Foo.prototype.constructor);//true
f1.hasOwnProperty('constructor');//false
Copy the code
Code above, the f1 is the constructor Foo instance of the object, but there is no constructor f1 itself attribute, this property is read on the prototype chain of Foo prototype. The constructor property
The constructor property lets you know which constructor generated an instance object.
function Foo(){};
var f1 = new Foo();
console.log(f1.constructor === Foo);//true
console.log(f1.constructor === Array);//false
Copy the code
The constructor property represents the association between the stereotype object and the constructor. If the stereotype object is modified, the constructor property is generally modified at the same time to prevent errors when referencing it
Here’s an example:
function Person(name){
this.name = name;
}
console.log(Person.prototype.constructor === Person);//true
// Modify the prototype object
Person.prototype = {
fn:function(){}};console.log(Person.prototype.constructor === Person);//false
console.log(Person.prototype.constructor === Object);//true
Copy the code
Therefore, when modifying a prototype object, you generally modify the point to the constructor property as well
function Person(name){
this.name = name;
}
console.log(Person.prototype.constructor === Person);//true
// Modify the prototype object
Person.prototype = {
constructor:Person,
fn:function(){
console.log(this.name); }};var p1 = new Person('huang');
console.log(p1 instanceof Person);//true
console.log(Person.constructor == Person);//true
console.log(Person.constructor === Object);//false
Copy the code
__proto__
The instance object contains a __proto__ attribute inside, pointing to the prototype object corresponding to the instance object
function Foo(){};
var f1 = new Foo;
console.log(f1.__proto__ === Foo.prototype);//true
Copy the code
conclusion
The relationship between constructors, stereotype objects, and instance objects is that there is no direct connection between instance objects and constructors
function Foo(){};
var f1 = new Foo();
Copy the code
The prototype object for the above code is foo. prototype, the instance object is f1, and the constructor is Foo
The relationship between prototype objects and instance objects
console.log(Foo.prototype === f1.__proto__);//true
Copy the code
The relationship between prototype objects and constructed objects
console.log(Foo.prototype.constructor === Foo);//true
Copy the code
Whereas there is no direct relationship between instance objects and constructors, the indirect relationship is that the instance object can inherit the constructor property of the prototype object
console.log(f1.constructor === Foo);//true
Copy the code
If the connection between an instance object and a constructor is anything to go by, it is this: the instance object is the result of the constructor’s new operation
var f1 = new Foo;
Copy the code
After this code is executed, if you reset the prototype object, you break the relationship between the three
function Foo(){};
var f1 = new Foo;
console.log(Foo.prototype === f1.__proto__);//true
console.log(Foo.prototype.constructor === Foo);//true
Foo.prototype = {};
console.log(Foo.prototype === f1.__proto__);//false
console.log(Foo.prototype.constructor === Foo);//false
Copy the code
So, code order is important
Five modes for creating objects
How to create objects, or how to create objects more elegantly. Starting with the simplest way to create objects, we will step through the pattern of creating objects in 5
Object literals
In general, we create an object using the literal form of the object
There are three ways to create objects, including the new constructor, the Object direct, and the object.create () function
[1] New constructor
Use the new operator followed by the Object constructor to initialize a newly created Object
var person = new Object(a); person.name ='mjj';
person.age = 28;
Copy the code
[2] Object literals
JavaScript provides shortcuts to literals for creating most native object values. Using literals simply hides the same basic process as the new operator, so it can also be called syntactic sugar
var person = {
name:'mjj';
age:28
}
Copy the code
Objects are defined using the method of object literals, and the property names are automatically converted to strings
【 3 】 Object. The create ()
A common way to generate instance objects is to make the constructor return an instance using the new command. But a lot of times, you can only get one instance object, it might not be generated by the constructor at all, so can you generate another instance object from one instance object?
ES5 defines a method called Object.create() to satisfy this requirement. The method takes an object as an argument and returns an instance object based on it. This instance fully inherits the properties of the prototype object.
// Prototype objects
var A = {
getX:function(){
console.log('hello'); }};// Instance object
var B = Object.create(A);
console.log(B.getX);//"hello"
Copy the code
In the code above, the object. create method generates an Object B from an Object A. B inherits all of A’s properties and methods.
var person1 = {
name:'mjj'.age:28.sayName: function(){
alert(this.name); }}Copy the code
If we were to create a large number of objects, we would look like this
var person1 = {
name:'mjj'.age:28.sayName: function(){
alert(this.name); }}var person2 = {
name:'alex'.age:38.sayName: function(){
alert(this.name); }}/* var person3 = {} var person4 = {} var person5 = {} ...... * /
Copy the code
Although object literals can be used to create a single object, creating multiple objects creates a lot of repetitive code
The factory pattern
To solve the above problems, people began to use the factory model. This pattern abstracts the process of creating concrete objects, encapsulating the details of creating objects with specific interfaces with functions
function createPerson(name,age){
var p = new Object(a); p.name = name; p.age = age; p.sayName =function(){
alert(this.name);
}
return p;
}
var p1 = createPerson('mjj'.28);
var p2 = createPerson('alex'.28);
var p3 = createPerson('huang'.8);
Copy the code
While the factory pattern solves the problem of creating multiple similar objects, it does not solve the problem of object recognition because the type of the object is not given using the pattern
Constructor pattern
You can define properties and methods of a custom object type by creating custom constructors. Creating a custom constructor means that instances of it can be identified as a specific type, which is where the constructor pattern trumps the factory pattern. Instead of explicitly creating an object, the pattern assigns properties and methods directly to this without a return statement
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("mjj".28);
var person2 = new Person("alex".25);
Copy the code
The main problem with using constructors is that each method has to be recreated on each instance, making it unnecessary to create multiple methods that accomplish the same task, wasting memory space
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
};
}
var p1 = new Person("mjj".28);
var p2 = new Person("alex".25);
// The same sayName() method occupies different memory space in both instances p1 and p2
console.log(person1.sayName === person2.sayName);//false
Copy the code
The constructor extends the schema
Moving method definitions outside of constructors, based on the constructor pattern, solves the problem of methods being created repeatedly
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
var p1 = new Person("mjj".28);
var p2 = new Person("alex".25);
console.log(person1.sayName === person2.sayName);//true
Copy the code
Now, a new problem arises. Functions defined in a global scope can really only be called by an object, which makes the global scope a bit of a misnomer. Moreover, if the object needs to define many methods, it will define many global functions, which will pollute the global space, and the custom reference type will not be encapsulated
Parasitic constructor pattern
The basic idea of this pattern is to create a function that simply encapsulates the code that created the object and then returns the newly created object. This pattern is a combination of the factory pattern and the constructor pattern
The parasitic constructor pattern has the same problem as the constructor pattern; each method is recreated on each instance, and creating multiple methods to accomplish the same task is unnecessary and wastes memory space
function Person(name,age){
var p = new Object(a); p.name = name; p.age = age; p.sayName =function(){
alert(this.name);
}
return p;
}
var p1 = new Person('mjj'.28);
var p2 = new Person('alex'.28);
The sayName() method, which has the same function, occupies different memory space in person1 and person2 instances
console.log(p1.sayName === p2.sayName);//false
Copy the code
Another problem is that there is no relationship between the object returned using this pattern and the constructor. Therefore, using the instanceof operator and the Prototype attribute doesn’t make sense. Therefore, this pattern should be avoided as much as possible
function Person(name,age){
var p = new Object(a); p.name = name; p.age = age; p.sayName =function(){
alert(this.name);
}
return p;
}
var p1 = new Person('mjj'.28);
console.log(p1 instanceof Person);//false
console.log(p1.__proto__ === Person.prototype);//false
Copy the code
Secure constructor pattern
A secure object is one that has no public properties and a method that does not reference this object. Secure objects are best used in secure environments that prohibit the use of this and new or when preventing data from being altered by other applications
The secure constructor is similar to the parasitic constructor pattern, except that the newly created object instance method does not reference this. The second is that the new operator does not apply to calling the constructor.
function Person(name,age){
// Create the object to return
var p = new Object(a);// Private variables and functions can be defined here
// Add method
p.sayName = function (){
console.log(name);
}
// Return the object
return p;
}
// There is no other way to access the value of name other than using the sayName() method in secure mode created objects
var p1 = Person('mjj'.28);
p1.sayName();//"mjj"
Copy the code
Like the parasitic constructor pattern, objects created using the secure constructor pattern have no relationship to the constructor, so the instanceof operator has no meaning for such objects. Right
The prototype pattern
With a stereotype object, you can share its properties and methods with all instances. In other words, instead of defining information about an object instance in a constructor, you can add that information directly to the prototype object
function Person(){
Person.prototype.name = "mjj";
Person.prototype.age = 29;
Person.prototype.sayName = function(){
console.log(this.name); }}var p1 = new Person();
p1.sayName();//"mjj"
var p2 = new Person();
p2.sayName();//"mjj"
alert(p1.sayName === p2.sayName);//true
Copy the code
Simpler prototype patterns
To reduce unnecessary input, and to better visually encapsulate the functionality of the prototype, rewrite the entire prototype object with an object literal containing methods of all attributes
However, after rewriting the object literal, constructor no longer points to Person. So this method overrides the default Prototype Object completely, leaving the constructor property of Person.prototype out of the way and finding the constructor property of Object.prototype from the prototype chain
function Person(){};
Person.prototype = {
name:'mjj'.age:28.sayName:function(){
console.log(this.name); }}var p1 = new Person();
p1.sayName();//"mjj"
console.log(p1.constructor === Person);//false
console.log(p1.constructor === Object);//true
Copy the code
The constructor property of the prototype object can be explicitly set
function Person(){};
Person.prototype = {
constructor:Person,
name:'mjj'.age:28.sayName:function(){
console.log(this.name); }}var p1 = new Person();
p1.sayName();//"mjj"
console.log(p1.constructor === Person);//true
console.log(p1.constructor === Object);//false
Copy the code
The problem with the stereotype pattern is that the reference type value attributes are shared and modified by all instance objects, which is why the stereotype pattern is rarely used alone.
function Person(){};
Person.prototype = {
constructor:Person,
name:'mjj'.age:28.friends: ['alex'.'huang'].sayName:function(){
console.log(this.name); }}var p1 = new Person();
var p2 = new Person();
p1.friends.push('black o');
alert(p1.friends);//[' Alex ',' Huang ',' Hei ']
alert(p2.friends);//[' Alex ',' Huang ',' Hei ']
alert(p1.friends === p2.friends);//true
Copy the code
Portfolio model
Using a combination of constructor and stereotype patterns is the most common way to create custom types. The constructor pattern is used to define instance properties, while the stereotype pattern is used to define method and shared properties, and the combined pattern also supports passing parameters to the constructor. Instance objects have their own copy of instance attributes and share references to methods, maximizing memory savings. This pattern is currently the most widely used and recognized pattern for creating custom objects
function Person(name,age){
this.name = name;
this.age = age;
this.friends = ['alex'.'huang'];
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name); }}var p1 = new Person('mjj'.28);
var p2 = new Person('jjm'.30);
p1.friends.push('wusir');
alert(p1.friends);/ / [' alex ', 'huang', 'wusir]
alert(p2.friends);/ / / 'alex', 'huang'
alert(p1.friends === p2.friends);//false
alert(p1.sayName === p2.sayName);//true
Copy the code
Dynamic prototype pattern
The dynamic stereotype pattern encapsulates the constructor and the stereotype object used separately in the composite pattern, and then decides whether to initialize the stereotype object by checking that the method has been created
Using this approach, separate constructors and prototype objects are merged together, resulting in cleaner code and less contamination of global controls
Note: If the prototype object contains more than one statement, you only need to check one of the statements
function Person(name,age){
/ / property
this.name = name;
this.age = age;
/ / method
if(typeof this.sayName ! ="function"){
Person.prototype.sayName = function(){
console.log(this.name); }}}var p1 = new Person('Little Pony'.28);
p1.sayName();//
Copy the code
conclusion
Starting with creating one object in literal form, creating multiple objects creates code redundancy; The factory pattern can solve this problem, but it has the problem of object recognition. Then the constructor pattern is introduced, which solves the problem of object recognition, but has the problem of method duplication. Then it introduces the prototype pattern, which is characterized by sharing, but leads to the problem that reference type value attributes will be shared and modified by all instance objects. Finally, the constructor and stereotype composite pattern is proposed. The constructor pattern is used to define instance properties, while the stereotype pattern is used to define methods and shared properties. This composite pattern also supports passing parameters to constructors, which is the most widely used pattern
The object-oriented tabbed case
The effect
Procedure oriented TAB implementation
HTML part
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01 Common mode TAB implementation</title>
<style type="text/css">* {padding: 0;
margin: 0;
}
a{
text-decoration: none;
}
body{
background-color: #BAA895;
}
#wrap{
width: 302px;
height: 400px;
margin: 100px auto;
}
ul{
list-style: none;
overflow: hidden;
border: 1px solid #3081BF;
height: 45px;
width: 300px;
}
ul li{
float: left;
width: 100px;
height: 45px;
line-height: 45px;
text-align: center;
}
ul li a{
display: inline-block;
width: 100px;
height: 100%;
font-size: 18px;
color: # 262626;
}
ul li.active{
background-color: #3081BF;
font-weight: bold;
}
.content{
width: 300px;
height: 300px;
border: 1px solid #3081BF;
display: none;
}
</style>
</head>
<body>
<div id="wrap">
<ul>
<li class="active">
<a href="javascript:void(0);">recommended</a>
</li>
<li>
<a href="javascript:void(0);">A novel</a>
</li>
<li>
<a href="javascript:void(0);">navigation</a>
</li>
</ul>
<div class="content" style="display:block;">recommended</div>
<div class="content">A novel</div>
<div class="content">navigation</div>
</div>
<script type="text/javascript">
window.onload = function(){
// 1. Obtain the required label
var tabLis = document.getElementsByTagName('li');
var contentDivs=document.getElementsByClassName('content');
for(var i = 0; i < tabLis.length; i++){
// Save each I
tabLis[i].index = i;
tabLis[i].onclick = function(){
for(var j = 0; j < tabLis.length; j++){ tabLis[j].className =' ';
contentDivs[j].style.display = 'none';
}
this.className = 'active';
contentDivs[this.index].style.display = 'block'; }}}</script>
</body>
</html>
Copy the code
Slowly change to an object-oriented format
Encapsulation: Separation of functions and methods
window.onload = function(){
// 1. Obtain the required label
var tabLis = document.getElementsByTagName('li');
var contentDivs = document.getElementsByClassName('content');
for(var i = 0; i < tabLis.length; i++){
// Save each I
tabLis[i].index = i;
tabLis[i].onclick = clickFun;
}
function clickFun(){
for(var j = 0; j < tabLis.length; j++){ tabLis[j].className =' ';
contentDivs[j].style.display = 'none';
}
this.className = 'active';
contentDivs[this.index].style.display = 'block'; }}Copy the code
Based on object-oriented to achieve
Ideas:1.Create a constructor for TabSwitch2.Add attributes to the current object (state: bound HTML elements, for example)3.Add methods to the prototype object of the current object (click Methods)Copy the code
window.onload = function(){
// 1. Create constructor
function TabSwitch(obj){
console.log(obj);
// 2. Bind instance properties
this.tabLis = obj.children[0].getElementsByTagName('li');
this.contentDivs = obj.getElementsByTagName('div');
for(var i = 0; i < this.tabLis.length; i++){
// Save each I
this.tabLis[i].index = i;
this.tabLis[i].onclick = this.clickFun;
}
}
TabSwitch.prototype.clickFun = function(){
// delete all
for(var j = 0; j < this.tabLis.length; j++){this.tabLis[j].className = ' ';
this.contentDivs[j].style.display = 'none';
}
this.className = 'active';
this.contentDivs[this.index].style.display = 'block';
}
var wrap = document.getElementById('wrap');
var tab = new TabSwitch(wrap);
}
Copy the code
When you feel like you’ve written it perfectly, you run it on the web and you get an error
This is because clickFun now points to the currently clicked Li tag, and we want this in this method to point to a TAB object.
Put the clickFun call in a function so that the clickFun object does not change. At the same time, also have another problem, at this point the clickFun this points to the TAB object, but this. The className, enclosing the index, the object of this should point to the TAB, so not the two attributes of the TAB object. That’s why the following modification is correct
// 1. Create the TabSwitch constructor
function TabSwitch(id){
/ / save this
var _this = this;
var wrap = document.getElementById(id);
this.tabLis = wrap.children[0].getElementsByTagName('li');
this.contentDivs = wrap.getElementsByTagName('div');
for(var i = 0; i< this.tabLis.length; i++){
// Set the index
this.tabLis[i].index = i;
// Add an event to the button
this.tabLis[i].onclick = function(){
_this.clickFun(this.index); }}}// Prototype method
TabSwitch.prototype.clickFun = function(index){
// delete all
for(var j = 0; j < this.tabLis.length; j++){this.tabLis[j].className = ' ';
console.log(this.contentDivs)
this.contentDivs[j].style.display = 'none';
}
this.tabLis[index].className = 'active';
this.contentDivs[index].style.display = 'block';
};
new TabSwitch('wrap');
Copy the code
The final version
Extract the code into a separate JS file and import it when you use it
Five ways to implement inheritance
Learning how to create objects is the first step to understanding object-oriented programming. Have you learned that? Object creation is described in detail in the above article. If you don’t have a thorough understanding of it, you are advised to go back and learn something new. So the second part is understanding inheritance. Inheritance means that all properties and methods in the stereotype object can be shared by the instance object. That is to say, we only need to make some modifications on the basis of the original object to get a new object.
Prototype chain inheritance
JavaScript uses prototype chains as the primary method for implementing inheritance, which essentially overwrites the prototype object and replaces it with an instance of a new type. In the following code, the properties and methods that once existed in the SuperType instance object now also exist in subType.prototype
function Super(){
this.value = true;
}
Super.prototype.getValue = function(){
return this.value
}
function Sub(){};
//Sub inherits Super
Sub.prototype = new Super();
Sub.prototype.constroctor = Sub;
var ins = new Sub();
console.log(ins.getValue());//true
Copy the code
The above code specifies two types: Super and Sub. Sub inherits Super, which is achieved by creating an instance of Super and assigning it to sub. prototype. The essence of the ** implementation is to rewrite the object and replace it with a property of a new type. ** In other words, all properties and methods that used to exist in instances of Super now also exist in sub.prototype. As shown in the figure.
As you can see from the image above, instead of using the default prototype provided by Sub, we replaced it with a new one; This new prototype is an example of Super. Thus, the new stereotype not only has the properties and methods that it has as an instance of Super, but it also points to the stereotype of Super. The end result looks like this:
Ins =>Sub prototype =>Super prototypeCopy the code
The getValue() method is still in sub.prototype, but the value property is in sub.prototype. This is because value is an instance property and getValue() is a prototype method. Since sub. prototype is now an instance of Super, value is in that instance.
Also, note that ins.constructor now points to Super because the original sub. prototype constructor was overwritten.
The main problem with stereotype chains is that private stereotype attributes are shared by instances, which is why you define attributes in constructors rather than stereotype objects. When inheritance is implemented through stereotypes, the stereotype instance becomes an instance of another type. Thus, the original instance property becomes the prototype property of course.
function Super(){
this.colors = ['red'.'green'.'blue'];
}
Super.prototype.getValue = function(){
return this.colors
}
function Sub(){};
//Sub inherits Super
Sub.prototype = new Super();
var ins1 = new Super();
ins1.colors.push('black');
console.log(ins1.colors);//['red','green','blue','black'];
var ins2 = new Sub();
console.log(ins2.colors);//['red','green','blue','black'];
Copy the code
The second problem with stereotype chains is that you cannot pass arguments to the constructor of the parent type when creating an instance of a subtype. In fact, there is no way to pass arguments to the constructor of the parent type without affecting all instances. Coupled with the problem that stereotype attributes containing reference type values are shared by all instances, stereotype chain inheritance alone is rarely used in practice
Pay attention to the problem
Methods are defined carefully with stereotype chain inheritance. Subtypes sometimes need to override a method of the parent class, or add a method that doesn’t exist in the parent class. However, the code that adds methods to the stereotype must come after the statement that replaces the stereotype.
function Super() {
this.colors = ['red'.'green'.'blue'];
}
Super.prototype.getValue = function() {
return this.colors
}
function Sub() {
this.colors = ['black'];
};
//Sub inherits Super
Sub.prototype = new Super();
// Adding an existing method to the parent overrides the parent method
Sub.prototype.getValue = function() {
return this.colors;
}
// Add a method that does not exist in the parent class
Sub.prototype.getSubValue = function(){
return false;
}
var ins = new Sub();
// The result of overwriting the parent method
console.log(ins.getValue()); //['black']
// The result of the newly defined method in the subclass
console.log(ins.getSubValue());//false
// The parent class calls getValue() with the same value
console.log(new Super().getValue());//['red', 'green', 'blue']
Copy the code
Borrow constructor inheritance
The technique of borrowing constructors (sometimes called pseudo-class inheritance or classical inheritance). The basic idea of this technique is fairly simple: call the superclass constructor inside the subclass constructor. Remember that functions are simply objects that execute code in a particular environment, so you can also execute constructors on newly created objects by using the Apply () and call() methods.
function Super() {
this.colors = ['red'.'green'.'blue'];
}
Super.prototype.getValue = function(){
return this.colors;
}
function Sub(){
// Inherits Super
Super.call(this);// Replace this in the constructor Super with an INS instance object, so that only the private properties defined in Super are inherited, not the public methods defined in the stereotype properties
}
var ins = new Sub();
console.log(ins.colors);
Copy the code
Passing parameters
One big advantage of borrowing constructor inheritance over prototype chain is that you can pass arguments to the superclass constructor in the subclass constructor
function B(name){
this.name = name;
}
function A(){
// Inherits B, passing arguments
B.call(this.'MJJ');
// Instance properties
this.age = 28;
}
var p = new A();
alert(p.name);//'MJJ'
alert(p.age);/ / 28
Copy the code
Borrowing constructor problems
If you just borrow constructors, you can’t avoid the problem of the constructor pattern — methods are defined in constructors, so function reuse is out of the question. Also, methods defined in the parent class’s stereotype are not visible to subclasses. So it’s less used
Composite inheritance (critical)
Combinatorial inheritance refers to an inheritance pattern that combines a stereotype chain with a borrowed constructor technique to exploit the best of both. The idea behind this is to use stereotype chains to implement inheritance of public attributes and methods on stereotypes, while borrowing constructor inheritance to implement inheritance of private attributes on parent classes. In this way, function reuse is achieved by defining methods on the parent class prototype, while ensuring that each instance has the private attributes of the parent class.
function Super(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
Super.prototype.sayName = function(){
alert(this.name);
}
function Sub(name,age){
Super.call(this,name);
this.age = age;
}
// Inheritance method
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
alert(this.age);
}
var ins = new Sub('mjj'.28);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'mjj'
ins.sayAge();/ / 28
var ins2 = new Sub('alex'.38);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'alex'
ins2.sayAge();/ / 38
Copy the code
In the previous example, the Sub constructor defines two attributes: name and age. The Super prototype defines a sayName() method. The Super constructor is called in the Sub constructor with the name argument, followed by the definition of its own property age. We then assign an instance of Super to the prototype of Sub, and define the method sayAge() on that new prototype. In this way, different Sub instances can have their own attributes — including the colors attribute — and use the same method
Composite inheritance avoids the pitfalls of stereotype chains and borrowed constructors and combines their strengths, making it the most commonly used inheritance pattern in JavaScript.
Problems with composite inheritance
In any case, the superclass constructor is called twice: once when the subclass prototype is created and once inside the subclass constructor.
Parasitic combinatorial inheritance
Composite inheritance is the most common inheritance pattern in JavaScript. However, it has its own disadvantages. The biggest problem with composite inheritance is that in any case, the superclass constructor is called twice: once when the subclass stereotype is created and once inside the subclass constructor. Yes, subtypes eventually contain all of the instance properties of the supertype object, but we have to override those properties when we call the subtype constructor. Take a look at the following composite inheritance example.
function Super(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
Super.prototype.sayName = function(){
alert(this.name);
}
function Sub(name,age){
Super.call(this,name);
this.age = age;
}
// Inheritance method
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
alert(this.age);
}
var ins = new Sub('mjj'.28);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'mjj'
ins.sayAge();/ / 28
var ins2 = new Sub('alex'.38);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'alex'
ins2.sayAge();/ / 38
Copy the code
Parasitic combinatorial inheritance inherits properties by borrowing constructors and inherits methods through a hybrid form of prototype chains. The basic idea behind this is that you don’t need to call the constructor of the supertype to specify the stereotype of the subtype; all you need is a copy of the stereotype of the supertype. Essentially, you use parasitic inheritance to inherit the stereotype of the supertype and then assign the result to the stereotype of the subtype. The basic pattern of parasitic combinatorial inheritance is shown below.
function Super(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
Super.prototype.sayName = function(){
alert(this.name);
}
function Sub(name,age){
// Inherit instance attributes
Super.call(this,name);
this.age = age;
}
// Inherit public methods
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
alert(this.age);
}
var ins = new Sub('mjj'.28);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'mjj'
ins.sayAge();/ / 28
var ins2 = new Sub('alex'.38);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'alex'
ins2.sayAge();/ / 38
Copy the code
Multiple inheritance
Multiple inheritance does not exist in JavaScript, which means that an object cannot inherit multiple objects at the same time, but workarounds can be used to do this.
<! DOCTYPEhtml>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>28 Multiple Inheritance</title>
</head>
<body>
<script type="text/javascript">
// Multiple inheritance: an object inherits multiple objects simultaneously
// Person Parent Me
function Person(){
this.name = 'Person';
}
Person.prototype.sayName = function(){
console.log(this.name);
}
/ / customize the Parent
function Parent(){
this.age = 30;
}
Parent.prototype.sayAge = function(){
console.log(this.age);
}
function Me(){
// Inherit the attributes of Person
Person.call(this);
Parent.call(this);
}
// Inherit the Person method
Me.prototype = Object.create(Person.prototype);
// You cannot override a prototype object to implement inheritance from another object
// Me.prototype = Object.create(Parent.prototype);
// Object.assign(targetObj,copyObj)
Object.assign(Me.prototype,Parent.prototype);
// Specify the constructor
Me.prototype.constructor = Me;
var me = new Me();
</script>
</body>
</html>
Copy the code
Object Related methods in an Object
Object Native methods of objects fall into two categories: methods of Object itself and methods of Object instances.
Methods are defined directly on the current constructor Object
/ / case
Object.xxx()
Copy the code
Object instance methods are the methods defined in Object.prototype. It can be shared directly by Object instances
Object.prototype.hello = function(){
console.log('hello');
}
var obj = new Object(a); obj.hello();//hello
Copy the code
Static methods of Object
Static methods are methods specified in the Object itself
The following two methods are used to traverse the properties of an object
Object.keys()
Keys takes an Object and returns an array. The members of this array are all the property names of the object itself
var arr = ['a'.'b'.'c'];
Object.keys(arr);/ / [' 0 ', '1', '2');
var obj = {
0:'a'.1:'b'.2: 'c'
}
Object.keys(obj);/ / [' 0 ', '1', '2')
Copy the code
Object.getOwnPropertyNames()
Object. The getOwnPropertyNames method and the Object. The keys are similar, is also accept an Object as a parameter, returns an array that contains all of the attributes of the Object itself.
var obj = {
0:'a'.1:'b'.2: 'c'
}
Object.getOwnPropertyNames(obj);/ / / "0", "1", "2"]
Copy the code
For general Object, the Object. The keys () and Object getOwnPropertyNames () returns the result is the same. The only difference is when non-enumerable properties are involved. Only returns an enumerable Object. Keys method the properties of the Object. The getOwnPropertyNames method returns an enumeration of the property name.
var arr = ['a'.'b'.'c'];
Object.getOwnPropertyNames(arr);//['0','1','2','length'];
Copy the code
The above code, the array length attribute is an enumerated attribute, so only appear in the Object. The getOwnPropertyNames method returns the result.
Since JavaScript does not provide a way to count the number of attributes on an object, you can use these two methods instead.
var obj = {
0:'a'.1:'b'.2: 'c'
}
Object.keys(obj).length / / 3
Object.getOwnPropertyNames(obj).length / / 3
Copy the code
In general, the Object. Keys method is the most popular way to traverse an Object’s properties.
Object.getPrototypeOf()
The object.getProtoTypeof method returns the prototype of the parameter Object. This is the standard way to get a prototype object
var Fn = function(){};
var f1 = new Fn();
console.log(Object.getPrototypeOf(f1) === Fn.prototyoe);//true
Copy the code
In the code above, the prototype for instance object F is F.prototype.
The following are prototypes for several special objects.
// The empty Object's prototype is object.prototype
Object.getPrototypeOf({}) === Object.prototype // true
// Object. Prototype is null
Object.getPrototypeOf(Object.prototype) === null // true
// The Function prototype is function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true
Copy the code
Object.setPrototypeOf()
The object. setPrototypeOf method takes two arguments, the first an existing Object and the second a prototype Object
var a = {};
var b = {x : 1};
Object.setPrototypeOf(a,b);
console.log(Object.getPrototypeOf(a));//{x:1}
a.x / / 1
Copy the code
The new command can be emulated using the object. setPrototypeOf method.
var F = function () {
this.foo = 'bar';
};
//var f = new F();
/ / is equivalent to
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);
Copy the code
Object.create()
A common way to generate instance objects is to make the constructor return an instance using the new command. But a lot of times, you can only get one instance object, it might not be generated by the constructor at all, so can you generate another instance object from one instance object?
JavaScript provides the object.create method to fulfill this requirement. The method takes an object as an argument and returns an instance object based on it. This instance fully inherits the properties of the prototype object.
// Prototype objects
var A = {
print: function () {
console.log('hello'); }};// Instance object
var B = Object.create(A);
Object.getPrototypeOf(B) === A // true
B.print() // hello
B.print === A.print // true
Copy the code
In the code above, the object. create method generates an Object B from an Object A. B inherits all of A’s properties and methods.
Other methods
In addition to the two methods mentioned above,Object has a number of other static methods, which we’ll cover in more detail later
(1) Methods related to object attribute model
Object.getOwnPropertyDescriptor()
: Gets the description object of an attribute.Object.defineProperty()
: Defines an attribute by describing an object.Object.defineProperties()
: Defines multiple properties by describing objects.
(2) Method of controlling object state (for details, refer to MDN by yourself)
Object.preventExtensions()
: Prevents object expansion.Object.isExtensible()
: Determines whether an object is extensible.Object.seal()
: Disables object configuration.Object.isSealed()
: Determines whether an object is configurable.Object.freeze()
: Freezes an object.Object.isFrozen()
: Determines whether an object is frozen.
Object instance method
Methods are defined on object.prototype objects. We call them instance methods, and all instance objects of Object inherit these methods
Object Instance Object methods, there are six main.
Object.prototype.valueOf()
: Returns the value of the current object.Object.prototype.toString()
: Returns the string representation of the current object.Object.prototype.toLocaleString()
: Returns the local string representation of the current object.Object.prototype.hasOwnProperty()
: Determines whether an attribute is a property of the current object itself or inherits from the prototype object.Object.prototype.isPrototypeOf()
: Determines whether the current object is a prototype of another object.Object.prototype.propertyIsEnumerable()
: Determines whether an attribute is enumerable.
Object.prototype.valueOf()
The valueOf method returns the valueOf an object, by default returning the object itself
var obj = new Object(a); obj.valueOf() === obj;//true
Copy the code
The main use of the valueof method is that it is called by default when JavaScript automatically converts to types
var obj = new Object(a);//JavaScript defaults to calling the valueOf() method to evaluate obj and add it to 1
console.log(1+obj);//"1[object Object]"
Copy the code
So, if you customize the valueOf method, you can get the result you want
var obj = new Object(a); obj.valueOf =function(){
return 2;
}
console.log(1+ obj);/ / 3
Copy the code
ValueOf override Object.prototype.valueof with custom object.valueof
Object.prototype.toString()
The toString method returns a string representation of an object. By default, it returns a string of type
var obj1 = new Object(a);console.log(obj1.toString());//"[object Object]"
var obj2 = {a:1};
obj2.toString() // "[object Object]"
Copy the code
The result returned indicates the type of the object.
Object object by itself is not very useful, but by customizing the toString method, you can make an object get the desired string form when the automatic type conversion is performed.
var obj = new Object(a); obj.toString =function(){
return 'hello';
}
console.log(obj + ' ' + 'world');//"hello world"
Copy the code
Like arrays, strings, functions, and the Date Object defines the custom toString method respectively, covering the Object. The prototype, the toString () method
[1.2.3].toString() / / "1, 2, 3"
'123'.toString() / / "123"
(function () {
return 123;
}).toString()
// "function () {
// return 123;
// }"
(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
Copy the code
* * Object. The prototype. ToLocaleString * * method with the toString () method of usage.
Currently, there are three main objects that have custom toLocaleString methods
- Array.prototype.toLocaleString()
- Number.prototype.toLocaleString()
- Date.prototype.toLocaleString()
For example, the toString and toLocaleString returns of date instances are different, and toLocaleString returns are dependent on the locale specified by the user
var date = new Date(a); date.toString()// "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
date.toLocaleString() // "1/01/2018, 12:01:33 PM"
Copy the code
Object.prototype.isPrototypeOf()
The isPrototypeOf method of an instance object that determines whether the object is a prototype of the object.
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3);//true
o1.isPrototypeOf(o3);//true
Copy the code
In the code above, o1 and O2 are both prototypes of O3. This indicates that the isPrototypeOf method returns true whenever the instance object is on the prototype chain of the parameter object.
Object.prototype.isPrototypeOf({}) // true
Object.prototype.isPrototypeOf([]) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
Copy the code
In the above code, because Object.prototype is at the top of the prototype chain, it returns true for all instances except for objects that inherit directly from NULL.
Object.prototype.__proto
__
Instance object’s __proto__ property (two underscores before and two underscores behind), returns the prototype of that object. This property is read and write.
var obj = {};
var p = {};
obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true
Copy the code
The code above sets the p object to be the prototype of the obj object via the __proto__ attribute.
According to the language standard, the __proto__ attribute is deployed only by browsers, and other environments may not have it. The underlining indicates that it is essentially an internal property and should not be exposed to the user. Therefore, use this property as little as possible, and instead use Object.getProtoTypeof () and Object.setProtoTypeof () for reading and writing prototyped objects.
Object.prototype.hasOwnProperty
Object. The prototype. The hasOwnProperty method accepts a string as an argument and returns a Boolean value that indicates that the instance whether Object itself has the attribute.
var obj = {
a: 123
}
obj.hasOwnProperty('b');//false
obj.hasOwnProperty('a');//true
obj.hasOwnProperty('toString');//false
Copy the code
In the code above, the object obj has an A property of its own, so returns true. The toString property is inherited, so it returns false.
Attribute description object
JavaScript provides an internal data structure that describes the properties of an object and controls its behavior, such as whether the property is writable, traversable, and so on. This internal data structure is called an attribute description object. Each attribute has its own attribute description object, which stores some meta information about the attribute
Here is an example of a property description object
{
value: 123.writable: false.enumerable: true.configurable: false.get: undefined.set: undefined
}
Copy the code
The property description object provides six meta-properties
attribute | meaning |
---|---|
value | value Is the property value of the property, which defaults toundefined . |
writable | writable Is a Boolean value that indicates whether the property value can be changed (that is, writable). The default istrue . |
enumerable | enumerable Is a Boolean value indicating whether the property is traversable, which is the defaulttrue . If set tofalse Can make certain operations (such asfor... in Circulation,Object.keys() Skip this property. |
configurable | configurable Is a Boolean value indicating configurability. Default istrue . If set tofalse Will prevent operations from overwriting the property, such as not being able to delete the property and not changing the property description object (value Attributes excluded). In other words,configurable Properties control the writability of property description objects. |
get | get Is a function that represents the getter for the property, default toundefined . |
set | set Is a function that represents a setter for the property, which defaults toundefined . |
Object.getOwnPropertyDescriptor()
Object. GetOwnPropertyDescriptor () method can obtain property description Object. Its first argument is the target object, and its second argument is a string corresponding to the name of an attribute of the target object.
var obj = {name:'MJJ'};
Object.getOwnPropertyDescriptor(obj,'name');
/*
{
configurable: true
enumerable: true
value: "MJJ"
writable: true
}
*/
//toString is an inherited property and cannot be retrieved
Object.getOwnPropertyDescriptor(obj,'toString');//undefined
Copy the code
Note: Object. GetOwnPropertyDescriptor () method can only be used for the properties of the Object itself, cannot be applied to inherit property
Object.defineProperty()
The Object.defineProperty method allows you to describe an Object by attribute, define or modify an attribute, and then return the modified Object.
The syntax is as follows:
Object.defineProperty(object, propertyName, attributesObject)
Copy the code
The object.defineProperty method takes three arguments, as follows.
- Object: The object of the attribute
- PropertyName: indicates the propertyName
- AttributesObject: Indicates an attribute description object
For example, defining obj.name can be written as follows
var obj = Object.defineProperty({},'name', {value:'mjj'.writable:false.enumerable:true.configurable:false
})
console.log(obj.name);//mjj
obj.name = 'alex';
console.log(obj.name);//mjj
Copy the code
In the code above, the object.defineProperty () method defines the obj.p property. Because the writable property of the property description object is false, the obj.p property is not writable. Notice that the first argument to the object.defineProperty method here is {} (a newly created empty Object), the p property is defined directly on the empty Object and then returns the Object, which is a common use of Object.defineProperty().
If the attribute already exists, the object.defineProperty () method is equivalent to updating the attribute description Object for the attribute.
Object.defineProperties()
If multiple attributes are defined or modified at once, the object.defineProperties () method can be used
var obj = Object.defineProperties({}, {
p1: { value: 123.enumerable: true },
p2: { value: 'abc'.enumerable: true },
p3: {
get: function () {
return this.p1 + this.p2
},
enumerable:true.configurable:true}});console.log(obj.p1);/ / 123
console.log(obj.p2);//"abc"
console.log(obj.p3);//"123abc"
Copy the code
In the above code, Object.defineProperties() defines three properties of an obj Object simultaneously. Where, attribute P3 defines the value function get, that is, the value function will be called every time the property is read.
Note that once you define the get (or set) function, you cannot set the writable property to true or also define the value property, otherwise an error will be reported
var obj = {};
Object.defineProperty(obj, 'p', {
value: 123.get: function() { return 456; }});// TypeError: Invalid property.
// A property cannot both have accessors and be writable or have a value
Object.defineProperty(obj, 'p', {
writable: true.get: function() { return 456; }});// TypeError: Invalid property descriptor.
// Cannot both specify accessors and a value or writable attribute
Copy the code
The above code, defining both the GET and value attributes and setting the writable attribute to true, generates an error.
DefineProperty () and Object.defineProperties(), the default value of writable, cis, and Enumerable is false.
var obj = {};
Object.defineProperty(obj, 'foo'{});Object.getOwnPropertyDescriptor(obj, 'foo')
/*
{
configurable: false,
enumerable: false,
value: undefined,
writable: false,
}
*/
Copy the code
In the code above, we define obj.foo with an empty attribute description object, so we can see the default values for each meta-attribute.
Object.prototype.propertyIsEnumerable()
The propertyIsEnumerable() method of the instance object returns a Boolean value that determines whether a property can be iterated. Note that this method can only be used to determine the properties of the object itself and returns false for inherited properties.
var obj = {};
obj.p = 123;
obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false
Copy the code
In the code above, obj.p is traversable and obj.toString is an inherited property.
Yuan properties
value
The value attribute is the value of the target attribute
var obj = {};
obj.p = 123;
Object.getOwnPropertyDescriptor(obj, 'p').value / / 123
Object.defineProperty(obj, 'p', { value: 246 });
obj.p / / 246
Copy the code
The above code is an example of reading or overwriting obj.p via the value property.
writable
The writable attribute is a Boolean value that determines whether the value of the target attribute can be changed.
var obj = {};
Object.defineProperty(obj, 'a', {
value: 37.writable: false
});
obj.a / / 37
obj.a = 25;
obj.a / / 37
Copy the code
In the code above, the writable property of obj. A is false. Then, changing the value of obj. A won’t do anything.
Note that in normal mode, assigning to a property with writable false will not report an error, but will silently fail. However, an error is reported in strict mode, even if the a attribute is reassigned to the same value.
'use strict';
var obj = {};
Object.defineProperty(obj, 'a', {
value: 37.writable: false
});
obj.a = 37;
// Uncaught TypeError: Cannot assign to read only property 'a' of object
Copy the code
The above code is in strict mode and will report an error for any assignment to obj.
If the writable of an attribute of the prototype object is false, the child object will not be able to customize that attribute.
var proto = Object.defineProperty({}, 'foo', {
value: 'a'.writable: false
});
var obj = Object.create(proto);
obj.foo = 'b';
obj.foo; // 'a'
Copy the code
In the code above, proto is a prototype object whose foo property is unwritable. Obj objects inherit from Proto and can no longer customize this property. In strict mode, doing so also throws an error.
One way around this limitation, however, is to override the attribute description object. The reason is that in this case, the prototype chain is completely ignored.
var proto = Object.defineProperty({}, 'foo', {
value: 'a'.writable: false
});
var obj = Object.create(proto);
Object.defineProperty(obj, 'foo', {
value: 'b'
});
obj.foo // "b"
Copy the code
enumerable
Enumerable Returns a Boolean value that indicates whether the target property is traversable.
If an attribute’s Enumerable is false, the following three operations do not retrieve it
for... in
cycleObject.key
methodsJSON.stringify
methods
Therefore, Enumerable can be used to set a “secret” property.
var obj = {};
Object.defineProperty(obj, 'x', {
value: 123.enumerable: false
});
obj.x / / 123
for (var key in obj) {
console.log(key);
}
// undefined
Object.keys(obj) / / []
JSON.stringify(obj) / / "{}"
Copy the code
In the code above, the obj.x property’s Enumerable is false, so it can’t be retrieved by normal traversal operations, making it a bit of a “secret” property, but not really private. You can still retrieve its value directly.
Note that for… The in loop includes inherited properties; the object. keys method does not. If you need to get all the attributes of the Object itself, whether can traverse, you can use the Object. The getOwnPropertyNames method.
In addition, the json.stringify method rules out enumerable properties that are false, which can sometimes be leveraged. You can set Enumerable to False if you want to exclude some properties in the JSON output of your object.
configurable
The property description object returns a Boolean value, which determines whether it can be modified without any additional information. If the 64x is false, the value, writable, Enumerable, and cis cannot be modified anymore.
var obj = Object.defineProperty({}, 'p', {
value: 1.writable: false.enumerable: false.configurable: false
});
Object.defineProperty(obj, 'p', {value: 2})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {writable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {enumerable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {configurable: true})
// TypeError: Cannot redefine property: p
Copy the code
In the above code, the 64x of OBJ. P is false. If any changes are made to the value, writable, Enumerable, and 64x, any error is reported.
Note that writable will only report an error if false is changed to true, which is allowed.
var obj = Object.defineProperty({}, 'p', {
writable: true.configurable: false
});
Object.defineProperty(obj, 'p', {writable: false})
// The modification succeeded
Copy the code
As for the value, any changes are allowed if either writable or 64x is true.
var o1 = Object.defineProperty({}, 'p', {
value: 1.writable: true.configurable: false
});
Object.defineProperty(o1, 'p', {value: 2})
// The modification succeeded
var o2 = Object.defineProperty({}, 'p', {
value: 1.writable: false.configurable: true
});
Object.defineProperty(o2, 'p', {value: 2})
// The modification succeeded
Copy the code
In addition, if writable is false, assignment to the target property is performed without error, but will not succeed.
var obj = Object.defineProperty({}, 'p', {
value: 1.writable: false.configurable: false
});
obj.p = 2;
obj.p / / 1
Copy the code
In the above code, the writable of obj.p is false. Assigning to obj.p directly will not work. If it is in strict mode, an error is reported.
Configurability determines whether a target attribute can be deleted (delete).
var obj = Object.defineProperties({}, {
p1: { value: 1.configurable: true },
p2: { value: 2.configurable: false}});delete obj.p1 // true
delete obj.p2 // false
obj.p1 // undefined
obj.p2 / / 2
Copy the code
In the above code, OBJ. P1 is configured with true, so it can be deleted without any additional information
accessor
In addition to being defined directly, attributes can also be defined using accessors. The store function, called setter, uses properties to describe the set property of the object. The value function, called a getter, uses properties to describe the get property of an object
Once an accessor is defined for a target property, the corresponding function is executed whenever it is accessed. With this feature, you can implement many advanced features, such as a property that disables assignment
var obj = Object.defineProperty({},'p', {get:function(){
return 'getter';
},
set:function(value){
console.log('setter:'+value);
}
})
obj.p //"getter"
obj.p = 123;//"setter:123"
Copy the code
In the code above, obj.p defines the get and set attributes. When obj.p is evaluated, get is called; When you assign, set is called.
JavaScript also provides another way to write accessors.
var obj = {
get p() {return 'getter';
},
set p(value) {console.log('setter:'+ value); }}Copy the code
The above notation is equivalent to defining an attribute description object, and is more widely used.
Note that the get function cannot take arguments, and the set function can take only one argument (that is, the value of an attribute).
Accessors are used when the value of a property depends on the internal data of the object
var obj = {
$n : 5.get next() {return this.$n++;
},
set next(value) {if(value >= this.$n){
this.$n = value;
}else{
throw new Error('New value must be greater than current value'); }}}; obj.next/ / 5
obj.next = 10;
obj.next / / 10
obj.next = 5;
// Uncaught Error: The new value must be greater than the current value
Copy the code
In the code above, both the store and the value functions of the next attribute must depend on the internal attribute $n
Depth copy
Copy of basic type
Let’s start with a very classic piece of code
var a = 1;
var b = a;
a = 200;
console.log(a);/ / 200
console.log(b);/ / 1
Copy the code
The base type is passed by value, the reference type is passed by reference, and the value as the base type is stored in stack memory and can be used directly. Assignment is what it is, and is not affected by changes in the passed element.
A copy of the reference type
To put it simply, a reference type generates a pointer and stores it in heap memory. When we assign a value to a reference type, we write in stack memory, which means we get a pointer. This pointer is the code that points to the reference type in stack memory.
There are two types of copy involved: deep copy and shallow copy
The operation of the copied data does not affect the value copy of the original data, which is a deep copy. In any case, it is a shallow copyCopy the code
Shallow copy
var a = {
name:'mjj'.age:20.hobby:'eat'.friend: {name:'alex'.age:38}}function shadowCopy(to,from){
for(var key in from){
to[key] = from[key]
}
return to;
}
var newObj = shadowCopy({},a);
newObj.age = 18;
newObj.friend.name = 'huang';
/* {age: 20,// not changed friend: {name: "yellow ",// not changed, so that the same reference age: 38}, hobby: "eat" name:" MJJ} */
Copy the code
We find that, first of all, the shallow copy does not assign a value directly. The shallow copy creates a new object and copies all the properties of the source object, copying the values instead of the references.
As we know, objects are accessed by address reference. A shallow copy copy only copies the attributes of the first layer and does not recursively copy all the values. Therefore, the operation of copying data affects the original data, so it is a shallow copy.
Deep copy
A deep copy is a full copy of the target, not just a layer of references, but also the values, as in a shallow copy.
As long as the deep copy, they will never communicate, no one will influence each other.
With deep copy, newly created objects can be completely separated from the original
var a = {
name:'mjj'.age:20.hobby:'eat'.friend: {name:'alex'.age: 38.hobby:'chicken'}}function deepCopy(to,from){
for(var key in from) {// Do not iterate over properties on the prototype chain
if(from.hasOwnProperty(key)){
// If the value is an object and has a value, recurse
if(from[key] && typeof from[key] === 'object') {// Distinguish between a generic object and an array object
to[key] = from[key].constructor === Array? [] : {}; to[key] = deepCopy(to[key],from[key]);
}else{
// If not, assign directly
to[key] = from[key]; }}}return to;
}
var newObj = deepCopy(a);
newObj.age = 18;
newObj.friend.name = 'huang';
Copy the code
The hasOwnProperty property is used to filter out inherited properties.