javascript中this的指向问题
使用 JavaScript 开发的时候,很多开发者多多少少会被 this
的指向搞蒙圈,但是实际上,关于 this
的指向,记住最核心的一句话:
哪个对象调用函数,函数里面的this指向哪个对象。
分几种情况谈论下:
一、普通函数调用
二、对象函数调用
三、构造函数调用
四、apply和call调用
五、箭头函数调用
文章同步交流社区地址:http://www.mwcxs.top/page/427.html
一、普通函数调用
这个情况没特殊意外,就是指向全局对象-window。
1、使用let
/*普通函数调用*/ let username = "程新松"; function fn(){ console.log(this.username); //undefined } fn();
使用node输出的是:undefined
使用谷歌浏览器console输出的是:undefined
2、使用var
var name = "程新松"; function fn(){ console.log(this.name); } fn();
使用node输出的是:undefined
使用谷歌浏览器console输出的是:程新松
3、使用window
window.username='程新松' function fn(){ console.log(this.username); } fn();
使用node输出的是:报错,window没有定义
使用谷歌浏览器console输出的是:
PS:为什么会出现这个错误呢,因为node里面没有window 对象,浏览器中有window对象。
4、let和var区别:
(1)let
允许把变量的作用域限制在块级域中;var 申明变量要么是全局的,要么是函数级的,而无法是块级的。
let varClass = function(){ var name='程新松'; if(true){ var name='saucxs'; console.log(name); } console.log(name); } varClass(); // saucxs // saucxs
let letClass = function(){ let name='程新松'; if(true){ let name='saucxs'; console.log(name); } console.log(name); } letClass();
上面的结果说明了let只在{}内使用。
(2)先let后var
let subClass = function(){ let name='程新松'; if(true){ var name='saucxs'; console.log(name); } console.log(name); } subClass();
var 是函数级作用域,相当于一个作用域中有两个n的变量了
var 作用于整个 subClass ,和let冲突了,let不能重复声明,already been declared=已经被声明
(3)先var后let
let subClass = function(){ var name='程新松'; if(true){ let name='saucxs'; console.log(name); } console.log(name); } subClass();
先申明var,再申明let,这个没有问题。
二、对象函数调用
这个相信不难理解,就是哪个函数调用,this指向哪里。
/*对象函数调用*/ //window.name='程新松'; //var name='程新松'; let name='程新松'; let obj={ id:201102304, fn:function(){ console.log(this.name); //undefined console.log(this.id); //201102304 } } obj.fn();
,,
很明显,第一次就是输出 obj.name
,但是没有这个name属性,输出的结果undefined。而第二次输出obj.id,有这个id属性,
输出 201102304,因为 this
指向 obj
。
有一种情况需要注意:
/*需要注意的情况*/ let obj1={ a:111 } let obj2={ a:222, fn:function(){ console.log(this.a); } } obj1.fn=obj2.fn; obj1.fn(); //111
这个也不难理解,虽然 obj1.fn
是从 obj2.fn
赋值而来,但是调用函数的是 obj1
,所以 this
指向 obj1
。
三、构造函数调用
/*构造函数调用*/ let structureClass=function(){ this.name='程新松'; } let subClass1=new structureClass(); console.log(subClass1.name); let subClass=new structureClass(); subClass.name='成才'; console.log(subClass.name);
但是有一个坑,虽然一般不会出现,但是有必要提一下。
在构造函数里面返回一个对象,会直接返回这个对象,而不是执行构造函数后创建的对象
let structureClass=function(){ this.name='程新松'; return { username:'saucxs' } } let subClass1=new structureClass(); console.log(subClass1); console.log(subClass1.name);
四、apply和call调用
1、apply和call简单来说就是会改变传入函数的this。
/*apply和call调用*/ let obj1={ name:'程新松' }; let obj2={ name:'saucxs', fn:function(){ console.log(this.name); } } obj2.fn.call(obj1);
此时虽然是 obj2
调用方法,但是使用 了 call
,动态的把 this
指向到 obj1
。相当于这个 obj2.fn
这个执行环境是 obj1
。
call
和 apply
两个主要用途:
1.改变 this
的指向(把 this
从 obj2
指向到 obj1
)
2.方法借用( obj1
没有 fn
,只是借用 obj2
方法)
2、call与apply区别
call
和 apply
的作用,完全一样,唯一的区别就是在参数上面。
call
接收的参数不固定,第一个参数是函数体内 this
的指向,第二个参数以下是依次传入的参数。
apply接收两个参数,第一个参数也是函数体内 this
的指向。第二个参数是一个集合对象(数组或者类数组)
/*apply和call区别*/ let fn=function(a,b,c){ console.log(a,b,c); } let arrArray=[1,2,3]; fn.call(window,arrArray); fn.apply(window,arrArray);
五、箭头函数调用
首先不得不说,ES6 提供了箭头函数,增加了我们的开发效率,但是在箭头函数里面,没有 this
,箭头函数里面的 this
是继承外面的环境。
/*箭头函数调用*/ let obj={ name:'程新松', fn:function(){ setTimeout(function(){console.log(this.name)}) } } obj.fn();
不难发现,虽然 fn() 里面的 this 是指向 obj ,但是,传给 setTimeout 的是普通函数, this 指向是 window , window 下面没有 name ,所以这里输出 underfind 。
换成箭头函数
//换成箭头函数 let obj={ name:"程新松", fn:function(){ setTimeout(()=>{console.log(this.name)}); } } obj.fn();
这次输出 程新松 是因为,传给 setTimeout 的是箭头函数,然后箭头函数里面没有 this ,所以要向上层作用域查找,在这个例子上, setTimeout 的上层作用域是 fn 。而 fn 里面的 this 指向 obj ,所以 setTimeout 里面的箭头函数的 this ,指向 obj 。所以输出 程新松。