-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
深入理解JS中声明提升、作用域(链)和this
关键字
#16
Comments
2.
|
3. 作用域(Scope)和闭包(closure)在第2部分对 3.1 Scope是什么?先尝试从几个方面描述下:
综合一下,Scope即上下文,包含当前所有可见的变量。 Scope分为Lexical Scope和Dynamic Scope。Lexical Scope正如字面意思,即词法阶段定义的Scope。换种说法,作用域是根据源代码中变量和块的位置,在词法分析器(lexer)处理源代码时设置。 让我们考虑下面的代码来分析Lexical Scope: function foo(a) {
// inner scope 'foo'
// defined argument a, and look-up b upwards
console.log( a + b );
}
// outmost/global scope
// defined b
var b = 2;
foo( 2 ); // 4 Scope是分层的,内层Scope可以访问外层Scope的变量,反之则不行。上面的代码中即有嵌套Scope。用泡泡来比喻Scope可能好理解一点:
Scope在我们写代码的时候就被定义好了,比如谁嵌套在谁里面。 3.2 JavaScript ScopeJavaScript采用Lexical Scope。 于是,我们仅仅通过查看代码(因为JavaScript采用Lexical Scope),就可以确定各个变量到底指代哪个值。 另外,变量的查找是从里往外的,直到最顶层(全局作用域),并且一旦找到,即停止向上查找。所以内层的变量可以shadow外层的同名变量。 3.2.1 Cheating Lexical如果Scope仅仅由函数在哪定义的决定(在写代码时决定),那么还有方式更改Scope吗?JS有 3.2.1.1
|
长文必火!前排挤挤~ |
男神起飞 |
_围观男神装逼!!!!_吓得我打字都歪了 |
不明觉厉 |
写的好,感觉理解又深了一些 |
文章很好啊谢谢 |
为何要发到issue里面,直接写成文档多好~ |
@CharlesOy 写issue里可以互动啊 😃 |
好棒~,很详细。复习了一遍,查缺补漏理解更深入了,谢谢 ~ |
那如果函数的形参和函数声明的名字一样,该如何理解呢? function test(arg){ console.log(arg); // [function : arg] var arg = 'hello'; function arg(){ console.log('hello world') } console.log(arg); // hello } test('hi'); |
@liu3042 可以看 变量的解析顺序(优先级),与变量进入作用域的4种方式的顺序一致。 相关部分。 function test(arg){
// 1. 形参 arg 是 "hi"
// 2. function arg声明提升,arg 是 function
console.log(arg); // [function : arg]
var arg = 'hello';
// 3. arg 此时是 "hello"
function arg(){
console.log('hello world')
}
console.log(arg); // hello
}
test('hi'); |
@liu3042 ,你的代码等价于: function test(arg){
var arg;
arg = function(){
console.log('hello world');
}
console.log(arg);
arg = 'hello';
console.log(arg);
}
test('hi'); |
好文,然而上下文和作用域并不是同一个概念。 |
@JackZhouMine 上面是MDN对
上面是 所以我觉得把 execution context 和 scope 等同起来理解没有问题(从实际使用来说,或者说至少从理解作用域这些来说没有问题)。 比如 http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/ 这篇文章也把两者等同。 但上下文和作用域是不是同一个概念?我觉得这要看定义。有些规范上这两个概念应该是有区别的,所以你的话没有错。 补充: |
稍微看了下ES6规范 Executable Code and Execution Contexts,可以看到,scope和规范中的 Lexical Environment 比较接近,而Execution Context要超出scope的概念,但scope是其重要的组成部分。 Lexical EnvironmentLexical Environment 是规范里的一种类型,用于定义 Identifiers 和 指定variables/functions 的关联(基于代码的词法嵌套结构lexical nesting structure of ECMAScript code)。 Lexical Environment由 Environment Record 和 一个可能为null的指向外层Lexical Environment的引用 组成。 通常,Lexical Environment和ECMAScript代码的特定词法结构由关,比如 一个 Environment Record 记录了它所属 Lexical Environment 的 scope 内的 identifier bindings。 Environment Record 有两种基本的类别:declarative Environment Records 和 object Environment Records。
一个 Declarative Environment Record 对应一个 ECMAScript program scope (scope包含
一个 function Environment Record 是用于表示一个函数的顶层scope的 Declarative Environment Record,并且,如果这个函数不是箭头函数,还提供 this 的绑定。
Execution Context
Execution Context的组成:
|
发现第 3 点中闭包被漏掉了,这里补上。 闭包(closure)
在 JavaScript 中,函数形成闭包。闭包就是函数和函数声明时的词法作用域的组合。 下面用例子说明: function outer () {
var name = 'Jerry'
function inner () {
console.log(name)
}
return inner
}
var fn = outer()
fn() // Jerry 在部分编程语言中,函数内部的局部变量仅仅存在于函数执行期间,一旦函数执行完毕,变量就销毁(不再能访问)。 但在 JavaScript 中,由于闭包的原因, |
闭包就是函数和函数声明的词法作用域的组合,这句话有点难懂... |
函数和函数声明时的词法作用域形成闭包 ——这样可能好理解一点。 |
大神您好,构造函数的call调用比较复杂 能请教下吗? function add(c, d){
this.d=d; // set调用对象o
return this.a + this.b + c + d; // get调用对象o
}
var o = {a:1, b:3};
add.call(o, 5, 7);
console.log(o); |
@Hibop 不是很明白你的问题。没有所谓的构造函数和非构造函数,在用 这里 |
写得很好,赞一下作者。 |
大佬好!
把函数fn的调用移到if块里面,就正常控制台输出fn inner; 严格模式下 这个情况我理解的是函数提升的时候是分为两步的,先是把函数名定义提升到广义的作用域顶部,然后再把函数体的定义提升到狭义的作用域顶部。 这里解释一下我自己定义的广义的作用域和狭义的作用域,广义的指的是ES6之前的作用域,即全局作用域和函数作用域,狭义的除了广义的作用域之外加上了这个if,for等块级作用域。 大佬看我这个理解是否正确呢? |
@hrpc 你注意到ES6开始作用域有所不同,这是对的。
|
你是哪里知道我的邮箱的
…---原始邮件---
发件人: "西门吹喵"<notifications@github.com>
发送时间: 2020年4月4日(周六) 晚上10:33
收件人: "creeperyang/blog"<blog@noreply.github.com>;
抄送: "Subscribed"<subscribed@noreply.github.com>;
主题: Re: [creeperyang/blog] 深入理解JS中声明提升、作用域(链)和`this`关键字 (#16)
有个疑问,请求大佬解惑!:关于作用域链的确认的
function a(){ let num = 0; return function(){ console.log(++num) } } const b =a() b()
当我们调用这个函数a的时候,会生成函数a的执行上下文,然后先预编译(我不知道这个“预编译”用的准确不?),然后创建了这个函数执行上下文的变量对象(变量num,**不知道这个里面有没有匿名函数🤔️,return的值在这个阶段咋个处理?**🤔️)和作用域链(a函数的作用域和=》全局作用域),创建完毕。再执行里面的代码。
当我们执行函数b()的时候,上面同理,然后创建作用域链(函数b =》 函数a =》 全局)。
提问:**这里并不是因为函数b里面引用了a的变量count才将函数a放在这条作用域链上的,而是因为函数a嵌套了函数a,所以对于函数b的执行上下文里面的作用域链来说,他们在一条作用域链上,我这样理解对吗?**🤔️
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
有个疑问,请求大佬解惑!:关于作用域链的确认的
当我们调用这个函数a的时候,会生成函数a的执行上下文,然后先预编译(我不知道这个“预编译”用的准确不?),然后创建了这个函数执行上下文的变量对象(变量num,**不知道这个里面有没有匿名函数🤔️,return的值在这个阶段咋个处理?**🤔️)和作用域链(a函数的作用域和=》全局作用域),创建完毕。再执行里面的代码。 当我们执行函数b()的时候,上面同理,然后创建作用域链(函数b =》 函数a =》 全局)。 |
没什么问题 |
这个issue试图阐述JavaScript这门语言的3个难点:声明提升、作用域(链)和
this
。首先推荐https://github.com/getify/You-Dont-Know-JS,这是一本非常棒的JavaScript书籍,几乎所有的JS知识点都包括并且详细解释了。看一遍相信必有大收获。
1. 声明提升
大部分编程语言都是先声明变量再使用,但在JS中,事情有些不一样:
上面是合法的JS代码,正常输出
undefined
而不是报错Uncaught ReferenceError: a is not defined
。为什么?就是因为声明提升(hoisting)。1.1 变量声明
参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
语法:
变量名可以是任意合法标识符;值可以是任意合法表达式。
重点:
var
声明的变量的作用域就是当前执行上下文(execution context),即某个函数,或者全局作用域(声明在函数外)。声明变量和未声明变量的区别:
在
es5 strict mode
,赋值给未声明的变量将报错。1.2 定义函数(Defining functions)
定义一个函数有两种方式:函数声明(function definition/declaration/statement)和函数表达式( function expression)。
1.2.1 function definition
语法:
function name(arguments) {}
对参数而言,primitive parameter是传值,对象是传引用。
1.2.2 function expression
语法:
var fun = function (arguments) {}
函数表达式中函数可以不需要名字,即匿名函数。
1.2.3 其它
还可以用
Function
构造函数来创建函数。在函数内部引用函数本身有3种方式。比如
var foo = function bar(){};
bar()
arguments.callee()
foo()
1.3 声明提升
1.1
提到,var
声明的变量会在任意代码执行前处理,这意味着在任意地方声明变量都等同于在顶部声明——即声明提升。1.2
特意强调了函数定义,因为声明提升中,需要综合考虑一般变量和函数。在JavaScript中,一个变量名进入作用域的方式有 4 种:
this
和arguments
两个变量名(global没有arguments
);function foo() {}
;var foo
,包括_函数表达式_。函数声明和变量声明总是会被移动(即hoist)到它们所在的作用域的顶部(这对你是透明的)。
而变量的解析顺序(优先级),与变量进入作用域的4种方式的顺序一致。
一个详细的例子:
The text was updated successfully, but these errors were encountered: