After a rather long digression of writing several posts about JavaScript in this blog, I got back to work on migrating the Optimizely module to Drupal 8, this time from Beta 10 to the recent Beta 13.
Once again, no code changes were needed.
Update: I made a serious mistake in migrating from Beta 10 to Beta 13. I thought that I had done so successfully without any code changes needed.
It turns out that I was testing against an older release, not Beta 13. In actuality, the Optimizely module completely breaks the site as soon as it is installed under Beta 13.
After discovering my mistake, I backtracked to Beta 11, where the method checkPlain() has been moved to a different class, that is, from String to SafeMarkup.
When I test this module against a new release of D8, I first carry out manual testing on the intended functionality, then run an automated suite of several tests written to the core Testing module.
When those automated tests all pass, they provide a lot of additional confidence that the module is working correctly.
At the moment, the suite consists only of the ones that I inherited when I started this conversion project. The suite lacks testing of two critical error conditions that must be detected when the user is editing projects.
Now, it's time for me to expand beyond that original set to provide some enhancements by adding new tests. For this module, D8 is a stable enough platform to start looking to new functionality.
Jul 31, 2015
Jun 13, 2015
Notes on object-oriented JavaScript for o-o developers (part 5)
This post continues with more notes on distinctive features of object-oriented JavaScript as described in the book The Principles of Object-Oriented JavaScript.
This is a follow-up to the earlier Part 4 and is my final post related to that book.
** Private properties can be made by using closures.
In JavaScript, all properties of an object are publicly accessible. Unlike some other languages, there are no keyword access modifiers that correspond to public/protected/private.
However, you can get the same effect as private by defining an object in the context of a function that provides local variables and methods that you don't want to be public. The object is then used as a closure on those items that would otherwise not be accessible at all outside of the function.
So here's an example that I have copied from the above book, which nicely illustrates the idea.
var person = (function () {
var age = 25;
return {
name: "Nick",
getAge: function () { return age; },
growOlder: function () { ++age; }
}
})();
The outer, anonymously defined function is immediately invoked and returns an object which is assigned to the variable person.
The key concept here is that the two methods of that object refer to a local variable of the function. Because of those references, the lifetime of the local variable is extended, while at the same time it is not visible in any other code.
This use of closures is an example of the Module pattern in JavaScript.
This provides an individual object with a private property, but a similar technique can be used in a constructor so that every object created via the constructor gets its own private, instance property.
function Person (initial_age) {
var age = initial_age;
this.getAge = function () { return age; };
this.setAge = function (new_age) { age = new_age; };
this.growOlder = function () { ++age; };
}
nick = new Person(59);
lena = new Person(37);
console.log(nick.age); // undefined
Sources:
Closures
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
Learning JavaScript Design Patterns
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/
This is a follow-up to the earlier Part 4 and is my final post related to that book.
** Private properties can be made by using closures.
In JavaScript, all properties of an object are publicly accessible. Unlike some other languages, there are no keyword access modifiers that correspond to public/protected/private.
However, you can get the same effect as private by defining an object in the context of a function that provides local variables and methods that you don't want to be public. The object is then used as a closure on those items that would otherwise not be accessible at all outside of the function.
So here's an example that I have copied from the above book, which nicely illustrates the idea.
var person = (function () {
var age = 25;
return {
name: "Nick",
getAge: function () { return age; },
growOlder: function () { ++age; }
}
})();
The outer, anonymously defined function is immediately invoked and returns an object which is assigned to the variable person.
The key concept here is that the two methods of that object refer to a local variable of the function. Because of those references, the lifetime of the local variable is extended, while at the same time it is not visible in any other code.
This use of closures is an example of the Module pattern in JavaScript.
This provides an individual object with a private property, but a similar technique can be used in a constructor so that every object created via the constructor gets its own private, instance property.
function Person (initial_age) {
var age = initial_age;
this.getAge = function () { return age; };
this.setAge = function (new_age) { age = new_age; };
this.growOlder = function () { ++age; };
}
nick = new Person(59);
lena = new Person(37);
console.log(nick.age); // undefined
Sources:
Closures
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
Learning JavaScript Design Patterns
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/
Jun 1, 2015
Syntax explanation of (function ($) { ..... })(jQuery)
A few months ago when I had to modify some JavaScript, I ran across code like the following that just baffled me in terms of the basic
syntax.
(function($){
$(document).ready( ..... );
})(jQuery);
I'm now in a much better position to explain at the language level what's going on.
This explanation is not about jQuery nor about jQuery plugins, which a web search will show results for, such as the second article mentioned below.
Previously, I wrote that one of the ways to code the literal definition of a function is by using an expression that 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");
}
The right-hand side of the = operator must be an expression that provides a value. In this case, it's a function object.
Likewise, here's a definition of another function object. The parentheses around it force it to be treated as an expression rather than as a regular function declaration.
(function ($) {
$(document).ready( ..... );
})
A single dollar sign $ is a valid identifier. So in this case, that's the name of the function's argument. That argument is apparently expected to be a function since $(document) looks like a call of $, whatever the actual argument is.
Let's add the final piece.
(function ($) {
$(document).ready( ..... );
})(jQuery);
The (jQuery) part of this expression means call the anonymously defined function, with jQuery as the actual argument.
In general, this kind of construct is known as an Immediately Invoked Function Expression (IIFE).
There's a slightly different way to write this code. I prefer the above, and it's what I've been seeing in practice. But the following is also correct.
(function ($) {
$(document).ready( ..... );
}(jQuery));
Sources:
Immediately-Invoked Function Expression (IIFE)
http://benalman.com/news/2010/11/immediately-invoked-function-expression/
What does (function($) {})(jQuery); mean?
http://stackoverflow.com/questions/2937227/what-does-function-jquery-mean
(function($){
$(document).ready( ..... );
})(jQuery);
I'm now in a much better position to explain at the language level what's going on.
This explanation is not about jQuery nor about jQuery plugins, which a web search will show results for, such as the second article mentioned below.
Previously, I wrote that one of the ways to code the literal definition of a function is by using an expression that 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");
}
The right-hand side of the = operator must be an expression that provides a value. In this case, it's a function object.
Likewise, here's a definition of another function object. The parentheses around it force it to be treated as an expression rather than as a regular function declaration.
(function ($) {
$(document).ready( ..... );
})
A single dollar sign $ is a valid identifier. So in this case, that's the name of the function's argument. That argument is apparently expected to be a function since $(document) looks like a call of $, whatever the actual argument is.
Let's add the final piece.
(function ($) {
$(document).ready( ..... );
})(jQuery);
The (jQuery) part of this expression means call the anonymously defined function, with jQuery as the actual argument.
In general, this kind of construct is known as an Immediately Invoked Function Expression (IIFE).
There's a slightly different way to write this code. I prefer the above, and it's what I've been seeing in practice. But the following is also correct.
(function ($) {
$(document).ready( ..... );
}(jQuery));
Sources:
Immediately-Invoked Function Expression (IIFE)
http://benalman.com/news/2010/11/immediately-invoked-function-expression/
What does (function($) {})(jQuery); mean?
http://stackoverflow.com/questions/2937227/what-does-function-jquery-mean
May 25, 2015
Notes on object-oriented JavaScript for o-o developers (part 4)
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.)
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 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.)
Subscribe to:
Posts (Atom)