为了弄清this 是个什么东西,我这边先假设大家对执行上下文都了解了,发一张执行上下文的过程图解:
this的指向,是在函数被调用的时候确定的。也就是执行上下文被创建时确定的。所以在执行的时候你若是去更改this的指向,是会报错的
一、全局对象中的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
通过构造函数创建一个对象其实执行这样几个步骤:
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的第一个参数。
继承
在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绑定。
原文链接:如有侵权,请联系作者删除