javacript中的this指向问题
this提供了一种更优雅的方式来“传递一个对象的引用
1 浏览器全局环境和node全局环境中的this
浏览器中的this指向windows,window为浏览器的全局对象。
console.log(this===window);//true
node环境中this指向为一个空对象。而global为node的全局对象。
console.log(this);//{}, console.log(this===global)://false
2 箭头函数和function关键字函数的this指向总结。
2.1 function关键字函数
(1)一般情况this 指向它的调用对象,如果没有调用者则指向全局对象。
(2)在构造函数下(也就是new function()),this指向被创建的新对象。
(3)在使用js监听dom事件情况下,this指向触发事件的元素。
(4)apply,call和bind可以强制改变this的指向,使this的指向为三个处理方法中上送的一个参数对象。
(5) 在html元素上使用内联事件时,如果调用js中方法或书写代码的情况下this指向当前元素。如果在事件直接使用()()立即执行,则非严格模式下this指向window,严格模式下指向undefined。
2.2 非箭头函数
指向箭头函数定义时所处的对象,而不是箭头函数使用(调用)时所在的对象,默认使用父级的this。
this等同于上一层非箭头函数的this值或全局对象(window或undefined)(严格模式this是undefined)
3 各种情况下的示例分析。
3.1 在全居环境下定义和调用。
function 关键字定义函数
function fun(){ // "use strict"; // 这里是严格模式 return this; } //true 浏览器环境 这里是非严格模式 在严格模式下指向undefined console.log(fun()===window); //treu node环境 这里是非严格模式 在严格模式下指向undefined console.log(fun()===global);
箭头函数
let foo=()=>{ // "use strict"; // 这里是严格模式 return this; } this.a="5"; //true 浏览器环境。严格模式与非严格模式一致 console.log(foo()===window); //true node环境下 全局的this为一个空对象{},所以这里this指向的是空对象。严格模式与非严格模式一致 console.log(foo()===this); //很显然这里定义的箭头函数指向的是,定义位置所处环境的this。严格模式与非严格模式一致 console.log(foo());//{a:'5'},
3.2 对象中this的调用
function 关键字定义函数
let obj={ a:"aa", foo:function(){ return this; } } //true 浏览器环境与node环境一致。 console.log(obj.foo()===obj); //obj中没有this这个属性,这里等于undefined。 //console.log(obj.this); let a=obj.foo; console.log(a()) //node 为global 浏览器为window
注:对于多层嵌套对象的调用,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window)。
箭头函数
let obj={ a:"aa", foo:()=>{ return this; } } //true 浏览器环境与node环境一致。 console.log(obj.foo()===window); //在node中,指向全局this中指向的空对象。 this.a="aa"; //{a:'aa'} node环境 console.log(obj.foo());
注:对于嵌套的多层对象来说,都是指向window。
3.3 构造函数中的this
function 关键字定义函数
当构造函数没有返回时 this指向new 所以创建的新对象。 浏览器环境与node环境是一致的。
function C(){ console.log(this); //{} this.a='a'; } let d=new C(); console.log(d);//{a:'a'}
注:由于箭头函数的this更像是进行了某种操作透传(继承)作用域的this,因此箭头函数我的理解是没有this的, 所以箭头函数不能作为构造函数。会抛出一个VM1565:5 Uncaught TypeError: C is not a constructor
3.4 call,apply和bind强制改变this的指向
function 关键字定义函数 function foo(){ console.log(this); console.log(...arguments); } let a={b:'b',foo:foo}; foo(); //浏览器环境输出 window ;node环境输出 global a.foo(); //输出a对象。 let c={c1:'c1'}; let d=[1,2,3,4]; foo.call(c,...d);//{ c1: 'c1' },1 2 3 4 foo.apply(c,d);//{ c1: 'c1' },1 2 3 4 let foo2 = foo.bind(c,...d); foo2(88,99); //{ c1: 'c1' } ,1 2 3 4 88 99
注;由于箭头函数没有自身的this,它的this是继承的,所以无法强制改变this的指向。箭头函数也没有arguments。
但如果强制使用不能改变其this指向。可以传入函数定义时的指定参数个数。
let foo=(a,b,c)=>{ console.log(this); //浏览器环境 this都输出为window。node环境 this都输出为{} console.log(a,b,c);//都输出为1,2,3 } let c={c1:'c1'}; let d=[1,2,3,4]; foo.call(c,...d); foo.apply(c,d); let foo2 = foo.bind(c,...d); foo2(88,99);
3.5 dom事件和内联事件
dom绑定事件
<div>我是dom绑定事件</div> <span>我是span</span> <button >哈哈哈</button> <script> // 被调用时,将关联的元素变成蓝色 function doClick(e){ //在控制台打印出所点击元素 console.log(this); //阻止时间冒泡 e.stopPropagation(); //阻止元素的默认事件 e.preventDefault(); this.style.backgroundColor = '#A5D9F3'; } // 获取文档中的所有元素的列表 var elements = document.getElementsByTagName('*'); // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色 for(var i=0 ; i<elements.length ; i++){ elements[i].addEventListener('click', doClick, false); } </script>
内联事件:
<button onclick="console.log(this)" >click me </span>
<button onclick="(function(){console.log(this)})()" >Non strict mode</span>
<button onclick="(()=>{console.log(this)})()" >Non strict mode Arrow function </span>
<button onclick="(function(){'use strict'; console.log(this)})()" >use strict</button>
3.6 setTimeout & setInterval
function 关键字定义函数
let a={a:'aa'}; setTimeout(function(){console.log(this)},2000); //浏览器环境过两秒输出 window ;node环境输出 一个Timeout对象。 setTimeout((function(){console.log(this)}).bind(a),3000); //可通过bind改变this指向
箭头函数
setTimeout(()=>{console.log(this)},2000); //浏览器环境,输出window,node环境输出{}
最后个人总结:箭头函数本身是没有this的,其this是继承函数定义(如果定义的作用域也是箭头函数,则往上继续继承,知道作用域有this为止)时所处作用域链中离它最近的this。
非常感谢很多前辈的优秀博客为我提供了巨大的帮助,感谢名单在此不一一列举。本博客仅供参考学习,不做盈利目的。如果错误,欢迎指正。