由JavaScript中“this“想到的函数作用域问题

用JavaScript写构造函数以及写箭头函数时我遇到了这个疑惑,于是特意研究了一番,总结如下:

一.作用域分类

首先,作用域分为全局局部作用域()

var a = 'apple';      //全局变量
function fighting(){
   var a = 'angel';   //局部变量
}
console.log(a); // 输出apple

其次,还有块级作用域的概念:(from ChatGPT

块级作用域是指由一对花括号({})括起来的代码块内部的范围。在块级作用域中声明的变量只在该代码块内部可见,超出该代码块就无法访问。这与函数级作用域有所不同,函数级作用域是指变量在整个函数体内都是可见的。

在以下代码中,初学者很容易被迷惑,误认为由于if所在的判断代码块为true因此输出angel,实际上并不是这样,是由于var a = 'angel'作为局部变量,覆盖了if所在的块级作用域,导致console.log打印出来的是它最后声明的变量,因此无论是true还是非true,打印出来的都将是a。

//类似的for,while循环语句中也有这个问题

var a = 'apple';

if(true){
   var a = 'angel';
}

console.log(a); //输出angel

为了优化这个问题,ES6推出了let和const作为声明关键字,它们具有更严格的作用域规则。

 letconst 关键字可以创建块级作用域的变量,而使用 var 则会创建函数级作用域的变量。上面的代码中如果我们想打印apple,可以将if代码块中的var a = 'angel'; 改成 let a = 'angel'

同时,当存在多个变量时,还存在作用域链这个概念

如果说作用域是一个区域,那么作用域链就是表示次序的,遵循的是从里到外的顺序,函数内部没有声明变量,那就去函数的外部寻找

var a = 'apple';
var b = 'boy';
function fighting(){
  var a = 'angel';
  console.log(a);   //angel
  console.log(b);   //boy
 
}
fighting();
console.log(a);    //apple

二.变量提升(hoisting)

var a = 'apple';
function fighting(){
   console.log(a);    //undefined
   var a = 'angel';
   console.log(a);    //angel
}
fighting();

为什么第一个打印出的是undefined?(讲解来自ChatGPT)

这是因为 JavaScript 中存在变量提升(hoisting)的机制。在你的代码中,函数中的变量声明会被提升到函数的顶部,但赋值操作不会随之提升。因此,代码的执行顺序实际上是这样的:

var a = 'apple';
function fighting() {
   var a;  // 变量声明被提升,但赋值操作不会提升
   console.log(a);    // 输出 undefined,因为此时 a 还没有被赋值
   a = 'angel';       // 赋值操作
   console.log(a);    // 输出 'angel'
}
fighting();  // 调用函数

如果有函数声明,则比变量声明优先级更高

三.this指向

理解了以上概念,可以说说箭头函数中this的指向问题吧。

Js规定:

箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。

在普通函数里:我们可以得知,sayHello这个方法是定义在A对象中的,当我们使用call方法,把其指向B对象,最后输出了B;可以得出,sayHello的this只跟使用时的对象有关。

var name = 'window'; // 其实是window.name = 'window'

var A = {
   name: 'A',
   sayHello: function(){
      console.log(this.name)
   }
}

A.sayHello();// 输出A

var B = {
  name: 'B'
}

A.sayHello.call(B);//输出B

A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window

那如果我将sayHello那块的普通函数改成箭头函数呢?A.sayHello()输出的就是window了。

原因是sayHello的箭头函数所在的作用域是最外层的js环境,没有被其他函数包裹,且最外层的js环境指向的对象是window对象,因此this指向的是window。

var name = 'window'; 

var A = {
   name: 'A',
   sayHello: () => {
      console.log(this.name)
   }
}

A.sayHello();// 这里输出的是window,而不是A了

如果想永远绑定A的话,代码如下:

var name = 'window'; 

var A = {
   name: 'A',
   sayHello: function(){
   var s = () => 
      console.log(this.name)
      return s //返回箭头函数s
  }
}

var sayHello = A.sayHello();
sayHello();   //输出A

var B = {
   name:'B' ;
}
sayHello.call(B);  //输出A
sayHello.call();  //输出A

因此我们再总结一下:

该函数所在的作用域:箭头函数s 所在的作用域是sayHello,因为sayHello是一个函数。

作用域指向的对象:A.sayHello指向的对象是A。

好吧,bind,call,apply方法我也不太懂,继续总结吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值