Another RayJune

Javascript Cookbook 小记

Just for self review.
The (*) section means it’s come from Internet (stackOverFlow, MDN or Medium).

Preface

It’s not a complete encyclopedia of JavaScript use today—no one book can cover all there is to cover. But hopefully, you’ll come away with an appreciation of all you can do with JavaScript.

This book does consist of two rather general sections: the first focuses on existing JavaScript functionality and objects(1-10); the second focuses more on JavaScript used within environments, such as a browser.

Working with JavaScript Strings

1
2
3
var city = new String("St. Louis"); // String object
var city = String("St. Louis"); // string literal

If you do need to access a String object method on a string literal, you can. What happens is the JavaScript engine creates a String object, wraps it around the string literal, performs the method call, and then discards the String object.

API

valueOf

The valueOf method is available for all JavaScript objects, and returns the primitive value of whatever the object is: for Number, String, and boolean, their primitive values; for Function, the function text, and so on.

1
2
3
4
var a = new Number(1);
console.log(typeof a); // object
console.log(a.valueOf()); // 1

trim

1
2
3
4
5
if (typeof String.trim == "undefined") {
String.prototype.trim = function() {
return this.replace(/(^\s*)|(\s*$)/g, "");
}
}

trim the unwanted whitespace from around a string.

1
2
3
var str = 'abc def';
console.log(str); // abc def
console.log(str.trim()) // abcdef

Using Regular Expressions

Regular expressions are search patterns that can be used to find text that matches a given pattern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Look for #a0b1c2
if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
// Look for #fff
if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
// Look for rgba(0, 0, 0, 0) == transparent
if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
return colors['transparent'];
// Otherwise, we're most likely dealing with a named color
return colors[$.trim(color).toLowerCase()];

Though the regular expressions seem complex, they’re really nothing more than a way to describe a pattern.

Trim whitespace

1
2
3
4
5
6
function leftTrim(str) {
return str.replace(/^\s+/,"");
}
function rightTrim(str) {
return str.replace(/\s+$/,"");
}

Dates, Time, and Timers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var intervalId = null;
window.onload = function() {
document.getElementById('redbox').onclick = stopStartElement;
}
function stopStartElement() {
if (intervalId === null) {
var x = 100;
intervalId = setInterval(function () {
x += 5;
var left = x + 'px';
document.getElementById('redbox').style.left = left;
}, 100);
} else {
clearInterval(intervalId);
intervalId = null;
}
}

Working with Numbers and Math

floor, round, ceil

1
2
3
4
5
console.log(Math.floor(1.6)); // 1
console.log(Math.round(1.6)); // 2
console.log(Math.ceil(1.1)); // 2
console.log(Math.round(1.1)); // 1

(*)How to convert decimal to hex in JavaScript?

Convert a number to a hexadecimal string with:

1
hexString = yourNumber.toString(16);

and reverse the process with:

1
yourNumber = parseInt(hexString, 16);

from https://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript#answer-57805

Math.random

The Math.random() function returns a floating-point, pseudo-random number in the range [0, 1)

1
2
3
4
5
6
7
8
window.onload = function() {
document.onclick = ranNumber;
}
function randomNumber() {
var randomNumber = Math.floor(Math.random() * 255);
document.getElementById('num').innerHTML = randomNumber;
}

range [0, 254] === [0, 255)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
window.onload = function() {
document.onclick = ranNumber;
}
function randomVal(val) {
return Math.floor(Math.random() * val);
}
function randomColor() {
return 'rgb(' + randomVal(255) + ',' + randomVal(255) + ',' + randomVal(255) + ')';
}
function ranNumber() {
var rgb = randomColor();
document.getElementById('num').style.backgroundColor = rgb;
}

even this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
window.onload = function() {
document.onclick = ranNumber;
}
function randomVal(val) {
return Math.floor(Math.random() * val);
}
function randomColor() {
var r; // get red
var g; // get green
var b; // get blue
// handle 0X
r = randomVal(255).toString(16);
if (r.length < 2) {
r = '0' + r;
}
g = randomVal(255).toString(16);
if (g.length < 2) {
g = '0' + g;
}
b = randomVal(255).toString(16);
if (b.length < 2) {
b = '0' + b;
}
return '#' + r + g + b;
}
function ranNumber() {
var hex = randomColor();
document.getElementById('num').style.backgroundColor = hex;
}

Converting Strings in a Table to Numbers

parseInt() & parseFloat()

Working with Arrays and Loops

Introduction

An array is an ordered collection of elements.

To create an array of several undefined elements, you can provide an array length when creating an array:

1
var largeCollection = new Array(100); // a new array with 100 undefined elements

indexOf

1
2
3
var animals = new Array("dog","cat","seal","elephant","walrus","lion");
alert(animals.indexOf("elephant")); // prints 3
alert(animals.indexOf('cat')); // print 1

Search and Remove or Replace Array Elements

Use the Array methods indexOf and splice to find and remove/replace array elements:

1
2
3
4
5
6
var animals = ['dog', 'cat', 'seal', 'walrus', 'lion', 'cat'];
// remove the element from array
animals.splice(animals.indexOf('walrus'), 1); // dog,cat,seal,lion,cat
// splice in new element
animals.splice(animals.lastIndexOf('cat'), 1, 'monkey'); // dog,cat,seal,lion,monkey

If the index is negative, the elements will be spliced from the end, not from the beginning of the array

1
2
3
4
var animals = ["cat","walrus","lion", "cat"];
// splice in new element
animals.splice(-1, 1, "monkey"); // cat,walrus,lion,monkey

Using an Associative Array to Store Form Element Names and Values

When we use an Array object to create an associative array, what we’re really doing is adding new properties to the array object, rather than adding new array elements.

1
obj[propName] = "somevalue";

what you’re really doing is adding a new object property:

1
obj.propName = "somevalue";

Instead of using an Array object to create the associative array, use the JavaScript Object directly.

Building Reusability with JavaScript Functions

Introduction

JavaScript functions have Function objects:

1
var fn = new Function (arg1, arg2, ..., argn, functionbody);

However, using this syntax is not as efficient as using the function statement, because using a function constructor requires that the function be parsed each time it’s called.

Functions defined with the function statement are parsed once, when the code is loaded.

Variable names can be any combination of characters, numbers, and underscores, as long as the variable name starts with a character or underscore and case-sensitivity is preserved.

Creating a Dynamic Runtime Function

You need to create a function, but you won’t know its structure until runtime.

Use an anonymous function, created using the Function object constructor:

1
2
3
// two parameters and one function body string
var functionName = new Function (x, y, functionBody);
functionName(varA, varB); // two parameters are processed by function

Anonymous functions are parsed at runtime, which makes them inefficient for general purposes.
However, they allow us to define both parameters and function body at runtime, which is handy if you’re not sure what the function body is going to be until runtime.

e.g.

1
2
3
4
5
6
7
window.onload = function() {
var func = prompt("Enter function body:","");
var x = prompt("Enter value for x:","");
var y = prompt("Enter value for y:","");
var newFun = new Function("x","y",func);
var result = newFun(x,y);
}

Create a Function That Remembers Its State

In JavaScript, there is one scope that is created for the outermost application environment. All global variables, functions, and objects are contained within this outer scope.

When you create a function, you create a new scope that exists as long as the function exists. The function has access to all variables in its scope, as well as all of the variables from the outer scope, but the outer scope does not have access to the variables in the function.

Because of these scoping rules, we can access window and document objects in all of our browser applications, and the inner function in the solution can also access the data passed to, or originating in, the outer function that wraps it.

However, the outer function cannot access the inner function’s arguments or local data, because they exist in a different scope.

So even though the outer function’s application scope no longer exists, the inner function’s scope exists at the time the function was returned, including a snapshot of the outer function’s data. It will continue to exist until the application is finished, and the outer, global scope is released.

accidental Closure

1
2
3
4
5
function outerFunction() {
var doc = document.getElementById("doc");
var newObj = { 'doc' : doc};
doc.newObj = newObj;
}

The problem with this circular reference is exacerbated in earlier versions of IE, because IE does not release memory associated with DOM objects (such as the doc element) if application scope is released. Even leaving the page does not reclaim the memory: you have to close the browser.

Luckily, newer versions of IE don’t have this problem. However, function closures should be deliberate, rather than accidental.

(*)Currying Function

from https://www.sitepoint.com/currying-in-functional-javascript/

More Readable And More Flexible

1
2
3
4
5
// no currying
var greet = function(greeting, name) {
console.log(greeting + ", " + name);
};
greet("Hello", "Heidi"); //"Hello, Heidi"

currying it

1
2
3
4
5
6
7
8
9
10
11
12
var greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
};
var greetHello = greetCurried("Hello");
greetHello("Heidi"); //"Hello, Heidi"
greetHello("Eddie"); //"Hello, Eddie"
// or you can also invoke it like that
greetCurried("Hi there")("Howard"); //"Hi there, Howard"

Currying Traditional Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var curryIt = function(uncurried) {
var parameters = Array.prototype.slice.call(arguments, 1);
return function() {
return uncurried.apply(this, parameters.concat(
Array.prototype.slice.call(arguments)
));
};
};
// test it
var greeter = function(greeting, separator, emphasis, name) {
console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); //"Hello, Heidi."
greetHello("Eddie"); //"Hello, Eddie."

instance from cookbook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function curry(fn) {
var scope = scope || window;
var args = [];
var len = arguments.length;
var i;
for (i = 2; i < len; i++) {
args.push(arguments[i]); // args has all arguments except [0, 1]
}
return function () {
var args2 = [];
var i;
var argstotal;
for (i = 0; i < arguments.length; i++) {
args2.push(arguments[i]); // args2 has all arguments
}
argstotal = args.concat(args2);
return fn.apply(scope, argstotal);
};
}
// use it
var greeter = function(greeting, separator, emphasis, name) {
console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); //"Hello, Heidi."
greetHello("Eddie"); //"Hello, Eddie."

When you want to curry a function:

1
2
3
4
5
function diffPoint (x1, y1, x2, y2) {
return [Math.abs(x2 - x1), Math.abs(y2 - y1)];
}
var diffOrigin = curry(diffPoint, null, 3.0, 4.0);
var newPt = diffOrigin(6.42, 8.0); // array with 3.42, 4

Conclusion

Adding currying to your coding practice will encourage the use of partially applied functions throughout your code, avoiding a lot of potential repetition, and may help get you into better habits about naming and dealing with function arguments.

Improve Application Performance with Memoization (Caching Calculations)

You want to optimize your JavaScript applications and libraries by reducing the need to repeat complex and CPU-intensive computations.

1
2
3
4
5
6
7
8
9
10
11
var fibonacci = (function lazyDef() {
var memo = [0, 1];
var fib = function getFib(n) {
if (typeof memo[n] !== 'number') {
memo[n] = fib(n - 1) + fib(n - 2);
}
return memo[n];
};
return fib;
}());

nonmemoized function

1
2
3
function fib(n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}

calculate time consume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// run nonmemo function, with timer
console.time("non-memo");
for (var i = 0; i <= 10; i++) {
console.log(i + " " + fib(i));
}
console.timeEnd("non-memo");
// now, memo function with timer
console.time("memo");
for (var i = 0; i <= 10; i++) {
console.log(i + " " + fibonacci(i));
}
console.timeEnd("memo");

Handling Events

You can also remove event listeners, as well as cancel the events themselves.
You can also prevent the event from propagating if the element receiving the event is nested in another element.

Canceling an event is helpful when dealing with form validation, and preventing event propagation is helpful when processing click events.

It’s up to you to decide which level of event handling meets your needs.

  • If you’re doing simple event handling—not using any external JavaScript libraries and not worried about canceling events, or whether events bubble up in a stack of elements—you can use DOM Level 0 events.
  • If you need the more sophisticated event handling, including the ability to more easily control events, you’ll want to use DOM Level 2 events. They can be easily encapsulated into a JavaScript library, and can be safely integrated into a multi-JavaScript library environment. However, the fly in this little event handling pie is that the DOM Level 2 event handling isn’t supported universally by all browsers: Microsoft has not implemented DOM Level 2 event handling with IE8 or earlier.

Creating a Generic, Reusable Event Handler Function

1
2
3
4
5
6
7
8
9
10
11
12
13
function listenEvent(eventTarget, eventType, eventHandler) {
if (eventTarget.addEventListener) {
eventTarget.addEventListener(eventType, eventHandler,false);
} else if (eventTarget.attachEvent) {
eventType = "on" + eventType;
eventTarget.attachEvent(eventType, eventHandler);
} else {
eventTarget["on" + eventType] = eventHandler;
}
}
// test
listenEvent(document, "click", processClick);

Luckily, most JavaScript libraries already provide the cross-browser event handling functions.

1
2
3
4
5
6
7
8
9
function stopListening(eventTarget, eventType, eventHandler) {
if (eventTarget.removeEventListener) {
eventTarget.removeEventListener(eventType, eventHandler, false);
} else if (eventTarget.detachEvent) {
eventTarget.detachEvent(eventType, eventHandler);
} else {
eventTarget['on' + eventType] = null;
}
}

e.preventDefault();

Canceling an Event Based on Changed Circumstance

You need to cancel an event, such as a form submission, before the event propagates to other elements.

e.stopPropagation();

Preventing an Event from Propagating Through a Set of Nested Elements

Browser Pieces

confirm

1
2
3
4
5
6
7
8
9
var answer = confirm('Are you sure you want to do that?');
if (answer === true) {
alert('yep');
} else {
alert('nope');
}
// and
var answer = prompt("What's your name", "anonymous");

open

1
var newWindow = window.open("http://oreilly.com", "namedWindow");

Warning the Web Page Reader About Leaving a Page

We don’t want to indiscriminately throw up pop ups, or intervene with links or people going about their business, but there are times when we want to ensure that people know that when they click a link, type in a new domain, or even close the browser, they’re leaving the current site.

You want people to be aware of the fact that they’re leaving the previously secure website and going somewhere that may be less than secure.

location

Form Elements and Validation

Introduction

With the advent of JavaScript, form elements could be validated before the data was sent to the server, saving the reader time and the website extra processing.

JavaScript can also be used to modify form elements based on the data the web reader provides, such as filling a selection list with the names of cities when a certain state is selected.

document.forms

1
2
3
4
<form id="picker">
Group 1: <input type="radio" name="group1" value="one" /><br />
<input type="text" id="intext" />
</form>

DOM Level 0 form collections:

1
document.forms['picker'].elements["intext"].value

you can also use an array index:

1
var txtValue = document.forms[0].elements[1].value;

Using an array index is tricky, since it may be difficult to determine the location of a specific form and element.
However, it’s also a simple way to process all form elements in a loop:

1
2
3
while (var i = 0; i < document.forms[0].elements.length; i++) {
var val = document.forms[0].elements[i].value;
}

Whenever you’re accessing data from a text or other field where the user can input whatever value he wants, before sending to the database or displaying in the page, you will want to strip or encode any harmful SQL, markup, or script that may be embedded in the value. You can use encodeURI and encodeURIComponent in JavaScript for encoding.

Dynamically Disabling and Enabling Form Elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<body>
<form id="picker">
Group 1: <input type="radio" name="group1" value="one" /><br />
Group 2: <input type="radio" name="group1" value="two" /><br />
<input type="text" id="intext" />
<input type="text" id="intext2" />
</form>
<script>
window.onload = function () {
// first, disable all the input fields
document.forms[0].elements['intext'].disabled = true;
document.forms[0].elements['intext2'].disabled = true;
// next attach the click event handler to the radio buttons
var radios = document.forms[0].elements['group1'];
radios.onclick = radioClicked;
for (var i = 0; i < radios.length; i++) {
radios[i].onclick = radioClicked;
}
}
function radioClicked(e) {
if (e.target.type === 'radio') {
var form = document.forms[0];
// find out which radio button was clicked and disable/enable appropriate input elements
switch(e.target.value) {
case 'one':
form.elements['intext'].disabled = false;
form.elements['intext2'].disabled = true;
break;
case 'two':
form.elements['intext'].disabled = true;
form.elements['intext2'].disabled = false;
break;
}
}
}
</script>
</body>

(*)Constraint validation

Note: HTML5 Constraint validation doesn’t remove the need for validation on the server side. Even though far fewer invalid form requests are to be expected, invalid ones can still be sent by non-compliant browsers (for instance, browsers without HTML5 and without JavaScript) or by bad guys trying to trick your web application. Therefore, like with HTML4, you need to also validate input constraints on the server side, in a way that is consistent with what is done on the client side.

Getting Information from a Form Element Based on an Event

In the form event handler function, you can access both the event and the element to get information about both. How you do this depends on your browser, and also how you assign the events.

Debugging and Error Handling

Accessing Page Elements

Creating and Removing Elements and Attributes

Working with Web Page Spaces

Creating Interactive and Accessible Effects with JavaScript, CSS, and ARIA

Creating Media Rich and Interactive Applications

JavaScript Objects

JavaScript Libraries

Communication

Working with Structured Data

Persistence

JavaScript Outside the Box

文章标题:Javascript Cookbook 小记

文章作者:RayJune

时间地点:下午7:58,于又玄图书馆

原始链接:http://rayjune.xyz/2017/10/09/Note-for-JavaScript-Cookbook/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。