this
指向,是前端开发人员一直比较头疼的问题,一般称为万恶的this指向
,我们去面试,经常也会遇到面试官给你的this
指向的有关题目,有些基本靠猜,希望经过本文的学习,会对this指向有个很直观的判断!
注意:
普通函数this指向由调用方式决定,跟定义环境无关,也就是说,普通函数的this指向,实际上是动态作用域
确定的
箭头函数this指向由定义环境决定的,与调用方式无关,也不可以强制改变它的this指向,实际上箭头函数是没有this指向的,指向的是外层.
this代表着函数运行时,自动生成的一个内部对象,只能在函数内部使用,随着函数使用场合不同,this的值会发生变化,但是有个总的原则:
this指的是函数调用对象或事件的调用对象,找不到调用对象时,this指向window对象
严格模式下,全局作用域下的this指向undefined
非严格模式下,全局作用域下的this指向window
下面所有内容均为非严格模式的条件下
function foo() {
console.log(this)//Window
}
foo();
结论:
(1) 非严格模式下,this指向window
(2) 严格模式下,this指向undefined
var obj={
a:1,
foo:function(){
console.log(this.a);//1
}
}
obj.foo();//1
结论:this指向obj这个调用它的对象
另外,我们再对它加以改造下
var a=10;
var obj={
a:1,
foo:function(){
console.log(this.a);//10
}
}
var b=obj.foo;
b();//10
这个时候,打印的是10,我们来分析下原因:
这里是将obj.foo赋值给了变量b,此时b是一个函数,只是赋值而已,并没有调用,所以this指向并不确定,后面b()调用了,是普通函数调用的模式,此时this指向的是window,所以打印结果应该是外面的a值
var a = 100;
var obj = {
a: 1,
c: {
foo: function () {
console.log(this.a);
}
}
}
obj.c.foo();
这里是obj.c来调用的这个函数,所以此时this指向应该指向的是 c这个对象,而c对象是没有a这个值的,所以打印undefined
var a = 100;
var obj = {
a: 10,
b: {
a: 1,
c: function () {
console.log(this.a);
}
}
}
obj.c = obj.b.c;
obj.c()//10
这里相当于动态给obj家里一个方法,此时是obj调用,所以this指向是obj
总结:对象调用方式下,this指向为调用它的对象
如果在一个函数前面加一个new关键字来调用,那么就会创建一个连接到该函数的prototype成员的新对象,同时,this会绑定到这个新对象上.此时,这个函数就可以成为此对象的构造函数
var name='liuqiao';
function Person(){
this.name='wangmiao';
}
var p1=Person()
console.log(p1.name);//报错 Cannot read property 'name' of undefined
这个地方不要被坑了,只是普通的调用函数而已,我们都知道,函数没有返回值,默认返回undefined,p1接收一个值为undefined,那肯定是没有name属性的.所以这里会报错
var name='liuqiao';
function Person(){
this.name='wangmiao';
console.log(this);//Person {name: "wangmiao"}
}
var p1=new Person()
console.log(p1.name);//wangmiao
在构造函数中,new出一个对象时,this指向这个构造函数.new关键字会改变这个this指向
原则上构造函数不应该有返回值,但是如果真的写了返回值,this指向就会有不一样的表现了
var name='liuqiao';
function Person(){
this.name='wangmiao';
return {
name:'yingbin'
};
}
var p1=new Person()
console.log(p1.name);//输出yingbin
当返回复杂对象时,this指向新对象,也就是new Person()
返回的新对象
var name='liuqiao';
function Person(){
this.name='wangmiao';
return 1;
}
var p1=new Person()
console.log(p1.name);//wangmiao
当返回值类型时,this指向不变,this对象实例本身
var name='liuqiao';
function Person(){
this.name='wangmiao';
return null;
}
var p1=new Person()
console.log(p1.name);//wangmiao
虽然typeof null
返回的是object
,但是说到底,它还是值类型的,所以还是返回this
对象实例本身
var oBtn = document.getElementById("btn");
oBtn.onclick = function() {
console.log(this); // btn
}
是oBtn
调用了这个匿名函数,所以指向此时this指向了oBtn
setInterval(function () {
console.log(this); // window
}, 1000);
定时器函数调用时,this指向window
箭头函数内部的this指向,取决于箭头函数定义的位置,而非箭头函数的调用对象.
当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
,并不是因为箭头函数内部有绑定this的机制.实际原因是:箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this.
var name = 'liuqiao';
var obj = {
name: 'zhangsan',
o: () => {
console.log(this.name);
}
}
obj.o(); //liuqiao
var obj = {
foo() {
console.log(this);
},
bar: () => {
console.log(this);
}
}
obj.foo() // {foo: ƒ, bar: ƒ}
obj.bar() // window
var name = 'liuqiao';
var obj = {
name: 'zhangsan',
fn1: function () {
console.log(this.name);
},
fn2: function () {
setTimeout(function () {
console.log(this.name);//liuqiao
},0);
}
}
obj.fn1();//zhangsan
obj.fn2();//liuqiao
上面的这段示例,因为setTimeout里面的this指向的是window,所以,fn1和fn2
的this
不一样,下面示例4,我们将其改造一下即可
var name = 'liuqiao';
var obj = {
name: 'zhangsan',
fn1: function () {
console.log(this.name);
},
fn2: function () {
setTimeout(() => {
console.log(this.name);
}, 0);
}
}
obj.fn1();//zhangsan
obj.fn2();//zhangsan
使用箭头函数,将其改造一下,即可得到我们想要的结果
当我们想用到某一个层的this,但是到了另一层之后,this会发生改变,这个时候,我们会使用一个变量将this给存起来,然后进行使用,如
var _this=this;
var name = 'liuqiao';
var obj = {
name: 'zhangsan',
fn1: function () {
console.log(this.name);
},
fn2: function () {
var _this=this;
setTimeout(function () {
console.log(_this.name);//zhangsan
},0);
}
}
obj.fn1();//zhangsan
obj.fn2();//zhangsan
call(),apply(),bind()这是那种都是强制改变this指向的,作用都是一样相同的,只是传参方式不一样
/*call()方法*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);
var name = 'liuqiao';
var obj = {
name: 'zhangsan',
fn1: function () {
console.log(this.name);
},
fn2: function () {
setTimeout(function () {
console.log(this.name);
}.call(obj), 0);
}
}
obj.fn1();//zhangsan
obj.fn2();//zhangsan
使用call
方法,强制性的将setTimeout
中的this
,改变为obj
var Person = {
uname: "liuqiao",
age: 27
}
function fn(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.uname);
console.log(this.age);
}
fn('lisi', 30);
//lisi,30
//Window
//undefined
//undefined
fn.call(Person, "zhangsan", 28);
//zhangsan 28
//Person{uname: "liuqiao", age: 27}
//zhangsan
//28
对比一下,当fn
方法没有调用call
时,this
指向是window
,当使用call
强制改变this
指向为Person
之后,this
指向发生了变化
经常做继承
/*apply()方法*/
function.apply(thisObj[, argArray])
var name = 'liuqiao';
var obj = {
name: 'zhangsan',
fn1: function () {
console.log(this.name);
},
fn2: function () {
setTimeout(function () {
console.log(this.name);
}.apply(obj), 0);
}
}
obj.fn1();//zhangsan
obj.fn2();//zhangsan
使用apply()
方法,强制性的将setTimeout
中的this
,改变为obj
var Person = {
uname: "liuqiao",
age: 27
}
function fn(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.uname);
console.log(this.age);
}
fn('lisi', 30);
//lisi,30
//Window
//undefined
//undefined
fn.apply(Person, ["zhangsan", 28]);
//zhangsan 28
//Person{uname: "liuqiao", age: 27}
//zhangsan
//28
跟call()
方法一样,apply()
方法同样具备改变this
指向的功能,但是与call()
不同的是,apply()
方法的第二个参数是一个数组,数组里面可以是多项成员,而call()
方法的第二个参数以后可以是多个
经常跟数组有关系
/*bind()方法*/
function.bind(thisArg[, arg1[, arg2[, ...]]])
bind()
方法,同样也能改变this
的指向,与call()
和apply()
不同的是,bind()
不会立即执行,相当于是预设置,把this
的指向预设置了
var Person = {
uname: "liuqiao",
age: 27
}
function fn(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.uname);
console.log(this.age);
}
fn('lisi', 30);
//lisi,30
//Window
//undefined
//undefined
var foo=fn.bind(Person, "zhangsan", 28);
foo();
//zhangsan 28
//Person{uname: "liuqiao", age: 27}
//zhangsan
//28
{/*
2.自定义函数写法
会存在this指向问题,需要使用bind强制改变this指向
注意,必须使用bind才可以,call和 apply不行
原因是call和apply改变this指向后,会立即执行函数
*/}
<button onClick={this.submitLogin.bind(this)}>提交(自定义函数写法)</button>
在React中.我们绑定事件的时候,就会有this指向的问题,但是又不想立即执行这个监听事件的方法,所以使用bind()
最合适不过了.
不调用函数,但是还想改变this指向
作用都是能够强制改变this的指向,并且第一个参数,都是新的this对象,官方点说就是:将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象
(1) call()
可以接受多个参数,第一个参数与另外两个(apply(),bind()一样)
,后面则是一串参数列表
(2) apply()
最多只能接受两个参数,第一个参数与另外两个(call(),bind()一样)
,后面则是一个数组argArray
,如果给该方法传递多个参数,则把参数都写在数组里,即使只有一个参数,也要写进数组里.
(3) bind()
可以接受多参数,第一个参数与另外两个(apply(),call()一样)
,后面则是一串参数列表
(4) call()和apply()
是会立即执行的函数,而bind()是预设值,不会立即执行
(5) bind()
会返回一个新的函数,称为绑定函数
(6) apply和call
是一次性传入参数,而bind
可以分为多次传入。
(7) 应用场景:call()
经常用作继承,apply()
经常跟数组有关系,bind()
不调用函数,但还是想改变this
指向