My Little World

变量提升

一个变量的生成需要经历 创建、初始化、赋值三个阶段
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。
const,其实 const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。
let
1、
假设有如下代码:

function fn(){
var x = 1
var y = 2
}
fn()

在执行 fn 时,会有以下过程(不完全):

进入 fn,为 fn 创建一个环境。
找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
将这些变量「初始化」为 undefined。
开始执行代码
x = 1 将 x 变量「赋值」为 1
y = 2 将 y 变量「赋值」为 2
也就是说 var 声明会在代码执行之前就将「创建变量,并将其初始化为 undefined」。

这就解释了为什么在 var x = 1 之前 console.log(x) 会得到 undefined。

2、
fn2()

function fn2(){
console.log(2)
}
JS 引擎会有一下过程:

找到所有用 function 声明的变量,在环境中「创建」这些变量。
将这些变量「初始化」并「赋值」为 function(){ console.log(2) }。
开始执行代码 fn2()
也就是说 function 声明会在代码执行之前就「创建、初始化并赋值」
3、
{
let x = 1
x = 2
}
只看 {} 里面的过程:

找到所有用 let 声明的变量,在环境中「创建」这些变量
开始执行代码(注意现在还没有初始化)
执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
执行 x = 2,对 x 进行「赋值」
这就解释了为什么在 let x 之前使用 x 会报错:

let x = ‘global’
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
原因有两个

console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
执行 log 时 x 还没「初始化」,所以不能使用(也就是所谓的暂时死区)

补充
1.
在进入一个执行环境后,先把 var 和 function 声明的变量前置, 再去顺序执行代码
是 var 声明在前还是 function 声明的在前?按先来后到,同名覆盖。当然如果一个变量已经有值,再 var 是无效的

1
2
3
4
5
6
7
8
9
10

var fn
function fn(){}

console.log(fn) //function

function fn(){}
var fn //已经声明过 fn, 再 var 无效,并不会重置为 undefined

console.log(fn) //function

2.
函数在执行的过程中,先从自己内部找变量
如果找不到,再从创建当前函数所在的作用域去找, 以此往上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
var a = 2
return fn2
}
var fn = fn1()
fn() //输出多少 2
////////////////////////////////
var a = 1
function fn1(){
var a = 2
return fn2
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn() //输出多少 1

相关

1
2
3
4
5
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a)//{n:2}
console.log(b)//{n:1,x:{n:2}}

a.x = a = {n:2};执行顺序

  1. a.x,这时a,b指向{n:1},增加x,属性,但值为undefined,即这时a,b为{n:1,x:undefined}

2.a={n:2},a这时指向{n:2},b依然为{n:1,x:undefined}

3.a.x = a;这时的x是{n:1,x:undefined}中的x,指向a,此时a指向{n:2}

1
a==1 && a==2 && a==3

可能为true
关键在于==在比较两端之前会调用对象的valueof,toString等方法,只要a对象中的toString方法有递增值返回就可以实现

1
2
3
4
5
6
const a = {
i: 1,
toString: function () {
return a.i++;
}
}

立即执行函数的目的
造出一个函数作用域,防止污染全局变量
ES 6 新语法

1
2
3
{
let name
}