this在js中是一个魔术变量,在不同的场景指向不同的对象,在使用的过程中也是千奇百怪,让人琢磨不透,令人头疼,在这里列举了this的指向问题
1.在这里普通的对象之中,this就是指向当前的对象
let obj = {
name: '张三',
info:'爱上你的美',
say(){
console.log(this);//{name: '张三', info: '爱上你的美', say: ƒ}
}
}
2.this在普通的函数之中就是指向window
function fn(){
console.log(this);//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}
fn()
3.在js事件动态绑定之中,this的指向就是绑定的当前节点对象
<body>
<div>我爱写代码</div>
<script>
let div = document.querySelector('div');
div.onclick = function(){
console.log(this);//<div>我爱写代码</div>
}
</script>
</body>
4.在行内事件的绑定之中,this就是指向window
<body>
<div onclick="fn()">我爱写代码</div>
<script>
function fn(){
console.log(this);//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}
</script>
</body>
5.箭头函数的this指向当前宿主对象也就是上级作用域,普通函数指向window
let obj = {
name: '我爱写代码',
say() {
let fn1 = () => {
console.log(this, this.name,'fn1');//{name: '我爱写代码', say: ƒ} '我爱写代码' 'fn1'
function fn2() {
console.log(this, 'fn2');//Window {window: Window, self: Window, document: document, name: '', location: Location, …} 'fn2'
}
fn1();
fn2();
}
}
obj.say()
6.this在构造函数中就是指向被new出来的实例化对象
<script>
function Good(name, age) {
this.name = name;
this.age = age;
this.say = function(){
console.log(this);//Good {name: '刘亦菲', age: 99, say: ƒ}
}
}
let g1 = new Good('刘亦菲', 99);//实例化对象
g1.say()
小案例 this指向的改变
<body>
<div>点击</div>
<p>我出来了</p>
<script>
function Fn(){
//获取div节点
this.div = document.querySelector('div');
//获取p节点的标签
this.p = document.querySelector('p')
// 给div添加点击事件
this.Eve(this.div,'click',this.divhandler.bind(this))
}
// 给节点添加2级绑定的事件函数
Fn.prototype.Eve = function(target,type,cb){
target.addEventListener(type,cb)
}
//div点击事件的回调函数
Fn.prototype.divhandler = function(){
this.p.style.display = 'block'
}
//实例化对象
new Fn();
</script>
</body>
获取两个设置好样式的标签div和P,我们先让p标签隐藏,用构造函数的方式给div绑定点击事件,点击时让p标签显现。 在构造函数中的this指向实例化对象的,但是在div的事件绑定中的回调函数divhandler指向当前绑定的节点对象div,所以无法调动构造函数里的p标签使其显现,然后在事件绑定的函数的后面添加.bind(this)使其的指向改变为实例化对象,就能再点击时调动构造函数里的p标签使其显现。这就是this指向的改变
this指向改变的方法
可以通过call、apply、bind来修改this的指向
let garen = {
name: "稻草人",
age: 22,
say: function () {
console.log("我的武器是" + this.name);
},
};
var weapon = {
name: "大天使之杖",
};
garen.say(); // 输出:我的武器是稻草人
call
call方法可以接收多个参数
call(修改后的this指向, 参数1, 参数2, 参数3...)
var garen = {
name: "稻草人",
age: 22,
say: function () {
console.log("我的武器是" + this.name);
},
};
var weapon = {
name: "大天使之杖",
};
garen.say.call(weapon) // 输出:我的武器是大天使之杖
call()传入多个参数的使用
var garen = {
name: "稻草人",
age: 22,
say: function (desc) {
console.log("我的武器是" + this.name, desc);
},
};
var weapon = {
name: "大天使之杖",
};
garen.say.call(weapon, "就是个稻草人罢了", "我要三星") // 输出:我的武器是大天使之杖 就是个稻草人罢了
这里可以看到输出的内容少了第三个参数,那是因为desc只传递了第二个参数,第三个参数没有传递。那传递多个参数可以通过新增参数的方式,或者使用 ES6 的扩展运算符来完成。
var garen = {
name: "稻草人",
age: 22,
say: function (...desc) {
console.log("我的武器是" + this.name, ...desc);
},
};
var weapon = {
name: "大天使之杖",
};
garen.say.call(weapon, "就是个稻草人罢了", "我要三星") // 输出:我的武器是大天使之杖 就是个稻草人罢了 我要三星
apply
apply和call的区别就在于接收的参数,call能接收多个参数,但apply只能接收2个参数。
第一个和call一样,都是this的指向。
第二个参数是一个数组,将call中的剩余参数都放到了这个数组中。
apply(修改后的this指向, [参数1, 参数2, 参数3...])
仅此而已,其他用法完全一样,只是接收参数的方式改变了。
var garen = {
name: "盖伦",
age: 22,
say: function (...desc) {
console.log("我的武器是" + this.name, ...desc);
},
};
var weapon = {
name: "暴风大剑",
};
// 只有这里修改了
garen.say.apply(weapon, ["我没有特色", "我很肉"]) // 输出:我的武器是暴风大剑 我没有特色 我很肉
bind
bind和call也很相似,不过返回结果不同,bind是返回一个新函数,call和apply不会返回新函数,而是直接执行原函数,并返回函数执行的结果
bind(修改后的this指向, 参数1, 参数2, 参数3...)
var garen = {
name: "盖伦",
age: 22,
say: function (...desc) {
console.log("我的武器是" + this.name, ...desc);
},
};
var weapon = {
name: "暴风大剑",
};
// 只有这里修改了
garen.say.bind(weapon, "我没有特色", "我很肉")() // 输出:我的武器是暴风大剑 我没有特色 我很肉
复制代码
3. 总结
一般情况下this指向调用它的实例/window,但也可以手动修改this的指向,call、apply、bind都是用来修改this的指向的,区别在于:
call接收多个参数,执行原函数,返回结果apply接收两个参数,第二个参数是数组,执行原函数,返回结果bind接收多个参数,不会执行函数,只会返回新函数,所以使用时要再调用一次