= 高级技巧

== undefined and void(0)
请不要认为 undefined 是 javascript 语言的特殊关键词并代表未定义值! 其实 undefined 是可以被覆盖的!

[source,javascript]
----
function test(){
	var undefined =100;
	console.log(undefined);	//此处显示 100
}
----

void(X) 一定能够返回 undefined，并代表未定义值。

[source,javascript]
----
// void(X) 中的 X 可以是任意值

console.log(void(undefined));	//undefined
console.log(void(null));		//undefined
console.log(void(0));			//undefined
console.log(void(false));		//undefined
console.log(void(true));		//undefined
console.log(void(1));			//undefined
console.log(void('str'));		//undefined
console.log(void({}));			//undefined
console.log(void([]));			//undefined

// void 0 是 void(0) 的简写
console.log(void 0);			//undefined
console.log(void 1);			//undefined
console.log(void true);		//undefined
----

NOTE: 在判断 undefined 时,最安全的写法应该采用 void(0)/void 0 代替 undefined


== Truthy and Falsy
在 javascript 语言中,每个值除了拥有它自己值外，还拥有一个用于表达真假的值,通常采用 Truthy and Falsy 术语表示。

* Falsy: 非真实值 (null,undefined,'',0,NaN)
* Truthy: 真实值 (除了 Falsy 之外的值)

既然如此，那么在 javascript 中任何值都可以进行布尔相关的操作，例如: 进行 if 判断, !取反操作等等


[source,javascript]
----
console.log(!undefined);  //true
console.log(!void 0);     //true
console.log(!void(0));    //true
console.log(!null);       //true
console.log(!0);          //true
console.log(!'');         //true
console.log(!NaN);        //true

console.log(!1);          //false
console.log(!'str');      //false
console.log(![]);         //false
console.log(!{});         //false
----

== 两个感叹号
[source,javascript]
----
// 二个感叹号不同于一个感叹号，二个感叹号是用于判断一个值是否是 Truthy，并返回 true/false
// 当一个值是: null、undefined、''、0 或 NaN 时，返回 false （表示该值是一个非真实值）
// 当一个值是: 其他，返回 true （表示该值是一个真实值）

console.log(!!undefined);  //false
console.log(!!void 0);     //false
console.log(!!void(0));    //false
console.log(!!null);       //false
console.log(!!0);          //false
console.log(!!'');         //false
console.log(!!NaN);        //false

console.log(!!1);          //true
console.log(!!'str');      //true
console.log(!![]);         //true
console.log(!!{});         //true
----

NOTE: 该方法在使用得当时，会显著改善代码可读性, 请看下面示例:

[source,javascript]
----
let a; 

// 未使用两个叹号前
if (a !== null && typeof(a) !== "undefined" &&  a !== undefined && a !== '' && a !== 0 && a !== NaN){
    //a为truthy时才执行，或者说a为真值时才执行
}

// 使用两个叹号后
if(!!a){

}

// 更简洁，更直观的写法
if(a){

}
----

NOTE: 使用时，需要注意 空字符串 和 0 是否和你的业务逻辑相匹配?


== 逗号表达式
[source,javascript]
----
// 执行单个或多个表达式，并返回最后一个表达式的值,多个表达式之间需要用逗号“,”分隔开
var v =(1,2+3,4+5,6);
// v= 6

// 其实逗号表达式经常用于将一个对象的方法转换为一个函数进行调用
// 对象的方法调用时, this 指向的是对象本身
// 函数调用时,this 指向 window 对象
// 逗号表达式起到了一个改变 this 的功能
var person ={
	cName: "张三",
  sayHello: function(msg){
    console.log("hello " + this.cName + ", " + msg);
  }
}

person.sayHello('how are you?');		//输出: hello 张三, how are you?
(0,person.sayHello)('how are you?');	//输出: hello undefined, how are you?

// 
----

== 双等号 和 三等号
在 javascript 判断相等时，会有两个语法:

. ==(双等/Loose Equality Comparisons/松散比较)
. ===(三等/Strict Equality Comparisons/严格比较)

.双等会出现很多非预期结果(红色部分)
|===
|==        |true    |false         |0            |''           |null         |undefined   |NaN          |Infinity   |[]           |{}

|true      |true    |false         |false        |false        |false        |false       |false        |false      |false        |false
|false     |false   |true          |[.red]*true* |[.red]*true* |false        |false       |false        |false      |[.red]*true* |false
|0         |false   |[.red]*true*  |true         |[.red]*true* |false        |false       |false        |false      |[.red]*true* |false
|''        |false   |[.red]*true*  |[.red]*true* |true         |false        |false       |false        |false      |[.red]*true* |false
|null      |false   |false         |false        |false        |true         |[.red]*true*|false        |false      |false        |false
|undefined |false   |false         |false        |false        |[.red]*true* |true        |false        |false      |false        |false
|NaN       |false   |false         |false        |false        |false        |false       |[.red]*false*|false      |false        |false
|Infinity  |false   |false         |false        |false        |false        |false       |false        |true       |false        |false
|[]        |false   |[.red]*true*  |[.red]*true* |[.red]*true* |false        |false       |false        |false      |[.red]*false*|false
|{}        |false   |false         |false        |false        |false        |false       |false        |false      |false        |[.red]*false*
|===

The rules:

. false, zero and empty strings are all equivalent.
. null and undefined are equivalent to themselves and each other but nothing else.
. NaN is not equivalent to anything – including another NaN!
. Infinity is truthy – but cannot be compared to true or false!
. An empty array is truthy – yet comparing with true is false and comparing with false is true?!

.三等只出现一个非预期结果(红色部分)
|===
|===       |true    |false |0     |''    |null  |undefined |NaN          |Infinity |[]    |{}

|true      |true    |false |false |false |false |false     |false        |false    |false |false
|false     |false   |true  |false |true  |false |false     |false        |false    |true  |false
|0         |false   |false |true  |true  |false |false     |false        |false    |true  |false
|''        |false   |false |false |true  |false |false     |false        |false    |true  |false
|null      |false   |false |false |false |true  |false     |false        |false    |false |false
|undefined |false   |false |false |false |false |true      |false        |false    |false |false
|NaN       |false   |false |false |false |false |false     |[.red]*false*|false    |false |false
|Infinity  |false   |false |false |false |false |false     |false        |true     |false |false
|[]        |false   |false |false |true  |false |false     |false        |false    |false |false
|{}        |false   |false |false |false |false |false     |false        |false    |false |true
|===

.最佳实践
. 尽量避免直接比较
+
--
[source,javascript]
----
// instead of
if (x == false) // ...
// runs if x is false, 0, '', or []

// use
if (!x) // ...
// runs if x is false, 0, '', NaN, null or undefined
----
--
. 尽量使用三等判断相等或不相等(===, !==),这样可以避免类型转换的问题
+
--
[source,javascript]
----
// instead of
if (x == y) // ...
// runs if x and y are both truthy or both falsy
// e.g. x = null and y = undefined

// use
if (x === y) // ...
// runs if x and y are identical...
// except when both are NaN
----
--
. 必要时将其转换为 boolean 值进行比较
+
--
[source,javascript]
----
// instead of
if (x === y) // ...
// runs if x and y are identical...
// except when both are NaN

// use
if (!!x === !!y) // ...
// runs if x and y are identical...
// including when either or both are NaN
----
--

== apply 、 call 、bind
这三个方法可应用到 Function 上，用于改变函数体内 this 的指向。

== 数组合并
[source,javascript]
----
var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// 将第二个数组融合进第一个数组
// 相当于 vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables);
// ['parsnip', 'potato', 'celery', 'beetroot']
----

