in Web App Development
// Assuming following codes run in global context
var a = 0; // "a" is a global variable here
function test() {
b = 10; // QUESTION: Is "b" a "global variable"?
for (var i = 0; i < 10; i++) {
a++; // global variable is visible in inner scopes
}
return i; // == 10 because "i" is scoped under "test()", not "for"
}
Variable is defined via and only via var keyword, therefore, "b" is even not a variable, it's a property of global object ...
var a = 10; // variable a = 10
b = 10; // this === window here, so window.b = 10;
delete a; // return false, a = 10
delete b; // return true, window.b is undefined
An object property can be deleted, while a variable cannot ...
String | Number | Boolean | Object | |
---|---|---|---|---|
undefined | "undefined" | NaN | false | Error |
null | "null" | 0 | false | Error |
Nonempty string | Numeric value or Nan | true | String | |
Empty string | 0 | false | String | |
0 | "0" | false | Number | |
NaN | "NaN" | false | Number | |
Infinity | "Infinity" | true | Number | |
Negative infinity | "-Infinity | true | Number | |
number | string value | true | Number | |
true | "true" | 1 | Boolean | |
false | "false" | 0 | Boolean |
// The quickest way to put variable into number context
+"" === +false === +null === 0
+"a" === +undefined === NaN
+"1" === +"0b1" === +true === 1
+"-1" === -1
+"1.1" === 1.1
The most straightforward approach is via implicit type conversion:
Some other alternatives could be:
// Convert using Number object constructor
Number("") === 0
Number("1.1") === 1.1
Number(true) === 1
// Convert using "parseInt" and "parseFloat" functions
parseInt("1.1") === 1
parseFloat("1.1") === 1.1
parseInt("") === parseFloat("") === NaN
parseInt("a") === parseFloat("a") === NaN
parseInt('10'); // 1
parseInt('0.10'); // 0
parseInt('-10'); // -1
parseInt(str, radix): returns the parsed number, or NaN if "str" does NOT begin with a valid integer.
parses and returns the first number (with an optional leading minus sign) that occurs in “str”. Parsing stops, and the value is returned, when parseInt encounters an invalid digit for the specified radix.
When no radix is specified, ECMAScript v3 allows an implementation to parse a string that begins with 0 (but not 0x or 0X) as an octal or as a decimal number.
parseInt('bug1'); // NaN
parseInt('bug1', 16); // 11
parseInt('0x10'); // 16
parseInt('010'); // 10???
const bigint = '78099864177253771992779766288266836166272662';
const result = parseInt(bigint);
// output: 7.809986417725377e+43
result.toString(10);
// "7.809986417725377e+43"
The Number.MAX_SAFE_INTEGER constant represents the maximum safe integer in JavaScript (2 ^ 53 - 1 = 9007199254740991).
const bigint = '78099864177253771992779766288266836166272662';
let result = BigInt(bigint);
// output: 78099864177253771992779766288266836166272662n
result.toString(10);
// "78099864177253771992779766288266836166272662"
BigInt is now a native JavaScript language feature. It's at Stage 3 in the TC39 process and it's shipping in V8 v6.7 and Chrome 67.
May simply turn to npm to find your favorite polyfill lib to deal with.
Use "+" operator for string concat was a hazard to be aware of ...
... creates performance issues in IE when used within an iteration. This is because, like Java and C#, JavaScript uses unmutable strings.
Actually, we have many other alternatives to join two or several strings:
var foo = "three";
// String.prototype.join
var bar = ["first", 2, foo].join('');
// String.prototype.concat
var baz = "first".concat(2).concat(foo);
// ES6 string template
var boo = `first2${foo}`;
How to convert "uissee compassss" to "uisee compass"?
"uissee compassss".replace('ss', 's'); // uisee compassss
If the first argument to String.prototype.replace( regex, replacement) is a string rather than a regular expression, the method searches for that string literally rather than converting it to a regular expression with the RegExp constructor.
"uissee compassss".replace(/ss/, 's'); // uisee compassss
If regex has the global "g" attribute specified, replace( ) replaces all matching substrings. Otherwise, it replaces only the first matching substring.
"uissee compassss".replace(/ss/g, 's'); // uisee compass
There are many approaches to empty the array, some look naive:
var arr1 = [1, 2, 3, 4, 5, 6];
var arr2 = arr;
// Method 1: assign a new empty array
arr1 = []; // arr2: [1, 2, 3, 4, 5, 6]
// Method 2: remove all items one by one
while(arr1.length > 0) arr1.pop(); // === arr1.shift(), arr2: []
And some will be tricky:
// Method 3: remove all and add nothing
arr1.splice(0, arr1.length); // return all deleted, arr2: []
// May be the most tricky approach
arr1.length = 0; // arr2: []
Q1. How to get numeric order of 33, 4, 1111, 222?
[33, 4, 1111, 222].sort(); // [1111, 222, 33, 4]
If Array.prototype.sort(sortfunc) is called with no arguments, the elements of the array are arranged in alphabetical order (more precisely, the order determined by the character encoding)..
function numorder(a, b) { return a - b; }
[33, 4, 1111, 222].sort(numorder);
Q2. How to get pinyin order of 一、二、三、四?
['一', '二', '三', '四'].sort(); // ["一", "三", "二", "四"]
function locale(a, b) { return a.localeCompare(b); }
['一', '二', '三', '四'].sort(locale); // ["一", "三", "二", "四"]
function locale(a, b) { return a.localeCompare(b, 'zh'); }
['一', '二', '三', '四'].sort(locale); // ["二", "三", "四", "一"]
The sort() method sorts the elements of an array in place and returns the array. The sort is not necessarily stable.
var items = [
{ name: 'a', value: 6 },
{ name: 'b', value: 6 },
{ name: 'c', value: 6 },
{ name: 'd', value: 3 },
{ name: 'e', value: 2 },
{ name: 'f', value: 1 },
];
// array is mutated in-place
items.sort(function (a, b) {
return a.value - b.value;
});
//items = [
// { name: 'd', value: 1 },
// { name: 'e', value: 2 },
// { name: 'f', value: 3 },
// { name: 'a', value: 6 },
// { name: 'b', value: 6 },
// { name: 'c', value: 6 },
//];
var items = [
{ name: 'a', value: 6 },
{ name: 'b', value: 6 },
{ name: 'c', value: 6 },
{ name: 'd', value: 3 },
{ name: 'e', value: 2 },
{ name: 'f', value: 1 },
];
var dup = items.concat(items);
// same items may not keep same seqenuce
dup.sort(function (a, b) {
return a.value - b.value;
});
//dup = [
// ...,
// { name: 'b', value: 6 },
// { name: 'b', value: 6 },
// { name: 'c', value: 6 },
// { name: 'c', value: 6 },
// { name: 'a', value: 6 },
// { name: 'a', value: 6 },
//];
JavaScript engines only have a single thread, forcing asynchronous events to queue waiting for execution.
Element | Restriction | Example |
---|---|---|
Variable | Using a variable without declaring it. | testvar = 4; |
Octals | Assigning an octal value to a numeric literal, or attempting to use an escape on an octal value. | var testoctal = 010; var testescape = \010; |
this | The value of this is not converted to the global object when it is null or undefined. |
function testFunc() { return this; } var testvar = testFunc(); In non-strict mode, the value of testvaris the global object, but in strict mode the value is undefined. |
arguments in a function | You cannot change the values of members of the local arguments object. |
function testArgs(oneArg) { arguments[0] = 20; } In non-strict mode, you can change the value of the oneArg parameter by changing the value of arguments[0], so that the value of both oneArg and arguments[0] is 20. |
A common mistake for new JavaScript programmers is to extract a method from an object, then to later call that function and expect it to use the original object as its this (e.g. by using that method in callback-based code).
this.x = 9; // this refers to global "window" object here in the browser
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// returns 9 - The function gets invoked at the global scope
// Create a new function with 'this' bound to module
// New programmers might confuse the global var x with module's property x
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
The bind() method creates a new function that, when called, has its this keyword set to the provided value, following with a given sequence of arguments ...
Block-scoped binding constructs. let is the new var. const is single-assignment. Static restrictions prevent use before assignment.
function f() {
{
let x;
{
// okay, block scoped name
const x = "sneaky";
// error, const
x = "foo";
}
// error, already declared in block
let x = "inner";
}
}
Special notice goes to const object which is open for any property manipulation:
const obj = { a: 1 }; // DO NOT use "let" here
obj.b = 2; // { a: 1, b: 2 }
delete obj.b; // { a: 1 }
Object.assign(obj, { b: 2 }); // { a: 1, b: 2 }
const obj = {
// __proto__
__proto__: theProtoObj,
// Shorthand for ‘handler: handler’
handler,
// Getter and setter
_id: 1, // obj._id === 1
get id() { return "id" + this._id; } // obj.id === "id1"
set id(v) { this._id = v + 1; } // obj.id = 1; (_id: 3)
// Prototype methods
toString() { return "d " + super.toString(); }, // call "super"
foo() { return this.id + this.toString(); }, // call "this"
// Computed (dynamic) property names
[ 'prop_' + (() => 42)() ]: 42
};
The most tricky part is always the "this" reference:
const obj = {
baz: function() { return this.id; } // same as baz() {}
bar: () => this.id; // "bar" is NOT proto method, "this" is global object
};
var obj = {};
var sym = Symbol('a');
obj[sym] = 'a'; // obj[Symbol('a')] === undefined
obj[sym] = 'b'; // obj[sym] === 'b'
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c'; // object['c'] === 'c'
obj.d = 'd';
for (var i in obj) { console.log(i); } // logs "c" and "d
Symbol('foo') === Symbol('foo'); // false
JSON.stringify({[Symbol('foo')]: 'foo'}); // '{}'
typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'
typeof Object(Symbol('foo')) === 'object'
// list matching
const [a, , c] = [1, 2, 3]; // a = 1, c = 3
// object matching
const node = { op: 1, lhs: { op: 2 }, rhs: 3 };
const { op, lhs, rhs } = node; // op = 1, lhs = { op: 2 }, rhs = 3
// object matching with new variable assigned
const { op: a, lhs: { op: b }, rhs: c } = node; // a = 1, b = 2, c = 3
// op === lhs === rhs === undefined
// Can be used in parameter position
const g = ({ name }) => console.log(name); // expected: 5
g({ name: 5 });
// Fail-soft destructuring
const [a] = []; // a === undefined;
// Fail-soft destructuring with defaults
let [a = 1] = []; // a === 1;
let { a = 1, b = 2 } = {}; // a === 1, b === 2
// Fail defaults ONLY effect for "undefined" item
const [a = 1] = [null]; // a === null
const { a = 1, b = 2 } = { a: null }; // a === null, b === 2
// Rest of array: r = [2, 3]
const [a, ...r] = [1, 2, 3]
// Rest of object: r = { b: 2, c: 3 }
const { a, ...r } = { a: 1, b: 2, c: 3}
// Rest of function arguments
function f(x, ...y) {
// y is an array: ["hello", true]
return x * y.length;
}
f(3, "hello", true) == 6
// Spread into array: arr = [1, 2, 3]
const arr = [1, ...r];
// ... effects as copying an array
var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()
arr2.push(4); // arr2: [1, 2, 3, 4],
// arr: [1, 2, 3]
// Spread into object: { a: 1, b: 2, c: 3 }
// (Object spread comes in ES2016)
const obj = { a: 1, ...r };
// ... will overwrite the previous prop
const obj = { a: 1, b: -1, ...r };
// Spead an array as the argument
function f(x, y, z) {
// as [x, y, z] = [1, 2, 3]
return x + y + z;
}
f(...[1, 2, 3]) == 6
// file: lib/math.js
// export named variable
export const pi = 3.141593;
export const subtract = (x, y) => x - y;
// export named function declaration
export function sum(x, y) { return x + y; };
// exports a function declared earlier
const divide = (x, y) => x / y;
const multiply = (x, y) => x * y;
export { divide, multiply };
// using default export
export default function cube(x) { return x * x * x; };
// file: lib/mathex.js
// module redirects
export { default, sum as add, divide } from './math';
export * from './min-max';
// file: foo.js
import {
pi,
subtract,
sum,
divide,
multiply,
},
AnyName from 'lib/math';
import AnyName, {
add, divide, min, max
} from 'lib/mathex';
export { cube: x => x * x * x }; // should export as variable or default
export sum from 'lib/math'; // still an ECMAScript proposal
export * as math from 'lib/math'; // still an ECMAScript proposal
0b111110111 === 503 // binary literal
0o767 === 503 // octal literal
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 3 ^ 2 + 4 ^ 2 = 5
"abcde".includes("cd") // true
"abcde".startsWith("abc") // true
"abcde".endsWith("cde") // false
Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0, 7, 7]
[1, 2, 3].find(x => x === 3) // 3
[1, 2, 3].findIndex(x => x === 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // "memmove" alike [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign({ a: 1 }, { a: -1, b: 2 }) // { a: -1, b: 2 }
Usually, we use _.map function to convert a collection of items:
// transform a plain array
map([1, 2, 3], v => v + 1); // [2, 3, 4]
// transform a collection of object
map([{ id: 1 }, { id: 2 }], v => v.id); // [1, 2]
map([{ id: 1 }, { id: 2 }], v => ({ id: v.id }); // [{ id: 1 }, { id: 2 }]
// transform a collection of object via `_.property` iteratee shorthand
map([{ id: 1 }, { id: 2 }], 'id'); // [{ id: 1 }, { id: 2 }]
// transform a plain object
map({ id: 1, name: 'foo' }, v => v + 1); // [2, 'foo1']
Though map function takes in object, but it always returns an array:
_.mapValues or _.transform come to the rescue if object required:
// transform the value of a plain object
mapValues({ id: 1, name: 'foo' }, v => v + 1); // { id: 2, name: 'foo1'}
// transform an object to another object
transform({ id: 1, name: 'foo' }, (result, v, k) => result[k] = v + 1 }, {});
_.get and _.set are really useful functions for handling object property:
// get and set root level property, given const foo = { id: 1 }
get(foo, 'id'); // 1
set(foo, 'id', 2); // foo = { id: 2 }
// try to get a certain level of property, backed up with a default value
get({ a: [{ b: 1}] }, 'a[0].b'); // 1
get({ a: [{ b: 1}] }, 'a[0].b.c'); // undefined
get({ a: [{ b: 1}] }, 'a[0].b.c', false); // false
get({ a: [{ b: 1}] }, ['a', 0, 'b']); // 1, via property array
// try to set the value of a property, with full property path created
set({ a: [{ b: 1}] }, 'a[0].b', 2); // { a: [{ b: 2}] }
set({ a: [{ b: 1}] }, 'a[0].c[1]', 2); // { a: [{ b: 1, c: [,2] }] }
set({ a: [{ b: 1}] }, ['a', 0, 'c', 1], 2); // via property array
// use _.update to produce value based on its original data
update({ a: [{ b: 1}] }, 'a[0].b', n => n * 6); // { a: [{ b: 6}] }
And _.invoke is a nice helper to ensure a object method is called safely:
const object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };
_.invoke(object, 'a[0].b.c.slice', 1, 3); // [2, 3]