This post continues with more notes on distinctive features of object-oriented JavaScript as described in the book The Principles of Object-Oriented JavaScript.
The post is a follow-up to the earlier Part 3.
** Prototype chaining provides inheritance.
Almost all objects refer to a prototype, from which methods and properties are inherited that are defined in the prototype rather than in the object itself.
But an object's prototype is itself an object with its own prototype (usually). This chain normally goes all the way back to Object.prototype.
The object inherits the methods and properties of each of these prototypes. When a method is called, the JavaScript interpreter walks the chain to search for it. The first occurrence that is found is the one that is used. (That occurrence shadows any others that may be defined farther along in the chain.)
This means that pretty much every object inherits generally useful methods such as valueOf() and toString() from Object.prototype.
Here's a simple example of inheritance via prototypes.
function Pet(name) {
this.name = name;
}
Pet.prototype.getName = function () { return this.name; };
my_pet = new Pet('My Pet');
function Dog(name) {
this.name = name;
}
Dog.prototype = my_pet;
Dog.prototype.constructor = Dog;
my_dog = new Dog('Foghat');
console.log(my_dog.getName()); // Displays 'Foghat'
An instance of Pet is created, then it is assigned as the prototype for the Dog constructor. So the getName() method is indirectly inherited by the instance of Dog.
(The constructor property of Dog.prototype has to be restored to refer to Dog. Otherwise, it would refer to Pet, which is not correct.)
A slightly more concise and cleaner version of this example is
function Pet(name) {
this.name = name;
}
Pet.prototype.getName = function () { return this.name; };
function Dog(name) {
this.name = name;
}
Dog.prototype = new Pet();
Dog.prototype.constructor = Dog;
my_dog = new Dog('Foghat');
console.log(my_dog.getName()); // Displays 'Foghat'
We still create an instance of Pet,but it is anonymously assigned directly as the prototype for Dog.
The argument to the Pet constructor can be omitted since it isn't used in this particular use case.
This example chains from one constructor to another so it is known as constructor inheritance. However, the prototype chain may also consist of objects that are not constructors.
Caveat: prototype chaining works well for inheriting methods, but not for inheriting data properties that are intended to be instance data, that is, data with values that are unique to each object. That's because prototype objects are shared, therefore, their data properties are also shared among all objects that have the same prototype. These are like static or class variables in other languages.
(Continued at the final Part 5.)
May 25, 2015
May 17, 2015
Notes on object-oriented JavaScript for o-o developers (part 3)
This post continues with more notes on distinctive features of object-oriented JavaScript as described in the book The Principles of Object-Oriented JavaScript.
The post is a follow-up to the earlier Part 1 and Part 2.
** Although Javascript does not have classes, it does have constructors.
Here's a simple example of using a constructor to instantiate an object.
function Mountain(name, height) {
this.name = name;
this.height = height;
this.howTall = function () {
return this.height;
}
}
mt1 = new Mountain('Kilimanjaro', 19340);
A constructor is much like other functions, but it is different in the following ways.
- By convention, the name of a constructor starts with a capital letter.
- The constructor should be invoked via the new operator.
- Within the body of a constructor, the keyword this refers to the object created by the new operator. There is no requirement to return a value because new does this.
The newly-created object is an instance of the constructor (which makes the constructor sound a bit like a class, doesn't it?).
This can be checked by using the instanceof operator. An object remains an instance of its constructor even as properties of the object are added and removed.
console.log(mt1 instanceof Mountain); // true
// Add a property, and delete a different one.
mt1.location = 'Earth';
delete mt1.name;
console.log(mt1 instanceof Mountain); // still true
** Prototypes fill a role similar to classes.
Using the Mountain constructor as defined above means that every object that is created by using the constructor gets its own copy of the howTall() method.
That redundancy can be eliminated by using the constructor's prototype.
Almost every function (except for a few that are built-in) has a prototype property that is a reference to an object. This includes functions that are used as constructors.
When an object is created, it is given a prototype property whose value is a reference to the same prototype object as its constructor. Therefore, the Mountain constructor and every object that is created by using Mountain have a reference to the same prototype object.
The properties and methods of the prototype are shared among all these objects and are visible through them.
So another way to provide the howTall() method is to do this:
function Mountain(name, height) {
this.name = name;
this.height = height;
}
Mountain.prototype.howTall = function () {
return this.height;
}
mt2 = new Mountain('Fuji', 12388);
console.log(mt2.howTall());
That is, howTall() is available to all instances of Mountain through their prototype.
Because prototypes are just objects, you can add more methods to a prototype. Those methods then become available to objects which have that prototype, even after the objects have already been created.
function Mountain(name, height) {
this.name = name;
this.height = height;
}
mt2 = new Mountain('Fuji', 12388);
Mountain.prototype.heightInMeters = function () {
return this.height * 0.305;
}
console.log(mt2.heightInMeters());
It's a bit like having classes that can change dynamically, adding and removing methods at will. Sounds like a lot of fun and some interesting debugging.
(Continued at Part 4.)
The post is a follow-up to the earlier Part 1 and Part 2.
** Although Javascript does not have classes, it does have constructors.
Here's a simple example of using a constructor to instantiate an object.
function Mountain(name, height) {
this.name = name;
this.height = height;
this.howTall = function () {
return this.height;
}
}
mt1 = new Mountain('Kilimanjaro', 19340);
A constructor is much like other functions, but it is different in the following ways.
- By convention, the name of a constructor starts with a capital letter.
- The constructor should be invoked via the new operator.
- Within the body of a constructor, the keyword this refers to the object created by the new operator. There is no requirement to return a value because new does this.
The newly-created object is an instance of the constructor (which makes the constructor sound a bit like a class, doesn't it?).
This can be checked by using the instanceof operator. An object remains an instance of its constructor even as properties of the object are added and removed.
console.log(mt1 instanceof Mountain); // true
// Add a property, and delete a different one.
mt1.location = 'Earth';
delete mt1.name;
console.log(mt1 instanceof Mountain); // still true
** Prototypes fill a role similar to classes.
Using the Mountain constructor as defined above means that every object that is created by using the constructor gets its own copy of the howTall() method.
That redundancy can be eliminated by using the constructor's prototype.
Almost every function (except for a few that are built-in) has a prototype property that is a reference to an object. This includes functions that are used as constructors.
When an object is created, it is given a prototype property whose value is a reference to the same prototype object as its constructor. Therefore, the Mountain constructor and every object that is created by using Mountain have a reference to the same prototype object.
The properties and methods of the prototype are shared among all these objects and are visible through them.
So another way to provide the howTall() method is to do this:
function Mountain(name, height) {
this.name = name;
this.height = height;
}
Mountain.prototype.howTall = function () {
return this.height;
}
mt2 = new Mountain('Fuji', 12388);
console.log(mt2.howTall());
That is, howTall() is available to all instances of Mountain through their prototype.
Because prototypes are just objects, you can add more methods to a prototype. Those methods then become available to objects which have that prototype, even after the objects have already been created.
function Mountain(name, height) {
this.name = name;
this.height = height;
}
mt2 = new Mountain('Fuji', 12388);
Mountain.prototype.heightInMeters = function () {
return this.height * 0.305;
}
console.log(mt2.heightInMeters());
It's a bit like having classes that can change dynamically, adding and removing methods at will. Sounds like a lot of fun and some interesting debugging.
(Continued at Part 4.)
May 10, 2015
Beta 9 --> Beta 10: No code changes needed
For only the second time since Drupal 8 went beta, no coding changes for the Optimizely module were needed for a new release of D8.
It's pretty amazing that a small module like this one, which uses only a very small subset of the API's, can reveal quite a few changes just by attempting to run through its normal functionality, its error checking, and automated tests.
Back in July of last year, I wrote about function cache_clear_all() being removed along with its wildcard for subpaths. At the time, I simply replaced that function with a clearing of the entire cache where any wildcard is used in the D7 version.
However, that cache invalidation must be re-written for Drupal 8 so that it works in a targeted manner and does not potentially cause such a big hit to performance.
Now that D8 has become much more stable, it's time I stopped procrastinating and do it right.
It's pretty amazing that a small module like this one, which uses only a very small subset of the API's, can reveal quite a few changes just by attempting to run through its normal functionality, its error checking, and automated tests.
Back in July of last year, I wrote about function cache_clear_all() being removed along with its wildcard for subpaths. At the time, I simply replaced that function with a clearing of the entire cache where any wildcard is used in the D7 version.
However, that cache invalidation must be re-written for Drupal 8 so that it works in a targeted manner and does not potentially cause such a big hit to performance.
Now that D8 has become much more stable, it's time I stopped procrastinating and do it right.
May 4, 2015
Notes on object-oriented JavaScript for o-o developers (part 2)
This post continues with more notes on distinctive features of object-oriented JavaScript as described in the book The Principles of Object-Oriented JavaScript.
The post is a follow-up to the earlier Part 1.
** Regardless of how a function is defined and what its declared signature, it can be called with any number of parameters without triggering a runtime error.
Almost always, you do want to pass and to use the parameters as declared and as named. If for no other reason, do this for the sake of making the code better self-documenting.
Otherwise, within the context of each function there is a special array called arguments[] that contains the actual params for the call. You can use arguments[] to get a count of params and to access their values.
** There is no function overloading.
If a function is defined more than once, it is the most recent definition that is used. The others are effectively overwritten.
** There are special methods on functions.
Since functions are objects, it's possible that those objects have methods. There are at least three: call(), apply(), and bind().
They are a bit too complicated to write about for these concise notes, so I won't describe them any further. I just wanted to mention that they exist and to reinforce the notion that functions are objects to which methods can be applied.
** Objects can be made non-modifiable in different degrees.
(1) You can prevent properties from being added to an object by calling Object.preventExtensions() on it. In other respects, the object is still modifiable.
var obj = new Object;
Object.preventExtensions(obj);
obj.greeting = 'hello, world'; // Not allowed.
(2) You can prevent properties from being added or removed from an object by calling Object.seal() on it. The values of the properties can be changed.
This makes the object similar to those created in a strongly-typed language with classes.
var obj = new Object;
obj.name = 'Turtle';
Object.seal(obj);
obj.greeting = 'hello, world'; // Not allowed.
delete obj.name; // Not allowed.
obj.name = 'Tortoise'; // Okay.
(3) You can prevent properties from being added or removed, and also prevent the values of properties from being changed by calling Object.freeze() on it. In general, this is sometimes called an immutable object.
var obj = new Object;
obj.name = 'Turtle';
Object.freeze(obj);
obj.greeting = 'hello, world'; // Not allowed.
delete obj.name; // Not allowed.
obj.name = 'Tortoise'; // Not allowed.
(Continued at Part 3.)
The post is a follow-up to the earlier Part 1.
** Regardless of how a function is defined and what its declared signature, it can be called with any number of parameters without triggering a runtime error.
Almost always, you do want to pass and to use the parameters as declared and as named. If for no other reason, do this for the sake of making the code better self-documenting.
Otherwise, within the context of each function there is a special array called arguments[] that contains the actual params for the call. You can use arguments[] to get a count of params and to access their values.
** There is no function overloading.
If a function is defined more than once, it is the most recent definition that is used. The others are effectively overwritten.
** There are special methods on functions.
Since functions are objects, it's possible that those objects have methods. There are at least three: call(), apply(), and bind().
They are a bit too complicated to write about for these concise notes, so I won't describe them any further. I just wanted to mention that they exist and to reinforce the notion that functions are objects to which methods can be applied.
** Objects can be made non-modifiable in different degrees.
(1) You can prevent properties from being added to an object by calling Object.preventExtensions() on it. In other respects, the object is still modifiable.
var obj = new Object;
Object.preventExtensions(obj);
obj.greeting = 'hello, world'; // Not allowed.
(2) You can prevent properties from being added or removed from an object by calling Object.seal() on it. The values of the properties can be changed.
This makes the object similar to those created in a strongly-typed language with classes.
var obj = new Object;
obj.name = 'Turtle';
Object.seal(obj);
obj.greeting = 'hello, world'; // Not allowed.
delete obj.name; // Not allowed.
obj.name = 'Tortoise'; // Okay.
(3) You can prevent properties from being added or removed, and also prevent the values of properties from being changed by calling Object.freeze() on it. In general, this is sometimes called an immutable object.
var obj = new Object;
obj.name = 'Turtle';
Object.freeze(obj);
obj.greeting = 'hello, world'; // Not allowed.
delete obj.name; // Not allowed.
obj.name = 'Tortoise'; // Not allowed.
(Continued at Part 3.)
Apr 29, 2015
Notes on object-oriented JavaScript for o-o developers (part 1)
I've started reading The Principles of Object-Oriented JavaScript by Nicholas C. Zakas.
So far, it's been excellent, one of the best technical books I've read. It's clear, concise, and doesn't have any fluff, a solid tutorial with fewer than one hundred pages.
Coming from having programmed a lot in the strongly-typed language C++ a couple of lifetimes ago, I find JavaScript very fluid and sometimes very different in certain aspects.
It has some commonalities with PHP, which is what I'm mostly working in these days and still learning.
The purpose of this post and its follow-up is to highlight those JavaScript features described in this book that might be of particular interest to someone with a background similar to mine.
** Javascript does not have classes nor class definitions in the usual sense.
It does have objects, which are instances of reference types. The built-in reference types include Object, Array, Date, Function, RegExp, and Error.
** An object is a dynamic collection of properties and their values.
Properties can be added or removed from an object at any time. In other words, by default, an object is not constrained by whatever type was used to instantiate it.
For example, create a Date object, then add an arbitrary property to it. Note that assigning a value to a property that didn't exist before implicitly adds that property to the object.
var date = new Date;
date.blessing = 'May you live in interesting times';
Then delete the property. The date object is now back to its initial state.
delete date.blessing;
** The primitive types, which include string, number, and boolean, sometimes can be treated as objects even though they are not objects.
Each of these primitive types has a corresponding primitive wrapper type, respectively, String, Number, and Boolean.
The JavaScript interpreter creates temporary objects of these primitive wrapper types as needed, uses them, and then discards them immediately. This is how properties and methods can seemingly be invoked on primitive types.
For example, extract the first character of a string using a method call.
var first = 'hello'.charAt(0);
** Functions are first-class objects.
That is, functions are objects. So functions are values that can be assigned to variables, passed as parameters, be returned, and appear in expressions, just like values of other types.
** There are two literal ways of defining functions.
The first is the familiar declaration that starts with the keyword function and must include a function name. For example,
function helloWorld() {
console.log("hello, world");
}
The second way is an expression that also starts with the keyword function but does not include a name. For example, this code defines an anonymous function and assigns it to a variable.
var helloWorld = function() {
console.log("hello, world");
}
** A function declaration may appear after a call to that function.
For example, this is allowed.
helloWorld();
function helloWorld() {
console.log("hello, world");
}
The function declaration is effectively hoisted to the top of its context.
But this only works for function declarations, not for function expressions. The code below results in a runtime error.
hello();
var hello = function() {
console.log("hello");
}
(Continued at Part 2.)
So far, it's been excellent, one of the best technical books I've read. It's clear, concise, and doesn't have any fluff, a solid tutorial with fewer than one hundred pages.
Coming from having programmed a lot in the strongly-typed language C++ a couple of lifetimes ago, I find JavaScript very fluid and sometimes very different in certain aspects.
It has some commonalities with PHP, which is what I'm mostly working in these days and still learning.
The purpose of this post and its follow-up is to highlight those JavaScript features described in this book that might be of particular interest to someone with a background similar to mine.
** Javascript does not have classes nor class definitions in the usual sense.
It does have objects, which are instances of reference types. The built-in reference types include Object, Array, Date, Function, RegExp, and Error.
** An object is a dynamic collection of properties and their values.
Properties can be added or removed from an object at any time. In other words, by default, an object is not constrained by whatever type was used to instantiate it.
For example, create a Date object, then add an arbitrary property to it. Note that assigning a value to a property that didn't exist before implicitly adds that property to the object.
var date = new Date;
date.blessing = 'May you live in interesting times';
Then delete the property. The date object is now back to its initial state.
delete date.blessing;
** The primitive types, which include string, number, and boolean, sometimes can be treated as objects even though they are not objects.
Each of these primitive types has a corresponding primitive wrapper type, respectively, String, Number, and Boolean.
The JavaScript interpreter creates temporary objects of these primitive wrapper types as needed, uses them, and then discards them immediately. This is how properties and methods can seemingly be invoked on primitive types.
For example, extract the first character of a string using a method call.
var first = 'hello'.charAt(0);
** Functions are first-class objects.
That is, functions are objects. So functions are values that can be assigned to variables, passed as parameters, be returned, and appear in expressions, just like values of other types.
** There are two literal ways of defining functions.
The first is the familiar declaration that starts with the keyword function and must include a function name. For example,
function helloWorld() {
console.log("hello, world");
}
The second way is an expression that also starts with the keyword function but does not include a name. For example, this code defines an anonymous function and assigns it to a variable.
var helloWorld = function() {
console.log("hello, world");
}
** A function declaration may appear after a call to that function.
For example, this is allowed.
helloWorld();
function helloWorld() {
console.log("hello, world");
}
The function declaration is effectively hoisted to the top of its context.
But this only works for function declarations, not for function expressions. The code below results in a runtime error.
hello();
var hello = function() {
console.log("hello");
}
(Continued at Part 2.)
Subscribe to:
Posts (Atom)