函数作用域、闭包与this指向问题(个人笔记)

本文参考:http://blog.alanwu.site/2020/03/04/this/

作用域


作用域是可访问变量的集合,在JavaScript中对象和函数同样是变量,作用域为可访问变量,对象,函数的集合。作用域可以分为全局作用域和局部作用域。

全局作用域: 变量在函数外定义,即为全局变量,全局变量有全局作用域,网页中所有脚本和函数都可以使用。如果变量在函数内没有声明,也是全局变量。

var name = "hello World";
// 此处可调用 name 变量
function myFunction() {
 // 函数内可调用 name 变量
}
// 此处可调用 name 变量
function myFunction() {
 name = "hello World";
 // 此处可调用 name 变量
}

局部作用域:变量在函数内声明,变量为局部作用域,只能在函数内部访问。

// 此处不能调用 name 变量
function myFunction() {
 var name = "hello World";
 // 函数内可调用 name 变量
}

局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。局部变量在函数执行时创建,函数执行完毕后局部变量就会自动销毁。

JavaScript变量生命周期,局部变量函数执行完毕后销毁,全局变量在页面关闭后销毁。函数参数只在函数内起作用,属于局部变量。

闭包


闭包 是指有权访问另一个函数作用域中的变量的函数(JavaScript高级程序设计)
闭包:函数A内部有函数B,函数B可以访问函数A的变量,那么函数B就是闭包。本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

function A(){
 var a = 123;
 function B(){
 console.log(a) //123
 }
 return B()
}
A()();

闭包有3大特性:

  • 函数嵌套函数
  • 函数内部可以引用函数外部的参数和变量
  • 参数和变量不会被垃圾回收机制回收

闭包优点:

  1. 可读取函数内部的变量,让外部访问函数内部变量成为可能;
  2. 局部变量可以保存在内存中,实现数据共享
  3. 执行过程所有变量都匿名在函数内部
  4. 可以避免使用全局变量,防止全局变量污染;

闭包缺点:

  1. 使函数内部变量存在内存中,内存消耗大
  2. 滥用闭包可能会导致内存泄漏
  3. 闭包可以在父函数外部改变父函数内部的值,慎操作

使用场景:

  1. 模拟私有方法
  2. setTimeout的循环
  3. 匿名自执行函数
  4. 结果要缓存场景
  5. 实现类和继承

this指向


this是在函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。通过捣鼓这么多代码,无非就是几种情况,在不同的环境下会有不同的值。发现网上很多关于this的文章都会让人觉得很难以理解,讲解一大堆例子但是没有讲到点上。

首先我们来看一下代码

var a = 1
function foo() {
 console.log(this.a)
}
foo()

const obj = {
 a: 2,
 foo: foo
}
obj.foo()

const c = new foo()
  • 对于直接调用函数来说,不管foo函数被放在了什么地方,this的指向一定是window
  • 对于obj.foo()来说,谁调用了函数那么谁就是this
  • 对于new操作实例化来说,this就会绑定在实例化对象上面且不会被改变
  • 箭头函数this只取决于包裹箭头函数的第一个普通函数的this

PS:箭头函数是没有this的,只会从自己的作用域链的上一层继承this。箭头函数的this在它被定义的时候就确定了,之后永远不会改变。

函数作用域、闭包与this指向问题(个人笔记)_第1张图片
image.png

改变this指向

JavaScript 中 call()、apply()、bind() 的用法
函数作用域、闭包与this指向问题(个人笔记)_第2张图片
image.png
obj.myFun.call(db);    // 德玛年龄 99
obj.myFun.apply(db);    // 德玛年龄 99
obj.myFun.bind(db)();   // 德玛年龄 99

以上除了 bind 方法后面多了个 () 外 ,结果返回都一致!
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。

对比call 、bind 、 apply 传参情况下

函数作用域、闭包与this指向问题(个人笔记)_第3张图片
image.png

obj.myFun.call(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海
obj.myFun.apply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
obj.myFun.bind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海

从上面四个结果不难看出:

call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:


call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' )。


apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])。


bind 除了返回是函数以外,它 的参数和 call 一样。

你可能感兴趣的:(函数作用域、闭包与this指向问题(个人笔记))