```
diff --git a/docs/events/mouse.md b/docs/events/mouse.md
index eb1a919..cd1d463 100644
--- a/docs/events/mouse.md
+++ b/docs/events/mouse.md
@@ -2,23 +2,30 @@
## 鼠标事件的种类
-鼠标事件指与鼠标相关的事件,继承了`MouseEvent`接口。具体的事件主要有以下一些。
+鼠标事件主要有下面这些,所有事件都继承了`MouseEvent`接口(详见后文)。
+
+(1)点击事件
+
+鼠标点击相关的有四个事件。
- `click`:按下鼠标(通常是按下主按钮)时触发。
- `dblclick`:在同一个元素上双击鼠标时触发。
- `mousedown`:按下鼠标键时触发。
- `mouseup`:释放按下的鼠标键时触发。
+
+`click`事件可以看成是两个事件组成的:用户在同一个位置先触发`mousedown`,再触发`mouseup`。因此,触发顺序是,`mousedown`首先触发,`mouseup`接着触发,`click`最后触发。
+
+双击时,`dblclick`事件则会在`mousedown`、`mouseup`、`click`之后触发。
+
+(2)移动事件
+
+鼠标移动相关的有五个事件。
+
- `mousemove`:当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。
- `mouseenter`:鼠标进入一个节点时触发,进入子节点不会触发这个事件(详见后文)。
- `mouseover`:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件(详见后文)。
- `mouseout`:鼠标离开一个节点时触发,离开父节点也会触发这个事件(详见后文)。
- `mouseleave`:鼠标离开一个节点时触发,离开父节点不会触发这个事件(详见后文)。
-- `contextmenu`:按下鼠标右键时(上下文菜单出现前)触发,或者按下"上下文菜单键"时触发。
-- `wheel`:滚动鼠标的滚轮时触发,该事件继承的是`WheelEvent`接口。
-
-`click`事件指的是,用户在同一个位置先完成`mousedown`动作,再完成`mouseup`动作。因此,触发顺序是,`mousedown`首先触发,`mouseup`接着触发,`click`最后触发。
-
-`dblclick`事件则会在`mousedown`、`mouseup`、`click`之后触发。
`mouseover`事件和`mouseenter`事件,都是鼠标进入一个节点时触发。两者的区别是,`mouseenter`事件只触发一次,而只要鼠标在节点内部移动,`mouseover`事件会在子节点上触发多次。
@@ -27,7 +34,7 @@
*/
@@ -90,19 +97,24 @@ ul.addEventListener('mouseout', function (event) {
上面代码中,在父节点内部离开子节点,不会触发`mouseleave`事件,但是会触发`mouseout`事件。
-## MouseEvent 接口概述
+(3)其他事件
+
+- `contextmenu`:按下鼠标右键时(上下文菜单出现前)触发,或者按下"上下文"菜单键时触发。
+- `wheel`:滚动鼠标的滚轮时触发,该事件继承的是`WheelEvent`接口。
+
+## MouseEvent 接口
`MouseEvent`接口代表了鼠标相关的事件,单击(click)、双击(dblclick)、松开鼠标键(mouseup)、按下鼠标键(mousedown)等动作,所产生的事件对象都是`MouseEvent`实例。此外,滚轮事件和拖拉事件也是`MouseEvent`实例。
-`MouseEvent`接口继承了`Event`接口,所以拥有`Event`的所有属性和方法。它还有自己的属性和方法。
+`MouseEvent`接口继承了`Event`接口,所以拥有`Event`的所有属性和方法,并且还提供鼠标独有的属性和方法。
-浏览器原生提供一个`MouseEvent`构造函数,用于新建一个`MouseEvent`实例。
+浏览器原生提供一个`MouseEvent()`构造函数,用于新建一个`MouseEvent`实例。
```javascript
var event = new MouseEvent(type, options);
```
-`MouseEvent`构造函数接受两个参数。第一个参数是字符串,表示事件名称;第二个参数是一个事件配置对象,该参数可选。除了`Event`接口的实例配置属性,该对象可以配置以下属性,所有属性都是可选的。
+`MouseEvent()`构造函数接受两个参数。第一个参数是字符串,表示事件名称;第二个参数是一个事件配置对象,该参数可选。除了`Event`接口的实例配置属性,该对象可以配置以下属性,所有属性都是可选的。
- `screenX`:数值,鼠标相对于屏幕的水平位置(单位像素),默认值为0,设置该属性不会移动鼠标。
- `screenY`:数值,鼠标相对于屏幕的垂直位置(单位像素),其他与`screenX`相同。
@@ -313,7 +325,7 @@ document.body.addEventListener(
```javascript
/*
HTML 代码如下
-
+
*/
@@ -325,10 +337,10 @@ inner.addEventListener('mouseover', function (event) {
inner.addEventListener('mouseenter', function (event) {
console.log('进入' + event.target.id + ' 离开' + event.relatedTarget.id);
});
-inner.addEventListener('mouseout', function () {
+inner.addEventListener('mouseout', function (event) {
console.log('离开' + event.target.id + ' 进入' + event.relatedTarget.id);
});
-inner.addEventListener("mouseleave", function (){
+inner.addEventListener("mouseleave", function (event){
console.log('离开' + event.target.id + ' 进入' + event.relatedTarget.id);
});
@@ -384,3 +396,4 @@ var wheelEvent = new WheelEvent(type, options);
- `WheelEvent.deltaY`:数值,表示滚轮的垂直滚动量。
- `WheelEvent.deltaZ`:数值,表示滚轮的 Z 轴滚动量。
- `WheelEvent.deltaMode`:数值,表示上面三个属性的单位,`0`是像素,`1`是行,`2`是页。
+
diff --git a/docs/features/conversion.md b/docs/features/conversion.md
index deea015..f495f4b 100644
--- a/docs/features/conversion.md
+++ b/docs/features/conversion.md
@@ -322,13 +322,13 @@ if ('abc') {
- [1, 2, 3] // NaN
```
-自动转换的规则是这样的:预期什么类型的值,就调用该类型的转换函数。比如,某个位置预期为字符串,就调用`String`函数进行转换。如果该位置即可以是字符串,也可能是数值,那么默认转为数值。
+自动转换的规则是这样的:预期什么类型的值,就调用该类型的转换函数。比如,某个位置预期为字符串,就调用`String()`函数进行转换。如果该位置既可以是字符串,也可能是数值,那么默认转为数值。
-由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用`Boolean`、`Number`和`String`函数进行显式转换。
+由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用`Boolean()`、`Number()`和`String()`函数进行显式转换。
### 自动转换为布尔值
-JavaScript 遇到预期为布尔值的地方(比如`if`语句的条件部分),就会将非布尔值的参数自动转换为布尔值。系统内部会自动调用`Boolean`函数。
+JavaScript 遇到预期为布尔值的地方(比如`if`语句的条件部分),就会将非布尔值的参数自动转换为布尔值。系统内部会自动调用`Boolean()`函数。
因此除了以下五个值,其他都是自动转为`true`。
@@ -351,7 +351,7 @@ if ( !undefined
} // true
```
-下面两种写法,有时也用于将一个表达式转为布尔值。它们内部调用的也是`Boolean`函数。
+下面两种写法,有时也用于将一个表达式转为布尔值。它们内部调用的也是`Boolean()`函数。
```javascript
// 写法一
@@ -392,7 +392,7 @@ obj.width + 20 // "10020"
### 自动转换为数值
-JavaScript 遇到预期为数值的地方,就会将参数值自动转换为数值。系统内部会自动调用`Number`函数。
+JavaScript 遇到预期为数值的地方,就会将参数值自动转换为数值。系统内部会自动调用`Number()`函数。
除了加法运算符(`+`)有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值。
diff --git a/docs/features/error.md b/docs/features/error.md
index c0b8ebb..670e377 100644
--- a/docs/features/error.md
+++ b/docs/features/error.md
@@ -9,7 +9,7 @@ var err = new Error('出错了');
err.message // "出错了"
```
-上面代码中,我们调用`Error`构造函数,生成一个实例对象`err`。`Error`构造函数接受一个参数,表示错误提示,可以从实例的`message`属性读到这个参数。抛出`Error`实例对象以后,整个程序就中断在发生错误的地方,不再往下执行。
+上面代码中,我们调用`Error()`构造函数,生成一个实例对象`err`。`Error()`构造函数接受一个参数,表示错误提示,可以从实例的`message`属性读到这个参数。抛出`Error`实例对象以后,整个程序就中断在发生错误的地方,不再往下执行。
JavaScript 语言标准只提到,`Error`实例对象必须有`message`属性,表示出错时的提示信息,没有提到其他属性。大多数 JavaScript 引擎,对`Error`实例还提供`name`和`stack`属性,分别表示错误的名称和错误的堆栈,但它们是非标准的,不是每种实现都有。
@@ -79,19 +79,15 @@ unknownVariable
// Uncaught ReferenceError: unknownVariable is not defined
```
-另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果或者`this`赋值。
+另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果赋值。
```javascript
// 等号左侧不是变量
console.log() = 1
// Uncaught ReferenceError: Invalid left-hand side in assignment
-
-// this 对象不能手动赋值
-this = 1
-// ReferenceError: Invalid left-hand side in assignment
```
-上面代码对函数`console.log`的运行结果和`this`赋值,结果都引发了`ReferenceError`错误。
+上面代码对函数`console.log`的运行结果赋值,结果引发了`ReferenceError`错误。
### RangeError 对象
@@ -109,7 +105,7 @@ new Array(-1)
```javascript
new 123
-// Uncaught TypeError: number is not a func
+// Uncaught TypeError: 123 is not a constructor
var obj = {};
obj.unknownMethod()
@@ -170,13 +166,15 @@ new UserError('这是自定义的错误!');
`throw`语句的作用是手动中断程序执行,抛出一个错误。
```javascript
+var x = -1;
+
if (x <= 0) { throw new Error('x 必须为正数'); } -// Uncaught ReferenceError: x is not defined +// Uncaught Error: x 必须为正数 ``` -上面代码中,如果变量`x`小于等于`0`,就手动抛出一个错误,告诉用户`x`的值不正确,整个程序就会在这里中断执行。可以看到,`throw`抛出的错误就是它的参数,这里是一个`Error`实例。 +上面代码中,如果变量`x`小于等于`0`,就手动抛出一个错误,告诉用户`x`的值不正确,整个程序就会在这里中断执行。可以看到,`throw`抛出的错误就是它的参数,这里是一个`Error`对象的实例。 `throw`也可以抛出自定义错误。 @@ -406,7 +404,7 @@ result 上面代码中,`catch`代码块结束执行之前,会先执行`finally`代码块。 -`catch`代码块之中,触发转入`finally`代码快的标志,不仅有`return`语句,还有`throw`语句。 +`catch`代码块之中,触发转入`finally`代码块的标志,不仅有`return`语句,还有`throw`语句。 ```javascript function f() { diff --git a/docs/features/style.md b/docs/features/style.md index 8947aca..0ea77e1 100644 --- a/docs/features/style.md +++ b/docs/features/style.md @@ -486,7 +486,7 @@ function doAction(action) { ## 参考链接 -- Eric Elliott, Programming JavaScript Applications, [Chapter 2. JavaScript Style Guide](http://chimera.labs.oreilly.com/books/1234000000262/ch02.html), O'Reilly, 2013 +- Eric Elliott, Programming JavaScript Applications, [Chapter 2. JavaScript Style Guide](https://www.oreilly.com/library/view/programming-javascript-applications/9781491950289/), O'Reilly, 2014 - Axel Rauschmayer, [A meta style guide for JavaScript](http://www.2ality.com/2013/07/meta-style-guide.html) - Axel Rauschmayer, [Automatic semicolon insertion in JavaScript](http://www.2ality.com/2011/05/semicolon-insertion.html) - Rod Vagg, [JavaScript and Semicolons](http://dailyjs.com/2012/04/19/semicolons/) diff --git a/docs/oop/new.md b/docs/oop/new.md index aad7aeb..ae7d32d 100644 --- a/docs/oop/new.md +++ b/docs/oop/new.md @@ -12,11 +12,11 @@ JavaScript 语言具有很强的面向对象编程能力,本章介绍 JavaScri **(1)对象是单个实物的抽象。** -一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。 +一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个远程服务器连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。 **(2)对象是一个容器,封装了属性(property)和方法(method)。** -属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为`animal`对象,使用"属性"记录具体是那一种动物,使用"方法"表示动物的某种行为(奔跑、捕猎、休息等等)。 +属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为`animal`对象,使用"属性"记录具体是哪一种动物,使用"方法"表示动物的某种行为(奔跑、捕猎、休息等等)。 ## 构造函数 @@ -26,7 +26,7 @@ JavaScript 语言具有很强的面向对象编程能力,本章介绍 JavaScri JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓"构造函数",就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。 -构造函数就是一个普通的函数,但是有自己的特征和用法。 +构造函数就是一个普通的函数,但具有自己的特征和用法。 ```javascript var Vehicle = function () { diff --git a/docs/oop/object.md b/docs/oop/object.md index f376b16..a7e0340 100644 --- a/docs/oop/object.md +++ b/docs/oop/object.md @@ -62,7 +62,7 @@ F.call(f); 生成实例对象的常用方法是,使用`new`命令让构造函数返回一个实例。但是很多时候,只能拿到一个实例对象,它可能根本不是由构建函数生成的,那么能不能从一个实例对象,生成另一个实例对象呢? -JavaScript 提供了`Object.create`方法,用来满足这种需求。该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。 +JavaScript 提供了`Object.create()`方法,用来满足这种需求。该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。 ```javascript // 原型对象 @@ -80,9 +80,9 @@ B.print() // hello B.print === A.print // true ``` -上面代码中,`Object.create`方法以`A`对象为原型,生成了`B`对象。`B`继承了`A`的所有属性和方法。 +上面代码中,`Object.create()`方法以`A`对象为原型,生成了`B`对象。`B`继承了`A`的所有属性和方法。 -实际上,`Object.create`方法可以用下面的代码代替。 +实际上,`Object.create()`方法可以用下面的代码代替。 ```javascript if (typeof Object.create !== 'function') { @@ -94,7 +94,7 @@ if (typeof Object.create !== 'function') { } ``` -上面代码表明,`Object.create`方法的实质是新建一个空的构造函数`F`,然后让`F.prototype`属性指向参数对象`obj`,最后返回一个`F`的实例,从而实现让该实例继承`obj`的属性。 +上面代码表明,`Object.create()`方法的实质是新建一个空的构造函数`F`,然后让`F.prototype`属性指向参数对象`obj`,最后返回一个`F`的实例,从而实现让该实例继承`obj`的属性。 下面三种方式生成的新对象是等价的。 @@ -104,7 +104,7 @@ var obj2 = Object.create(Object.prototype); var obj3 = new Object(); ``` -如果想要生成一个不继承任何属性(比如没有`toString`和`valueOf`方法)的对象,可以将`Object.create`的参数设为`null`。 +如果想要生成一个不继承任何属性(比如没有`toString()`和`valueOf()`方法)的对象,可以将`Object.create()`的参数设为`null`。 ```javascript var obj = Object.create(null); @@ -113,9 +113,9 @@ obj.valueOf() // TypeError: Object [object Object] has no method 'valueOf' ``` -上面代码中,对象`obj`的原型是`null`,它就不具备一些定义在`Object.prototype`对象上面的属性,比如`valueOf`方法。 +上面代码中,对象`obj`的原型是`null`,它就不具备一些定义在`Object.prototype`对象上面的属性,比如`valueOf()`方法。 -使用`Object.create`方法的时候,必须提供对象原型,即参数不能为空,或者不是对象,否则会报错。 +使用`Object.create()`方法的时候,必须提供对象原型,即参数不能为空,或者不是对象,否则会报错。 ```javascript Object.create() @@ -124,7 +124,7 @@ Object.create(123) // TypeError: Object prototype may only be an Object or null ``` -`Object.create`方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。 +`Object.create()`方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。 ```javascript var obj1 = { p: 1 }; @@ -136,7 +136,7 @@ obj2.p // 2 上面代码中,修改对象原型`obj1`会影响到实例对象`obj2`。 -除了对象的原型,`Object.create`方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。 +除了对象的原型,`Object.create()`方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。 ```javascript var obj = Object.create({}, { @@ -160,7 +160,7 @@ obj.p1 = 123; obj.p2 = 'abc'; ``` -`Object.create`方法生成的对象,继承了它的原型对象的构造函数。 +`Object.create()`方法生成的对象,继承了它的原型对象的构造函数。 ```javascript function A() {} diff --git a/docs/oop/prototype.md b/docs/oop/prototype.md index f01c701..d1ae8df 100644 --- a/docs/oop/prototype.md +++ b/docs/oop/prototype.md @@ -262,7 +262,7 @@ v instanceof Vehicle // true 上面代码中,对象`v`是构造函数`Vehicle`的实例,所以返回`true`。 -`instanceof`运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上。因此,下面两种写法是等价的。 +`instanceof`运算符的左边是实例对象,右边是构造函数。它会检查右边构造函数的原型对象(prototype),是否在左边对象的原型链上。因此,下面两种写法是等价的。 ```javascript v instanceof Vehicle @@ -270,7 +270,7 @@ v instanceof Vehicle Vehicle.prototype.isPrototypeOf(v) ``` -上面代码中,`Object.prototype.isPrototypeOf`的详细解释见后文。 +上面代码中,`Vehicle`是对象`v`的构造函数,它的原型对象是`Vehicle.prototype`,`isPrototypeOf()`方法是 JavaScript 提供的原生方法,用于检查某个对象是否为另一个对象的原型,详细解释见后文。 由于`instanceof`检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回`true`。 @@ -298,10 +298,10 @@ null instanceof Object // false ```javascript var obj = Object.create(null); typeof obj // "object" -Object.create(null) instanceof Object // false +obj instanceof Object // false ``` -上面代码中,`Object.create(null)`返回一个新对象`obj`,它的原型是`null`(`Object.create`的详细介绍见后文)。右边的构造函数`Object`的`prototype`属性,不在左边的原型链上,因此`instanceof`就认为`obj`不是`Object`的实例。但是,只要一个对象的原型不是`null`,`instanceof`运算符的判断就不会失真。 +上面代码中,`Object.create(null)`返回一个新对象`obj`,它的原型是`null`(`Object.create()`的详细介绍见后文)。右边的构造函数`Object`的`prototype`属性,不在左边的原型链上,因此`instanceof`就认为`obj`不是`Object`的实例。这是唯一的`instanceof`运算符判断会失真的情况(一个对象的原型是`null`)。 `instanceof`运算符的一个用处,是判断值的类型。 @@ -637,4 +637,4 @@ var module1 = (function (,ドル YAHOO) { ## 参考链接 -- [JavaScript Modules: A Beginner’s Guide](https://medium.freecodecamp.com/javascript-modules-a-beginner-s-guide-783f7d7a5fcc), by Preethi Kasireddy +- [JavaScript Modules: A Beginner’s Guide](https://www.freecodecamp.org/news/javascript-modules-a-beginner-s-guide-783f7d7a5fcc), by Preethi Kasireddy diff --git a/docs/oop/strict.md b/docs/oop/strict.md index 3dffbe6..cc339c8 100644 --- a/docs/oop/strict.md +++ b/docs/oop/strict.md @@ -12,7 +12,7 @@ - 明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。 - 增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。 -- 提高编译器效率,增加运行速度。 +- 提高编译器效率,提升运行速度。 - 为未来新版本的 JavaScript 语法做好铺垫。 总之,严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向。 @@ -108,7 +108,7 @@ function notStrict() { ```javascript // 对只读属性赋值会报错 'use strict'; -Object.defineProperty({}, 'a', { +var obj = Object.defineProperty({}, 'a', { value: 37, writable: false }); diff --git a/docs/oop/this.md b/docs/oop/this.md index ca6158b..6792c30 100644 --- a/docs/oop/this.md +++ b/docs/oop/this.md @@ -679,26 +679,26 @@ Array.prototype.slice.apply({length: 1}) // [undefined] 前面的按钮点击事件的例子,可以改写如下。 ```javascript -var o = new Object(); +var obj = new Object(); -o.f = function () { - console.log(this === o); +var func = function () { + console.log(this === obj); } -var f = function (){ - o.f.apply(o); - // 或者 o.f.call(o); +var handler = function (){ + func.apply(obj); + // 或者 func.call(obj); }; // jQuery 的写法 -$('#button').on('click', f); +$('#button').on('click', handler); ``` -上面代码中,点击按钮以后,控制台将会显示`true`。由于`apply`方法(或者`call`方法)不仅绑定函数执行时所在的对象,还会立即执行函数,因此不得不把绑定语句写在一个函数体内。更简洁的写法是采用下面介绍的`bind`方法。 +上面代码中,点击按钮以后,控制台将会显示`true`。由于`apply()`方法(或者`call()`方法)不仅绑定函数执行时所在的对象,还会立即执行函数,因此不得不把绑定语句写在一个函数体内。更简洁的写法是采用下面介绍的`bind()`方法。 ### Function.prototype.bind() -`bind`方法用于将函数体内的`this`绑定到某个对象,然后返回一个新函数。 +`bind()`方法用于将函数体内的`this`绑定到某个对象,然后返回一个新函数。 ```javascript var d = new Date(); @@ -708,16 +708,16 @@ var print = d.getTime; print() // Uncaught TypeError: this is not a Date object. ``` -上面代码中,我们将`d.getTime`方法赋给变量`print`,然后调用`print`就报错了。这是因为`getTime`方法内部的`this`,绑定`Date`对象的实例,赋给变量`print`以后,内部的`this`已经不指向`Date`对象的实例了。 +上面代码中,我们将`d.getTime()`方法赋给变量`print`,然后调用`print()`就报错了。这是因为`getTime()`方法内部的`this`,绑定`Date`对象的实例,赋给变量`print`以后,内部的`this`已经不指向`Date`对象的实例了。 -`bind`方法可以解决这个问题。 +`bind()`方法可以解决这个问题。 ```javascript var print = d.getTime.bind(d); print() // 1481869925657 ``` -上面代码中,`bind`方法将`getTime`方法内部的`this`绑定到`d`对象,这时就可以安全地将这个方法赋值给其他变量了。 +上面代码中,`bind()`方法将`getTime()`方法内部的`this`绑定到`d`对象,这时就可以安全地将这个方法赋值给其他变量了。 `bind`方法的参数就是所要绑定`this`的对象,下面是一个更清晰的例子。 @@ -734,7 +734,7 @@ func(); counter.count // 1 ``` -上面代码中,`counter.inc`方法被赋值给变量`func`。这时必须用`bind`方法将`inc`内部的`this`,绑定到`counter`,否则就会出错。 +上面代码中,`counter.inc()`方法被赋值给变量`func`。这时必须用`bind()`方法将`inc()`内部的`this`,绑定到`counter`,否则就会出错。 `this`绑定到其他对象也是可以的。 @@ -754,9 +754,9 @@ func(); obj.count // 101 ``` -上面代码中,`bind`方法将`inc`方法内部的`this`,绑定到`obj`对象。结果调用`func`函数以后,递增的就是`obj`内部的`count`属性。 +上面代码中,`bind()`方法将`inc()`方法内部的`this`,绑定到`obj`对象。结果调用`func`函数以后,递增的就是`obj`内部的`count`属性。 -`bind`还可以接受更多的参数,将这些参数绑定原函数的参数。 +`bind()`还可以接受更多的参数,将这些参数绑定原函数的参数。 ```javascript var add = function (x, y) { @@ -772,9 +772,9 @@ var newAdd = add.bind(obj, 5); newAdd(5) // 20 ``` -上面代码中,`bind`方法除了绑定`this`对象,还将`add`函数的第一个参数`x`绑定成`5`,然后返回一个新函数`newAdd`,这个函数只要再接受一个参数`y`就能运行了。 +上面代码中,`bind()`方法除了绑定`this`对象,还将`add()`函数的第一个参数`x`绑定成`5`,然后返回一个新函数`newAdd()`,这个函数只要再接受一个参数`y`就能运行了。 -如果`bind`方法的第一个参数是`null`或`undefined`,等于将`this`绑定到全局对象,函数运行时`this`指向顶层对象(浏览器为`window`)。 +如果`bind()`方法的第一个参数是`null`或`undefined`,等于将`this`绑定到全局对象,函数运行时`this`指向顶层对象(浏览器为`window`)。 ```javascript function add(x, y) { @@ -785,19 +785,19 @@ var plus5 = add.bind(null, 5); plus5(10) // 15 ``` -上面代码中,函数`add`内部并没有`this`,使用`bind`方法的主要目的是绑定参数`x`,以后每次运行新函数`plus5`,就只需要提供另一个参数`y`就够了。而且因为`add`内部没有`this`,所以`bind`的第一个参数是`null`,不过这里如果是其他对象,也没有影响。 +上面代码中,函数`add()`内部并没有`this`,使用`bind()`方法的主要目的是绑定参数`x`,以后每次运行新函数`plus5()`,就只需要提供另一个参数`y`就够了。而且因为`add()`内部没有`this`,所以`bind()`的第一个参数是`null`,不过这里如果是其他对象,也没有影响。 -`bind`方法有一些使用注意点。 +`bind()`方法有一些使用注意点。 **(1)每一次返回一个新函数** -`bind`方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。 +`bind()`方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。 ```javascript element.addEventListener('click', o.m.bind(o)); ``` -上面代码中,`click`事件绑定`bind`方法生成的一个匿名函数。这样会导致无法取消绑定,所以,下面的代码是无效的。 +上面代码中,`click`事件绑定`bind()`方法生成的一个匿名函数。这样会导致无法取消绑定,所以下面的代码是无效的。 ```javascript element.removeEventListener('click', o.m.bind(o)); @@ -814,7 +814,7 @@ element.removeEventListener('click', listener); **(2)结合回调函数使用** -回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含`this`的方法直接当作回调函数。解决方法就是使用`bind`方法,将`counter.inc`绑定`counter`。 +回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含`this`的方法直接当作回调函数。解决方法就是使用`bind()`方法,将`counter.inc()`绑定`counter`。 ```javascript var counter = { @@ -833,7 +833,7 @@ callIt(counter.inc.bind(counter)); counter.count // 1 ``` -上面代码中,`callIt`方法会调用回调函数。这时如果直接把`counter.inc`传入,调用时`counter.inc`内部的`this`就会指向全局对象。使用`bind`方法将`counter.inc`绑定`counter`以后,就不会有这个问题,`this`总是指向`counter`。 +上面代码中,`callIt()`方法会调用回调函数。这时如果直接把`counter.inc`传入,调用时`counter.inc()`内部的`this`就会指向全局对象。使用`bind()`方法将`counter.inc`绑定`counter`以后,就不会有这个问题,`this`总是指向`counter`。 还有一种情况比较隐蔽,就是某些数组方法可以接受一个函数当作参数。这些函数内部的`this`指向,很可能也会出错。 @@ -852,7 +852,7 @@ obj.print() // 没有任何输出 ``` -上面代码中,`obj.print`内部`this.times`的`this`是指向`obj`的,这个没有问题。但是,`forEach`方法的回调函数内部的`this.name`却是指向全局对象,导致没有办法取到值。稍微改动一下,就可以看得更清楚。 +上面代码中,`obj.print`内部`this.times`的`this`是指向`obj`的,这个没有问题。但是,`forEach()`方法的回调函数内部的`this.name`却是指向全局对象,导致没有办法取到值。稍微改动一下,就可以看得更清楚。 ```javascript obj.print = function () { @@ -867,7 +867,7 @@ obj.print() // true ``` -解决这个问题,也是通过`bind`方法绑定`this`。 +解决这个问题,也是通过`bind()`方法绑定`this`。 ```javascript obj.print = function () { @@ -882,9 +882,9 @@ obj.print() // 张三 ``` -**(3)结合`call`方法使用** +**(3)结合`call()`方法使用** -利用`bind`方法,可以改写一些 JavaScript 原生方法的使用形式,以数组的`slice`方法为例。 +利用`bind()`方法,可以改写一些 JavaScript 原生方法的使用形式,以数组的`slice()`方法为例。 ```javascript [1, 2, 3].slice(0, 1) // [1] @@ -892,9 +892,9 @@ obj.print() Array.prototype.slice.call([1, 2, 3], 0, 1) // [1] ``` -上面的代码中,数组的`slice`方法从`[1, 2, 3]`里面,按照指定位置和长度切分出另一个数组。这样做的本质是在`[1, 2, 3]`上面调用`Array.prototype.slice`方法,因此可以用`call`方法表达这个过程,得到同样的结果。 +上面的代码中,数组的`slice`方法从`[1, 2, 3]`里面,按照指定的开始位置和结束位置,切分出另一个数组。这样做的本质是在`[1, 2, 3]`上面调用`Array.prototype.slice()`方法,因此可以用`call`方法表达这个过程,得到同样的结果。 -`call`方法实质上是调用`Function.prototype.call`方法,因此上面的表达式可以用`bind`方法改写。 +`call()`方法实质上是调用`Function.prototype.call()`方法,因此上面的表达式可以用`bind()`方法改写。 ```javascript var slice = Function.prototype.call.bind(Array.prototype.slice); diff --git a/docs/operators/bit.md b/docs/operators/bit.md index f02a028..3c86e3d 100644 --- a/docs/operators/bit.md +++ b/docs/operators/bit.md @@ -303,7 +303,7 @@ var FLAG_D = 8; // 1000 上面代码设置 A、B、C、D 四个开关,每个开关分别占有一个二进制位。 -然后,就可以用二进制与运算检验,当前设置是否打开了指定开关。 +然后,就可以用二进制与运算,检查当前设置是否打开了指定开关。 ```javascript var flags = 5; // 二进制的0101 @@ -331,6 +331,8 @@ var mask = FLAG_A | FLAG_B | FLAG_D; flags = flags | mask; ``` +上面代码中,计算后得到的`flags`变量,代表三个开关的二进制位都打开了。 + 二进制与运算可以将当前设置中凡是与开关设置不一样的项,全部关闭。 ```javascript @@ -351,6 +353,6 @@ flags = ~flags; ## 参考链接 -- Michal Budzynski, [JavaScript: The less known parts. Bitwise Operators](http://michalbe.blogspot.co.uk/2013/03/javascript-less-known-parts-bitwise.html) +- Michal Budzynski, [JavaScript: The less known parts. Bitwise Operators](https://michalbe.blogspot.com/2013/03/javascript-less-known-parts-bitwise.html) - Axel Rauschmayer, [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Mozilla Developer Network, [Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) diff --git a/docs/operators/boolean.md b/docs/operators/boolean.md index 87c6805..caeed9c 100644 --- a/docs/operators/boolean.md +++ b/docs/operators/boolean.md @@ -96,7 +96,7 @@ true && 'foo' && '' && 4 && 'foo' && true // 3 ``` -上面代码中,例一里面,第一个布尔值为`false`的表达式为第三个表达式,所以得到一个空字符串。例二里面,所有表达式的布尔值都是`true`,所有返回最后一个表达式的值`3`。 +上面代码中,例一里面,第一个布尔值为`false`的表达式为第三个表达式,所以得到一个空字符串。例二里面,所有表达式的布尔值都是`true`,所以返回最后一个表达式的值`3`。 ## 或运算符(||) diff --git a/docs/operators/comparison.md b/docs/operators/comparison.md index c348da5..002254a 100644 --- a/docs/operators/comparison.md +++ b/docs/operators/comparison.md @@ -74,7 +74,7 @@ true> false // true
上面代码中,字符串和布尔值都会先转成数值,再进行比较。
-这里需要注意与`NaN`的比较。任何值(包括`NaN`本身)与`NaN`比较,返回的都是`false`。
+这里需要注意与`NaN`的比较。任何值(包括`NaN`本身)与`NaN`使用非相等运算符进行比较,返回的都是`false`。
```javascript
1> NaN // false
@@ -99,7 +99,7 @@ x> '11' // true
x.valueOf = function () { return '1' };
x> '11' // false
-// 等同于 [2].valueOf()> '11'
+// 等同于 (function () { return '1' })()> '11'
// 即 '1'> '11'
```
@@ -114,8 +114,8 @@ x> '11' // false
// 等同于 [2].valueOf().toString()> [11].valueOf().toString()
// 即 '2'> '11'
-{ x: 2 }>= { x: 1 } // true
-// 等同于 { x: 2 }.valueOf().toString()>= { x: 1 }.valueOf().toString()
+({ x: 2 })>= ({ x: 1 }) // true
+// 等同于 ({ x: 2 }).valueOf().toString()>= ({ x: 1 }).valueOf().toString()
// 即 '[object Object]'>= '[object Object]'
```
@@ -227,7 +227,7 @@ v1 === v2 // true
1 === 1.0
```
-比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。下面分成四种情况,讨论不同类型的值互相比较的规则。
+比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。下面分成几种情况,讨论不同类型的值互相比较的规则。
**(1)原始类型值**
@@ -272,38 +272,61 @@ v1 === v2 // true
对象(这里指广义的对象,包括数组和函数)与原始类型的值比较时,对象转换成原始类型的值,再进行比较。
+具体来说,先调用对象的`valueOf()`方法,如果得到原始类型的值,就按照上一小节的规则,互相比较;如果得到的还是对象,则再调用`toString()`方法,得到字符串形式,再进行比较。
+
+下面是数组与原始类型值比较的例子。
+
```javascript
-// 对象与数值比较时,对象转为数值
+// 数组与数值的比较
[1] == 1 // true
-// 等同于 Number([1]) == 1
-// 对象与字符串比较时,对象转为字符串
+// 数组与字符串的比较
[1] == '1' // true
-// 等同于 String([1]) == '1'
[1, 2] == '1,2' // true
-// 等同于 String([1, 2]) == '1,2'
-// 对象与布尔值比较时,两边都转为数值
+// 对象与布尔值的比较
[1] == true // true
-// 等同于 Number([1]) == Number(true)
[2] == true // false
-// 等同于 Number([2]) == Number(true)
```
-上面代码中,数组`[1]`与数值进行比较,会先转成数值,再进行比较;与字符串进行比较,会先转成字符串,再进行比较;与布尔值进行比较,对象和布尔值都会先转成数值,再进行比较。
+上面例子中,JavaScript 引擎会先对数组`[1]`调用数组的`valueOf()`方法,由于返回的还是一个数组,所以会接着调用数组的`toString()`方法,得到字符串形式,再按照上一小节的规则进行比较。
+
+下面是一个更直接的例子。
+
+```javascript
+const obj = {
+ valueOf: function () {
+ console.log('执行 valueOf()');
+ return obj;
+ },
+ toString: function () {
+ console.log('执行 toString()');
+ return 'foo';
+ }
+};
+
+obj == 'foo'
+// 执行 valueOf()
+// 执行 toString()
+// true
+```
+
+上面例子中,`obj`是一个自定义了`valueOf()`和`toString()`方法的对象。这个对象与字符串`'foo'`进行比较时,会依次调用`valueOf()`和`toString()`方法,最后返回`'foo'`,所以比较结果是`true`。
**(3)undefined 和 null**
-`undefined`和`null`与其他类型的值比较时,结果都为`false`,它们互相比较时结果为`true`。
+`undefined`和`null`只有与自身比较,或者互相比较时,才会返回`true`;与其他类型的值比较时,结果都为`false`。
```javascript
+undefined == undefined // true
+null == null // true
+undefined == null // true
+
false == null // false
false == undefined // false
0 == null // false
0 == undefined // false
-
-undefined == null // true
```
**(4)相等运算符的缺点**
diff --git a/docs/operators/priority.md b/docs/operators/priority.md
index 8b2a65a..d347f87 100644
--- a/docs/operators/priority.md
+++ b/docs/operators/priority.md
@@ -162,34 +162,56 @@ f() // 1
### 左结合与右结合
-对于优先级别相同的运算符,大多数情况,计算顺序总是从左到右,这叫做运算符的"左结合"(left-to-right associativity),即从左边开始计算。
+对于优先级别相同的运算符,同时出现的时候,就会有计算顺序的问题。
+
+```javascript
+a OP b OP c
+```
+
+上面代码中,`OP`表示运算符。它可以有两种解释方式。
+
+```javascript
+// 方式一
+(a OP b) OP c
+
+// 方式二
+a OP (b OP c)
+```
+
+上面的两种方式,得到的计算结果往往是不一样的。方式一是将左侧两个运算数结合在一起,采用这种解释方式的运算符,称为"左结合"(left-to-right associativity)运算符;方式二是将右侧两个运算数结合在一起,这样的运算符称为"右结合"运算符(right-to-left associativity)。
+
+JavaScript 语言的大多数运算符是"左结合",请看下面加法运算符的例子。
```javascript
x + y + z
+
+// 引擎解释如下
+(x + y) + z
```
-上面代码先计算最左边的`x`与`y`的和,然后再计算与`z`的和。
+上面代码中,`x`与`y`结合在一起,它们的预算结果再与`z`进行运算。
-但是少数运算符的计算顺序是从右到左,即从右边开始计算,这叫做运算符的"右结合"(right-to-left associativity)。其中,最主要的是赋值运算符(`=`)和三元条件运算符(`?:`)。
+少数运算符是"右结合",其中最主要的是赋值运算符(`=`)和三元条件运算符(`?:`)。
```javascript
w = x = y = z;
q = a ? b : c ? d : e ? f : g;
```
-上面代码的运算结果,相当于下面的样子。
+上面代码的解释方式如下。
```javascript
w = (x = (y = z));
q = a ? b : (c ? d : (e ? f : g));
```
-上面的两行代码,各有三个等号运算符和三个三元运算符,都是先计算最右边的那个运算符。
+上面的两行代码,都是右侧的运算数结合在一起。
-指数运算符(`**`)也是右结合的。
+另外,指数运算符(`**`)也是右结合。
```javascript
-// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
+// 相当于 2 ** (3 ** 2)
// 512
```
+
diff --git a/docs/stdlib/array.md b/docs/stdlib/array.md
index b8736d4..e7b4359 100644
--- a/docs/stdlib/array.md
+++ b/docs/stdlib/array.md
@@ -10,17 +10,19 @@ arr.length // 2
arr // [ empty x 2 ]
```
-上面代码中,`Array`构造函数的参数`2`,表示生成一个两个成员的数组,每个位置都是空值。
+上面代码中,`Array()`构造函数的参数`2`,表示生成一个两个成员的数组,每个位置都是空值。
-如果没有使用`new`,运行结果也是一样的。
+如果没有使用`new`关键字,运行结果也是一样的。
```javascript
-var arr = new Array(2);
-// 等同于
var arr = Array(2);
+// 等同于
+var arr = new Array(2);
```
-`Array`构造函数有一个很大的缺陷,就是不同的参数,会导致它的行为不一致。
+考虑到语义性,以及与其他构造函数用法保持一致,建议总是加上`new`。
+
+`Array()`构造函数有一个很大的缺陷,不同的参数个数会导致不一致的行为。
```javascript
// 无参数时,返回一个空数组
@@ -44,7 +46,7 @@ new Array(1, 2) // [1, 2]
new Array('a', 'b', 'c') // ['a', 'b', 'c']
```
-可以看到,`Array`作为构造函数,行为很不一致。因此,不建议使用它生成新数组,直接使用数组字面量是更好的做法。
+可以看到,`Array()`作为构造函数,行为很不一致。因此,不建议使用它生成新数组,直接使用数组字面量是更好的做法。
```javascript
// bad
@@ -54,7 +56,7 @@ var arr = new Array(1, 2);
var arr = [1, 2];
```
-注意,如果参数是一个正整数,返回数组的成员都是空位。虽然读取的时候返回`undefined`,但实际上该位置没有任何值。虽然可以取到`length`属性,但是取不到键名。
+注意,如果参数是一个正整数,返回数组的成员都是空位。虽然读取的时候返回`undefined`,但实际上该位置没有任何值。虽然这时可以读取到`length`属性,但是取不到键名。
```javascript
var a = new Array(3);
@@ -70,7 +72,7 @@ b[0] // undefined
0 in b // true
```
-上面代码中,`a`是一个长度为3的空数组,`b`是一个三个成员都是`undefined`的数组。读取键值的时候,`a`和`b`都返回`undefined`,但是`a`的键位都是空的,`b`的键位是有值的。
+上面代码中,`a`是`Array()`生成的一个长度为3的空数组,`b`是一个三个成员都是`undefined`的数组,这两个数组是不一样的。读取键值的时候,`a`和`b`都返回`undefined`,但是`a`的键名(成员的序号)都是空的,`b`的键名是有值的。
## 静态方法
@@ -282,13 +284,13 @@ a // ["c", "b", "a"]
### slice()
-`slice`方法用于提取目标数组的一部分,返回一个新数组,原数组不变。
+`slice()`方法用于提取目标数组的一部分,返回一个新数组,原数组不变。
```javascript
arr.slice(start, end);
```
-它的第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员。
+它的第一个参数为起始位置(从0开始,会包括在返回的新数组之中),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员。
```javascript
var a = ['a', 'b', 'c'];
@@ -300,9 +302,9 @@ a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]
```
-上面代码中,最后一个例子`slice`没有参数,实际上等于返回一个原数组的拷贝。
+上面代码中,最后一个例子`slice()`没有参数,实际上等于返回一个原数组的拷贝。
-如果`slice`方法的参数是负数,则表示倒数计算的位置。
+如果`slice()`方法的参数是负数,则表示倒数计算的位置。
```javascript
var a = ['a', 'b', 'c'];
@@ -320,7 +322,7 @@ a.slice(4) // []
a.slice(2, 1) // []
```
-`slice`方法的一个重要应用,是将类似数组的对象转为真正的数组。
+`slice()`方法的一个重要应用,是将类似数组的对象转为真正的数组。
```javascript
Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
@@ -330,11 +332,11 @@ Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);
```
-上面代码的参数都不是数组,但是通过`call`方法,在它们上面调用`slice`方法,就可以把它们转为真正的数组。
+上面代码的参数都不是数组,但是通过`call`方法,在它们上面调用`slice()`方法,就可以把它们转为真正的数组。
### splice()
-`splice`方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。
+`splice()`方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。
```javascript
arr.splice(start, count, addElement1, addElement2, ...);
@@ -402,7 +404,7 @@ a // [1, 2]
// [10111, 1101, 111]
```
-上面代码的最后两个例子,需要特殊注意。`sort`方法不是按照大小排序,而是按照字典顺序。也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以`101`排在`11`的前面。
+上面代码的最后两个例子,需要特殊注意。`sort()`方法不是按照大小排序,而是按照字典顺序。也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以`101`排在`11`的前面。
如果想让`sort`方法按照自定义方式排序,可以传入一个函数作为参数。
@@ -430,9 +432,21 @@ a // [1, 2]
// ]
```
+注意,自定义的排序函数应该返回数值,否则不同的浏览器可能有不同的实现,不能保证结果都一致。
+
+```javascript
+// bad
+[1, 4, 2, 6, 0, 6, 2, 6].sort((a, b) => a> b)
+
+// good
+[1, 4, 2, 6, 0, 6, 2, 6].sort((a, b) => a - b)
+```
+
+上面代码中,前一种排序算法返回的是布尔值,这是不推荐使用的。后一种是数值,才是更好的写法。
+
### map()
-`map`方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
+`map()`方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
```javascript
var numbers = [1, 2, 3];
@@ -448,7 +462,7 @@ numbers
上面代码中,`numbers`数组的所有成员依次执行参数函数,运行结果组成一个新数组返回,原数组没有变化。
-`map`方法接受一个函数作为参数。该函数调用时,`map`方法向它传入三个参数:当前成员、当前位置和数组本身。
+`map()`方法接受一个函数作为参数。该函数调用时,`map()`方法向它传入三个参数:当前成员、当前位置和数组本身。
```javascript
[1, 2, 3].map(function(elem, index, arr) {
@@ -457,9 +471,9 @@ numbers
// [0, 2, 6]
```
-上面代码中,`map`方法的回调函数有三个参数,`elem`为当前成员的值,`index`为当前成员的位置,`arr`为原数组(`[1, 2, 3]`)。
+上面代码中,`map()`方法的回调函数有三个参数,`elem`为当前成员的值,`index`为当前成员的位置,`arr`为原数组(`[1, 2, 3]`)。
-`map`方法还可以接受第二个参数,用来绑定回调函数内部的`this`变量(详见《this 变量》一章)。
+`map()`方法还可以接受第二个参数,用来绑定回调函数内部的`this`变量(详见《this 变量》一章)。
```javascript
var arr = ['a', 'b', 'c'];
@@ -470,9 +484,9 @@ var arr = ['a', 'b', 'c'];
// ['b', 'c']
```
-上面代码通过`map`方法的第二个参数,将回调函数内部的`this`对象,指向`arr`数组。
+上面代码通过`map()`方法的第二个参数,将回调函数内部的`this`对象,指向`arr`数组。
-如果数组有空位,`map`方法的回调函数在这个位置不会执行,会跳过数组的空位。
+如果数组有空位,`map()`方法的回调函数在这个位置不会执行,会跳过数组的空位。
```javascript
var f = function (n) { return 'a' };
@@ -482,13 +496,13 @@ var f = function (n) { return 'a' };
[1, , 2].map(f) // ["a", , "a"]
```
-上面代码中,`map`方法不会跳过`undefined`和`null`,但是会跳过空位。
+上面代码中,`map()`方法不会跳过`undefined`和`null`,但是会跳过空位。
### forEach()
-`forEach`方法与`map`方法很相似,也是对数组的所有成员依次执行参数函数。但是,`forEach`方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用`map`方法,否则使用`forEach`方法。
+`forEach()`方法与`map()`方法很相似,也是对数组的所有成员依次执行参数函数。但是,`forEach()`方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用`map()`方法,否则使用`forEach()`方法。
-`forEach`的用法与`map`方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。
+`forEach()`的用法与`map()`方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。
```javascript
function log(element, index, array) {
@@ -501,9 +515,9 @@ function log(element, index, array) {
// [2] = 9
```
-上面代码中,`forEach`遍历数组不是为了得到返回值,而是为了在屏幕输出内容,所以不必使用`map`方法。
+上面代码中,`forEach()`遍历数组不是为了得到返回值,而是为了在屏幕输出内容,所以不必使用`map()`方法。
-`forEach`方法也可以接受第二个参数,绑定参数函数的`this`变量。
+`forEach()`方法也可以接受第二个参数,绑定参数函数的`this`变量。
```javascript
var out = [];
@@ -515,9 +529,9 @@ var out = [];
out // [1, 4, 9]
```
-上面代码中,空数组`out`是`forEach`方法的第二个参数,结果,回调函数内部的`this`关键字就指向`out`。
+上面代码中,空数组`out`是`forEach()`方法的第二个参数,结果,回调函数内部的`this`关键字就指向`out`。
-注意,`forEach`方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用`for`循环。
+注意,`forEach()`方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用`for`循环。
```javascript
var arr = [1, 2, 3];
@@ -529,9 +543,9 @@ for (var i = 0; i < arr.length; i++) { // 1 ``` -上面代码中,执行到数组的第二个成员时,就会中断执行。`forEach`方法做不到这一点。 +上面代码中,执行到数组的第二个成员时,就会中断执行。`forEach()`方法做不到这一点。 -`forEach`方法也会跳过数组的空位。 +`forEach()`方法也会跳过数组的空位。 ```javascript var log = function (n) { @@ -553,11 +567,11 @@ var log = function (n) { // 3 ``` -上面代码中,`forEach`方法不会跳过`undefined`和`null`,但会跳过空位。 +上面代码中,`forEach()`方法不会跳过`undefined`和`null`,但会跳过空位。 ### filter() -`filter`方法用于过滤数组成员,满足条件的成员组成一个新数组返回。 +`filter()`方法用于过滤数组成员,满足条件的成员组成一个新数组返回。 它的参数是一个函数,所有数组成员依次执行该函数,返回结果为`true`的成员组成一个新数组返回。该方法不会改变原数组。 @@ -577,9 +591,9 @@ arr.filter(Boolean) // [1, "a"] ``` -上面代码中,`filter`方法返回数组`arr`里面所有布尔值为`true`的成员。 +上面代码中,`filter()`方法返回数组`arr`里面所有布尔值为`true`的成员。 -`filter`方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组。 +`filter()`方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组。 ```javascript [1, 2, 3, 4, 5].filter(function (elem, index, arr) { @@ -590,7 +604,7 @@ arr.filter(Boolean) 上面代码返回偶数位置的成员组成的新数组。 -`filter`方法还可以接受第二个参数,用来绑定参数函数内部的`this`变量。 +`filter()`方法还可以接受第二个参数,用来绑定参数函数内部的`this`变量。 ```javascript var obj = { MAX: 3 }; @@ -602,7 +616,7 @@ var arr = [2, 8, 3, 4, 1, 3, 2, 9]; arr.filter(myFilter, obj) // [8, 4, 9] ``` -上面代码中,过滤器`myFilter`内部有`this`变量,它可以被`filter`方法的第二个参数`obj`绑定,返回大于`3`的成员。 +上面代码中,过滤器`myFilter()`内部有`this`变量,它可以被`filter()`方法的第二个参数`obj`绑定,返回大于`3`的成员。 ### some(),every() @@ -647,7 +661,7 @@ function isEven(x) { return x % 2 === 0 } ### reduce(),reduceRight() -`reduce`方法和`reduceRight`方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,`reduce`是从左到右处理(从第一个成员到最后一个成员),`reduceRight`则是从右到左(从最后一个成员到第一个成员),其他完全一样。 +`reduce()`方法和`reduceRight()`方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,`reduce()`是从左到右处理(从第一个成员到最后一个成员),`reduceRight()`则是从右到左(从最后一个成员到第一个成员),其他完全一样。 ```javascript [1, 2, 3, 4, 5].reduce(function (a, b) { @@ -661,18 +675,33 @@ function isEven(x) { return x % 2 === 0 } //最后结果:15 ``` -上面代码中,`reduce`方法求出数组所有成员的和。第一次执行,`a`是数组的第一个成员`1`,`b`是数组的第二个成员`2`。第二次执行,`a`为上一轮的返回值`3`,`b`为第三个成员`3`。第三次执行,`a`为上一轮的返回值`6`,`b`为第四个成员`4`。第四次执行,`a`为上一轮返回值`10`,`b`为第五个成员`5`。至此所有成员遍历完成,整个方法的返回值就是最后一轮的返回值`15`。 +上面代码中,`reduce()`方法用来求出数组所有成员的和。`reduce()`的参数是一个函数,数组每个成员都会依次执行这个函数。如果数组有 n 个成员,这个参数函数就会执行 n - 1 次。 -`reduce`方法和`reduceRight`方法的第一个参数都是一个函数。该函数接受以下四个参数。 +- 第一次执行:`a`是数组的第一个成员`1`,`b`是数组的第二个成员`2`。 +- 第二次执行:`a`为上一轮的返回值`3`,`b`为第三个成员`3`。 +- 第三次执行:`a`为上一轮的返回值`6`,`b`为第四个成员`4`。 +- 第四次执行:`a`为上一轮返回值`10`,`b`为第五个成员`5`。至此所有成员遍历完成,整个方法的返回值就是最后一轮的返回值`15`。 -1. 累积变量,默认为数组的第一个成员 -2. 当前变量,默认为数组的第二个成员 -3. 当前位置(从0开始) -4. 原数组 +`reduce()`方法和`reduceRight()`方法的第一个参数都是一个函数。该函数接受以下四个参数。 + +1. 累积变量。第一次执行时,默认为数组的第一个成员;以后每次执行时,都是上一轮的返回值。 +2. 当前变量。第一次执行时,默认为数组的第二个成员;以后每次执行时,都是下一个成员。 +3. 当前位置。一个整数,表示第二个参数(当前变量)的位置,默认为`1`。 +4. 原数组。 这四个参数之中,只有前两个是必须的,后两个则是可选的。 -如果要对累积变量指定初值,可以把它放在`reduce`方法和`reduceRight`方法的第二个参数。 +```javascript +[1, 2, 3, 4, 5].reduce(function ( + a, // 累积变量,必须 + b, // 当前变量,必须 + i, // 当前位置,可选 + arr // 原数组,可选 +) { + // ... ... +``` + +如果要对累积变量指定初值,可以把它放在`reduce()`方法和`reduceRight()`方法的第二个参数。 ```javascript [1, 2, 3, 4, 5].reduce(function (a, b) { @@ -681,9 +710,9 @@ function isEven(x) { return x % 2 === 0 } // 25 ``` -上面代码指定参数`a`的初值为10,所以数组从10开始累加,最终结果为25。注意,这时`b`是从数组的第一个成员开始遍历。 +上面代码指定参数`a`的初值为10,所以数组从10开始累加,最终结果为25。注意,这时`b`是从数组的第一个成员开始遍历,参数函数会执行5次。 -上面的第二个参数相当于设定了默认值,处理空数组时尤其有用。 +建议总是加上第二个参数,这样比较符合直觉,每个数组成员都会依次执行`reduce()`方法的参数函数。另外,第二个参数可以防止空数组报错。 ```javascript function add(prev, cur) { @@ -696,9 +725,9 @@ function add(prev, cur) { // 1 ``` -上面代码中,由于空数组取不到初始值,`reduce`方法会报错。这时,加上第二个参数,就能保证总是会返回一个值。 +上面代码中,由于空数组取不到累积变量的初始值,`reduce()`方法会报错。这时,加上第二个参数,就能保证总是会返回一个值。 -下面是一个`reduceRight`方法的例子。 +下面是一个`reduceRight()`方法的例子。 ```javascript function subtract(prev, cur) { @@ -709,9 +738,9 @@ function subtract(prev, cur) { [3, 2, 1].reduceRight(subtract) // -4 ``` -上面代码中,`reduce`方法相当于`3`减去`2`再减去`1`,`reduceRight`方法相当于`1`减去`2`再减去`3`。 +上面代码中,`reduce()`方法相当于`3`减去`2`再减去`1`,`reduceRight`方法相当于`1`减去`2`再减去`3`。 -由于这两个方法会遍历数组,所以实际上还可以用来做一些遍历相关的操作。比如,找出字符长度最长的数组成员。 +由于这两个方法会遍历数组,所以实际上可以用来做一些遍历相关的操作。比如,找出字符长度最长的数组成员。 ```javascript function findLongest(entries) { @@ -723,7 +752,7 @@ function findLongest(entries) { findLongest(['aaa', 'bb', 'c']) // "aaa" ``` -上面代码中,`reduce`的参数函数会将字符长度较长的那个数组成员,作为累积值。这导致遍历所有成员之后,累积值就是字符长度最长的那个成员。 +上面代码中,`reduce()`的参数函数会将字符长度较长的那个数组成员,作为累积值。这导致遍历所有成员之后,累积值就是字符长度最长的那个成员。 ### indexOf(),lastIndexOf() diff --git a/docs/stdlib/attributes.md b/docs/stdlib/attributes.md index 851ac0a..cad729f 100644 --- a/docs/stdlib/attributes.md +++ b/docs/stdlib/attributes.md @@ -33,7 +33,7 @@ JavaScript 提供了一个内部数据结构,用来描述对象的属性,控 (4)`configurable` -`configurable`是一个布尔值,表示可配置性,默认为`true`。如果设为`false`,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(`value`属性除外)。也就是说,`configurable`属性控制了属性描述对象的可写性。 +`configurable`是一个布尔值,表示属性的可配置性,默认为`true`。如果设为`false`,将阻止某些操作改写属性描述对象,比如无法删除该属性,也不得改变各种元属性(`value`属性除外)。也就是说,`configurable`属性控制了属性描述对象的可写性。 (5)`get` @@ -350,7 +350,7 @@ JSON.stringify(obj) // "{}" ### configurable -`configurable`(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。也就是说,`configurable`为`false`时,`value`、`writable`、`enumerable`和`configurable`都不能被修改了。 +`configurable`(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。也就是说,`configurable`为`false`时,`writable`、`enumerable`和`configurable`都不能被修改了。 ```javascript var obj = Object.defineProperty({}, 'p', { @@ -360,9 +360,6 @@ var obj = Object.defineProperty({}, 'p', { configurable: false }); -Object.defineProperty(obj, 'p', {value: 2}) -// TypeError: Cannot redefine property: p - Object.defineProperty(obj, 'p', {writable: true}) // TypeError: Cannot redefine property: p @@ -371,11 +368,14 @@ Object.defineProperty(obj, 'p', {enumerable: true}) Object.defineProperty(obj, 'p', {configurable: true}) // TypeError: Cannot redefine property: p + +Object.defineProperty(obj, 'p', {value: 2}) +// TypeError: Cannot redefine property: p ``` -上面代码中,`obj.p`的`configurable`为`false`。然后,改动`value`、`writable`、`enumerable`、`configurable`,结果都报错。 +上面代码中,`obj.p`的`configurable`属性为`false`。然后,改动`writable`、`enumerable`、`configurable`,结果都报错。 -注意,`writable`只有在`false`改为`true`会报错,`true`改为`false`是允许的。 +注意,`writable`属性只有在`false`改为`true`时会报错,`true`改为`false`是允许的。 ```javascript var obj = Object.defineProperty({}, 'p', { @@ -387,7 +387,7 @@ Object.defineProperty(obj, 'p', {writable: false}) // 修改成功 ``` -至于`value`,只要`writable`和`configurable`有一个为`true`,就允许改动。 +`value`属性的情况比较特殊。只要`writable`和`configurable`有一个为`true`,就允许改动`value`。 ```javascript var o1 = Object.defineProperty({}, 'p', { @@ -409,7 +409,7 @@ Object.defineProperty(o2, 'p', {value: 2}) // 修改成功 ``` -另外,`writable`为`false`时,直接目标属性赋值,不报错,但不会成功。 +另外,`writable`为`false`时,直接对目标属性赋值,不报错,但不会成功。 ```javascript var obj = Object.defineProperty({}, 'p', { @@ -445,7 +445,7 @@ obj.p2 // 2 除了直接定义以外,属性还可以用存取器(accessor)定义。其中,存值函数称为`setter`,使用属性描述对象的`set`属性;取值函数称为`getter`,使用属性描述对象的`get`属性。 -一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。利用这个功能,可以实现许多高级特性,比如某个属性禁止赋值。 +一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。利用这个功能,可以实现许多高级特性,比如定制属性的读取和赋值行为。 ```javascript var obj = Object.defineProperty({}, 'p', { @@ -466,6 +466,7 @@ obj.p = 123 // "setter: 123" JavaScript 还提供了存取器的另一种写法。 ```javascript +// 写法二 var obj = { get p() { return 'getter'; @@ -476,7 +477,7 @@ var obj = { }; ``` -上面的写法与定义属性描述对象是等价的,而且使用更广泛。 +上面两种写法,虽然属性`p`的读取和赋值行为是一样的,但是有一些细微的区别。第一种写法,属性`p`的`configurable`和`enumerable`都为`false`,从而导致属性`p`是不可遍历的;第二种写法,属性`p`的`configurable`和`enumerable`都为`true`,因此属性`p`是可遍历的。实际开发中,写法二更常用。 注意,取值函数`get`不能接受参数,存值函数`set`只能接受一个参数(即属性的值)。 @@ -634,7 +635,7 @@ Object.getOwnPropertyDescriptor(obj, 'p') // configurable: false // } -Object.defineProperty(o, 'p', { +Object.defineProperty(obj, 'p', { enumerable: false }) // TypeError: Cannot redefine property: p diff --git a/docs/stdlib/date.md b/docs/stdlib/date.md index 357913c..d0475da 100644 --- a/docs/stdlib/date.md +++ b/docs/stdlib/date.md @@ -317,15 +317,15 @@ d.toTimeString() // "00:00:00 GMT+0800 (CST)" var d = new Date(2013, 0, 1); d.toLocaleString() -// 中文版浏览器为"2013年1月1日 上午12:00:00" +// 中文版浏览器为"2013/1/1 00:00:00" // 英文版浏览器为"1/1/2013 12:00:00 AM" d.toLocaleDateString() -// 中文版浏览器为"2013年1月1日" +// 中文版浏览器为"2013/1/1" // 英文版浏览器为"1/1/2013" d.toLocaleTimeString() -// 中文版浏览器为"上午12:00:00" +// 中文版浏览器为"00:00:00" // 英文版浏览器为"12:00:00 AM" ``` @@ -337,28 +337,38 @@ dateObj.toLocaleDateString([locales[, options]]) dateObj.toLocaleTimeString([locales[, options]]) ``` -这两个参数中,`locales`是一个指定所用语言的字符串,`options`是一个配置对象。下面是`locales`的例子。 +这两个参数中,`locales`是一个指定所用语言的字符串,`options`是一个配置对象。下面是`locales`的例子,分别采用`en-US`和`zh-CN`语言设定。 ```javascript var d = new Date(2013, 0, 1); d.toLocaleString('en-US') // "1/1/2013, 12:00:00 AM" -d.toLocaleString('zh-CN') // "2013/1/1 上午12:00:00" +d.toLocaleString('zh-CN') // "2013/1/1 00:00:00" d.toLocaleDateString('en-US') // "1/1/2013" d.toLocaleDateString('zh-CN') // "2013/1/1" d.toLocaleTimeString('en-US') // "12:00:00 AM" -d.toLocaleTimeString('zh-CN') // "上午12:00:00" +d.toLocaleTimeString('zh-CN') // "00:00:00" ``` -下面是`options`的例子。 +`options`配置对象有以下属性。 + +- `dateStyle`:可能的值为`full`、`long`、`medium`、`short`。 +- `timeStyle`:可能的值为`full`、`long`、`medium`、`short`。 +- `month`:可能的值为`numeric`、`2-digit`、`long`、`short`、`narrow`。 +- `year`:可能的值为`numeric`、`2-digit`。 +- `weekday`:可能的值为`long`、`short`、`narrow`。 +- `day`、`hour`、`minute`、`second`:可能的值为`numeric`、`2-digit`。 +- `timeZone`:可能的值为 IANA 的时区数据库。 +- `timeZoneName`:可能的值为`long`、`short`。 +- `hour12`:24小时周期还是12小时周期,可能的值为`true`、`false`。 + +下面是用法实例。 ```javascript var d = new Date(2013, 0, 1); -// 时间格式 -// 下面的设置是,星期和月份为完整文字,年份和日期为数字 d.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', @@ -367,7 +377,13 @@ d.toLocaleDateString('en-US', { }) // "Tuesday, January 1, 2013" -// 指定时区 +d.toLocaleDateString('en-US', { + day: "2-digit", + month: "long", + year: "2-digit" +}); +// "January 01, 13" + d.toLocaleTimeString('en-US', { timeZone: 'UTC', timeZoneName: 'short' @@ -380,7 +396,6 @@ d.toLocaleTimeString('en-US', { }) // "12:00:00 AM China Standard Time" -// 小时周期为12还是24 d.toLocaleTimeString('en-US', { hour12: false }) @@ -464,7 +479,7 @@ d.getUTCDate() // 5 - `setDate(date)`:设置实例对象对应的每个月的几号(1-31),返回改变后毫秒时间戳。 - `setFullYear(year [, month, date])`:设置四位年份。 - `setHours(hour [, min, sec, ms])`:设置小时(0-23)。 -- `setMilliseconds()`:设置毫秒(0-999)。 +- `setMilliseconds(ms)`:设置毫秒(0-999)。 - `setMinutes(min [, sec, ms])`:设置分钟(0-59)。 - `setMonth(month [, date])`:设置月份(0-11)。 - `setSeconds(sec [, ms])`:设置秒(0-59)。 @@ -480,7 +495,7 @@ d.setDate(9) // 1357660800000 d // Wed Jan 09 2013 00:00:00 GMT+0800 (CST) ``` -`set*`方法的参数都会自动折算。以`setDate`为例,如果参数超过当月的最大天数,则向下一个月顺延,如果参数是负数,表示从上个月的最后一天开始减去的天数。 +`set*`方法的参数都会自动折算。以`setDate()`为例,如果参数超过当月的最大天数,则向下一个月顺延,如果参数是负数,表示从上个月的最后一天开始减去的天数。 ```javascript var d1 = new Date('January 6, 2013'); @@ -490,10 +505,12 @@ d1 // Fri Feb 01 2013 00:00:00 GMT+0800 (CST) var d2 = new Date ('January 6, 2013'); -d.setDate(-1) // 1356796800000 -d // Sun Dec 30 2012 00:00:00 GMT+0800 (CST) +d2.setDate(-1) // 1356796800000 +d2 // Sun Dec 30 2012 00:00:00 GMT+0800 (CST) ``` +上面代码中,`d1.setDate(32)`将日期设为1月份的32号,因为1月份只有31号,所以自动折算为2月1日。`d2.setDate(-1)`表示设为上个月的倒数第二天,即12月30日。 + `set`类方法和`get`类方法,可以结合使用,得到相对时间。 ```javascript @@ -528,5 +545,5 @@ d // Sun Jan 06 2013 06:00:00 GMT+0800 (CST) ## 参考链接 -- Rakhitha Nimesh,[Getting Started with the Date Object](http://jspro.com/raw-javascript/beginners-guide-to-javascript-date-and-time/) -- Ilya Kantor, [Date/Time functions](http://javascript.info/tutorial/datetime-functions) +- Rakhitha Nimesh,[Getting Started with the Date Object](https://www.sitepoint.com/beginners-guide-to-javascript-date-and-time/) +- Ilya Kantor, [Date/Time functions](https://javascript.info/date) diff --git a/docs/stdlib/json.md b/docs/stdlib/json.md index b4250aa..c395d46 100644 --- a/docs/stdlib/json.md +++ b/docs/stdlib/json.md @@ -59,7 +59,7 @@ JSON 对值的类型和格式有严格的规定。 ### 基本用法 -`JSON.stringify`方法用于将一个值转为 JSON 字符串。该字符串符合 JSON 格式,并且可以被`JSON.parse`方法还原。 +`JSON.stringify()`方法用于将一个值转为 JSON 字符串。该字符串符合 JSON 格式,并且可以被`JSON.parse()`方法还原。 ```javascript JSON.stringify('abc') // ""abc"" @@ -93,7 +93,7 @@ JSON.stringify('false') // "\"false\"" 上面代码中,如果不是内层的双引号,将来还原的时候,引擎就无法知道原始值是布尔值还是字符串。 -如果对象的属性是`undefined`、函数或 XML 对象,该属性会被`JSON.stringify`过滤。 +如果对象的属性是`undefined`、函数或 XML 对象,该属性会被`JSON.stringify()`过滤。 ```javascript var obj = { @@ -121,7 +121,7 @@ JSON.stringify(arr) // "[null,null]" JSON.stringify(/foo/) // "{}" ``` -`JSON.stringify`方法会忽略对象的不可遍历的属性。 +`JSON.stringify()`方法会忽略对象的不可遍历的属性。 ```javascript var obj = {}; @@ -143,7 +143,7 @@ JSON.stringify(obj); // "{"foo":1}" ### 第二个参数 -`JSON.stringify`方法还可以接受一个数组,作为第二个参数,指定需要转成字符串的属性。 +`JSON.stringify()`方法还可以接受一个数组,作为第二个参数,指定参数对象的哪些属性需要转成字符串。 ```javascript var obj = { @@ -158,7 +158,7 @@ JSON.stringify(obj, selectedProperties) // "{"prop1":"value1","prop2":"value2"}" ``` -上面代码中,`JSON.stringify`方法的第二个参数指定,只转`prop1`和`prop2`两个属性。 +上面代码中,`JSON.stringify()`方法的第二个参数指定,只转`prop1`和`prop2`两个属性。 这个类似白名单的数组,只对对象的属性有效,对数组无效。 @@ -172,7 +172,7 @@ JSON.stringify({0: 'a', 1: 'b'}, ['0']) 上面代码中,第二个参数指定 JSON 格式只转`0`号属性,实际上对数组是无效的,只对对象有效。 -第二个参数还可以是一个函数,用来更改`JSON.stringify`的返回值。 +第二个参数还可以是一个函数,用来更改`JSON.stringify()`的返回值。 ```javascript function f(key, value) { @@ -191,26 +191,26 @@ JSON.stringify({ a: 1, b: 2 }, f) 注意,这个处理函数是递归处理所有的键。 ```javascript -var o = {a: {b: 1}}; +var obj = {a: {b: 1}}; function f(key, value) { console.log("["+ key +"]:" + value); return value; } -JSON.stringify(o, f) +JSON.stringify(obj, f) // []:[object Object] // [a]:[object Object] // [b]:1 // '{"a":{"b":1}}' ``` -上面代码中,对象`o`一共会被`f`函数处理三次,最后那行是`JSON.stringify`的输出。第一次键名为空,键值是整个对象`o`;第二次键名为`a`,键值是`{b: 1}`;第三次键名为`b`,键值为1。 +上面代码中,对象`obj`一共会被`f`函数处理三次,输出的最后那行是`JSON.stringify()`的默认输出。第一次键名为空,键值是整个对象`obj`;第二次键名为`a`,键值是`{b: 1}`;第三次键名为`b`,键值为1。 递归处理中,每一次处理的对象,都是前一次返回的值。 ```javascript -var o = {a: 1}; +var obj = {a: 1}; function f(key, value) { if (typeof value === 'object') { @@ -219,11 +219,11 @@ function f(key, value) { return value * 2; } -JSON.stringify(o, f) +JSON.stringify(obj, f) // "{"b": 4}" ``` -上面代码中,`f`函数修改了对象`o`,接着`JSON.stringify`方法就递归处理修改后的对象`o`。 +上面代码中,`f`函数修改了对象`obj`,接着`JSON.stringify()`方法就递归处理修改后的对象`obj`。 如果处理函数返回`undefined`或没有返回值,则该属性会被忽略。 @@ -243,7 +243,26 @@ JSON.stringify({ a: "abc", b: 123 }, f) ### 第三个参数 -`JSON.stringify`还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性。如果是数字,表示每个属性前面添加的空格(最多不超过10个);如果是字符串(不超过10个字符),则该字符串会添加在每行前面。 +`JSON.stringify()`还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性。 + +默认返回的是单行字符串,对于大型的 JSON 对象,可读性非常差。第三个参数使得每个属性单独占据一行,并且将每个属性前面添加指定的前缀(不超过10个字符)。 + +```javascript +// 默认输出 +JSON.stringify({ p1: 1, p2: 2 }) +// JSON.stringify({ p1: 1, p2: 2 }) + +// 分行输出 +JSON.stringify({ p1: 1, p2: 2 }, null, '\t') +// { +// "p1": 1, +// "p2": 2 +// } +``` + +上面例子中,第三个属性`\t`在每个属性前面添加一个制表符,然后分行显示。 + +第三个属性如果是一个数字,则表示每个属性前面添加的空格(最多不超过10个)。 ```javascript JSON.stringify({ p1: 1, p2: 2 }, null, 2); @@ -253,19 +272,11 @@ JSON.stringify({ p1: 1, p2: 2 }, null, 2); "p2": 2 }" */ - -JSON.stringify({ p1:1, p2:2 }, null, '|-'); -/* -"{ -|-"p1": 1, -|-"p2": 2 -}" -*/ ``` -### 参数对象的 toJSON 方法 +### 参数对象的 toJSON() 方法 -如果参数对象有自定义的`toJSON`方法,那么`JSON.stringify`会使用这个方法的返回值作为参数,而忽略原对象的其他属性。 +如果参数对象有自定义的`toJSON()`方法,那么`JSON.stringify()`会使用这个方法的返回值作为参数,而忽略原对象的其他属性。 下面是一个普通的对象。 @@ -283,7 +294,7 @@ JSON.stringify(user) // "{"firstName":"三","lastName":"张","fullName":"张三"}" ``` -现在,为这个对象加上`toJSON`方法。 +现在,为这个对象加上`toJSON()`方法。 ```javascript var user = { @@ -305,9 +316,9 @@ JSON.stringify(user) // "{"name":"张三"}" ``` -上面代码中,`JSON.stringify`发现参数对象有`toJSON`方法,就直接使用这个方法的返回值作为参数,而忽略原对象的其他参数。 +上面代码中,`JSON.stringify()`发现参数对象有`toJSON()`方法,就直接使用这个方法的返回值作为参数,而忽略原对象的其他参数。 -`Date`对象就有一个自己的`toJSON`方法。 +`Date`对象就有一个自己的`toJSON()`方法。 ```javascript var date = new Date('2015-01-01'); @@ -315,9 +326,9 @@ date.toJSON() // "2015-01-01T00:00:00.000Z" JSON.stringify(date) // ""2015-01-01T00:00:00.000Z"" ``` -上面代码中,`JSON.stringify`发现处理的是`Date`对象实例,就会调用这个实例对象的`toJSON`方法,将该方法的返回值作为参数。 +上面代码中,`JSON.stringify()`发现处理的是`Date`对象实例,就会调用这个实例对象的`toJSON()`方法,将该方法的返回值作为参数。 -`toJSON`方法的一个应用是,将正则对象自动转为字符串。因为`JSON.stringify`默认不能转换正则对象,但是设置了`toJSON`方法以后,就可以转换正则对象了。 +`toJSON()`方法的一个应用是,将正则对象自动转为字符串。因为`JSON.stringify()`默认不能转换正则对象,但是设置了`toJSON()`方法以后,就可以转换正则对象了。 ```javascript var obj = { @@ -336,7 +347,7 @@ JSON.stringify(/foo/) // ""/foo/"" ## JSON.parse() -`JSON.parse`方法用于将 JSON 字符串转换成对应的值。 +`JSON.parse()`方法用于将 JSON 字符串转换成对应的值。 ```javascript JSON.parse('{}') // {} @@ -349,7 +360,7 @@ var o = JSON.parse('{"name": "张三"}'); o.name // 张三 ``` -如果传入的字符串不是有效的 JSON 格式,`JSON.parse`方法将报错。 +如果传入的字符串不是有效的 JSON 格式,`JSON.parse()`方法将报错。 ```javascript JSON.parse("'String'") // illegal single quotes @@ -358,7 +369,7 @@ JSON.parse("'String'") // illegal single quotes 上面代码中,双引号字符串中是一个单引号字符串,因为单引号字符串不符合 JSON 格式,所以报错。 -为了处理解析错误,可以将`JSON.parse`方法放在`try...catch`代码块中。 +为了处理解析错误,可以将`JSON.parse()`方法放在`try...catch`代码块中。 ```javascript try { @@ -368,7 +379,7 @@ try { } ``` -`JSON.parse`方法可以接受一个处理函数,作为第二个参数,用法与`JSON.stringify`方法类似。 +`JSON.parse()`方法可以接受一个处理函数,作为第二个参数,用法与`JSON.stringify()`方法类似。 ```javascript function f(key, value) { @@ -382,7 +393,16 @@ JSON.parse('{"a": 1, "b": 2}', f) // {a: 11, b: 2} ``` -上面代码中,`JSON.parse`的第二个参数是一个函数,如果键名是`a`,该函数会将键值加上10。 +上面代码中,`JSON.parse()`的第二个参数是一个函数,如果键名是`a`,该函数会将键值加上10。 + +`JSON.parse()`和`JSON.stringify()`可以结合使用,像下面这样写,实现对象的深拷贝。 + +```javascript +JSON.parse(JSON.stringify(obj)) +``` + +上面这种写法,可以深度克隆一个对象,但是对象内部不能有 JSON +不允许的数据类型,比如函数、正则对象、日期对象等。 ## 参考链接 diff --git a/docs/stdlib/math.md b/docs/stdlib/math.md index de60029..a5e8947 100644 --- a/docs/stdlib/math.md +++ b/docs/stdlib/math.md @@ -37,7 +37,7 @@ Math.SQRT2 // 1.4142135623730951 - `Math.floor()`:向下取整 - `Math.max()`:最大值 - `Math.min()`:最小值 -- `Math.pow()`:指数运算 +- `Math.pow()`:幂运算 - `Math.sqrt()`:平方根 - `Math.log()`:自然对数 - `Math.exp()`:`e`的指数 @@ -66,14 +66,14 @@ Math.max() // -Infinity ### Math.floor(),Math.ceil() -`Math.floor`方法返回小于参数值的最大整数(地板值)。 +`Math.floor`方法返回小于或等于参数值的最大整数(地板值)。 ```javascript Math.floor(3.2) // 3 Math.floor(-3.2) // -4 ``` -`Math.ceil`方法返回大于参数值的最小整数(天花板值)。 +`Math.ceil`方法返回大于或等于参数值的最小整数(天花板值)。 ```javascript Math.ceil(3.2) // 4 @@ -121,7 +121,7 @@ Math.round(-1.6) // -2 ### Math.pow() -`Math.pow`方法返回以第一个参数为底数、第二个参数为幂的指数值。 +`Math.pow`方法返回以第一个参数为底数、第二个参数为指数的幂运算值。 ```javascript // 等同于 2 ** 2 diff --git a/docs/stdlib/number.md b/docs/stdlib/number.md index d585fff..53fb8c5 100644 --- a/docs/stdlib/number.md +++ b/docs/stdlib/number.md @@ -117,7 +117,7 @@ Number.MIN_SAFE_INTEGER // -9007199254740991 上面代码中,`10`和`10.005`先转成2位小数,然后转成字符串。其中`10`必须放在括号里,否则后面的点会被处理成小数点。 -`toFixed()`方法的参数为小数位数,有效范围为0到20,超出这个范围将抛出 RangeError 错误。 +`toFixed()`方法的参数为小数位数,有效范围为0到100,超出这个范围将抛出 RangeError 错误。 由于浮点数的原因,小数`5`的四舍五入是不确定的,使用的时候必须小心。 @@ -140,11 +140,11 @@ Number.MIN_SAFE_INTEGER // -9007199254740991 (1234).toExponential(2) // "1.23e+3" ``` -`toExponential`方法的参数是小数点后有效数字的位数,范围为0到20,超出这个范围,会抛出一个 RangeError 错误。 +`toExponential`方法的参数是小数点后有效数字的位数,范围为0到100,超出这个范围,会抛出一个 RangeError 错误。 ### Number.prototype.toPrecision() -`toPrecision`方法用于将一个数转为指定位数的有效数字。 +`Number.prototype.toPrecision()`方法用于将一个数转为指定位数的有效数字。 ```javascript (12.34).toPrecision(1) // "1e+1" @@ -154,9 +154,9 @@ Number.MIN_SAFE_INTEGER // -9007199254740991 (12.34).toPrecision(5) // "12.340" ``` -`toPrecision`方法的参数为有效数字的位数,范围是1到21,超出这个范围会抛出 RangeError 错误。 +该方法的参数为有效数字的位数,范围是1到100,超出这个范围会抛出 RangeError 错误。 -`toPrecision`方法用于四舍五入时不太可靠,跟浮点数不是精确储存有关。 +该方法用于四舍五入时不太可靠,跟浮点数不是精确储存有关。 ```javascript (12.35).toPrecision(3) // "12.3" @@ -165,6 +165,41 @@ Number.MIN_SAFE_INTEGER // -9007199254740991 (12.45).toPrecision(3) // "12.4" ``` +### Number.prototype.toLocaleString() + +`Number.prototype.toLocaleString()`方法接受一个地区码作为参数,返回一个字符串,表示当前数字在该地区的当地书写形式。 + +```javascript +(123).toLocaleString('zh-Hans-CN-u-nu-hanidec') +// "一二三" +``` + +该方法还可以接受第二个参数配置对象,用来定制指定用途的返回字符串。该对象的`style`属性指定输出样式,默认值是`decimal`,表示输出十进制形式。如果值为`percent`,表示输出百分数。 + +```javascript +(123).toLocaleString('zh-Hans-CN', { style: 'percent' }) +// "12,300%" +``` + +如果`style`属性的值为`currency`,则可以搭配`currency`属性,输出指定格式的货币字符串形式。 + +```javascript +(123).toLocaleString('zh-Hans-CN', { style: 'currency', currency: 'CNY' }) +// "123円.00" + +(123).toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }) +// "123,00 €" + +(123).toLocaleString('en-US', { style: 'currency', currency: 'USD' }) +// "123ドル.00" +``` + +如果`Number.prototype.toLocaleString()`省略了参数,则由浏览器自行决定如何处理,通常会使用操作系统的地区设定。注意,该方法如果使用浏览器不认识的地区码,会抛出一个错误。 + +```javascript +(123).toLocaleString('123') // 出错 +``` + ## 自定义方法 与其他对象一样,`Number.prototype`对象上面可以自定义方法,被`Number`的实例继承。 diff --git a/docs/stdlib/object.md b/docs/stdlib/object.md index 51ba1e6..783175c 100644 --- a/docs/stdlib/object.md +++ b/docs/stdlib/object.md @@ -56,7 +56,7 @@ obj instanceof Object // true `instanceof`运算符用来验证,一个对象是否为指定的构造函数的实例。`obj instanceof Object`返回`true`,就表示`obj`对象是`Object`的实例。 -如果参数是原始类型的值,`Object`方法将其转为对应的包装对象的实例(参见《原始类型的包装对象》一章)。 +如果参数是原始类型的值,`Object`方法将其转为对应的包装对象的实例(参见《包装对象》一章)。 ```javascript var obj = Object(1); @@ -156,7 +156,7 @@ var obj = { Object.getOwnPropertyNames(obj) // ["p1", "p2"] ``` -对于一般的对象来说,`Object.keys()`和`Object.getOwnPropertyNames()`返回的结果是一样的。只有涉及不可枚举属性时,才会有不一样的结果。`Object.keys`方法只返回可枚举的属性(详见《对象属性的描述对象》一章),`Object.getOwnPropertyNames`方法还返回不可枚举的属性名。 +对于一般的对象来说,`Object.keys()`和`Object.getOwnPropertyNames()`返回的结果是一样的。只有涉及不可枚举属性时,才会有不一样的结果。`Object.keys`方法只返回可枚举的属性(详见《属性描述对象》一章),`Object.getOwnPropertyNames`方法还返回不可枚举的属性名。 ```javascript var a = ['Hello', 'World']; @@ -231,7 +231,7 @@ obj.valueOf() === obj // true 上面代码比较`obj.valueOf()`与`obj`本身,两者是一样的。 -`valueOf`方法的主要用途是,JavaScript 自动类型转换时会默认调用这个方法(详见《数据类型转换》一章)。 +`valueOf`方法的主要用途是,JavaScript 自动类型转换时会默认调用这个方法(详见《数据类型的转换》一章)。 ```javascript var obj = new Object(); @@ -308,7 +308,7 @@ var obj = {}; obj.toString() // "[object Object]" ``` -上面代码调用空对象的`toString`方法,结果返回一个字符串`object Object`,其中第二个`Object`表示该值的构造函数。这是一个十分有用的判断数据类型的方法。 +上面代码调用空对象的`toString`方法,结果返回一个字符串`[object Object]`,其中第二个`Object`表示该值的构造函数。这是一个十分有用的判断数据类型的方法。 由于实例对象可能会自定义`toString`方法,覆盖掉`Object.prototype.toString`方法,所以为了得到类型字符串,最好直接使用`Object.prototype.toString`方法。通过函数的`call`方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。 diff --git a/docs/stdlib/regexp.md b/docs/stdlib/regexp.md index 3ba5f86..5a1d884 100644 --- a/docs/stdlib/regexp.md +++ b/docs/stdlib/regexp.md @@ -104,7 +104,7 @@ r.lastIndex = 4; r.test(s) // false r.lastIndex // 0 -r.test(s) +r.test(s) // true ``` 上面代码指定从字符串的第五个位置开始搜索,这个位置为空,所以返回`false`。同时,`lastIndex`属性重置为`0`,所以第二次执行`r.test(s)`会返回`true`。 @@ -137,7 +137,7 @@ new RegExp('').test('abc') ### RegExp.prototype.exec() -正则实例对象的`exec`方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回`null`。 +正则实例对象的`exec()`方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回`null`。 ```javascript var s = '_x_x'; @@ -150,7 +150,7 @@ r2.exec(s) // null 上面代码中,正则对象`r1`匹配成功,返回一个数组,成员是匹配结果;正则对象`r2`匹配失败,返回`null`。 -如果正则表示式包含圆括号(即含有"组匹配"),则返回的数组会包括多个成员。第一个成员是整个匹配成功的结果,后面的成员就是圆括号对应的匹配成功的组。也就是说,第二个成员对应第一个括号,第三个成员对应第二个括号,以此类推。整个数组的`length`属性等于组匹配的数量再加1。 +如果正则表达式包含圆括号(即含有"组匹配"),则返回的数组会包括多个成员。第一个成员是整个匹配成功的结果,后面的成员就是圆括号对应的匹配成功的组。也就是说,第二个成员对应第一个括号,第三个成员对应第二个括号,以此类推。整个数组的`length`属性等于组匹配的数量再加1。 ```javascript var s = '_x_x'; @@ -159,12 +159,12 @@ var r = /_(x)/; r.exec(s) // ["_x", "x"] ``` -上面代码的`exec`方法,返回一个数组。第一个成员是整个匹配的结果,第二个成员是圆括号匹配的结果。 +上面代码的`exec()`方法,返回一个数组。第一个成员是整个匹配的结果,第二个成员是圆括号匹配的结果。 -`exec`方法的返回数组还包含以下两个属性: +`exec()`方法的返回数组还包含以下两个属性: - `input`:整个原字符串。 -- `index`:整个模式匹配成功的开始位置(从0开始计数)。 +- `index`:模式匹配成功的开始位置(从0开始计数)。 ```javascript var r = /a(b+)a/; @@ -178,7 +178,7 @@ arr.input // "_abbba_aba_" 上面代码中的`index`属性等于1,是因为从原字符串的第二个位置开始匹配成功。 -如果正则表达式加上`g`修饰符,则可以使用多次`exec`方法,下一次搜索的位置从上一次匹配成功结束的位置开始。 +如果正则表达式加上`g`修饰符,则可以使用多次`exec()`方法,下一次搜索的位置从上一次匹配成功结束的位置开始。 ```javascript var reg = /a/g; @@ -204,7 +204,7 @@ r4 // null reg.lastIndex // 0 ``` -上面代码连续用了四次`exec`方法,前三次都是从上一次匹配结束的位置向后匹配。当第三次匹配结束以后,整个字符串已经到达尾部,匹配结果返回`null`,正则实例对象的`lastIndex`属性也重置为`0`,意味着第四次匹配将从头开始。 +上面代码连续用了四次`exec()`方法,前三次都是从上一次匹配结束的位置向后匹配。当第三次匹配结束以后,整个字符串已经到达尾部,匹配结果返回`null`,正则实例对象的`lastIndex`属性也重置为`0`,意味着第四次匹配将从头开始。 利用`g`修饰符允许多次匹配的特点,可以用一个循环完成全部匹配。 @@ -222,7 +222,7 @@ while(true) { // #8:a ``` -上面代码中,只要`exec`方法不返回`null`,就会一直循环下去,每次输出匹配的位置和匹配的文本。 +上面代码中,只要`exec()`方法不返回`null`,就会一直循环下去,每次输出匹配的位置和匹配的文本。 正则实例对象的`lastIndex`属性不仅可读,还可写。设置了`g`修饰符的时候,只要手动设置了`lastIndex`的值,就会从指定位置开始匹配。 @@ -298,7 +298,7 @@ str.replace(search, replacement) 'aaa'.replace(/a/g, 'b') // "bbb" ``` -上面代码中,最后一个正则表达式使用了`g`修饰符,导致所有的`b`都被替换掉了。 +上面代码中,最后一个正则表达式使用了`g`修饰符,导致所有的`a`都被替换掉了。 `replace`方法的一个应用,就是消除字符串首尾两端的空格。 @@ -485,7 +485,7 @@ str.split(separator, [limit]) 上面代码指的是,`a`和`b`之间有一个空格或者一个制表符。 -其他的元字符还包括`\`、`\*`、`+`、`?`、`()`、`[]`、`{}`等,将在下文解释。 +其他的元字符还包括`\`、`*`、`+`、`?`、`()`、`[]`、`{}`等,将在下文解释。 ### 转义符 @@ -694,7 +694,7 @@ var html = "
Hello\n
world!";
### 贪婪模式
-上一小节的三个量词符,默认情况下都是最大可能匹配,即匹配直到下一个字符不满足匹配规则为止。这被称为贪婪模式。
+上一小节的三个量词符,默认情况下都是最大可能匹配,即匹配到下一个字符不满足匹配规则为止。这被称为贪婪模式。
```javascript
var s = 'aaa';
@@ -703,29 +703,31 @@ s.match(/a+/) // ["aaa"]
上面代码中,模式是`/a+/`,表示匹配1个`a`或多个`a`,那么到底会匹配几个`a`呢?因为默认是贪婪模式,会一直匹配到字符`a`不出现为止,所以匹配结果是3个`a`。
-如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。
+除了贪婪模式,还有非贪婪模式,即最小可能匹配。只要一发现匹配,就返回结果,不要往下检查。如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。
```javascript
var s = 'aaa';
s.match(/a+?/) // ["a"]
```
-上面代码中,模式结尾添加了一个问号`/a+?/`,这时就改为非贪婪模式,一旦条件满足,就不再往下匹配。
+上面例子中,模式结尾添加了一个问号`/a+?/`,这时就改为非贪婪模式,一旦条件满足,就不再往下匹配,`+?`表示只要发现一个`a`,就不再往下匹配了。
-除了非贪婪模式的加号,还有非贪婪模式的星号(`*`)和非贪婪模式的问号(`?`)。
+除了非贪婪模式的加号(`+?`),还有非贪婪模式的星号(`*?`)和非贪婪模式的问号(`??`)。
- `+?`:表示某个模式出现1次或多次,匹配时采用非贪婪模式。
- `*?`:表示某个模式出现0次或多次,匹配时采用非贪婪模式。
- `??`:表格某个模式出现0次或1次,匹配时采用非贪婪模式。
```javascript
-'abb'.match(/ab*b/) // ["abb"]
-'abb'.match(/ab*?b/) // ["ab"]
+'abb'.match(/ab*/) // ["abb"]
+'abb'.match(/ab*?/) // ["a"]
-'abb'.match(/ab?b/) // ["abb"]
-'abb'.match(/ab??b/) // ["ab"]
+'abb'.match(/ab?/) // ["ab"]
+'abb'.match(/ab??/) // ["a"]
```
+上面例子中,`/ab*/`表示如果`a`后面有多个`b`,那么匹配尽可能多的`b`;`/ab*?/`表示匹配尽可能少的`b`,也就是0个`b`。
+
### 修饰符
修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部。
diff --git a/docs/stdlib/string.md b/docs/stdlib/string.md
index 08feda3..63d21fa 100644
--- a/docs/stdlib/string.md
+++ b/docs/stdlib/string.md
@@ -110,7 +110,7 @@ s.charAt(s.length - 1) // "c"
### String.prototype.charCodeAt()
-`charCodeAt`方法返回字符串指定位置的 Unicode 码点(十进制表示),相当于`String.fromCharCode()`的逆操作。
+`charCodeAt()`方法返回字符串指定位置的 Unicode 码点(十进制表示),相当于`String.fromCharCode()`的逆操作。
```javascript
'abc'.charCodeAt(1) // 98
@@ -131,7 +131,7 @@ s.charAt(s.length - 1) // "c"
'abc'.charCodeAt(4) // NaN
```
-注意,`charCodeAt`方法返回的 Unicode 码点不会大于65536(0xFFFF),也就是说,只返回两个字节的字符的码点。如果遇到码点大于 65536 的字符(四个字节的字符),必需连续使用两次`charCodeAt`,不仅读入`charCodeAt(i)`,还要读入`charCodeAt(i+1)`,将两个值放在一起,才能得到准确的字符。
+注意,`charCodeAt`方法返回的 Unicode 码点不会大于65536(0xFFFF),也就是说,只返回两个字节的字符的码点。如果遇到码点大于 65536 的字符(四个字节的字符),必须连续使用两次`charCodeAt`,不仅读入`charCodeAt(i)`,还要读入`charCodeAt(i+1)`,将两个值放在一起,才能得到准确的字符。
### String.prototype.concat()
@@ -166,7 +166,7 @@ one + two + three // "33"
### String.prototype.slice()
-`slice`方法用于从原字符串取出子字符串并返回,不改变原字符串。它的第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置)。
+`slice()`方法用于从原字符串取出子字符串并返回,不改变原字符串。它的第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置)。
```javascript
'JavaScript'.slice(0, 4) // "Java"
@@ -186,7 +186,7 @@ one + two + three // "33"
'JavaScript'.slice(-2, -1) // "p"
```
-如果第一个参数大于第二个参数,`slice`方法返回一个空字符串。
+如果第一个参数大于第二个参数(正数情况下),`slice()`方法返回一个空字符串。
```javascript
'JavaScript'.slice(2, 1) // ""
diff --git a/docs/stdlib/wrapper.md b/docs/stdlib/wrapper.md
index 7c3367b..7e89bf3 100644
--- a/docs/stdlib/wrapper.md
+++ b/docs/stdlib/wrapper.md
@@ -124,5 +124,5 @@ Number.prototype.double = function () {
(123).double() // 246
```
-上面代码在`String`和`Number`这两个对象的原型上面,分别自定义了一个方法,从而可以在所有实例对象上调用。注意,最后一张的`123`外面必须要加上圆括号,否则后面的点运算符(`.`)会被解释成小数点。
+上面代码在`String`和`Number`这两个对象的原型上面,分别自定义了一个方法,从而可以在所有实例对象上调用。注意,最后一行的`123`外面必须要加上圆括号,否则后面的点运算符(`.`)会被解释成小数点。
diff --git a/docs/types/array.md b/docs/types/array.md
index d2fde5b..94944bf 100644
--- a/docs/types/array.md
+++ b/docs/types/array.md
@@ -124,7 +124,7 @@ arr.length // 1001
上面代码表示,数组的数字键不需要连续,`length`属性的值总是比最大的那个整数键大`1`。另外,这也表明数组是一种动态的数据结构,可以随时增减数组的成员。
-`length`属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员会自动减少到`length`设置的值。
+`length`属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到`length`设置的值。
```javascript
var arr = [ 'a', 'b', 'c' ];
@@ -303,7 +303,7 @@ var a = [1, , 1];
a.length // 3
```
-上面代码表明,数组的空位不影响`length`属性。
+上面代码表明,数组的空位不影响`length`属性。虽然这个位置没有值,引擎依然认为这个位置是有效的。
需要注意的是,如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果都是一样的。
diff --git a/docs/types/function.md b/docs/types/function.md
index 71df9bb..751bca5 100644
--- a/docs/types/function.md
+++ b/docs/types/function.md
@@ -1,6 +1,6 @@
# 函数
-函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
+函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数有唯一对应的返回值。
## 概述
@@ -186,7 +186,9 @@ f();
f = function () {};
```
-上面代码第二行,调用`f`的时候,`f`只是被声明了,还没有被赋值,等于`undefined`,所以会报错。因此,如果同时采用`function`命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义。
+上面代码第二行,调用`f`的时候,`f`只是被声明了,还没有被赋值,等于`undefined`,所以会报错。
+
+注意,如果像下面例子那样,采用`function`命令和`var`赋值语句声明同一个函数,由于存在函数提升,最后会采用`var`赋值语句的定义。
```javascript
var f = function () {
@@ -200,6 +202,8 @@ function f() {
f() // 1
```
+上面例子中,表面上后面声明的函数`f`,应该覆盖前面的`var`赋值语句,但是由于存在函数提升,实际上正好反过来。
+
## 函数的属性和方法
### name 属性
@@ -256,7 +260,7 @@ f.length // 2
### toString()
-函数的`toString`方法返回一个字符串,内容是函数的源码。
+函数的`toString()`方法返回一个字符串,内容是函数的源码。
```javascript
function f() {
@@ -273,6 +277,8 @@ f.toString()
// }
```
+上面示例中,函数`f`的`toString()`方法返回了`f`的源码,包含换行符在内。
+
对于那些原生的函数,`toString()`方法返回`function (){[native code]}`。
```javascript
@@ -280,7 +286,7 @@ Math.sqrt.toString()
// "function sqrt() { [native code] }"
```
-上面代码中,`Math.sqrt`是 JavaScript 引擎提供的原生函数,`toString()`方法就返回原生代码的提示。
+上面代码中,`Math.sqrt()`是 JavaScript 引擎提供的原生函数,`toString()`方法就返回原生代码的提示。
函数内部的注释也可以返回。
@@ -315,6 +321,8 @@ multiline(f);
// 多行注释"
```
+上面示例中,函数`f`内部有一个多行注释,`toString()`方法拿到`f`的源码后,去掉首尾两行,就得到了一个多行字符串。
+
## 函数作用域
### 定义
@@ -546,7 +554,7 @@ f(obj);
obj // [1, 2, 3]
```
-上面代码中,在函数`f`内部,参数对象`obj`被整个替换成另一个值。这时不会影响到原始值。这是因为,形式参数(`o`)的值实际是参数`obj`的地址,重新对`o`赋值导致`o`指向另一个地址,保存在原地址上的值当然不受影响。
+上面代码中,在函数`f()`内部,参数对象`obj`被整个替换成另一个值。这时不会影响到原始值。这是因为,形式参数(`o`)的值实际是参数`obj`的地址,重新对`o`赋值导致`o`指向另一个地址,保存在原地址上的值当然不受影响。
### 同名参数
@@ -560,7 +568,7 @@ function f(a, a) {
f(1, 2) // 2
```
-上面代码中,函数`f`有两个参数,且参数名都是`a`。取值的时候,以后面的`a`为准,即使后面的`a`没有值或被省略,也是以其为准。
+上面代码中,函数`f()`有两个参数,且参数名都是`a`。取值的时候,以后面的`a`为准,即使后面的`a`没有值或被省略,也是以其为准。
```javascript
function f(a, a) {
@@ -570,7 +578,7 @@ function f(a, a) {
f(1) // undefined
```
-调用函数`f`的时候,没有提供第二个参数,`a`的取值就变成了`undefined`。这时,如果要获得第一个`a`的值,可以使用`arguments`对象。
+调用函数`f()`的时候,没有提供第二个参数,`a`的取值就变成了`undefined`。这时,如果要获得第一个`a`的值,可以使用`arguments`对象。
```javascript
function f(a, a) {
@@ -613,7 +621,7 @@ var f = function(a, b) {
f(1, 1) // 5
```
-上面代码中,函数`f`调用时传入的参数,在函数内部被修改成`3`和`2`。
+上面代码中,函数`f()`调用时传入的参数,在函数内部被修改成`3`和`2`。
严格模式下,`arguments`对象与函数参数不具有联动关系。也就是说,修改`arguments`对象不会影响到实际的函数参数。
@@ -691,7 +699,7 @@ f1() // 999
上面代码中,函数`f1`可以读取全局变量`n`。
-但是,函数外部无法读取函数内部声明的变量。
+但是,正常情况下,函数外部无法读取函数内部声明的变量。
```javascript
function f1() {
@@ -736,7 +744,7 @@ result(); // 999
闭包就是函数`f2`,即能够读取其他函数内部变量的函数。由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。闭包最大的特点,就是它可以"记住"诞生的环境,比如`f2`记住了它诞生的环境`f1`,所以从`f2`可以得到`f1`的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
-闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。
+闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。
```javascript
function createIncrementor(start) {
@@ -754,7 +762,7 @@ inc() // 7
上面代码中,`start`是函数`createIncrementor`的内部变量。通过闭包,`start`的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包`inc`使得函数`createIncrementor`的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。
-为什么会这样呢?原因就在于`inc`始终在内存中,而`inc`的存在依赖于`createIncrementor`,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
+为什么闭包能够返回外层函数的内部变量?原因是闭包(上例的`inc`)用到了外层变量(`start`),导致外层函数(`createIncrementor`)不能从内存释放。只要闭包没有被垃圾回收机制清除,外层函数提供的运行环境也不会被清除,它的内部变量就始终保存着当前值,供闭包读取。
闭包的另一个用处,是封装对象的私有属性和私有方法。
@@ -786,7 +794,7 @@ p1.getAge() // 25
### 立即调用的函数表达式(IIFE)
-在 JavaScript 中,圆括号`()`是一种运算符,跟在函数名之后,表示调用该函数。比如,`print()`就表示调用`print`函数。
+根据 JavaScript 的语法,圆括号`()`跟在函数名之后,表示调用该函数。比如,`print()`就表示调用`print`函数。
有时,我们需要在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。
@@ -795,7 +803,7 @@ function(){ /* code */ }();
// SyntaxError: Unexpected token (
```
-产生这个错误的原因是,`function`这个关键字即可以当作语句,也可以当作表达式。
+产生这个错误的原因是,`function`这个关键字既可以当作语句,也可以当作表达式。
```javascript
// 语句
@@ -805,9 +813,18 @@ function f() {}
var f = function f() {}
```
-为了避免解析上的歧义,JavaScript 引擎规定,如果`function`关键字出现在行首,一律解释成语句。因此,JavaScript 引擎看到行首是`function`关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
+当作表达式时,函数可以定义后直接加圆括号调用。
+
+```javascript
+var f = function f(){ return 1}();
+f // 1
+```
+
+上面的代码中,函数定义后直接加圆括号调用,没有报错。原因就是`function`作为表达式,引擎就把函数定义当作一个值。这种情况下,就不会报错。
+
+为了避免解析的歧义,JavaScript 规定,如果`function`关键字出现在行首,一律解释成语句。因此,引擎看到行首是`function`关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
-解决方法就是不要让`function`出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
+函数定义后立即调用的解决方法,就是不要让`function`出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
```javascript
(function(){ /* code */ }());
@@ -815,7 +832,7 @@ var f = function f() {}
(function(){ /* code */ })();
```
-上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做"立即调用的函数表达式"(Immediately-Invoked Function Expression),简称 IIFE。
+上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表达式,而不是函数定义语句,所以就避免了错误。这就叫做"立即调用的函数表达式"(Immediately-Invoked Function Expression),简称 IIFE。
注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。
@@ -931,7 +948,7 @@ a // 2
上面代码中,严格模式下,`eval`内部还是改写了外部变量,可见安全风险依然存在。
-总之,`eval`的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,所以一般不推荐使用。通常情况下,`eval`最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的`JSON.parse`方法。
+总之,`eval`的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,一般不推荐使用。通常情况下,`eval`最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的`JSON.parse`方法。
### eval 的别名调用
@@ -975,8 +992,8 @@ window.eval('...')
## 参考链接
- Ben Alman, [Immediately-Invoked Function Expression (IIFE)](http://benalman.com/news/2010/11/immediately-invoked-function-expression/)
-- Mark Daggett, [Functions Explained](http://markdaggett.com/blog/2013/02/15/functions-explained/)
-- Juriy Zaytsev, [Named function expressions demystified](http://kangax.github.com/nfe/)
+- Mark Daggett, [Functions Explained](https://web.archive.org/web/20160911170816/http://markdaggett.com/blog/2013/02/15/functions-explained/)
+- Juriy Zaytsev, [Named function expressions demystified](http://kangax.github.io/nfe/)
- Marco Rogers polotek, [What is the arguments object?](http://docs.nodejitsu.com/articles/javascript-conventions/what-is-the-arguments-object)
- Juriy Zaytsev, [Global eval. What are the options?](http://perfectionkills.com/global-eval-what-are-the-options/)
- Axel Rauschmayer, [Evaluating JavaScript code via eval() and new Function()](http://www.2ality.com/2014/01/eval.html)
diff --git a/docs/types/general.md b/docs/types/general.md
index 3c0ea7e..6d512ef 100644
--- a/docs/types/general.md
+++ b/docs/types/general.md
@@ -2,12 +2,12 @@
## 简介
-JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。(ES6 又新增了第七种 Symbol 类型的值,本教程不涉及。)
+JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。(ES6 又新增了 Symbol 和 BigInt 数据类型,本教程不涉及。)
-- 数值(number):整数和小数(比如`1`和`3.14`)
+- 数值(number):整数和小数(比如`1`和`3.14`)。
- 字符串(string):文本(比如`Hello World`)。
-- 布尔值(boolean):表示真伪的两个特殊值,即`true`(真)和`false`(假)
-- `undefined`:表示"未定义"或不存在,即由于目前没有定义,所以此处暂时没有任何值
+- 布尔值(boolean):表示真伪的两个特殊值,即`true`(真)和`false`(假)。
+- `undefined`:表示"未定义"或不存在,即由于目前没有定义,所以此处暂时没有任何值。
- `null`:表示空值,即此处的值为空。
- 对象(object):各种值组成的集合。
diff --git a/docs/types/null-undefined-boolean.md b/docs/types/null-undefined-boolean.md
index 9a76679..129dda3 100644
--- a/docs/types/null-undefined-boolean.md
+++ b/docs/types/null-undefined-boolean.md
@@ -35,7 +35,7 @@ undefined == null
既然含义与用法都差不多,为什么要同时设置两个这样的值,这不是无端增加复杂度,令初学者困扰吗?这与历史原因有关。
-1995年 JavaScript 诞生时,最初像 Java 一样,只设置了`null`表示"无"。根据 C 语言的传统,`null`可以自动转为`0`。
+1995年 JavaScript 诞生时,最初像 Java 一样,只设置了`null`表示"无"。根据 C 语言的传统,`null`可以自动转为`0`。
```javascript
Number(null) // 0
@@ -46,7 +46,7 @@ Number(null) // 0
但是,JavaScript 的设计者 Brendan Eich,觉得这样做还不够。首先,第一版的 JavaScript 里面,`null`就像在 Java 里一样,被当成一个对象,Brendan Eich 觉得表示"无"的值最好不是对象。其次,那时的 JavaScript 不包括错误处理机制,Brendan Eich 觉得,如果`null`自动转为0,很不容易发现错误。
-因此,他又设计了一个`undefined`。区别是这样的:`null`是一个表示"空"的对象,转为数值时为`0`;`undefined`是一个表示"此处无定义"的原始值,转为数值时为`NaN`。
+因此,他又设计了一个`undefined`。区别是这样的:`null`是一个表示"空"的对象,转为数值时为`0`;`undefined`是一个表示"此处无定义"的原始值,转为数值时为`NaN`。
```javascript
Number(undefined) // NaN
diff --git a/docs/types/number.md b/docs/types/number.md
index af8954e..53ebfb5 100644
--- a/docs/types/number.md
+++ b/docs/types/number.md
@@ -43,7 +43,7 @@ JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整
上面公式是正常情况下(指数部分在0到2047之间),一个数在 JavaScript 内部实际的表示形式。
-精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-2
53到2
53,都可以精确表示。
+精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-2
53 + 1 到 2
53 - 1,都可以精确表示。
```javascript
Math.pow(2, 53)
@@ -280,6 +280,12 @@ NaN * 32 // NaN
NaN / 32 // NaN
```
+但是,ES6 引入指数运算符(`**`)后,出现了一个例外。
+
+```javascript
+NaN ** 0 // 1
+```
+
### Infinity
**(1)含义**
@@ -552,7 +558,15 @@ parseFloat('3.14more non-digit characters') // 3.14
parseFloat('\t\v\r12.34\n ') // 12.34
```
-如果参数不是字符串,或者字符串的第一个字符不能转化为浮点数,则返回`NaN`。
+如果参数不是字符串,则会先转为字符串再转换。
+
+```javascript
+parseFloat([1.23]) // 1.23
+// 等同于
+parseFloat(String([1.23])) // 1.23
+```
+
+如果字符串的第一个字符不能转化为浮点数,则返回`NaN`。
```javascript
parseFloat([]) // NaN
@@ -651,4 +665,4 @@ isFinite(-1) // true
## 参考链接
- Dr. Axel Rauschmayer, [How numbers are encoded in JavaScript](http://www.2ality.com/2012/04/number-encoding.html)
-- Humphry, [JavaScript 中 Number 的一些表示上/下限](http://blog.segmentfault.com/humphry/1190000000407658)
+- Humphry, [JavaScript 中 Number 的一些表示上/下限](https://segmentfault.com/a/1190000000407658)
diff --git a/docs/types/object.md b/docs/types/object.md
index 9e50984..3ff539c 100644
--- a/docs/types/object.md
+++ b/docs/types/object.md
@@ -497,5 +497,5 @@ console.log(temp.p1 + temp.p2);
- Dr. Axel Rauschmayer,[Object properties in JavaScript](http://www.2ality.com/2012/10/javascript-properties.html)
- Lakshan Perera, [Revisiting JavaScript Objects](http://www.laktek.com/2012/12/29/revisiting-javascript-objects/)
-- Angus Croll, [The Secret Life of JavaScript Primitives](http://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/)i
+- Angus Croll, [The Secret Life of JavaScript Primitives](http://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/)
- Dr. Axel Rauschmayer, [JavaScript’s with statement and why it’s deprecated](http://www.2ality.com/2011/06/with-statement.html)
diff --git a/package.json b/package.json
index a7eeda3..479b7bb 100644
--- a/package.json
+++ b/package.json
@@ -11,13 +11,9 @@
"build-and-commit": "npm run build && npm run commit",
"commit": "gh-pages --dist dist --dest dist/javascript --branch master --add --repo git@github.com:wangdoc/website.git",
"chapter": "loppo chapter",
+ "server": "loppo server",
"test": "echo \"Error: no test specified\" && exit 1"
},
- "husky": {
- "hooks": {
- "pre-push": "npm update"
- }
- },
"repository": {
"type": "git",
"url": "git+https://github.com/wangdoc/javascript-tutorial.git"
@@ -34,9 +30,8 @@
},
"homepage": "https://github.com/wangdoc/javascript-tutorial",
"dependencies": {
- "gh-pages": "latest",
- "husky": "^3.0.5",
- "loppo": "latest",
- "loppo-theme-wangdoc": "latest"
+ "gh-pages": "6.x",
+ "loppo": "^0.6.24",
+ "loppo-theme-wangdoc": "^0.7.3"
}
}