{"action":"reopened","issue":{"url":"https://api.github.com/repos/A1546488968/Blog/issues/1","repository_url":"https://api.github.com/repos/A1546488968/Blog","labels_url":"https://api.github.com/repos/A1546488968/Blog/issues/1/labels{/name}","comments_url":"https://api.github.com/repos/A1546488968/Blog/issues/1/comments","events_url":"https://api.github.com/repos/A1546488968/Blog/issues/1/events","html_url":"https://github.com/A1546488968/Blog/issues/1","id":324698571,"number":1,"title":"Python入门（一）——Hello World","user":{"login":"A1546488968","id":7280980,"avatar_url":"https://avatars3.githubusercontent.com/u/7280980?v=4","gravatar_id":"","url":"https://api.github.com/users/A1546488968","html_url":"https://github.com/A1546488968","followers_url":"https://api.github.com/users/A1546488968/followers","following_url":"https://api.github.com/users/A1546488968/following{/other_user}","gists_url":"https://api.github.com/users/A1546488968/gists{/gist_id}","starred_url":"https://api.github.com/users/A1546488968/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/A1546488968/subscriptions","organizations_url":"https://api.github.com/users/A1546488968/orgs","repos_url":"https://api.github.com/users/A1546488968/repos","events_url":"https://api.github.com/users/A1546488968/events{/privacy}","received_events_url":"https://api.github.com/users/A1546488968/received_events","type":"User","site_admin":false},"labels":[{"id":938839103,"url":"https://api.github.com/repos/A1546488968/Blog/labels/Python","name":"Python","color":"1fabaf","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-05-20T10:19:41Z","updated_at":"2018-05-20T10:26:56Z","closed_at":null,"author_association":"OWNER","body":"# 函数\r\n\r\n## 函数的定义\r\n\r\nJS函数是一种对象，即函数对象，函数名视为指向该函数的变量。JS函数没有return语句，结果则是undefined，即隐含了`return undefined;`。\r\n\r\n函数定义的两种方式如下：\r\n\r\n``` JavaScript\r\nfunction fun1(x) {\r\n    return x * x + 1;\r\n}\r\nvar fun2 = function (x) {\r\n    return x * x + 1;\r\n}\r\n```\r\n\r\n第一种方式和Java等语言一样的风格。第二种方式中，`function (x) {...}`是一个匿名函数，即没有函数名，该匿名函数赋值给了变量fun2，于是可以通过fun2调用该匿名函数。\r\n\r\n两种方式完全等价，注意第二种方式需要在最后加上`;`，表示赋值语句结束。\r\n\r\n## 函数的调用\r\n\r\n调用函数时，按顺序传入参数即可。**JS中，传入任意多个参数都不影响调用，传入过多，内部参数不需要，过少时不影响调用，但是结果可能不对**。如上面的fun1函数，不传入参数时，**x将收到undefined**，于是结果是`NaN`。可以**对输入参数检查**。\r\n\r\n``` JavaScript\r\nfunction fun1(x) {\r\n    if (typeof x != 'number') {\r\n        throw 'Not a number';\r\n    }\r\n    return x * x + 1;\r\n}\r\n```\r\n\r\n## arguments关键字\r\n\r\n只在函数内部起作用，永远指向**当前函数调用者传入的**所有参数，类似于数组，但不是数组。\r\n\r\n``` JavaScript\r\nfunction fun1() {\r\n    for (var x = 0; x < arguments.length; x++) {\r\n        alert(arguments[x]);\r\n    }\r\n}\r\nfun1(1, 2, 3);\r\n```\r\n\r\narguments永远获取到调用者传入的所有参数，即使函数不声明任何参数。arguments常常用于获取调用者传入的所有参数的个数，进行后续处理。\r\n\r\n## rest参数\r\n\r\nES6引入了`rest`参数，以数组形式接收后续不要的参数，如果没有多余的参数，rest是空数组[]，而不是undefined。rest只能写在最后面，前面有`...`标识。\r\n\r\n``` JavaScript\r\nfunction fun1(a, b, ...rest) {\r\n    alert('a = ' + a);\r\n    alert('b = ' + b);\r\n    alert(rest);\r\n}\r\nfun1(1, 2, 3, 4, 5, NaN);\r\nfun1(1);    // b是undefined，rest是空数组[]\r\n```\r\n\r\n## return语句的陷阱\r\n\r\n``` JavaScript\r\nfunction fun1() {\r\n    return {name: 'Hello'};\r\n}\r\nalert(fun1());  // [object Object]\r\n\r\n// 假如分成两行写\r\nfunction fun2() {\r\n    return              // JS自动加上分号\r\n    {name: 'Hello'};    // 该句无法执行到\r\n}\r\nalert(fun2());  // undefined\r\n\r\nfunction fun3() {\r\n    return {            // 不会自动加分号，因为{表示块语句未结束\r\n        name: 'Hello'\r\n    };\r\n}\r\nalert(fun3());  // [object Object]\r\n```\r\n\r\n## 变量的作用域\r\n\r\nJS中使用`var`声明的变量不是全局变量，自然拥有有限的作用域。\r\n\r\n如果在函数体内用`var`声明变量，则其作用域为整个函数体，在函数体外引用该变量将引发错误。由于作用域限制在该函数体内，因此，不同函数体内的同名变量互不影响，相互独立。\r\n\r\n``` JavaScript\r\nfunction fun1() {\r\n    var x = 1;\r\n    x = x + 1;\r\n}\r\nalert(x);   // Uncaught ReferenceError: x is not defined\r\n```\r\n\r\nJS的函数十分灵活，**JS的函数可以嵌套**，内部函数可以访问外部函数内定义的变量，反过来则不行。\r\n\r\n``` JavaScript\r\nfunction fun1() {\r\n    var x = 1;\r\n    x = x + 1;\r\n    function fun2() {\r\n        var y = x + 1;\r\n        y = y + 1;\r\n        alert(y);\r\n    }\r\n    fun2();         // 调用内部函数\r\n    var z = y + 1;  // Uncaught ReferenceError: y is not defined\r\n}\r\nfun1();     // 需要调用才会出错\r\n```\r\n\r\n当内部函数和外部函数出现同名的变量时，JS函数寻找变量将从内部函数开始，从内向外开始查找，如果出现同名变量，内部的将屏蔽外部的变量。\r\n\r\n``` JavaScript\r\nfunction fun1() {\r\n    var x = 1;\r\n    function fun2() {\r\n        var x = 'a';\r\n        alert(x);   // a\r\n    }\r\n    fun2();\r\n    alert(x);       // 1\r\n}\r\nfun1();\r\n```\r\n\r\n### 变量的提升\r\n\r\n**JS函数的一个特别之处在于，会扫描整个函数体的语句，将所有声明的变量提升到函数顶部。注意是仅仅提升变量的声明，不会提升变量的赋值**。\r\n\r\n``` JavaScript\r\n'use strict';\r\nfunction fun1() {\r\n    var x = 'Hello ' + y;\r\n    alert(x);   // Hello undefined\r\n    var y = 'Tim';\r\n}\r\nfun1();\r\n```\r\n\r\n上面代码相当于：\r\n\r\n``` JavaScript\r\n'use strict';\r\nfunction fun1() {\r\n    var y;\r\n    var x = 'Hello ' + y;\r\n    alert(x);   // Hello undefined\r\n    y = 'Tim';\r\n}\r\nfun1();\r\n```\r\n\r\n由于JS的这种怪异的特性，我们在函数内定义变量，一定遵守**在函数内部首先声明所有变量**。一个最常见的做法就是使用一个`var`声明函数内用到的所有变量。\r\n\r\n``` JavaScript\r\nvar \r\n    x = 1;\r\n    y = x + 1;\r\n    z, i;\r\nfor (i = 0; i < 100; i++) {\r\n    \r\n}\r\n```\r\n\r\n### 全局作用域\r\n\r\n不在任何函数内定义的变量就拥有全局作用域。JS有个默认的全局对象`window`，而所有的全局作用域的变量都被绑定到这个`window`的一个个属性。\r\n\r\n``` JavaScript\r\nvar greet = 'Hello Tim!';\r\nalert(greet);\r\nalert(window.greet);    // 上面两行等价\r\n```\r\n\r\n在函数外以`var fun = function () {}`形式声明的变量fun也是全局变量，也绑定到全局对象`window`上。\r\n\r\n``` JavaScript\r\nvar fun = function () { alert('Hello') };\r\nfun();\r\nwindow.fun();    // 上面两行等价\r\n```\r\n\r\n我们前面使用很多次的`alert()`函数从未定义过，而且是直接调用的，实际上，它也是window的属性，即`window.alert`，因此也可以随意更改该属性！\r\n\r\n``` JavaScript\r\nwindow.alert('Hello');\r\nvar old_alert = window.alert;\r\nwindow.alert = function () {  };\r\nwindow.alert();\r\nwindow.alert = old_alert;\r\nwindow.alert();\r\n```\r\n\r\n**JS只有一个全局作用域，任何变量（函数也是一种变量）会从当前函数作用域查找，没找到就继续往上查找，最后再在全局作用域中查找，如果也没找到，就报错ReferenceError**。\r\n\r\n### 名字空间\r\n\r\n全局作用域只有一个，并且全局变量（包括全局函数）都绑定到`window`对象中，因此，不同JS文件中定义了同名的全局变量（包括全局函数）会造成命名冲突且难以调试发现错误。\r\n\r\n减少冲突的一个有效方法是建立一个命名空间，即把自己的所有变量和函数都全部绑定到一个全局变量中去。像`jQuery`和`underscore`等JS库就是这么做的。\r\n\r\n``` JavaScript\r\n'use strict';\r\nvar TIM = {};\r\nTIM.name = 'Hello';\r\nTIM.version = 1.0;\r\nTIM.fun1 = function () {\r\n    return TIM.name;\r\n}\r\nalert(TIM.fun1());\r\n```\r\n\r\n### let与块级作用域\r\n\r\n由于JS变量提升的原因，JS的非全局变量作用域就在函数内部，而不能在`for`循环中定义出`for`循环语句块内部的局部作用域。即不存在`块级作用域`。\r\n\r\n``` JavaScript\r\nfunction fun() {\r\n    for (var i = 0; i < 2; i++) {\r\n        alert(i);\r\n    }\r\n    alert(i);   // 2，不报错\r\n}\r\nfun();\r\n```\r\n\r\nES6中引入新的关键字`let`，从而可以声明块级作用域的变量。\r\n\r\n``` JavaScript\r\nfunction fun() {\r\n    for (let i = 0; i < 2; i++) {\r\n        alert(i);\r\n    }\r\n    alert(i);   // Uncaught ReferenceError: i is not defined\r\n}\r\nfun();\r\n```\r\n\r\n### const与常量\r\n\r\n`let`和`var`都是声明变量，在ES6之前，我们使用全部大写的字母形式化地表示某个“变量”是常量，请不要去修改。ES6引入了`const`关键字来表示常量，和`let`一样，也支持块级作用域。和以前一样，我们依然采用全部大写的方式声明常量。\r\n\r\n``` JavaScript\r\nconst PI = 3.1415926;\r\nPI += 1;   // Uncaught TypeError: Assignment to constant variable.（据说，某些浏览器不报错，但是无效果！？）\r\n```\r\n\r\n## 方法\r\n\r\n为对象绑定的函数就称之为方法。在方法内部可以使用关键字`this`，它始终指向当前对象，因此可以使用`this`访问对象内部的属性。\r\n\r\n``` JavaScript\r\n var person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n    greeting: function () {\r\n        return 'My name is ' + this.name + ', I am ' + this.age;\r\n    }\r\n}\r\nalert(person.greeting);     // 打印函数全文\r\nalert(person.greeting());   // 打印函数返回值\r\n```\r\n\r\n如果把greeting函数抽取出来做成全局函数，此时的this指向全局对象，即`window`。\r\n\r\n``` JavaScript\r\nfunction greeting() {\r\n    return 'My name is ' + this.name + ', I am ' + this.age;\r\n}\r\nvar person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n}\r\nalert(window.name);     // 空字符\r\nalert(greeting());      // My name is , I am undefined.\r\n```\r\n\r\n下面的写法也不能保证this指向正确。必须使用`obj.fun()`形式调用！不能将对象的方法传递给其他变量来间接调用。\r\n\r\n``` JavaScript\r\nvar person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n    greeting: function () {\r\n        return 'My name is ' + this.name + ', I am ' + this.age;\r\n    }\r\n}\r\nvar fun = person.greeting;\r\nalert(fun());   // My name is , I am undefined. this指向依然是window！\r\n```\r\n\r\n这是一个设计错误，ECMA决定在strict模式下，将函数的this指向`undefined`，从而让错误及时暴露出来。但this的指向问题木有解决。\r\n\r\n``` JavaScript\r\n'use strict';\r\nvar person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n    greeting: function () {\r\n        return 'My name is ' + this.name + ', I am ' + this.age;\r\n    }\r\n}\r\nvar fun = person.greeting;\r\nalert(fun());   // Uncaught TypeError: Cannot read property 'name' of undefined\r\n```\r\n\r\n同样，**在对象方法的内部函数（匿名函数）中使用this也会出现问题，非strict模式下this指向window，strict模式下指向undefined**！\r\n\r\n``` JavaScript\r\n'use strict';\r\nvar person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n    greeting: function () {\r\n        function fun() {\r\n            return 'My name is ' + this.name + ', I am ' + this.age;\r\n        }\r\n        return fun();   // TypeError: Cannot read property 'name' of undefined\r\n    }\r\n}\r\nalert(person.greeting());\r\n```\r\n\r\n### var that = this;\r\n\r\n修复对象方法内部函数使用this的问题的一个办法是首先使用一个变量（比如`that`）去捕获`this`。这样就可以大胆地在方法内部定义各种内部函数，而不用都直接写在方法里面。\r\n\r\n``` JavaScript\r\n'use strict';\r\nvar person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n    greeting: function () {\r\n        var that = this;\r\n        function fun() {\r\n            return 'My name is ' + that.name + ', I am ' + that.age;\r\n        }\r\n        return fun();\r\n    }\r\n}\r\nalert(person.greeting());\r\n```\r\n\r\n### apply()和call()\r\n\r\n在非对象方法的函数中，this要么指向window要么指向undefined。我们还可以使用函数自带的`apply`方法指定this指向哪个对象。第一个参数是this要指向的对象，第二个是函数需要的参数数组。`call`与`apply`唯一的区别是`call`是按顺序传入参数而不是直接传入数组。\r\n\r\n``` JavaScript\r\n'use strict';\r\nfunction greeting() {\r\n    return 'My name is ' + this.name + ', I am ' + this.age;\r\n}\r\nvar person = {\r\n    name: 'Tim',\r\n    age: 22,\r\n}\r\nalert(greeting.apply(person, []));\r\nalert(greeting.call(person));\r\n```\r\n\r\n普通的函数改成`apply`或者`call`模式，往往把`this`绑定为`null`。\r\n\r\n``` JavaScript\r\n'use strict';\r\nalert(Math.max(2, 3, 6));\r\nalert(Math.max.apply(null, [2, 3, 6]));\r\nalert(Math.max.call(null, 2, 3, 6));\r\n```\r\n\r\n### 装饰器\r\n\r\n使用`apply()`方法，可以改变函数的行为，甚至是内置的函数，毕竟JS所有对象都是动态的。比如下面的代码统计调用`parseInt`的次数。【没看懂！】\r\n\r\n``` JavaScript\r\n'use strict';\r\nvar count = 0;\r\nvar old_parseInt = window.parseInt;\r\nwindow.parseInt = function () {\r\n    count += 1;\r\n    // return old_parseInt(arguments);  // 报错未知？\r\n    return old_parseInt.apply(null, arguments);   // 没看懂…\r\n}\r\nalert(parseInt('1'));\r\nalert(parseInt('2'));\r\nalert(parseInt('3'));\r\nalert(parseInt('4'));\r\nalert(count);\r\n```\r\n\r\n貌似下面的代码也行。\r\n\r\n``` JavaScript \r\n'use strict';\r\nvar count = 0;\r\nvar old_parseInt = window.parseInt;\r\nwindow.parseInt = function (x) {\r\n    count++;\r\n    return old_parseInt(x);\r\n}\r\nalert(parseInt('1'));\r\nalert(parseInt('2'));\r\nalert(parseInt('3'));\r\nalert(parseInt('4'));\r\nalert(count);\r\n```\r\n\r\n## 高阶函数\r\n\r\n即Higher-order function。JS中函数名就相当于变量，指向一个函数。如果函数接收另外一个函数作为参数，就是高阶函数。\r\n\r\n``` JavaScript\r\nfunction add(f, a, b) {\r\n    return f(a) + f(b);\r\n}\r\nvar f = Math.abs;\r\nalert(add(f, 2, -3));\r\n```\r\n\r\n### map和reduce\r\n\r\n`map`是数组`Array`的方法，传入一个函数作为参数，就能对数组内每个元素执行该函数，并返回新的数组。因此`map`就是一个高阶函数。\r\n\r\n``` JavaScript\r\nfunction square(x) {\r\n    return x * x;\r\n}\r\nvar arr = [1, -2, 3.3, 4];\r\nvar ret = arr.map(square);\r\nalert(ret);\r\n\r\nvar arr = [1, -2, 3.3, 4];\r\nalert(arr.map(String));     // 全部转成字符串\r\n\r\nvar arr = ['1', '-2', '3', '4'];\r\nalert(arr.map(function (x) {\r\n    return parseInt(x);\r\n}));     // 全部转成Number，不要直接写成arr.map(parseInt)，避免parseInt传入两个参数导致NaN\r\n```\r\n\r\n`reduce`也是数组`Array`的方法，该方法传入一个函数作为参数，该函数接收数组中前后2个元素，运算后的结果与下一个元素作为函数的参数继续运算下去。类似于：`[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4);`。\r\n\r\n``` JavaScript\r\nvar arr = [1, 2, 3, 4, 5];\r\nvar sum = arr.reduce(function (x, y) {\r\n    return x + y;\r\n});\r\nalert(sum);\r\n```\r\n\r\n### filter\r\n\r\n`filter`也是数组`Array`的方法，该方法传入一个函数作为参数，该函数接收数组中每个元素，返回true或者false，true则保留，false则移除，最终返回新数组。\r\n\r\n```JavaScript\r\nvar arr = [1, 2, 3, 4, 5, 6, 7];\r\nvar ret = arr.filter(function (x) {\r\n    return x % 2 != 0;\r\n});\r\nalert(ret);\r\n\r\nvar arr2 = ['1', '', '  ', \" \", ' ', \"   \"];\r\nvar ret2 = arr2.filter(function (s){\r\n    return s && s.trim();   // 注意：IE9以下没有trim()方法\r\n});\r\nalert(ret2);\r\n```\r\n\r\n`filter`接收的函数（即回调函数）其实有多个参数，通常仅仅使用第一个参数element，即数组的特定元素。另外两个参数是元素的索引index和数组本身self。下面的代码巧妙的去除数组的重复元素。\r\n\r\n``` JavaScript\r\nvar arr = [1, 2, 1, 3, 2, 5, 5, '1', '1', '2', '3', '3'];\r\nvar ret = arr.filter(function (element, index, self) {\r\n    return self.indexOf(element) === index;\r\n});\r\nalert(ret);\r\n```\r\n\r\n### sort\r\n\r\n数组自带了排序方法，比较**坑**的是该方法**先把数组元素转成字符串在按照字典顺序排序**。**所以千万不要用数组自带的sort来对数字排序！**。\r\n\r\n``` JavaScript\r\nalert(['hello', 'Tim', 'aaa'].sort());  // 'Tim' 'aaa' 'helo'\r\nalert([10, 2, 34, 1, 20].sort());       // 1 10 2 20 34\r\n```\r\n\r\nsort也是高阶函数，可以传入自定义比较函数，注意，返回值是确定的，即等于返回0，小于返回-1，否则返回1。\r\n\r\n**注意，sort是对原数组操作，返回的数组依然是原数组！**\r\n\r\n``` JavaScript\r\nvar arr = [10, 2, 34, 1, 20];\r\nvar ret = arr.sort(function (x, y) {\r\n    if (x == y) {\r\n        return 0;\r\n    } else if (x < y) {\r\n        return -1;\r\n    } else {\r\n        return 1;\r\n    }\r\n});\r\nalert(ret);\r\nalert(arr);     // 注意sort是在原数组操作！\r\n```\r\n\r\n可以看出，高阶函数的抽象能力十分强大，可以保证核心代码十分简洁。\r\n\r\n# 闭包\r\n\r\n## 函数作为返回值\r\n\r\n高阶函数除了可以把函数作为参数，也可以把函数作为返回值。下面的sum函数传入数组后将立即进行求和运算。\r\n\r\n``` JavaScript\r\nfunction sum(arr) {\r\n    return arr.reduce(function (x, y) {\r\n        return x + y;\r\n    });\r\n}\r\nvar arr = [1, 2, 3, 4, 5];\r\nalert(sum(arr));\r\n```\r\n\r\n如果无需立即计算，而是等到需要的时候再去计算，我们可以不返回计算结果，而是返回计算的函数！最后在需要的时候调用返回的函数完成计算。\r\n\r\n``` JavaScript\r\nfunction lazy_sum(arr) {\r\n    var sum = function () {\r\n        return arr.reduce(function (x, y) {\r\n            return x + y;\r\n        });\r\n    }\r\n    return sum;\r\n}\r\nvar arr = [1, 2, 3, 4, 5];\r\nvar f = lazy_sum(arr);\r\nvar g = lazy_sum(arr);\r\nalert(f());\r\nalert(g());\r\nalert(f === g);     // false！\r\n```\r\n\r\n为了返回函数，我们必须在函数内部定义内部函数，定义的内部函数访问了外部函数的参数和局部变量，当函数返回函数时，返回的函数里面包含了外部函数的参数和局部变量，这样的结构就是“**闭包**”。\r\n\r\n还可以看到，每次返回的函数都是新的函数，即使传入参数一样，即两者互不影响。\r\n\r\n## 使用闭包\r\n\r\n当函数返回函数后，其内部的变量还被新的函数引用！并且，返回的函数没有立即执行，直到被调用。\r\n\r\n注意下面的结果都是16，因为数组内的三个函数都引用了局部变量`i`，当3个函数执行前一刻，`i`值已经变成了4！所以，使用闭包记住一点：**返回函数不要引用任何循环变量，或者后续会发生变化的量**。\r\n\r\n``` JavaScript\r\nfunction count() {\r\n    var arr = [];\r\n    for (var i = 1; i <= 3; i++) {\r\n        arr.push(function () {\r\n            return i * i;\r\n        });\r\n    }\r\n    return arr;\r\n}\r\nvar ret = count();\r\nvar f1 = ret[0];\r\nvar f2 = ret[1];\r\nvar f3 = ret[2];\r\nalert(f1());    // 16\r\nalert(f2());    // 16\r\nalert(f3());    // 16\r\n```\r\n\r\n**如果一定要引用循环变量，方法是再创建一个函数（包装原有函数），将该函数参数设置为要引用的循环变量，这样无论循环变量如何变化，已经绑定的参数不再变化。**\r\n\r\n``` JavaScript\r\nfunction count() {\r\n    var arr = [];\r\n    for (var i = 1; i <= 3; i++) {\r\n        arr.push((function (n) {\r\n            return function () {\r\n                return n * n;\r\n            }\r\n        })(i));\r\n    }\r\n    return arr;\r\n}\r\nvar ret = count();\r\nvar f1 = ret[0];\r\nvar f2 = ret[1];\r\nvar f3 = ret[2];\r\nalert(f1());    // 1\r\nalert(f2());    // 4\r\nalert(f3());    // 9\r\n```\r\n\r\n上面用到了**创建一个匿名函数并立即执行**的用法。如下\r\n\r\n``` JavaScript\r\n(function (n) {\r\n    return function () {\r\n        return n * n;\r\n    }\r\n})(i);\r\n```\r\n注意匿名函数需要`()`包围，否则报SyntaxError。\r\n\r\n因此可以看出，**闭包可以返回一个函数并且延迟执行**。\r\n\r\n**闭包还可以像面向对象语言那样封装私有变量**。下面的代码创建了一个计时器。该闭包引用了局部变量`x`，外部无法访问该变量。即**闭包是携带状态的函数，并且该状态可以完全对外隐藏起来**。\r\n\r\n``` JavaScript\r\nfunction createCounter(inital) {\r\n    var x = inital || 0;\r\n    return {\r\n        inc: function () {\r\n            x++;\r\n            return x;\r\n        }\r\n    };\r\n}\r\nvar c1 = createCounter();\r\nalert(c1.inc());\r\nalert(c1.inc());\r\nalert(c1.inc());\r\n\r\nvar c2 = createCounter(10);\r\nalert(c2.inc());\r\nalert(c2.inc());\r\n```\r\n\r\n**闭包还可以把多参数函数变成单参数函数**。\r\n\r\n``` JavaScript\r\nfunction make_pow(n) {\r\n    return function (x) {\r\n        return Math.pow(x, n);\r\n    }\r\n}\r\nvar pow2 = make_pow(2);     // 新的单参数函数\r\nvar pow3 = make_pow(3);     // 新的单参数函数\r\nalert(pow2(3));\r\nalert(pow3(3));\r\n```\r\n\r\n## 箭头函数\r\n\r\nES6提供了一种新的函数，箭头函数，顾名思义，该函数的定义采用了`=>`的形式。\r\n\r\n箭头函数就是匿名函数，可以简洁地定义函数。当函数只有一条语句，可以`{}`和return语句省略；否则，需要`{}`和return语句。如果需要多个输入参数，则需要用`()`括起来。\r\n\r\n``` JavaScript\r\nvar f = x => x * x;\r\nalert(f(3));\r\n\r\nvar g = x => { \r\n    if (x < 0) {\r\n        return -x;\r\n    } else {\r\n        return x;\r\n    }\r\n};\r\nalert(g(-2));\r\n\r\nvar h = (x, y) => x + y;\r\nalert(h(2, -3));\r\n\r\n// 无参数的情况\r\nvar j = () => 100;\r\nalert(j());\r\n\r\n// 可变参数\r\nvar k = (x, y, ...rest) => {\r\n    var sum = x + y;\r\n    for (var i in rest) {\r\n        sum += rest[i];     // 注意i是字符串形式的索引\r\n    }\r\n    return sum;\r\n};\r\nalert(k(1, 2, 3, 4, 5));\r\n```\r\n\r\n如果要返回对象，并且是单行表达式，不能写成`var f = x => {name: 'Tim'};`。正确写法如下：\r\n\r\n``` JavaScript\r\nvar f = x => ({name: 'Tim'});\r\nalert(f());\r\n```\r\n\r\n## 箭头函数与this\r\n\r\n箭头函数与匿名函数的一个区别是，箭头函数的this是词法作用域，由上下文确定。\r\n\r\n``` JavaScript\r\nvar obj = {\r\n    name: 'Tim',\r\n    print: function () {\r\n        var f = function () {\r\n            return 'I am' + this.name;  // 方法内部的函数即匿名函数里面的this指向window或undefined！\r\n        }\r\n        return f();\r\n    }\r\n};\r\nalert(obj.print());\r\n```\r\n\r\n上面可以使用`var that = this`来修复。而使用箭头函数则修复了JS的错误，**this始终指向词法作用域，即外层的调用者——`obj`***！\r\n\r\n``` JavaScript\r\nvar obj = {\r\n    name: 'Tim',\r\n    print: function () {\r\n        var f = () => 'I am' + this.name;  // 箭头函数里面的this始终指向调用者obj！\r\n        return f();\r\n    }\r\n};\r\nalert(obj.print());\r\n```\r\n\r\n由于this在箭头函数中已经根据词法作用域绑定到了调用者上，因此使用`call()`或者`apply()`时无法对this进行绑定，即第一个参数将无效。\r\n\r\n``` JavaScript\r\nvar obj = {\r\n    name: 'Tim',\r\n    print: function (greet) {\r\n        var f = () => greet + ', I am ' + this.name;\r\n        return f.apply({name: 'Jim'});\r\n    }\r\n};\r\nalert(obj.print('Hello'));  // Hello, I am Tim\r\n```\r\n\r\n## generator\r\n\r\ngenerator是ES6引入的新数据类型，思想借鉴自python的generator。generator与函数类似，由`function*`定义，除了使用return返回，还可以使用`yeild`返回多次。\r\n\r\n下面是一个斐波那契数列生成函数，返回一个数组。\r\n\r\n``` JavaScript\r\nfunction fib(n) {\r\n    var t,\r\n        a = 0,\r\n        b = 1,\r\n        arr = [0, 1];\r\n    while (arr.length < n) {\r\n        t = a + b;\r\n        a = b;\r\n        b = t;\r\n        arr.push(t);\r\n    }\r\n    return arr;\r\n}\r\nalert(fib(5));\r\nalert(fib(10));\r\n```\r\n\r\ngenerator的写法如下：\r\n\r\n``` JavaScript\r\nfunction* fib(n) {\r\n    var t,\r\n        a = 0,\r\n        b = 1,\r\n        count = 1;\r\n    while (count < n) {\r\n        yield a;\r\n        t = a + b;\r\n        a = b;\r\n        b = t;\r\n        count++;\r\n    }\r\n    return a;\r\n}\r\nalert(fib(5));  // [object Generator]\r\n```\r\n\r\n`fib(5)`仅仅创建了一个generator对象，还没有去执行。有两种调用方法：一是不断调用它的next()，next()将会执行该genarator，然后每次遇到`yeild a;`就会返回一个对象`{value: x, done: true/false}`，value表示返回值，done表示该generator是否执行完，当done为true，则value是return的返回值，无需再调用next()。\r\n\r\n二是使用`for ... of`循环，这样无需自己判断done了。【貌似少打了最后一个？】\r\n\r\n``` JavaScript\r\nfunction* fib(n) {\r\n    var t,\r\n        a = 0,\r\n        b = 1,\r\n        count = 1;\r\n    while (count < n) {\r\n        yield a;\r\n        t = a + b;\r\n        a = b;\r\n        b = t;\r\n        count++;\r\n    }\r\n    return a;\r\n}\r\nvar f = fib(5);\r\nalert(f.next().value);\r\nalert(f.next().value);\r\nalert(f.next().value);\r\nalert(f.next().value);\r\nalert(f.next().value);\r\n\r\nvar g = fib(5);\r\nfor (var i of fib(5)) {\r\n    alert(i);   // 0 1 1 2 少打了3？\r\n}\r\n```\r\n\r\ngenerator可以多次返回，可以看成是记住状态的函数，因此可以实现面向对象才能实现的状态保存功能。\r\n\r\n还可以把异步回调代码变成“同步”代码。例如后面的AJAX。\r\n\r\n# 错误处理\r\n\r\nJS执行时有2种错误。一种是程序逻辑错误导致的，需要修复程序的bug。另一种是程序遇到某些无法预测的异常，如网络连接中断，文件不存在，没有操作权限。这种错误需要处理并反馈给客户。\r\n\r\n处理的方法通常是`try ... catch ... finally`。\r\n\r\ncatch和finally可以不必都出现。因此`try ... catch ... finally`有三种形式：\r\n\r\n- `try ... catch ... finally`\r\n- `try ... catch`\r\n- `try ... finally`\r\n\r\n## try ... catch ... finally的执行流程。\r\n\r\n有错误发生时，执行流程像这样：\r\n\r\n- 先执行`try { ... }`的代码；\r\n- 执行到出错的语句时，后续语句不再继续执行，转而执行`catch (e) { ... }`代码；\r\n- 最后执行`finally { ... }`代码。\r\n\r\n而没有错误发生时，执行流程像这样：\r\n\r\n- 先执行`try { ... }`的代码；\r\n- 因为没有出错，`catch (e) { ... }`代码不会被执行；\r\n- 最后执行`finally { ... }`代码。\r\n\r\n``` JavaScript\r\n<html>\r\n<head>\r\n    <script src=\"jquery-2.2.4.js\"></script>\r\n</head>\r\n<body>\r\n    \r\n</body>\r\n<script>\r\n    var a1, a2, s = null;\r\n    try {\r\n        a1 = s.length;\r\n        a2 = 100;\r\n    } catch (e) {\r\n        alert('ERROR: ' + e);   // ERROR: TypeError: Cannot read property 'length' of null\r\n    } finally {\r\n        alert('finally code...');\r\n    }\r\n    alert(a1);  // undefined\r\n    alert(a2);  // undefined\r\n</script>\r\n</html>\r\n```\r\n\r\n## 错误类型\r\n\r\nJS的`Error`对象表示错误，派生的有TypeError、ReferenceError等错误对象。在处理错误时，可以通过catch(e)捕获的变量e访问错误对象，如对e是什么类型的错误对象分别处理，类似于`if (e instanceof TypeError) {}`。\r\n\r\n## 抛出错误\r\n\r\n程序可以直接抛出`throw`一个错误，代码将直接执行到catch块。\r\n\r\n``` JavaScript\r\n<html>\r\n<head>\r\n    <script src=\"jquery-2.2.4.js\"></script>\r\n</head>\r\n<body>\r\n    \r\n</body>\r\n<script>\r\n    var s, n, r;\r\n    try {\r\n        s = prompt('请输入一个数字：');\r\n        n = parseInt(s);\r\n        if (isNaN(n)) {\r\n            throw new Error('输入的不是数字');\r\n        }\r\n        r = n * n;\r\n        alert(r);\r\n    } catch (e) {\r\n        alert('ERROR: ' + e);\r\n    }\r\n</script>\r\n</html>\r\n```\r\n\r\n实际上，JS允许抛出任意对象，包括数字、字符串。但是，最好还是抛出一个Error对象。\r\n\r\n当我们用catch捕获错误时，一定要编写错误处理语句，哪怕仅仅把错误打印出来，也不要什么也不干，因为catch到错误却什么都不执行，就不知道程序执行过程中到底有没有发生错误。\r\n\r\n## 错误传播\r\n\r\n如果函数内部发生了错误，它自身没有捕获，错误就会被抛到外层调用函数，如果外层函数也没有捕获，该错误会一直沿着函数调用链向上抛出，直到被JS引擎捕获，代码终止执行。\r\n\r\n不必在每一个函数内部捕获错误，只需要在合适的地方来个统一捕获，一网打尽。\r\n\r\n## 异步错误处理\r\n\r\n**需要时刻牢记，JS引擎是一个事件驱动的执行引擎，代码总是以单线程执行，而回调函数的执行需要等到下一个满足条件的事件出现后，才会被执行。**。\r\n\r\n例如下面的函数，会先打印done，1s后才打印出It is time!。\r\n\r\n``` JavaScript\r\nfunction printTime() {\r\n    alert('It is time!');\r\n}\r\nsetTimeout(printTime, 1000);\r\nalert('done');\r\n```\r\n\r\n需要注意地是，如果printTime函数发生了错误，试图用`try ... catch`包裹调用者setTimeout函数是无法“catch”住错误的。上面的程序控制台报错：Uncaught Error。\r\n\r\n这是因为调用setTimeout()函数时，传入的printTime函数并未立刻执行！紧接着，JS引擎会继续执行alert('done');语句，而此时并没有错误发生。直到1秒钟后，执行printTime函数时才发生错误，但此时**除了在printTime函数内部捕获错误外，外层代码并无法捕获**。\r\n\r\n所以，**涉及到异步代码，无法在调用时捕获，原因就是在捕获的当时，回调函数并未执行**。（采用回调！）\r\n\r\n类似的，**当我们处理一个事件时，在绑定事件的代码处，无法捕获事件处理函数的错误**。\r\n\r\n``` JavaScript\r\n<html>\r\n<head>\r\n    <script src=\"jquery-2.2.4.js\"></script>\r\n</head>\r\n<body>\r\n    <form>\r\n        <input type=\"text\" id=\"x\"> \r\n        <input type=\"text\" id=\"y\"> \r\n        <button id=\"calc\" type=\"button\">计算</button>\r\n    </form>\r\n</body>\r\n<script>\r\n    var btn = $('#calc');\r\n\r\n    // 控制台报错：Uncaught Error: 输入有误！\r\n    // 不能在绑定事件的代码处试图捕获事件处理函数的错误。\r\n    try {\r\n        btn.click(function () {\r\n            var x = parseFloat($('#x').val()),\r\n            y = parseFloat($('#y').val()),\r\n            r;\r\n\r\n            if (isNaN(x) || isNaN(y)) {\r\n                throw new Error('输入有误！');\r\n            }\r\n            r = x + y;\r\n            alert(r);\r\n        });\r\n    } catch (e) {\r\n        alert('输入有误！');\r\n    }\r\n</script>\r\n</html>\r\n```\r\n"}}
{"actor":{"display_login":"A1546488968"}}