`

JavaScript Object-Oriented Programming -- Object Creation

 
阅读更多

 

The Factory Pattern
The factory pattern is a well-known design pattern used in software engineering to abstract away the process of creating specific objects.
function createPerson(name, age, job) {
	var p = new Object();
	p.name = name;
	p.age = age;
	p.job = job;
	p.sayName = function() {
		alert(this.name);
	};

	return p;
}
	
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
	
person1.sayName();
person2.sayName();
Here, the function createPerson() accepts arguments with which to build an object with all of the necessary information to represent a Person object. The function can be called any number of times with different arguments and will still return an object that has three properties and one method. Though this solved the problem of creating multiple similar objects, the factory pattern didn't address the issue of object identification (what type of object an object is).
 
The Constructor Pattern
Constructors in ECMAScript are used to create specific types of objects. There are native constructors, such as Object and Array, which are available automatically in the execution environment at runtime. It is also possible to define custom constructors that define properties and methods for your own type of object. 
function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = sayName;
}

function sayName() {
	alert(this.name);
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName(); //"Nicholas"
person2.sayName(); //"Greg"
In this example, the sayName() function is defined outside the constructor. Inside the constructor, the sayName property is set equal to the global sayName() function. Since the sayName property now contains just a pointer to a function, both person1 and person2 end up sharing the sayName() function that is defined in the global scope. This solves the problem of having duplicate functions that do the same thing but also creates some clutter in the global scope by introducing a function that can realistically be used only in relation to an object. If the object needed multiple methods, that would mean multiple global functions, and all of a sudden the custom reference type definition is no longer nicely grouped in the code. These problems are addressed by using the prototype pattern.
 
The Prototype Pattern
Each function is created with a prototype property, which is an object containing properties and methods that should be available to instances of a particular reference type. This object is literally a prototype for the object to be created once the constructor is called. The benefit of using the prototype is that all of its properties and methods are shared among object instances. Instead of assigning object information in the constructor, they can be assigned directly to the prototype.
function Person() {
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
	alert(this.name);
}

var person1 = new Person();
person1.sayName(); //"Nicholas"

var person2 = new Person();
person2.sayName(); //"Nicholas"
Alternate Prototype Syntax
You may have noticed in the previous example that Person.prototype had to be typed out for each property and method. To limit this redundancy and to better visually encapsulate functionality on the prototype, it has become more common to simply overwrite the prototype with an object literal that contains all of the properties and methods.
function Person() {
}

Person.prototype = {
	constructor : Person,
	name : "Nicholas",
	age : 29,
	job : "Software Engineer",
	sayName : function() {
		alert(this.name);
	}
};

var friend = new Person();
alert(friend.sayName());
The prototype pattern isn’t without its faults. For one, it negates the ability to pass initialization arguments into the constructor, meaning that all instances get the same property values by default. Although this is an inconvenience, it isn’t the biggest problem with prototypes. The main problem comes with their shared nature.
All properties on the prototype are shared among instances, which is ideal for functions. Properties that contain primitive values also tend to work well, as shown in the previous example, where it’s possible to hide the prototype property by assigning a property of the same name to the instance. The real problem occurs when a property contains a reference value. 
function Person() {
}

Person.prototype = {
	constructor : Person,
	name : "Nicholas",
	age : 29,
	job : "Software Engineer",
	friends : [ "Shelby", "Court" ],
	sayName : function() {
		alert(this.name);
	}
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Van");

alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true
Here, the Person.prototype object has a property called friends that contains an array of strings. Two instances of Person are then created. The person1.friends array is altered by adding another string. Because the friends array exists on Person.prototype, not on person1, the changes made are also reflected on person2.friends (which points to the same array). If the intention is to have an array shared by all instances, then this outcome is okay. Typically, though, instances want to have their own copies of all properties. This is why the prototype pattern is rarely used on its own.
 
Combination Constructor/Prototype Pattern
The most common way of defining custom types is to combine the constructor and prototype patterns. The constructor pattern defines instance properties, whereas the prototype pattern defines methods and shared properties. With this approach, each instance ends up with its own copy of the instance properties, but they all share references to methods, conserving memory. This pattern allows arguments to be passed into the constructor as well, effectively combining the best parts of each pattern. 
function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.friends = [ "Shelby", "Court" ];
}

Person.prototype = {
	constructor : Person,
	sayName : function() {
		alert(this.name);
	}
};

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.friends.push("Van");

alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court"
alert(person1.friends === person2.friends); //false
Note that the instance properties are now defined solely in the constructor, and the shared property constructor and the method sayName() are defined on the prototype. When person1.friends is augmented by adding a new string, person2.friends is not affected, because they each have separate arrays.
The hybrid constructor/prototype pattern is the most widely used and accepted practice for defining custom reference types in ECMAScript. Generally speaking, this is the default pattern to use for defining reference types. 
 
Dynamic Prototype Pattern
Developers coming from other OO languages may find the visual separation between the constructor and the prototype confusing. The dynamic prototype pattern seeks to solve this problem by encapsulating all of the information within the constructor while maintaining the benefits of using both a constructor and a prototype by initializing the prototype inside the constructor, but only if it is needed. You can determine if the prototype needs to be initialized by checking for the existence of a method that should be available.
function Person(name, age, job) {

	//properties
	this.name = name;
	this.age = age;
	this.job = job;

	//methods
	if (typeof this.sayName != "function") {

		Person.prototype.sayName = function() {
			alert(this.name);
		};

	}
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();
The section of code inside the constructor adds the sayName() method if it doesn't already exist. This block of code is executed only the first time the constructor is called. After that, the prototype has been initialized and doesn’t need any further modification. Remember that changes to the prototype are reflected immediately in all instances, so this approach works perfectly.The if statement may check for any property or method that will be present once initialized — there’s no need for multiple if statements to check each property or method; any one will do. This pattern preserves the use of instanceof in determining what type of object was created. 
 
Parasitic Constructor Pattern
The parasitic constructor pattern is typically a fallback when the other patterns fail. The basic idea of this pattern is to create a constructor that simply wraps the creation and return of another object while looking like a typical constructor. 
function Person(name, age, job) {
	var o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function() {
		alert(this.name);
	};
	return o;
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
In this example, the Person constructor creates a new object, initializes it with properties and methods, and then returns the object. This is exactly the same as the factory pattern except that the function is called as a constructor, using the new operator. When a constructor doesn’t return a value, it returns the new object instance by default. Adding a return statement at the end of a constructor allows you to override the value that is returned when the constructor is called.This pattern allows you to create constructors for objects that may not be possible otherwise. 
A few important things to note about this pattern: there is no relationship between the returned object and the constructor or the constructor’s prototype; the object exists just as if it were created outside of a constructor. Therefore, you cannot rely on the instanceof operator to indicate the object type. Because of these issues, this pattern should not be used when other patterns work.
 

Summary

There are total 7 approaches to create object, e.g. 

  •  The Factory Pattern
  •  The Constructor Pattern
  •  The Prototype Pattern
  •  Combination Constructor/Prototype Pattern(The most common way)
  •  Dynamic Prototype Pattern
  •  Parasitic Constructor Pattern
  •  Durable Constructor Pattern

The most common way example

function Person(name) {
	this.name = name;
}

Person.prototype = {
	constructor: Person,
	sayName: function () {
		console.log(this.name);
	},
	toString: function () {
		return "[Person " + this.name + "]";
	}
};

var person1 = new Person("Michael");
var person2 = new Person("Jordan");

console.log(person1 instanceof Person); // true
console.log(person1.constructor === Person); // true
console.log(person1.constructor === Object); // false

console.log(person2 instanceof Person); // true
console.log(person2.constructor === Person); // true
console.log(person2.constructor === Object); // false

 

Reference

Wrox.Professional.javascript.for.Web.Developers.3rd.Edition.2012

Leanpub.Principles.of.Object-Oriented.Programming.in.JavaScript.Jun.2014

分享到:
评论

相关推荐

    Mastering JavaScript Object-Oriented Programming

    Mastering JavaScript Object-Oriented Programming 2016 | ISBN: 1785889109 | English | 287 pages | PDF | 1.9 MB With this book, we'll provide you with a comprehensive overview of OOP principles in ...

    Mastering+JavaScript+Functional+Programming-Packt+Publishing(2017).pdf

    programming, structured (go to less) programming, object-oriented programming, aspect-oriented programming, and declarative programming. Lately, there has been renewed interest in a particular ...

    Mastering+JavaScript+Functional+Programming-Packt+Publishing(2017).epub

    In computer programming, paradigms abound: Some examples are imperative programming, structured (go to less) programming, object-oriented programming, aspect-oriented programming, and declarative ...

    Dart in Action

    These features facilitate object-oriented programming and help organize code into reusable components. - **Concurrency with Isolates**: Dart provides a unique approach to concurrency using isolates, ...

    JavaScript in 10 Minutes

    2. **Java Classes and Interfaces**: Although the title mentions "Java classes and interfaces," this section likely discusses JavaScript's approach to object-oriented programming, including class-like ...

    javascript权威指南(第六版)

    9.6 Object-Oriented Techniques in JavaScript 215 9.7 Subclasses 228 9.8 Classes in ECMAScript 5 238 9.9 Modules 246 10. Pattern Matching with Regular Expressions . . . . . . . . . . . . . . . . . . . ...

    python3.6.5参考手册 chm

    The json module: JavaScript Object Notation The plistlib module: A Property-List Parser ctypes Enhancements Improved SSL Support Deprecations and Removals Build and C API Changes Port-Specific ...

Global site tag (gtag.js) - Google Analytics