【js基础】关于this

为了弄清this 是个什么东西,我这边先假设大家对执行上下文都了解了,发一张执行上下文的过程图解:

image.png
this的指向,是在函数被调用的时候确定的。也就是执行上下文被创建时确定的。所以在执行的时候你若是去更改this的指向,是会报错的

image.png

一、全局对象中的this

全局环境中的this,指向它本身

// 通过this绑定到全局对象
this.a2 = 20;

// 通过声明绑定到变量对象,但在全局环境中,变量对象就是它自身
var a1 = 10;

// 仅仅只有赋值操作,标识符会隐式绑定到全局对象
a3 = 30;

复制代码

二、函数中的this

var a = 'eeee'
  let obj= {
     console.log(this.a) 
  }
  //单独的{},就不会形成新的作用域,由于并没有作用域的限制,它仍然处于全局作用域之中。
复制代码
在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。
如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。
如果这个对象是个单独的存在,则内部this则指向全局
如果函数独立调用,那么该函数内部的this,则指向undefined。
但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。
复制代码

箭头函数


function Person(name){
  this.name = name;
  this.say = () => {
    var name = "xb";
    return this.name;
  }
}
var person = new Person("axuebin");
console.log(person.say()); // axuebin
复制代码

所有的箭头函数都没有自己的this,都指向外层。箭头函数会捕获其所在上下文的this值,作为自己的this值。

构造函数和原型方法上的this

image.png

通过构造函数创建一个对象其实执行这样几个步骤:

1 创建新对象
2 将this指向这个对象
3 给对象赋值(属性、方法)
4 返回this
所以this就是指向创建的这个对象上。
例子:
function Person(name){
  this.name = name;
  this.age = 25;
  this.say = function(){
    console.log(this.name + ":" + this.age);
  }
}
var person = new Person("axuebin");
console.log(person.name); // axuebin
person.say(); // axuebin:25
复制代码

作为一个DOM事件处理函数

this指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。

var ele = document.getElementById("id");
ele.addEventListener("click",function(e){
  console.log(this);
  console.log(this === e.target); // true
})
复制代码

HTML标签内联事件处理函数

this指向所在的DOM元素

<button onclick="console.log(this);">Click Me</button>
复制代码

jQuery的this

在许多情况下JQuery的this都指向DOM元素节点。

$(".btn").on("click",function(){
  console.log(this); 
});
复制代码

call(指向目标,a1,a2,a3)、apply(指向目标,[……])

javaScript内部提供了一种机制,让我们可以自行手动设置this的指向。它们就是call与apply。它们除了参数略有不同之外,其功能完全一样。它们的第一个参数都为this将要指向的对象。

call与applay后面的参数,都是向将要执行的函数传递参数。其中call以一个一个的形式传递,apply以数组的形式传递。这是他们唯一的不同。

call和apply从this的绑定角度上来说是一样的,唯一不同的是它们的第二个参数

function fn(num1, num2) {
  console.log(this.a + num1 + num2);
}
var obj = {
  a: 20
}

fn.call(obj, 100, 10); // 130
fn.apply(obj, [20, 10]); // 50

复制代码

将伪数组转成数组的方法:

function exam(a, b, c, d, e) {

  // 先看看函数的自带属性 arguments 什么是样子的
    console.log(arguments);
  
    var arg = [].slice.call(arguments);
  // 使用call/apply将arguments转换为数组, 返回结果为数组,arguments自身不会改变
  // [].slice->返回一个新的空数组,再将arguments复制放入这个空数组中
  console.log(arg);
}

exam(2, 8, 9, 10, 3);

// result:
// [2, 8, 9, 10, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// [ 2, 8, 9, 10, 3 ]
//
// 也常常使用该方法将DOM中的nodelist转换为数组
// [].slice.call( document.getElementsByTagName('li') );

复制代码

bind()

this将永久地被绑定到了bind的第一个参数。

继承

image.png
在Student的构造函数中,借助call方法,将父级的构造函数执行了一次,相当于将Person中的代码,在Sudent中复制了一份,其中的this指向为从Student中new出来的实例对象。call方法保证了this的指向正确,因此就相当于实现了继承。Student的构造函数等同于下。


var Student = function (name, age, high) {
  this.name = name;
  this.age = age;
  this.gender = ['man', 'woman'];
  // Person.call(this, name, age); 这一句话,相当于上面三句话,因此实现了继承
  this.high = high;
}

复制代码

在向其他执行上下文的传递中,确保this的指向保持不变

var obj = {
  a: 20,
  getA: function () {
    setTimeout(function () {
      console.log(this.a)
    }, 1000)
  }
}

obj.getA(); // undefined
// 由于匿名函数的存在导致了this指向的丢失,在这个匿名函数中this指向了全局,因此我们需要想一些办法找回正确的this指向。

var obj = {
  a: 20,
  getA: function () {
    var self = this;// 常规的解决办法很简单,就是使用一个变量,将this的引用保存起来
    setTimeout(function () {
      console.log(self.a)
    }, 1000)
  }
}

也可以使用ES5中已经自带的bind方法
var obj = {
  a: 20,
  getA: function () {
    setTimeout(function () {
      console.log(this.a)
    }.bind(this), 1000)
  }
}
ES6中也常常使用箭头函数的方式来替代这种方案
复制代码

总结

判断一个函数的this绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this的绑定对象:

  • 由new调用:绑定到新创建的对象
  • 由call或apply、bind调用:绑定到指定的对象
  • 由上下文对象调用:绑定到上下文对象
  • 默认:全局对象

注意:箭头函数不使用上面的绑定规则,根据外层作用域来决定this,继承外层函数调用的this绑定。

原文链接:如有侵权,请联系作者删除

github.com/axuebin/art…

github.com/mqyqingfeng…

www.jianshu.com/p/d647aa6d1…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享