introduce
The four code reuse modes introduced in this article are best practices and are recommended to everyone during the programming process.
Pattern 1: Prototype inheritance
Prototype inheritance is to let the parent object be the prototype of the child object, so as to achieve the purpose of inheritance:
The code copy is as follows:
function object(o) {
function F() {
}
F.prototype = o;
return new F();
}
// Parent object to inherit
var parent = {
name: "Papa"
};
// New object
var child = object(parent);
// test
console.log(child.name); // "Papa"
// Parent constructor
function Person() {
// an "own" property
this.name = "Adam";
}
// Add new attributes to the prototype
Person.prototype.getName = function () {
return this.name;
};
// Create a new person
var papa = new Person();
// Inheritance
var kid = object(papa);
console.log(kid.getName()); // "Adam"
// Parent constructor
function Person() {
// an "own" property
this.name = "Adam";
}
// Add new attributes to the prototype
Person.prototype.getName = function () {
return this.name;
};
// Inheritance
var kid = object(Person.prototype);
console.log(typeof kid.getName); // "function", because it is defined in the prototype
console.log(typeof kid.name); // "undefined", because only the prototype is inherited
At the same time, ECMAScript5 also provides a similar method called Object.create for inheriting objects, and the usage is as follows:
The code copy is as follows:
/* Use the new version of ECMAScript 5 to provide features*/
var child = Object.create(parent);
var child = Object.create(parent, {
age: { value: 2} // ECMA5 descriptor
});
console.log(child.hasOwnProperty("age")); // true
Moreover, properties can also be defined on the second parameter in a more fine-grained manner:
The code copy is as follows:
// First, define a new object man
var man = Object.create(null);
// Next, create a configuration setting containing properties
// The properties are set to writable, enumerable, and configurable
var config = {
writable: true,
enumerable: true,
configurable: true
};
// Usually use Object.defineProperty() to add new properties (ECMAScript5 supports)
// Now, for convenience, we customize an encapsulation function
var defineProp = function (obj, key, value) {
config.value = value;
Object.defineProperty(obj, key, config);
}
defineProp(man, 'car', 'Delorean');
defineProp(man, 'dob', '1981');
defineProp(man, 'beard', false);
So, inheritance can be done:
The code copy is as follows:
var driver = Object.create( man );
defineProp (driver, 'topSpeed', '100mph');
driver.topSpeed // 100mph
But there is one thing to note, that is, the prototype of the object created by Object.create(null) is undefined, that is, there is no toString and valueOf methods, so an error will occur when alert(man);, but alert(man.car); is fine.
Mode 2: Copy all attributes for inheritance
Inheritance in this way is to copy all the properties in the parent object to the child object. Generally, the child object can use the parent object's data.
Let’s first look at a shallow copy example:
The code copy is as follows:
/* Shallow copy*/
function extend(parent, child) {
var i;
child = child || {};
for (i in parent) {
if (parent.hasOwnProperty(i)) {
child[i] = parent[i];
}
}
return child;
}
var dad = { name: "Adam" };
var kid = extend(dad);
console.log(kid.name); // "Adam"
var dad = {
counts: [1, 2, 3],
reads: { paper: true }
};
var kid = extend(dad);
kid.counts.push(4);
console.log(dad.counts.toString()); // "1,2,3,4"
console.log(dad.reads === kid.reads); // true
On the last line of the code, you can find that the reads of dad and kid are the same, that is, they use the same reference, which is the problem caused by shallow copy.
Let's take a look at the deep copy:
The code copy is as follows:
/* Deep copy*/
function extendDeep(parent, child) {
var i,
toStr = Object.prototype.toString,
astr = "[object Array]";
child = child || {};
for (i in parent) {
if (parent.hasOwnProperty(i)) {
if (typeof parent[i] === 'object') {
child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
}
var dad = {
counts: [1, 2, 3],
reads: { paper: true }
};
var kid = extendDeep(dad);
kid.counts.push(4);
console.log(kid.counts.toString()); // "1,2,3,4"
console.log(dad.counts.toString()); // "1,2,3"
console.log(dad.reads === kid.reads); // false
kid.reads.paper = false;
After deep copying, the two values are no longer equal, bingo!
Mode 3: Mix-in
Mixed in is to copy one or more (or all) properties (or methods) of an object to another object. Let's give an example:
The code copy is as follows:
function mix() {
var arg, prop, child = {};
for (arg = 0; arg < arguments.length; arg += 1) {
for (prop in arguments[arg]) {
if (arguments[arg].hasOwnProperty(prop)) {
child[prop] = arguments[arg][prop];
}
}
}
return child;
}
var cake = mix(
{ eggs: 2, large: true },
{ butter: 1, salted: true },
{ flour: '3 cups' },
{ sugar: 'sure!' }
);
console.dir(cake);
The mix function copies the child attributes of all the parameters passed into the child object to generate a new object.
So how do we just want to mix in some attributes? How to do it? In fact, we can use extra parameters to define the properties that need to be mixed, such as mix (child, parent, method1, method2) so that we can only mix method1 and method2 in parent into child. On code:
The code copy is as follows:
// Car
var Car = function (settings) {
this.model = settings.model || 'no model provided';
this.colour = settings.colour || 'no colour provided';
};
// Mixin
var Mixin = function () { };
Mixin.prototype = {
driveForward: function () {
console.log('drive forward');
},
driveBackward: function () {
console.log('drive backward');
}
};
// The two parameters defined are the object being mixed into (reciving) and the object being mixed into (giving)
function augment(receivedObj, givingObj) {
// If the specified method name is provided, there are 3 extra parameters
if (arguments[2]) {
for (var i = 2, len = arguments.length; i < len; i++) {
receivingObj.prototype[arguments[i]] = givingObj.prototype[arguments[i]];
}
}
// If you do not specify the third parameter, or more parameters, all methods will be mixed
else {
for (var methodName in givingObj.prototype) {
// Check that the receiving object does not contain the name to be mixed in, so if it is included, it will not be mixed in
if (!receivingObj.prototype[methodName]) {
receivingObj.prototype[methodName] = givingObj.prototype[methodName];
}
}
}
}
// Mix the attributes for Car, but the values are mixed with 'driveForward' and 'driveBackward'*/
augment(Car, Mixin, 'driveForward', 'driveBackward');
// Create a new object Car
var vehicle = new Car({ model: 'Ford Escort', colour: 'blue' });
// Method to test whether the mixing is successfully obtained
vehicle.driveForward();
vehicle.driveBackward();
This method is more flexible to use.
Pattern 4: Borrowing Method
One object borrows one or two methods of another object, and there is no direct connection between these two objects. No need to explain more, just use the code to explain:
The code copy is as follows:
var one = {
name: 'object',
say: function (greet) {
return greet + ', ' + this.name;
}
};
// test
console.log(one.say('hi')); // "hi, object"
var two = {
name: 'another object'
};
console.log(one.say.apply(two, ['hello'])); // "hello, another object"
// Assign say to a variable, this will point to the global variable
var says = one.say;
console.log(say('hoho')); // "hoho, undefined"
// Pass a callback function callback
var yetanother = {
name: 'Yet another object',
method: function (callback) {
return callback('Hola');
}
};
console.log(yetanother.method(one.say)); // "Holla, undefined"
function bind(o, m) {
return function () {
return m.apply(o, [].slice.call(arguments));
};
}
var twosay = bind(two, one.say);
console.log(twosay('yo')); // "yo, another object"
// ECMAScript 5 adds a bind() method to Function.prototype so that it is easy to use apply() and call().
if (typeof Function.prototype.bind === 'undefined') {
Function.prototype.bind = function (thisArg) {
var fn = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function () {
return fn.apply(thisArg, args.concat(slice.call(arguments)));
};
};
}
var twosay2 = one.say.bind(two);
console.log(twosay2('Bonjour')); // "Bonjour, another object"
var twosay3 = one.say.bind(two, 'Enchanté');
console.log(twosay3()); // "Enchanté, another object"
Summarize
No need to summarize.