存在性
myObject.a的属性访问返回值可能是undefined,但是这个值有可 能是属性中存储的undefined,也可能是因为属性不存在所以返回undefined。那么如何 区分这两种情况呢?
var myObject = { a:2 };
("a" in myObject); // true
("b" in myObject); // false
myObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "b" ); // false
in操作符会检查属性是否在对象及其[[Prototype]]原型链中。相比之 下,hasOwnProperty(…)只会检查属性是否在myObject对象中,不会检查 [[Prototype]]链。所有的普通对象都可以通过对于Object.prototype的委托来访问 hasOwnProperty(…),但是有的对象可能没有连接到Object.prototype(通过 Object.create(null)来创建。在这种情况下,形如 myObejct.hasOwnProperty(…)就会失败。 这时可以使用一种更加强硬的方法来进行判 断:Object.prototype.hasOwnProperty.call(myObject,“a”),它借用基础的 hasOwnProperty(…)方法并把它显式绑定到myObject上。 看起来in操作符可以检查容器内是否有某个值,但是它实际上检查的是某个属性 名是否存在。
注意
对于数组来说这个区别非常重要,4 in [2, 4, 6]的结果并不是你期 待的True,因为[2, 4, 6]这个数组中包含的属性名是0、1、2,没有4。
枚举
var myObject = { };
Object.defineProperty( myObject, "a", // 让a像普通属性一样可以枚举
{ enumerable: true, value: 2 } );
Object.defineProperty( myObject, "b", // 让b不可枚举
{ enumerable: false, value: 3 } );
myObject.b; // 3 ("b" in myObject); // true myObject.hasOwnProperty( "b" ); // true
for (var k in myObject) {
console.log( k, myObject[k] ); }// "a" 2
myObject.b确实存在并且有访问值,但是却不会出现在for…in循环中(尽 管可以通过in操作符来判断是否存在)。原因是“可枚举”就相当于“可以出现在对象属性 的遍历中”。
注意:
在数组上应用for…in循环有时会产生出人意料的结果,因为这种枚举不仅会包 含所有数值索引,还会包含所有可枚举属性。最好只在对象上应用for…in循环,如 果要遍历数组就使用传统的for循环来遍历数值索引。或者es6的 for…of… 方法
var myArray = [ 1, 2, 3 ];
for (var v of myArray) {
console.log( v ); }// 1 // 2 // 3
for…in循环可以用来遍历对象的可枚举属性列表(包括[[Prototype]]链)。但是如何 遍历属性的值呢? 对于数值索引的数组来说,可以使用标准的for循环来遍历值: var myArray = [1, 2, 3]; for (var i = 0; i < myArray.length; i++) { console.log( myArray[i] ); }// 1 2 3 这实际上并不是在遍历值,而是遍历下标来指向值,如myArray[i]。 ES5中增加了一些数组的辅助迭代器,包括forEach(…)、every(…)和some(…)。每 种辅助迭代器都可以接受一个回调函数并把它应用到数组的每个元素上,唯一的区别就是 它们对于回调函数返回值的处理方式不同。 forEach(…)会遍历数组中的所有值并忽略回调函数的返回值。every(…)会一直运行直 到回调函数返回false(或者“假”值),some(…)会一直运行直到回调函数返回 true(或者“真”值)。 every(…)和some(…)中特殊的返回值和普通for循环中的break语句类似,它们会提前 终止遍历。 使用for…in遍历对象是无法直接获取属性值的,因为它实际上遍历的是对象中的所有可 枚举属性,你需要手动获取属性值。
Getter和Setter
var myObject = { //给a定义一个getter
get a() {
return 2;
}
};
Object.defineProperty( myObject, "b", {// 给b设置一个getter
get: function(){
return this.a * 2
},
// 确保b会出现在对象的属性列表中
enumerable: true
});
myObject.a; // 2
myObject.b; // 4
setter会覆盖单个属性默认的 [[Put]](也被称为赋值)操作。通常来说getter和setter是成对出现的(只定义一个的话 通常会产生意料之外的行为)
var myObject = {
// 给 a 定义一个getter
get a() {
return this._a_;
},
// 给 a 定义一个setter
set a(val) {
this._a_ = val * 2;
}
};
myObject.a = 2;
myObject.a; // 4
实际上我们把赋值([[Put]])操作中的值2存储到了另一个变量_a_ 中。名称_a_只是一种惯例,没有任何特殊的行为——和其他普通属性一样。
两种把Bar.prototype关联到Foo.prototype的方法:
// ES6之前需要抛弃默认的Bar.prototype
Bar.ptototype = Object.create( Foo.prototype );
// ES6开始可以直接修改现有的
Bar.prototype = Object.setPrototypeOf( Bar.prototype, Foo.prototype );
console.log(Bar.prototype instanceof Foo) // true
typeof
typeof 运算符的返回值永远是这 6 个(对 ES6 来说是 7 个)字符串值之一。也就是说,
typeof “abc” 返回 “string”,而不是 string。
instanceof
instanceof操作符的左操作数是一个普通的对象,右操作数是一个函数。instanceof回 答的问题是:在a的整条[[Prototype]]链中是否有指向Foo.prototype的对象?
boj.isPrototypeOf()
var b = {}
var c = Object.create(b)
// 非常简单:b是否出现在c的[[Prototype]]链中?
b.isPrototypeOf( c ); //true
Object.getPrototypeOf( c ) === b; // true
使用复合条件来检测 null 值的类型:
var a = null;
(!a && typeof a === "object"); // true
typeof 运算符总是会返回一个字符串:
typeof typeof 42; // "string"
字符串借用数组函数
var a = "wqwsdasd"
var c = a
// 将a的值转换为字符数组
.split( "" )
// 将数组中的字符进行倒转
.reverse()
// 将数组中的字符拼接回字符串
.join( "" );
c; // "oof"
Number.prototype 中的方法
//tofixed(..) 方法可指定小数部分的显示位数:
var a = 42.59;
a.toFixed( 0 ); // "43"
a.toFixed( 1 ); // "42.6"
a.toFixed( 2 ); // "42.59"
a.toFixed( 3 ); // "42.590"
a.toFixed( 4 ); // "42.5900"
var a = 42.59;
a.toPrecision( 1 ); // "4e+1"
a.toPrecision( 2 ); // "43"
a.toPrecision( 3 ); // "42.6"
a.toPrecision( 4 ); // "42.59"
a.toPrecision( 5 ); // "42.590"
a.toPrecision( 6 ); // "42.5900"
// 无效语法:
42.toFixed( 3 ); // SyntaxError
// 下面的语法都有效:
(42).toFixed( 3 ); // "42.000"
0.42.toFixed( 3 ); // "0.420"
42..toFixed( 3 ); //
var a = 5E10;
a; // 500000000
a.toExponential(); // "5e+10"
判断 0.1 + 0.2 和 0.3 是否相等
从 ES6 开始,误差值定义在 Number.EPSILON 中
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCloseEnoughToEqual( a, b ); // true
numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
能够呈现的最大浮点数大约是 1.798e+308(这是一个相当大的数字),它定义在 Number.MAX_VALUE 中。最小浮点数定义在 Number.MIN_VALUE 中,大约是 5e-324,它不是负数,但
无限接近于 0 !
整数检测
Number.isInteger( 42 ); // true
Number.isInteger( 42.000 ); // true
Number.isInteger( 42.3 ); // false
if (!Number.isInteger) {
Number.isInteger = function(num) {
return typeof num == "number" && num % 1 == 0;
};
}
不是值的值
• null 指空值(empty value)
• undefined 指没有值(missing value)
null 是一个特殊关键字,不是标识符,我们不能将其当作变量来使用和赋值。然而undefined 却是一个标识符,可以被当作变量来使用和赋值。
void 运算符
var a = 42;
console.log( void a, a ); // undefined 42
不是数字的数字NaN
var a = 2 / "foo";
isNaN( a ); // true
var a = 2 / "foo";
a == NaN; // false
a === NaN; // false
NaN != NaN // true
Infinity/ Infinity // NaN
var a = 2 / "foo";
var b = "foo";
a; // NaN
b; "foo"
window.isNaN( a ); // true
window.isNaN( b ); // true——晕!
//Number.isNaN(..)
if (!Number.isNaN) {
Number.isNaN = function(n) {
return n !== n;
};
}
-0
var a = 0;
var b = 0 / -3;
b //-0
a === b; // true
b.toString(); // "0"
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0
function isNegZero(n) {
n = Number( n );
return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 ); // true
isNegZero( 0 / -3 ); // true
isNegZero( 0 ); // false
//恶es6
var a = 2 / "foo";
var b = -3 * 0;
Object.is( a, NaN ); // true
Object.is( b, -0 ); // true
Object.is( b, 0 ); // false
引用
var a = 2;
var b = a; // b是a的值的一个副本
b++;
a; // 2
b; // 3
var c = [1,2,3];
var d = c; // d是[1,2,3]的一个引用
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
简单值(即标量基本类型值,scalar primitive)总是通过值复制的方式来赋值 / 传递,包括null、undefined、字符串、数字、布尔和 ES6 中的 symbol
复合值(compound value)——对象(包括数组和封装对象,参见第 3 章)和函数,则总 是通过引用复制的方式来赋值 / 传递
参数
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// 然后
x = [4,5,6];
x.push( 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // 是[1,2,3,4],不是[4,5,6,7]
//x.length = 0 和 x.push(4,5,6,7)更改了当前的数组
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// 然后
x.length = 0; // 清空数组
x.push( 4, 5, 6, 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // 是[4,5,6,7],不是[1,2,3,4]
如动态定义正则表达式时
var name = "Kyle";
var namePattern = new RegExp( "\\b(?:" + name + ")+\\b", "ig" );
var matches = someText.match( namePattern );
symbol
var mysym = Symbol( "my own symbol" );
mysym; // Symbol(my own symbol)
mysym.toString(); // "Symbol(my own symbol)"
typeof mysym; // "symbol"
var a = { };
a[mysym] = "foobar";
Object.getOwnPropertySymbols( a );
// [ Symbol(my own symbol) ]
假值:
• undefined
• null
• false
• +0、-0 和 NaN
• “”
+强制转换
var c = "3.14";
var d = 5+ +c;
d; // 8.14
var d = new Date( "Mon, 18 Aug 2014 08:53:06 CDT" );
+d; // 1408369986000
//获得当前的时间戳
var timestamp = +new Date();
var a = [1,2];
var b = [3,4];
a + b; // "1,23,4"
~ !!
//~x 大致等同于 -(x+1)
~42; // -(42+1) ==> -43
!! NaN //false
var a = null;
var b;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
"0" == false; // true -- 晕!
false == 0; // true -- 晕!
false == ""; // true -- 晕!
false == []; // true -- 晕!
"" == 0; // true -- 晕!
"" == []; // true -- 晕!
0 == []; // true -- 晕!
Number / parseInt
var a = "42";
var b = "42px";
Number( a ); // 42
parseInt( a ,10); // 42
Number( b ); // NaN
parseInt( b ,10); // 42
注意
浮点数可以使用 parseFloat(…) 函数
应该避免向 parseInt(…) 传递非字符串参数。
如果没有第二个参数来指定转换的
基数(又称为 radix),parseInt(…) 会根据字符串的第一个字符来自行决定基数。
如果第一个字符是 x 或 X,则转换为十六进制数字。如果是 0,则转换为八进制数字。
{ … } 也可用于“解构赋值”
{ a, b } = { a: 42, b: “foo” };
promise与·ajax
// Promise-aware ajax
function request(url) {
return new Promise( function(resolve,reject){
// ajax(..)回调应该是我们这个promise的resolve(..)函数
ajax( url, resolve );
} );
}
request( "http://some.url.1/" )
.then( function(response1){
return request( "http://some.url.2/?v=" + response1 );
} )
.then( function(response2){
console.log( response2 );
} );
function ajax(url) {
return new Promise( function pr(resolve,reject){
// 建立请求,最终会调用resolve(..)或者reject(..)
} );
}
// ..
ajax( "http://some.url.1" )
.then(
function fulfilled(contents){
// 处理contents成功情况
},
function rejected(reason){
// 处理ajax出错原因
}
);
生成器
function *foo() {
var x = yield 2;
z++;
var y = yield (x * z);
console.log( x, y, z );
}
var z = 1;
var it1 = foo();
var it2 = foo();
var val1 = it1.next().value; // 2 <-- yield 2
var val2 = it2.next().value; // 2 <-- yield 2
val1 = it1.next( val2 * 10 ).value; // 40 <-- x:20, z:2
val2 = it2.next( val1 * 5 ).value; // 600 <-- x:200, z:3
it1.next( val2 / 2 ); // y:300
// 20 300 3
it2.next( val1 / 4 ); // y:10
// 200 10 3
(1) *foo() 的两个实例同时启动,两个 next() 分别从 yield 2 语句得到值 2。
(2) val2 * 10 也就是 2 * 10,发送到第一个生成器实例 it1,因此 x 得到值 20。z 从 1 增
加到 2,然后 20 * 2 通过 yield 发出,将 val1 设置为 40。
(3) val1 * 5 也就是 40 * 5,发送到第二个生成器实例 it2,因此 x 得到值 200。z 再次从 2
递增到 3,然后 200 * 3 通过 yield 发出,将 val2 设置为 600。
(4) val2 / 2 也就是 600 / 2,发送到第一个生成器实例 it1,因此 y 得到值 300,然后打印
出 x y z 的值分别是 20 300 3。
(5) val1 / 4 也就是 40 / 4,发送到第二个生成器实例 it2,因此 y 得到值 10,然后打印出
x y z 的值分别为 200 10 3。
function *main() {
var x = yield “Hello World”;
yield x.toLowerCase(); // 引发一个异常!
}
var it = main();
it.next().value; // Hello World
try {
it.next( 42 );
}
catch (err) {
console.error( err ); // TypeError
}
数组方法:
arr.pop()
console.log(arr)//[1,2,3,4,5,6,7,8,9]
arr.unshift(0)//返回数组中元素的个数向数组的头部增加元素,括号中有多少就加多少,原数组发生改变
arr.shift()//从数组的头部删除一个元素,返回这个删除的元素,不接收参数
arr.slice(4)//从下标为4的开始截取,直至数组结束
arr.slice(0,2)//从下标0开始截取,到下标2结束,不包含下标2的数,此方法原数组不变
arr.splice(4)//一个参数,从下标4对应的位置开始,直到数组结束
arr.splice(0,1)//两个参数从下标为0开始,第二个参数截取长度
arr.aplice(1,2,1,1,1)//三个及三个以上的参数,从截取的起始位置开始添加第三个及以后的所有参数,此方法原数组改变 arr.join('-')//将数组转化为为字符串,以括号内的拼接
TCO 优化
function factorial(n) {
function fact(n,res) {
if (n < 2) return res;
return fact( n - 1, n * res );
}
return fact( n, 1 );
}
factorial( 5 ); // 120
<= >
JavaScript 中 <= 是
“不大于”的意思(即 !(a > b),处理为 !(b < a))。同理 a >= b 处理为 b <= a。
var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true
var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false
if 里面的声明
if (something) {
function foo() {
console.log( "1" );
}
}
else {
function foo() {
console.log( "2" );
}
}
foo(); // ??
在前 ES6 环境中,不管 something 的值是什么,foo() 都会打印出 “2”,因为两个函数声明都被提升到了块外,第二个总是会胜出。而在 ES6 中,最后一行会抛出一个 ReferenceError。
默认参数
function foo(x = 11, y = 31) {
console.log( x + y );
}
foo(); // 42
foo( 5, 6 ); // 11
foo( 0, 42 ); // 42
foo( 5 ); // 36
foo( 5, undefined ); // 36 <-- 丢了undefined
foo( 5, null ); // 5 <-- null被强制转换为0
foo( undefined, 6 ); // 17 <-- 丢了undefined
foo( null, 6 ); // 6 <-- null被强制转换为0
递归
function runSomething(o) {
var x = Math.random(),
y = Math.random();
return o.something( x, y );
}
runSomething( {
something: function something(x,y) {
if (x > y) {
// 交换x和y的递归调用
return something( y, x );
}
return y - x;
}
} )
数字转换
var a = 42;
a.toString(); // "42"--也可以用a.toString( 10 )
a.toString( 8 ); // "52"
a.toString( 16 ); // "2a"
a.toString( 2 ); // "101010
类
class Foo {
static cool() { console.log( "cool" ); }
wow() { console.log( "wow" ); }
}
class Bar extends Foo {
static awesome() {
super.cool();
console.log( "awesome" );
}
neat() {
super.wow();
console.log( "neat" );
}
}
Foo.cool(); // "cool"
Bar.cool(); // "cool"
Bar.awesome(); // "cool"
// "awesome"
var b = new Bar();
b.neat(); // "wow"
// "neat"
b.awesome; // undefined
b.cool; // undefined
set
var s = new Set();
var x = { id: 1 },
y = { id: 2 };
s.add( x );
s.has( x ); // true
s.has( y ); // false
静态函数 Object.is(…)
var x = NaN, y = 0, z = -0;
x === x; // false
y === z; // true
Object.is( x, x ); // true
Object.is( y, z ); // false
简单请求
满足一下两个条件的请求。
请求方法是以下三种方法之一:
HEAD
GET
POST
HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
复杂请求:
非简单请求就是复杂请求。