this取值是在函数执行是确定的:
- 1.普通函数 ://window
- 2.普通函数的call方法fn.call({name:1})://{name:1}
- 3.普通函数的bind方法fn.bind({name:1})://{name:1}
const fn2= fn1.bind({name:1})
fn2()//bind会返回一个新的函数执行
- 4.对象的方法执行,this指向该对象 obj.say()
- 5.setTimeout(function(){console.log(this)},2000)function里面的this指向window
- 6.setTimeout(()=>{},2000)指向外面的对象
- 7.class中的指向实例本身
第一部分:常见的this指向问题1. 全局上下文中的 this
在浏览器环境中,全局上下文中的 this,就是 window。
[JavaScript] 纯文本查看 复制代码 let a = 12;
console.log(this); // 控制台输出为 window
2. 块级上下文中的 this
- 块级上下文中没有自己的 this,它的 this 是继承所在上级上下文的 this。
- this是执行主体,不是执行上下文;执行上下文是EC ; ( 比如我在北京饭店吃饭,我就是this,北京饭店是执行上下文).
[JavaScript] 纯文本查看 复制代码 let obj = {
fn() {
// fn函数中的this是obj
{
let a = 12;
console.log(this); // 输出obj。继承上级上下文,即函数fn的私有上下文中的this
}
},
};
obj.fn();
3. 给事件绑定方法中的this
函数私有上下文中的 this 千变万化,可以总结为以下五种情况:
3.1 事件绑定
给元素的某个事件行为绑定方法,事件触发时方法执行,此时方法中的 this一般为当前元素本身。
[JavaScript] 纯文本查看 复制代码 let body = document.body;
//DOM0中事件绑定
body.onclick = function () {
console.log(this); // body
};
3.2 在 element 上绑定事件
[JavaScript] 纯文本查看 复制代码 <div class="container">
<div id="aa"></div>
</div>;
function clicke() {
console.log(this); // Window
}
3.3 js 绑定 onclick 事件
this 指向该元素
[JavaScript] 纯文本查看 复制代码 <div class="container">
<div id="aa"></div>
</div>;
document.getElementById("aa").onclick = function () {
console.log(this); // <div id="aa"></div>
};
3.4 js 使用 addEventListener 绑定事件
此时的 this 指向 该元素, 注意: 在 IE 浏览器中,使用为 attachEvent(), this 指向全局变量
[JavaScript] 纯文本查看 复制代码 <div class="container">
<div id="aa"></div>
</div>;
document.getElementById("aa").addEventListener("click", function () {
console.log(this); // <div id="aa"></div>
});
3.5 jquery 的 3 种绑定 click 事件
此时的 this 均指向该元素
[JavaScript] 纯文本查看 复制代码 $("#aa").bind("click", function () {
console.log(this); // <div id="aa"></div>
});
$("#aa").click(function () {
console.log(this); // <div id="aa"></div>
});
$("#aa").on("click", function () {
console.log(this); // <div id="aa"></div>
});
4. 普通函数执行中的this
这里的普通方法包含:普通函数、自执行函数、对象成员访问调取方法执行…等。只要看函数执行时,方法名前是否有“点”就是了。如果有“点”——xxx.方法()中 this 就是 xxx;如果没有“点”——this 即为 window(严格模式下为 undefined)。
[JavaScript] 纯文本查看 复制代码 // 自执行函数
(function(){
console.log(this); // 非严格模式下输出window,严格模式下输出undefined
})();
let obj = {
fn: (function(){
console.log(this); // 输出window
return function(){};
})();
}
/*
上面两个例子都是自执行函数执行,输出均为window。说明this的指向,与函数在哪
定义和执行的无关。自执行函数中的this一般都为window(因为函数执行时前面没有“点”)
*/
[JavaScript] 纯文本查看 复制代码 function func(){
console.log(this);
}
let obj = {
func:func
}
// 普通函数执行
func(); // this:window
// 成员访问方法执行
obj.func(); // this:obj
[].slice()方法中this->当前空数组。
Array.prototype.slice()方法中的this->Array.prototype。
涵盖以上两种情况的一道题: 点击body输出什么?
[JavaScript] 纯文本查看 复制代码 function func(){
console.log(this);//Window
}
document.body.onclick = function(){
console.log(this)//HTMLBodyElement
func();
}
/*
body
window
*/
5 构造函数执行的this
构造函数体中的 this 指向的就是当前类的实例。因此,函数中出现的 this.xxx = xxx 这样的代码就是给当前实例设置私有属性;
换句话说,类中凡是不带 this 的代码统统都与它创建的实例无关,充其量也就是这个函数私有上下文中的私有变量而已。
[JavaScript] 纯文本查看 复制代码 function Func() {
console.log(this);
// 构造函数执行,构造函数体中的this指向当前类的实例——f,因此输出的this为f
}
Func.prototype.getNum = function getNum() {
console.log(this);
};
let f = new Func();
//原型上方法中的this不一定是实例,要看执行时前面有没有点( 即当作普通函数看待 )
f.getNum(); //f
f.__proto__.getNum(); //f.__proto__
Func.prototype.getNum(); //Func.prototype
6. ES6 中的箭头函数
- 箭头函数没有自己的 this,它的 this 继承自上级上下文中的 this===>这一点和块级上下文类似。
[JavaScript] 纯文本查看 复制代码 let obj = {
func:function(){
console.log(this);
}
sum:()=>{
console.log(this);
}
}
obj.func(); // this:obj 对象成员访问
obj.sum(); // this:window
obj.sum.call(obj); // this:window 箭头函数没有this,改也没用
7. 回调函数中的 this
回调函数中的 this 一般指向 window,但也有例外;
[JavaScript] 纯文本查看 复制代码 let obj = {
i: 0,
func() {
console.log("func", this); //obj
setTimeout(function () {
console.log("定时器里面的", this); //window
}, 1000);
},
};
obj.func();
如何改写 this?
1.赋值that=this
[JavaScript] 纯文本查看 复制代码 let obj = {
i:0,
let that=this;
func(){
setTimeout(function(){
that.i++;
console.log(that); // {i:1,func:f}
},1000);
}
}
obj.func();
2.用bind强行改 function () {}.bind(this) [JavaScript] 纯文本查看 复制代码 let obj = {
i: 0,
func() {
setTimeout(
function () {
this.i++;
console.log(obj); // {i:1,func:f}
}.bind(this),
1000
); // 这里强行让内部这个回调函数中的this指向外层的this(即obj)
},
};
obj.func();
3.用箭头函数 [JavaScript] 纯文本查看 复制代码 //第三种解决方案:回调函数用箭头函数,箭头函数中没有自己的this,这里用的this是继承自外层的,也就是func函数的this->obj!
let obj = {
i: 0,
func() {
setTimeout(() => {
this.i++;
console.log(obj); // {i:1,func:f}
}, 1000);
},
};
obj.func();
第二部分:改变this指向
- call/apply/bind 方法强制手动改变函数中的 this 指向
- 这三个方法都在函数类的原型对象 Function.prototype 上,因此所有的函数都能基于原型链proto找到并调用这三个方法 ;
2.1 call
[JavaScript] 纯文本查看 复制代码 [function].call([context],params1,params2,…)
含义:执行 call 方法时,会将后面的参数传递给[function]执行,并把函数中的 this 修改为[context]:
@ [function]: 希望改变自身 this 的那个函数
@ [context]: 希望把 this 变为它!
@ params1, params2, … : 传递给函数的参数
第一个参数不传 / null / undefined时:
非严格模式——this:window
严格模式——传谁this就是谁,不传就是undefined
2.2 apply
与 call 方法相同,只不过传递函数实参以数组的方式。
[JavaScript] 纯文本查看 复制代码 [function].call([context],[params1,params2,…])
2.3 bind
bind 的语法与 call 相同,作用与以上两个方法均不同。 它是预先修改 this,预先存储参数,而函数不被立即执行; 也就是说,call / apply 都是把函数立即执行的,并且改变 this。
[JavaScript] 纯文本查看 复制代码 [function].call([context],params1,params2,…)
2.4案例分析
需求:把func函数绑定给body的click事件,触发body的click事件时,func函数执行要让func函数中的this指向obj,并给func函数传递参数10,20;
[JavaScript] 纯文本查看 复制代码 document.body.onclick = function anonymous() {
func.call(obj, 10, 20);
};
/*
将一个匿名函数绑定给body的点击事件,也就是将这个匿名函数的地址绑定给了事件,
并没有立即执行。当点击事件触发时,将匿名函数执行,然后执行func.call。
事实上,在bind方法之前,大家就是这样处理的。已经和bind的原理非常类似了。
*/
[JavaScript] 纯文本查看 复制代码 document.body.onclick = func.bind(obj, 10, 20);
/*
bind函数不会立即执行,而是预先修改this,并预先存储需要传递的参数。后续需要时(事件触发)再执行函数。
*/
2.5 bind 原理
[JavaScript] 纯文本查看 复制代码 function func(x, y) {
console.log(this, x, y); //改了之后就是obj
}
Function.prototype.bind1 = function bind1(context = window, ...params) {
/*
1.bind方法执行的时候,预先修改this,预先存储参数,而函数不被立即执行,所以要
返回一个函数特定时(比如点击之后)候执行
*/
//2.bind执行的时候,因为是func调用的所以this指向func
console.log("bind函数里面的this:", this); //func
//3.当点击事件时会执行bind()的返回结果,因为func.bind()执行后返回一个函数,被onclick绑定了
let that = this;
return function anonymous() {
//匿名函数的this指向window的,要改变func的this,让that=this
console.log("匿名函数的this", this); //window
//改变this,并执行函数
that.call(context, ...params);
};
};
let obj = {};
onclick = func.bind1(obj, 10, 20);
/*
第一步:bind传参设置:如果不传参,this默认为改为window,后面的参数用展开运算符接受
第二步:特定条件下,bind才会改变this指向,比如点击了才改变this指向,而func.bind1(obj, 10, 20);时立即执行的,所以他的执行结果时返回一个函数,让点击事件绑定
第三步:bind执行的时候,因为是func调用的所以this指向func;而返回的供点击事件执行的匿名函数他的this是window;
第四步:bind函数里面让that=this,然后在匿名函数里面执行that.call()也就是func.call()
*/
|