轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

    • 核心

      • 关于this关键字的魔幻现实
        • 1、完整的函数调用
        • 2、对象中的this
        • 3、对象方法中嵌套函数的this
        • 4、原型与this
        • 5、箭头函数与this
        • 总结
  • HTML & CSS

  • 前端
  • JavaScript
  • 核心
轩辕李
2020-08-25
目录

关于this关键字的魔幻现实

不得不承认JS中存在一些“历史的遗产”,比如基于全局变量的编程模型。
如果写一个函数:

function test(name){
    var namex = name
    console.log(this.namex)
}
test('jack')

方法调用之后,this.namex=undefined,这感觉很奇怪,为什么会等于undefined?因为此处的this是全局对象window,这就令人比较头大。
不过别着急,慢慢往下看,关于this还有很多魔幻的现实,我们来一一解锁。

# 1、完整的函数调用

还是上面的代码,其实他的完整调用写法是这样的:

function test(name){
    var namex = name
    console.log(this.namex)
}
test.call(undefined, 'jack')

test是一个函数对象--即Function对象,Function.prototype有call方法。call()会有两个参数,第一个参数是this上下文对象,第二个参数是函数入参列表。
如果call()传入的this上下文是undefined或null,那么window对象将成为默认的this上下文。这也就解释了开头例子中this为啥为window的原因了

# 2、对象中的this

const obj = {
    name: 'Jack',
    greet: function() {
        console.log(this.name)
    }
}
obj.greet()  //简写调用
obj.greet.call(obj) //完整调用

obj.greet()中的this无疑就是obj对象

# 3、对象方法中嵌套函数的this

对2中的代码进行修改:

const obj = {
    name: 'Jack',
    greet: function() {
        retufn function(){console.log(this.name)}
    }
}
obj.greet()()	  //输出undefined

需要注意的是嵌套函数中的this依然是window,为什么呢?可以拆分来看:

var greet = obj.greet()
greet()		// = greet.call(undefined)

嵌套函数被调用的时候,真实的调用者上下文是undefined,也就是window

# 4、原型与this

function Clt() {
}

Clt.prototype.x = 10
Clt.prototype.test = function () {
    console.log(this)
    this.y = this.x + 1
}
let bean = new Clt()
bean.test();
console.log(bean.y)

test方法中输出的this是一个Clt对象。
这里需要引入一个新的概念:构造器函数。
new关键字就是构造器函数,它的作用是:

一旦函数被new来调用,就会创建一个链接到该函数的prototype属性的新对象,同时this会被绑定到那个新对象上

理解了构造器函数,我们就理解了原型与this的关系了,因为new会重新指定this上下文

# 5、箭头函数与this

ECMAScript6出现了箭头函数的用法,关于箭头函数中的this,需要先记住一句话:

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

看代码(引自阮一峰老师的教程 (opens new window)):

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

此处的this指向了foo的this上下文,即定义时的this对象。

箭头函数与非箭头函数的this区别:

function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
    this.s2++;
  }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

箭头函数的this指向了定义时所在对象的this;非箭头函数的this指向了运行时的作用域,即全局域window

this的指向固定化,也算是解决了一些历史旧账,无疑带来的很大好处,比如封装调用:

var handler = {
  id: '123456',

  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false);
  },

  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id);
  }
};

一个嵌套的例子:

function foo() {
  return () => {
    return () => {
      return () => {
        console.log('id:', this.id);
      };
    };
  };
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

最里层的this也绑定到了定义时所在对象的—即foo的this

# 总结

ECMAScript6的箭头函数也算是解决了一些历史问题。不过正如HTML5离不开HTML4一样,因为只有保持了继承与兼容,新技术的推广才会更加的迅速。
享受语言新特性之余,也要搞清楚旧版本的一些细节,这有助于我们更全面的掌握知识。

编辑 (opens new window)
#JavaScript
上次更新: 2022/08/05
HTML基础全景图

HTML基础全景图→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式