{"action":"created","issue":{"url":"https://api.github.com/repos/creeperyang/blog/issues/16","repository_url":"https://api.github.com/repos/creeperyang/blog","labels_url":"https://api.github.com/repos/creeperyang/blog/issues/16/labels{/name}","comments_url":"https://api.github.com/repos/creeperyang/blog/issues/16/comments","events_url":"https://api.github.com/repos/creeperyang/blog/issues/16/events","html_url":"https://github.com/creeperyang/blog/issues/16","id":127858638,"number":16,"title":"深入理解JS中声明提升、作用域（链）和`this`关键字","user":{"login":"creeperyang","id":8046480,"avatar_url":"https://avatars.githubusercontent.com/u/8046480?v=3","gravatar_id":"","url":"https://api.github.com/users/creeperyang","html_url":"https://github.com/creeperyang","followers_url":"https://api.github.com/users/creeperyang/followers","following_url":"https://api.github.com/users/creeperyang/following{/other_user}","gists_url":"https://api.github.com/users/creeperyang/gists{/gist_id}","starred_url":"https://api.github.com/users/creeperyang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/creeperyang/subscriptions","organizations_url":"https://api.github.com/users/creeperyang/orgs","repos_url":"https://api.github.com/users/creeperyang/repos","events_url":"https://api.github.com/users/creeperyang/events{/privacy}","received_events_url":"https://api.github.com/users/creeperyang/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":9,"created_at":"2016-01-21T06:51:49Z","updated_at":"2016-11-10T04:09:24Z","closed_at":null,"body":"这个issue试图阐述JavaScript这门语言的3个难点：**声明提升**、**作用域（链）**和**`this`**。\n\n首先推荐https://github.com/getify/You-Dont-Know-JS，这是一本非常棒的JavaScript书籍，几乎所有的JS知识点都包括并且详细解释了。看一遍相信必有大收获。\n### 1. 声明提升\n\n大部分编程语言都是先声明变量再使用，但在JS中，事情有些不一样：\n\n``` js\nconsole.log(a); // undefined\nvar a = 1;\n```\n\n上面是合法的JS代码，正常输出`undefined`而不是报错`Uncaught ReferenceError: a is not defined`。为什么？就是因为声明提升（hoisting）。\n#### 1.1 变量声明\n\n参考：https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var\n\n**语法：**\n\n``` js\nvar varname1 [= value1 [, varname2 [, varname3 ... [, varnameN]]]];\n```\n\n变量名可以是任意合法标识符；值可以是任意合法表达式。\n\n**重点：**\n- 变量声明，不管在哪里发生（声明），都会在任意代码执行前处理。（Variable declarations, wherever they occur, are processed before any code is executed. ）。\n- 以`var`声明的变量的作用域就是当前执行上下文（**execution context**），即某个函数，或者全局作用域（声明在函数外）。\n- 赋值给未声明的变量，当执行时会隐式创建全局变量（成为global的属性）。\n\n**声明变量和未声明变量的区别**：\n- 声明变量通常是局部的，未声明变量通常全局的。\n- 声明变量在任意代码执行前创建，未声明变量直到赋值时才存在。\n- 声明变量是execution context（function/global）的non-configurable 属性，未声明变量则是configurable。\n\n在`es5 strict mode`，赋值给未声明的变量将报错。\n#### 1.2 定义函数（[Defining functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions)）\n\n定义一个函数有两种方式：函数声明（**function definition/declaration/statement**）和函数表达式（ **function expression**）。\n##### 1.2.1 function definition\n\n语法：`function name(arguments) {}`\n\n对参数而言，primitive parameter是传值，对象是传引用。\n##### 1.2.2 function expression\n\n语法：`var fun = function (arguments) {}`\n\n函数表达式中函数可以不需要名字，即匿名函数。\n##### 1.2.3 其它\n\n还可以用 `Function`构造函数来创建函数。\n\n在**函数内部**引用函数本身有3种方式。比如`var foo = function bar(){};`\n- 函数名字，即`bar()`\n- `arguments.callee()`\n- `foo()`\n#### 1.3 声明提升\n\n`1.1`提到，`var` 声明的变量会在任意代码执行前处理，这意味着在任意地方声明变量都等同于在顶部声明——即声明提升。`1.2`特意强调了函数定义，因为声明提升中，需要综合考虑一般变量和函数。\n\n在JavaScript中，一个变量名进入作用域的方式有 **4** 种：\n1. **Language-defined**：所有的作用域默认都会给出 `this` 和 `arguments` 两个变量名（global没有`arguments`）;\n2. **Formal parameters**（形参）：函数有形参，形参会添加到函数的作用域中;\n3. **Function declarations**（函数声明）：如 `function foo() {}`;\n4. **Variable declarations**（变量声明）：如 `var foo`，包括_函数表达式_。\n\n**函数声明和变量声明总是会被移动（即hoist）到它们所在的作用域的顶部**（这对你是透明的）。\n\n**而变量的解析顺序（优先级），与变量进入作用域的4种方式的顺序一致。**\n\n一个详细的例子：\n\n``` js\nfunction testOrder(arg) {\n    console.log(arg); // arg是形参，不会被重新定义\n    console.log(a); // 因为函数声明比变量声明优先级高，所以这里a是函数\n    var arg = 'hello'; // var arg;变量声明被忽略， arg = 'hello'被执行\n    var a = 10; // var a;被忽视; a = 10被执行，a变成number\n    function a() {\n        console.log('fun');\n    } // 被提升到作用域顶部\n    console.log(a); // 输出10\n    console.log(arg); // 输出hello\n}; \ntestOrder('hi');\n/* 输出：\nhi \nfunction a() {\n        console.log('fun');\n    }\n10 \nhello \n*/\n```\n"},"comment":{"url":"https://api.github.com/repos/creeperyang/blog/issues/comments/259597368","html_url":"https://github.com/creeperyang/blog/issues/16#issuecomment-259597368","issue_url":"https://api.github.com/repos/creeperyang/blog/issues/16","id":259597368,"user":{"login":"CharlesOy","id":16682569,"avatar_url":"https://avatars.githubusercontent.com/u/16682569?v=3","gravatar_id":"","url":"https://api.github.com/users/CharlesOy","html_url":"https://github.com/CharlesOy","followers_url":"https://api.github.com/users/CharlesOy/followers","following_url":"https://api.github.com/users/CharlesOy/following{/other_user}","gists_url":"https://api.github.com/users/CharlesOy/gists{/gist_id}","starred_url":"https://api.github.com/users/CharlesOy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CharlesOy/subscriptions","organizations_url":"https://api.github.com/users/CharlesOy/orgs","repos_url":"https://api.github.com/users/CharlesOy/repos","events_url":"https://api.github.com/users/CharlesOy/events{/privacy}","received_events_url":"https://api.github.com/users/CharlesOy/received_events","type":"User","site_admin":false},"created_at":"2016-11-10T04:09:24Z","updated_at":"2016-11-10T04:09:24Z","body":"为何要发到issue里面，直接写成文档多好~"}}
{"actor":{"display_login":"CharlesOy"}}