Several commonly used object creation modes
Create using the new keyword
The most basic method of object creation is nothing more than the same as in most other languages: there is no object, you create a new one!
var gf = new Object(); gf.name = "tangwei"; gf.bar = "c++"; gf.sayWhat = function() { console.log(this.name + "said:love you forever"); }
Create using literals
This seems appropriate, but how can geeks like such a complicated and low-key way of defining variables? As a scripting language, it should have the same style as other brothers, so it appeared. How object literals are defined:
var gf = { name : "tangwei", bar : "c++", sayWhat : function() { console.log(this.name + "said:love you forever"); } }
Factory Mode
Actually, this is the most commonly used way to define objects in practice, but what should I do if I want to have many objects with similar properties (thinking about it is exciting...)? If we define them one by one, a lot of code will be generated. Why not build a factory and produce our objects in batches? Hence, the first inflatable baby in the JavaScript world. . . No, the "factory model" was born!
function createGf(name, bar) { var o = new Object(); o.name = name; o.bar = bar; o.sayWhat = function() { alert(this.name + "said:love you forever"); } return o; } var gf1 = createGf("bingbing","d"); var gf2 = createGf("mimi","a");
Constructor
The factory pattern solves the problem of creating multiple similar objects, but the problem comes again. These objects are all created from Object. How to distinguish their specific object types? At this time we need to switch to another mode, constructor mode:
function Gf(name,bar){ this.name = name; this.bar = bar; this.sayWhat = function(){ alert(this.name + "said:love you forever"); } } var gf1 = new Gf("vivian","f"); var gf2 = new Gf("vivian2","f");
Here we use a constructor starting with a capital letter to replace createGf in the above example. Note that according to the convention, the first letter of the constructor must be capitalized. Here we create a new object, then assign the scope of the constructor to the new object and call the methods in the constructor.
There seems to be nothing wrong with the above method, but we can find that the sayWhat method in the constructor called in the two instances is not the same Function instance:
console.log(gf1.sayWhat == gf2.sayWhat); //false
Calling the same method but declaring different instances is a waste of resources. We can optimize and declare the sayWhat function outside the constructor:
function Gf(name,bar){ this.name = name; this.bar = bar; this.sayWhat = sayWhat } function sayWhat(){ alert(this.name + "said:love you forever"); }
This solves the problem of multiple instances defining the same method instance multiple times, but a new problem arises. The sayWhat we defined is a global scope method, but this method cannot be called directly. This is a bit contradictory. How to define an object with certain encapsulation properties more elegantly? Let’s take a look at the JavaScript prototype object pattern.
Prototype Object Pattern
Understanding prototype objects
When we create a function, the function will have a prototype attribute, which points to the prototype object of the function created through the constructor. In layman's terms, a prototype object is an object in memory that provides shared properties and methods for other objects.
In prototype mode, there is no need to define instance attributes in the constructor, and attribute information can be directly assigned to the prototype object:
function Gf(){ Gf.prototype.name = "vivian"; Gf.prototype.bar = "c++"; Gf.prototype.sayWhat = function(){ alert(this.name + "said:love you forever"); } } var gf1 = new Gf(); gf1.sayWhat(); var gf2 = new Gf();
The difference from the constructor is that the properties and methods of the new object here can be shared by all instances. In other words, gf1 and gf2 access the same properties and methods. In addition to the attributes we assign to the prototype object, there are also some built-in attributes. All prototype objects have a constructor attribute, which is a pointer to a function containing the prototype attribute (dare you go further!). Let’s clearly understand this tongue-twisting process through a picture:
All objects have a prototype object (prototype). The prototype object has a constructor attribute pointing to the function containing the prototype attribute. Gf instances gf1 and gf2 both contain an internal attribute pointing to the prototype object (shown in the firefox browser is a private property proto), when we access a property in an object, we will first ask whether the property exists in the instance object, and if not, continue to look for the prototype object.
Use prototype objects
In the previous example, we noticed that when adding attributes to prototype objects, we need to add Gf.prototype to each one. This work is very repetitive. In the above object creation mode, we know that we can use literals. Create an object, we can also improve it here:
function Gf(){} Gf.prototype = { name : "vivian", bar : "c++", sayWhat : function(){ alert(this.name + "said:love you forever"); } }
There is one thing that needs special attention here. The constructor attribute no longer points to the object Gf, because every time a function is defined, a prototype object will be created for it at the same time. This object will also automatically obtain a new constructor attribute. This is We use Gf.prototype to essentially overwrite the original prototype object, so the constructor becomes the constructor property of the new object, no longer pointing to Gf, but Object:
var gf1 = new Gf(); console.log(gf1.constructor == Gf);//false console.log(gf1.constructor == Object)//true
一般情况下,这个微妙的改变是不会对我们造成影响的,但如果你对constructor有特殊的需求,我们也可以显式的指定下Gf.prototype的constructor属性:
Gf.prototype = { constructor : Gf, name : "vivian", bar : "c++", sayWhat : function() { alert(this.name + "said:love you forever"); } } var gf1 = new Gf(); console.log(gf1.constructor == Gf);//true
通过对原型对象模式的初步了解,我们发现所有的实例对象都共享相同的属性,这是原型模式的基本特点,但往往对于开发者来说这是把“双刃剑”,在实际开发中,我们希望的实例应该是具备自己的属性,这也是在实际开发中很少有人单独使用原型模式的主要原因。
构造函数和原型组合模式
在实际开发中,我们可以使用构造函数来定义对象的属性,使用原型来定义共享的属性和方法,这样我们就可以传递不同的参数来创建出不同的对象,同时又拥有了共享的方法和属性。
function Gf(name,bar){ this.name = name; this.bar = bar; } Gf.prototype = { constructor : Gf, sayWhat : function() { alert(this.name + "said:love you forever"); } } var gf1 = new Gf("vivian", "f"); var gf2 = new Gf("vivian1", "c");
在这个例子中,我们再构造函数中定义了对象各自的属性值,在原型对象中定义了constructor属性和sayWhat函数,这样gf1和gf2属性之间就不会产生影响了。这种模式也是实际开发中最常用的对象定义方式,包括很多js库(bootstrap等)默认的采用的模式。