注:牛客职导前端课程笔记
1.变量提升
浏览器执行JavaScript之前会预编译变量声明(只预编译声明不赋值)和函数定义。
编译时变量如果重名,后面的会覆盖前面的。
2.调用栈
JavaScript在执行之前就进行编译并创建执行上下文。
调用栈:
先进后出。
①先创建全局执行上下文,压入栈底。全局执行上下文在整个页面生存周期内只有一份。
②调用函数时,会创建函数执行上下文压入栈中,执行完函数会从栈顶弹出。
3.作用域链和闭包
3.1作用域
在ES6之前,ES的作用域只有两种:全局作用域和函数作用域。
ES6支持块级作用域。
块级作用域特点:在代码块外部是访问不到 ,代码执行完成之后,代码块中定义的变量会被销毁。
形式:使用一对大括号包裹的一段代码 。
ES6中如何使块级作用域生效:使用let和const关键字 。
-
let和const关键字 存储在语法环境中,var存储在变量环境中。
-
块级内部代码执行结束,立马销毁内部let、const创建的变量
-
let和const创建的变量,初始化不提升,创建提升,所以造成暂时性死区
-
访问变量先在当前执行上下文的词法环境中查找,再到变量环境中查找
关于变量提升问题
-
var的创建和初始化被提升,赋值不会被提升。
-
let的创建被提升,初始化和赋值不会被提升,所以会造成暂时性死区(就是访问不到)。
-
function的创建、初始化和赋值均会被提升。
块级作用域的特点:是代码编译阶段就决定好的,和函数是怎么调用的没有关系。
3.2作用域链
当一个函数中使用了某个变量,首先会在自己内部作用域查找,然后再向外部一层一层查找,直到全局作用域,这个链式查找就是作用域链
3.3闭包
什么是闭包,为什么会有闭包?
在全局创建变量为全局变量,在局部创建变量就是局部变量。局部变量有局部执行完会被销毁的特点,但有时候我们希望它不会立即被销毁而是可以在函数外部进行操作和访问。
闭包变量能在函数外部访问是因为作用域链。
为什么用闭包,不用全局变量?
因为全局变量在全局作用域中可以被修改,容易被污染。
闭包容易造成内存的泄露。
闭包的应用:
①模仿块级作用域
②埋点计数器
③柯里化
4.this指向
4.1this关键字的由来
JavaScript的作用域机制不支持在对象内部的方法中使用访问对象内部的属性。
作用域链和this是两套不同的系统。
4.2this可以在哪里使用
全局上下文的this为window对象。
函数上下文的this:
①在全局环境中调用一个函数,函数内部的this指向的是全局变量window。
②通过一个对象调用内部的方法,该方法执行上下文中的this指向对象本身。
4.3this指向总结
-
当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;
-
通过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象本身
-
ES6 中的箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数
-
new 关键字构建好了一个新对象,并且构造函数中的 this 其实就是新对象本身
-
嵌套函数中的 this 不会继承外层函数的 this 值。
解决this不继承的方法
-
内部函数使用箭头函数
-
将在外层函数中创建一个变量,用来存储this,内层函数通过作用域链即可访问。
4.4改变this指向的方法
①call
②apply
call和apply的区别是apply的第二个参数,必须是数组或者类数组 ,而call为任意参数。
③bind bind() 方法创建一个新的函数 ,bind 方法的返回值是函数,并且需要稍后调用,才会执行。