有关this的指向问题
this的一般指向
1.指向window对象
(1)全局环境
全局环境下this就是window对象的引用
注意:使用严格模式时在全局函数内this为undefined
console.log(this == window);
//true
(2)对象里的普通方法
show属于obj对象的方法,因此里面的this指向obj对象;
而hd是普通方法,里面的this指向window对象,只要window对象没有site属性,就无法访问到该属性
let obj = {
site: "demo",
show() {
console.log(this.site);
function hd() {
console.log(this.site); //undefined
}
hd();
}
};
obj.show();
//demo
//undefined
2.指向实例对象
一般构造函数中包含属性和方法,函数中的上下文指向到实例对象,属性和方法里面的this默认就是指当前对象。
function User() {
this.name = "user1";
this.say = function() {
return this.name;
};
}
let hd = new User();
console.log(hd.say());
//user1
3.箭头函数
- 在全局环境中依然指向window对象
- 在对象的方法中,箭头函数和父级函数指向同一个this,会继承定义函数时的上下文,因此如果想使用函数定义时的上下文中的this,那就使用箭头函数
var name = "zhangsan";
let def = ()=>{
console.log(this.name);
}
def();
let obj = {
name: 'lisi',
getName: function () {
return () => {
return this.name;
}
}
}
console.log(obj.getName()());
//zhangsan
//lisi
4.事件函数
- 使用普通事件函数时this指向当前dom对象
<button>button</button>
let Dom = {
site: "demo",
bind() {
const button = document.querySelector("button");
//事件函数可以这样理解,所有的事件都是定义在当前dom对象的方法上
//button.click = function() {}
//相当于定义button对象的click方法,因此函数里面的this指向botton对象
//event.target为当前dom元素对象
button.addEventListener("click", function() {
console.log(this.site + event.target.innerHTML);
});
}
};
Dom.bind();
//undefinedbutton
- 使用箭头函数时this指向上下文对象,和父级函数指向同一个this
let Dom = {
site: "demo",
bind() {
const button = document.querySelector("button");
button.addEventListener("click", () => {
console.log(this.site + event.target.innerHTML);
});
}
};
Dom.bind();
//demobutton
5.特殊情况
-
this在闭包中的历史遗留问题
this 总是指向调用该函数的对象,下面的函数因为是在全局环境下调用的,所以this指向window,但这不是我们想要的。
let hd = { user: "demo", get: function() { return function() { return this.user; }; } }; console.log(hd.get()()); //undefined
这个问题可以通过使用箭头函数解决
let hd = { user: "demo", get: function() { return () => this.user; } }; console.log(hd.get()()); //demo
改变this的指向
每个函数都包含两个非继承而来的方法:call()方法、apply()方法和bind方法。
它们都可以用来重新定义函数的执行环境,也就是this的指向;都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。
- call和apply的方法一样,只是参数列表不同。
- apply 用数组传参
- call 需要分别传参
- 与 bind 不同 call/apply 会立即执行函数
function show(title) {
this.title = title;
}
let lisi = {
name: 'lisi'
};
let zhangsan = {
name: 'zhangsan'
};
show.call(lisi, 'good');
show.apply(zhangsan, ['nice']);
console.log(lisi);
console.log(zhangsan);
//{name: "lisi", title: "good"}
//{name: "zhangsan", title: "nice"}
- bind()方法会创建并返回一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
let bar = function(y) {
this.y = y;
};
let foo={
x:3
};
let nbar = bar.bind(foo);
nbar(10);
console.log(foo);
//{x: 3, y: 10}
apply的其他用法
apply有一个巧妙的用处,就是可以将一个数组默认的转换为一个参数列表([param1,param2,param3]转换为param1,param2,param3))
例,用Math.max得到数组中最大的一项:
由于Math.max参数里面不支持Math.max([param1,param2]),也就是数组,但是它支持Math.max(param1,param2,param3…),所以可利用apply来实现
let array = [1, 2, 3];
//由于不需要改变this的指向,所以第一个参数为null,也可以写this,相当于用全局对象去调用
ley max = Math.max.apply(null, array);
console.log(max);
//3