beforeEach(inject(function(_ModalService_){
close = ModalService.close;
}))
var app = angular.module('myApp',[]);
var app = angular.module('myApp');
angular.module('app',[ 'app.avengers', 'app.dashboard', 'app.widgets', 'app.layout' ]);
angular.module('app',[ //Angular Modules 'ngRoute', //Custome Modules 'common', // 3rd Party Modules 'ui.bootstrap', 'breeze.angular' ]);
angular .module('app.avengers') .controller('Avenger',Avenger); // "Classic" $scope syntax function Avenger($scope){ $scope.name = 'Captain America'; $scope.prop = 'shield'; } // "Controller As" syntax function Avenger(){ var vm = this; vm.name = 'Captain America'; vm.prop = 'shield'; }6. Clean Code
angular .module('app.dashboard') .controller('Dashboard',Dashboard); function Dashboard(common, dataservice){ // when minified, the common and dataservice may become a and b, which won't be found }
angular .module('app.dashboard') .controller('Dashboard',['common','dataservice',Dashboard]); function Dashboard(common, dataservice){ }
angular .module('app.dashboard') .controller('Dashboard',Dashboard); Dashboard.$inject = ['$q','dataservice','logger']; function Dashboard($q, dataservice, logger){ }
searchBtnIsEnabled: {value: function(){ var isEnabled; this.searchBtn.isEnabled().then(function(result){ isEnabled = result; console.log("the isEnabled is: "+isEnabled); }); console.log(isEnabled); }}
searchBtnIsEnabled: {value: function(callback){ this.searchBtn.isEnabled().then(function(result){ callback(result); }) }} this.searchBtnIsEnabled(function(enable){ console.log("The enable is: "+enable); })
function sloppyFunc() {
console.log(this === window); // true
}
sloppyFunc();
function strictFunc() {
'use strict';
console.log(this === undefined); // true
}
strictFunc();
function func(arg1, arg2) {
console.log(this); // a
console.log(arg1); // b
console.log(arg2); // c
}
func.call('a', 'b', 'c'); // (this, arg1, arg2)
func.apply('a', ['b', 'c']); // (this, arrayWithArgs)
var savedThis;
function Constr() {
savedThis = this;
}
var inst = new Constr();
console.log(savedThis === inst); // true
Implemented in JavaScript, the new operator looks roughly as follows (a more accurate implementation is slightly more complex): function newOperator(Constr, arrayWithArgs) {
var thisValue = Object.create(Constr.prototype);
Constr.apply(thisValue, arrayWithArgs);
return thisValue;
}
var obj = {
method: function () {
console.log(this === obj); // true
}
}
obj.method();
<script>
console.log(this === window); // true
</script>
In Node.js, you normally execute code in modules. Therefore, the top-level scope is a special module scope: // `global` (not `window`) refers to global object:
console.log(Math === global.Math); // true
// `this` doesn’t refer to the global object:
console.log(this !== global); // true
// `this` refers to a module’s exports:
console.log(this === module.exports); // true
> (0,eval)('this === window') trueOtherwise, if eval() is called directly, this remains the same as in the surroundings ofeval(). For example:
// Real functions
function sloppyFunc() {
console.log(eval('this') === window); // true
}
sloppyFunc();
function strictFunc() {
'use strict';
console.log(eval('this') === undefined); // true
}
strictFunc();
// Constructors
var savedThis;
function Constr() {
savedThis = eval('this');
}
var inst = new Constr();
console.log(savedThis === inst); // true
// Methods
var obj = {
method: function () {
console.log(eval('this') === obj); // true
}
}
obj.method();
function Point(x, y) {
this.x = x;
this.y = y;
}
var p = Point(7, 5); // we forgot new!
console.log(p === undefined); // true
// Global variables have been created:
console.log(x); // 7
console.log(y); // 5
Thankfully, you get a warning in strict mode (this === undefined): function Point(x, y) {
'use strict';
this.x = x;
this.y = y;
}
var p = Point(7, 5);
// TypeError: Cannot set property 'x' of undefined
/** Similar to setTimeout() and setImmediate() */
function callIt(func) {
func();
}
If you call a sloppy-mode method as a function, this refers to the global object and global variables will be created: var counter = {
count: 0,
// Sloppy-mode method
inc: function () {
this.count++;
}
}
callIt(counter.inc);
// Didn’t work:
console.log(counter.count); // 0
// Instead, a global variable has been created
// (NaN is result of applying ++ to undefined):
console.log(count); // NaN
If you call a strict-mode method as a function, this is undefined. Things don’t work, either. But at least you get a warning: var counter = {
count: 0,
// Strict-mode method
inc: function () {
'use strict';
this.count++;
}
}
callIt(counter.inc);
// TypeError: Cannot read property 'count' of undefined
console.log(counter.count);
The fix is to use bind(): var counter = {
count: 0,
inc: function () {
this.count++;
}
}
callIt(counter.inc.bind(counter));
// It worked!
console.log(counter.count); // 1
bind() created a new function that always receives a this whose value is counter. var obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop: function () {
'use strict';
this.friends.forEach(
function (friend) {
console.log(this.name+' knows '+friend);
}
);
}
};
obj.loop();
// TypeError: Cannot read property 'name' of undefined
In the previous example, this.name fails, because the function’s this is undefined, it is not the same as the this of the method loop(). There are three ways to fix … this.Fix 1: that = this. Assign this to a variable that isn’t shadowed (another popular name is self) and use that one. loop: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' knows '+friend);
});
}
Fix 2: bind(). Use bind() to create a function whose this always has the correct value (the method’s this in the following example). loop: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' knows '+friend);
}.bind(this));
}
Fix 3: forEach’s second parameter. This method has a second parameter whose value is passed to the callback as this. loop: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' knows '+friend);
}, this);
}
loop: function () {
'use strict';
// The parameter of forEach() is an arrow function
this.friends.forEach(friend => {
// `this` is loop’s `this`
console.log(this.name+' knows '+friend);
});
}
I don’t like APIs that use this as an additional parameter of real functions: beforeEach(function () {
this.addMatchers({
toBeInRange: function (start, end) {
...
}
});
});
Turning such an implicit parameter into an explicit one makes things more obvious and is compatible with arrow functions.beforeEach(api => { api.addMatchers({ toBeInRange(start, end) { ... } }); });
robots.txt
to tell the crawler explicitly what to search for..cn
or .com.cn
. If you want significant traffic from China (this might be important for e-commerce sites for instance), it may be worth maintaining a Chinese version of your website with a .cn
domain name to rank higher in Baidu.module.exports
and exports
in Node.js. Here’s what I have learned.greetings.js
and it contains the following two functions:
1
2
3
4
5
6
7
8
| // greetings.js sayHelloInEnglish = function () { return "Hello" ; }; sayHelloInSpanish = function () { return "Hola" ; }; |
greetings.js
increases when its encapsulated code can be utilized in other files. So let’s refactor greetings.js
to achieve this goal. To comprehend what is actually happening, we can follow a three-step process:greetings.js
:
1
2
| // greetings.js var exports = module.exports = {}; |
greetings.js
that we want to become available in other files to the exports
object:
1
2
3
4
5
6
7
8
9
10
| // greetings.js // var exports = module.exports = {}; exports.sayHelloInEnglish = function () { return "HELLO" ; }; exports.sayHelloInSpanish = function () { return "Hola" ; }; |
exports
with module.exports
and achieved the same result. If this seems confusing, remember that exports
and module.exports
reference the same object.
1
2
3
4
5
6
7
8
9
| module.exports = { sayHelloInEnglish: function () { return "HELLO" ; }, sayHelloInSpanish: function () { return "Hola" ; } }; |
greetings.js
to a new file called main.js
. This process can be described in three steps:require
is used in Node.js to import modules. Imagine that this is how require
is defined:
1
2
3
4
5
6
| var require = function (path) { // ... return module.exports; }; |
greetings.js
in main.js
:
1
2
| // main.js var greetings = require( "./greetings.js" ); |
1
2
3
4
5
6
7
8
9
10
| // main.js var greetings = { sayHelloInEnglish: function () { return "HELLO" ; }, sayHelloInSpanish: function () { return "Hola" ; } }; |
greetings.js
as a property of our greetings
variable in main.js
.
1
2
3
4
5
6
7
8
| // main.js var greetings = require( "./greetings.js" ); // "Hello" greetings.sayHelloInEnglish(); // "Hola" greetings.sayHelloInSpanish(); |
require
returns an object, which references the value of module.exports
for a given file. If a developer unintentionally or intentionally re-assigns module.exports
to a different object or different data structure, then any properties added to the original module.exports
object will be unaccessible.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // greetings.js // var exports = module.exports = {}; exports.sayHelloInEnglish = function () { return "HELLO" ; }; exports.sayHelloInSpanish = function () { return "Hola" ; }; /* * this line of code re-assigns * module.exports */ module.exports = "Bonjour" ; |
greetings.js
in main.js
:
1
2
| // main.js var greetings = require( "./greetings.js" ); |
greetings
to any code that is publicly available in greetings.js
.module.exports
to a data structure other than its default value is revealed when we attempt to invoke sayHelloInEnglish
and sayHelloInSpanish
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // main.js // var greetings = require("./greetings.js"); /* * TypeError: object Bonjour has no * method 'sayHelloInEnglish' */ greetings.sayHelloInEnglish(); /* * TypeError: object Bonjour has no * method 'sayHelloInSpanish' */ greetings.sayHelloInSpanish(); |
greetings
to a console:
1
2
| // "Bonjour" console.log(greetings); |
sayHelloInEnglish
and sayHelloInSpanish
on the string “Bonjour.” module.exports
, in other words, is no longer referencing the default object that contain those methods.exports
and module.exports
is clearer. Moreover, if you ever encounter an error in accessing publicly available methods in the future, then I hope that you have a better understanding of why those errors may occur.