1. 变量提升/声明提升
省略var 操作符即未声明变量,会创建一个全局变量(成为global的属性。给未经声明的变量赋值在严格模式下会导致抛出ReferenceError 错误)。
声明变量在任意代码执行前创建,未声明变量直到赋值时才存在。变量声明,不管在哪里发生(声明),都会在任意代码执行前处理。
使用var声明的变量会有变量提升机制,let没有。变量提升看一看老廖的教程。
函数定义不仅提升了函数名,也提升了真正的函数定义。函数定义提升仅仅作用于函数定义,而不是函数表达式。函数表达式可以看作变量,变量提升如果不赋值只是undefined。
2. 作用域
在JavaScript中,一个变量名进入作用域的方式有 4 种:
Language-defined:所有的作用域默认都会给出 this 和 arguments 两个变量名(global没有arguments);
Formal parameters(形参):函数有形参,形参会添加到函数的作用域中;
Function declarations(函数声明):如 function foo() {};
Variable declarations(变量声明):如 var foo,包括函数表达式。
而变量的解析顺序(优先级),与变量进入作用域的4种方式的顺序一致。
一个作用域的例子:
泡泡1是全局作用域,有标识符foo;
泡泡2是作用域foo,有标识符a,bar,b;
泡泡3是作用域bar,仅有标识符c。
一般来说语言的Scope分为Lexical Scope和Dynamic Scope。
JavaScript采用Lexical Scope! 我们仅仅通过查看代码,就可以确定各个变量到底指代哪个值。另外,变量的查找是从里往外的,直到最顶层(全局作用域),并且一旦找到,即停止向上查找。所以内层的变量可以shadow外层的同名变量。JS有eval和with两种机制可以改变scope,但两者都会导致代码性能差。JavaScript没有Dynamic Scope。JS中的this机制跟Dynamic Scope很像,都是运行时绑定。
JS没有Block Scope。除了Global Scope,只有function可以创建新作用域(Function Scope)。 不过这已经是老黄历了,ES6引入了Block Scope。
3. this
当函数被调用,一个activation record(即 execution context)被创建。这个record包涵信息:函数在哪调用(call-stack),函数怎么调用的,参数等等。record的一个属性就是this,指向函数执行期间的this对象。
this不是author-time binding,而是 runtime binding。
this的上下文基于函数调用的情况。和函数在哪定义无关,而和函数怎么调用有关。
在箭头函数中,this由词法/静态作用域设置(set lexically)。它被设置为包含它的execution context的this,并且不再被调用方式影响(call/apply/bind),简单点说就是定义的时候所在的this。
如果在 DOM event handler中, this自动设置为触发事件的dom元素。
call/apply/bind
当用call和apply而传进去作为this的不是对象时,将会调用内置的ToObject操作转换成对象。所以4将会装换成new Number(4),而null/undefined由于无法转换成对象,全局对象将作为this。
【我的补充】记住this一般指向的是一个对象(object)!
http://harttle.com/2015/08/14/event-and-this.html
http://imweb.io/topic/56d4763e0848801a4ba198d9
https://github.com/zhengweikeng/blog/issues/11
this作为函数内/对象内的作用指向区别一下。
REF:
深入理解JS中声明提升、作用域(链)和this关键字
MDN变量提升
关于函数传参和变量提升的问题
JS 只有单一的数字类型,内部表示为64位的浮点数,没有分离出整数类型。1 和 1.0 是相同的值。