likes
comments
collection
share

javascript中的this讲解

作者站长头像
站长
· 阅读数 38

javascript中的this讲解

引言

  • 关于this,很多地方都有他的身影,比如构造函数(可能说法有误,应该是对于函数的构造调用),亦或是为了面试,去手写一些方法,也会用到this,(比如forEach, map, find, filter, bind, call, apply, new)。或者平时会用一个变量,将this保存起来,再或者es6的"胖箭头" => ,箭头函数.框架层面,vue2中调用方法,要用this,而vue3 中,无需this,就可以直接调方法,诸如此多this,让我想重新学习一下this

什么是this?

  • 当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文),这个记录会包含函数在哪里调用(调用栈),函数的调用方式,传入参数的信息等。this就是这个记录的一个属性,会在函数执行(调用)的时候用到

总结

  • 从this的这段机制来说,this实际上是在函数被调用时发生的绑定,他的指向完全取决于函数在哪里被调用(被调用的时执行上下文)

我们这么执着于this,他解决了什么问题?

  • 看一段代码
  function identify() {
    return this.name.toUpperCase();
  }
  let me = {
    name: '英俊',
  };
  let you = {
    name: '潇洒',
  };
  console.log(identify.call(me)); // 英俊;
  console.log(identify.call(you)); // 潇洒;
  • 这段代码可以在不同的上下文对象(me和you) 中重复使用函数identify(),不用针对每个对象编写不同版本的函数。如果不使用this,那就需要给identify() 显示的传递一个上下文对象
  function identify(content) { // 显示传递上下文对象
    return content.name.toUpperCase();
  }
  let me = {
    name: '英俊',
  };
  let you = {
    name: '潇洒',
  };
  console.log(identify(me)); // 英俊;
  console.log(identify(you)); // 潇洒;

总结

  • this提供了一种更加优雅的方式来隐式"传递"一个对象的引用,因此可以将API设计的更加简洁并且易于复用。类似的还有工厂函数和构造函数工厂函数自己声明对象为对象赋值,并且需要显示的将对象返回,而构造函数,无需声明对象,无需显示返回。只需要使用函数的构造调用,就可以实现类似工厂函数的功能

工厂函数和构造函数链接https://juejin.cn/post/7119428250635337742

this的绑定规则(重点)

this的绑定,主要分为隐式绑定和显式绑定。

隐式绑定:决定于函数的调用上下文,而这里面的水很深。

显式绑定:通过call,apply,bind 强行绑定this

下面罗列了几种判断this执行的总结

  • 作为对象的方法调用
  • 作为普通函数调用
  • 构造器调用
  • Function.prototype.call 或者 Function.prototype.call 调用
  1. 作为对象的方法调用
  • 当函数作为对象的方法被调用时,this指向该对象
  let obj = {
    a: 1,
    getA: function () {
      console.log(this === obj); // true
      console.log(this.a); // 1
    },
  };
  console.log(obj.getA());
  1. 作为普通函数调用
  • 当函数不能作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的this总是指向全局对象。在javascript中,这个全局对象就是window
  window.a = 'globalA';
  let obj = {
    a: 1,
    getA: function () {
      console.log(this === obj); // false
      console.log(this.a); // globalA
    },
  };
  let getName = obj.getA;
  console.log(getName());
  • tips: 在严格模式下,这种情况this已经被规定不会指向全局对象,而是undefined
  1. 构造器调用
  • javascript中没有类,但是可以从构造器(构造函数)中创建对象,同时也提供了new 运算符,使得构造器看起来更像一个类。构造器的外表跟普通函数一模一样,区别在于他们被调用的方式,与其说构造函数,不如说函数的构造调用
 let Person = function (name) {
    this.name = name;
  };
  let p = new Person('珍珍');
  console.log(p.name); // 珍珍

使用new 调用构造器时,还要注意一个问题。

3.1 new会隐式的返回当前对象,如果构造器显示的返回了一个 object对象,那么此次运算结果,最终是返回的这个对象而不是我们期待的this

  let Person = function (name) {
    this.name = name;
    return {
      // 显式的返回一个对象
      name: '显示返回数据',
    };
  };
  let p = new Person('珍珍');
  console.log(p.name); // 显示返回数据

3.2 如果构造器不显示地返回任何数据,或者返回一个一个非对象类型的数据,就不会造成上述问题

  let Person = function (name) {
    this.name = name;
    return '显示返回数据';
  };
  let p = new Person('珍珍');
  console.log(p.name); // 珍珍
  1. Function.prototype.call 或者 Function.prototype.call 调用

跟普通的函数调用相比,用Function.prototype.call 或者 Function.prototype.call 可以动态地改变传入函数的this

  let obj = {
    a: 1,
    getA: function () {
      return this.a;
    },
  };
  let getName = obj.getA();
  console.log(getName); // 1

通过call 来强行改变this执行

  let obj = {
    a: 1,
    getA: function () {
      return this.a;
    },
  };
  let obj1 = {
    a: 2,
  };
  let getName = obj.getA.call(obj1);
  console.log(getName); // 2

总结

  • this的绑定规则,主要是js内在机制,决定于函数的调用上下文。有时我们并不想遵循js的内在机制,去绑定this(),所以出了call,apply,bind 来强行绑定this的指向。通常我们把js的内在绑定机制称为,隐性绑定。把call,apply,bind 称之为显性绑定

this绑定的实际应用

  1. 比如在div节点的事件函数中,有一个局部的callback方法,callback被当做普通函数调用时,callback内部的this指向了windlow。但我们往往是想让他指向该div节点
<body>
	<div id="div1">我是div</div>
</body>
<script>
	window.id = 'window'
	document.getElementById('div1').onclick = function(){
		console.log(this.id) // div1
		let callback = function(){
                    console.log(this.id) // window
		}
		callback()
	}
</script>

方案1:可以用一个变量保存div节点的引用(其实就是闭包)

<body>
	<div id="div1">我是div</div>
</body>
<script>
	window.id = 'window'
	document.getElementById('div1').onclick = function(){
		console.log(this.id)
		let that = this
		let callback = function(){
                    console.log(that.id)
		}
		callback()
	}
</script>

方案2: 使用bind,强行绑定

<body>
	<div id="div1">我是div</div>
</body>
<script>
	window.id = 'window'
	document.getElementById('div1').onclick = function(){
		console.log(this.id)
		let callback = function(){
			console.log(this.id)
		}.bind(this)
		callback()
	}
</script>

使用call来强行绑定

<body>
	<div id="div1">我是div</div>
</body>
<script>
	window.id = 'window'
	document.getElementById('div1').onclick = function(){
		console.log(this.id)
		let callback = function(){
			console.log(this.id)
		}
		callback.call(this)
	}
</script>

方案3:可以使用箭头函数

<body>
	<div id="div1">我是div</div>
</body>
<script>
	window.id = 'window'
	document.getElementById('div1').onclick = function(){
		console.log(this.id)
		let callback = () => {
                    console.log(this.id)
		}
		callback()
	}
</script>

箭头函数

  • 上面说到了一个箭头函数。我们上面介绍的四条法则包含了所有正常函数,但是ES6中介绍了一种无法使用这些规则的特殊函数类型:箭头函数。其根据外层(函数或者全局)作用域来决定this

  • 箭头函数也可以向bind(...)一样,确保函数的this被绑定到指定对象。此外,最重要的一点是,用更常见的词法作用域代替了传统的this机制

最佳实践

虽然that = this 和箭头函数看起来都可以取代bind(...).但从本质上来说,他们是想替代的是this的机制。如果你·经常编写this风格的代码,最好是统一绑定方式,如果再一个函数或者同一个程序中混入这几种风格,通常会是你的代码难以维护。

转载自:https://juejin.cn/post/7135836448036487199
评论
请登录