js中this的指向问题
this指向性问题在开发过程中,可以说是时时刻刻都在,自己也知道一些this指向的区别,但是并没有细致的研究过,今天看到https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this里面的解释很详细,摘录一些留着自己以后复习复习。
函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
一、在全局环境中
在全局执行环境中(在任何函数体外部),this都是指向全局对象。在浏览器中,window对象即是全局对象:
console.log(this); //Window var a = 1; console.log(window.a); //1 this.b = 3; console.log(b); // 3 console.log(window.b) //3
二、在函数环境中
在函数内容,this指向取决于函数调用的方式:
function f(){ "use strict"; //使用严格模式 console.log(this); } f(); // window ;使用严格模式时,输出undefined
这里我理解为实际调用函数的是浏览器的window.f();实际并非如果,在严格模式下,返回值:false,
因为f
是被直接调用的,而不是作为对象的属性或方法调用的(如window.f()
)。浏览器可能在支持严格模式时没有正确实现这个功能,于是它们
错误地返回了window
对象。
this指向如何发生改变?
1、一般想到的是call和apply方法:将一个对象作为call或者apply的第一个参数,this将会被绑定到这个参数对象上
var obj = {parent:'男'}; var parent = '28'; function child(obj){ console.log(this.parent); } child(); // 28 child.call(obj); //男 child.apply(obj); //男
2、bind方法,调用f.bind(someObject)
会创建一个与f
具有相同函数体和作用域的函数,但是在这个新函数中,this
将永久地被绑定到了bind
的第一个参数,不管函数是怎样调用的。
function f(){ return this.a; } var g = f.bind({a:"js"}); console.log(g()); // js var h = g.bind({a:'html'}); // this已经被绑定bind的第一个参数,不会重复绑定,输出的值还是js console.log(h()); // js var o = {a:css, f:f, g:g, h:h}; console.log(o.f(), o.g(), o.h()); // css, js, js
3、箭头函数
官方有解释,箭头函数引入的其中一个原因,就是其不绑定this;在箭头函数中,箭头函数的this
被设置为封闭的词法环境的,换句话说,箭头函数中的this取决于该函数被创建时的环境。
var objProject = this; var foo = (() => this); console.log(foo()); // window console.log(objProject); // window console.log(foo() === objProject ); // true // 作为对象的一个方法调用 var obj = {foo: foo}; console.log(obj.foo() === objProject ); // true // 尝试使用call来设定this console.log(foo.call(obj) === objProject ); // true // 尝试使用bind来设定this foo = foo.bind(obj); console.log(foo() === objProject ); // true
4、作为对象的方法调用时
当函数作为对象的方法被调用时,this指向调用的该函数的对象:
var obj = { a: 37, fn: function() { return this.a; } }; console.log(obj.fn()); // 37
请注意,这样的行为,根本不受函数定义方式或位置的影响。在前面的例子中,我们在定义对象obj
的同时,将函数内联定义为成员 fn
。但是,我们也可以先定义函数,然后再将其附属到obj.fn
。这样做会导致相同的行为:
var obj = {a: 47}; function independent() { return this.a; } obj.fn = independent; console.log(obj); //{a:47,fn:f} console.log(obj.fn()); // 47
对于在对象原型链上某处定义的方法,this
指向的是调用这个方法的对象,就像该方法在对象上一样
var o = { f: function() { return this.a + this.b; } }; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5
在这个例子中,对象p
没有属于它自己的f
属性,它的f属性继承自它的原型。虽然在对 f
的查找过程中,最终是在 o
中找到 f
属性的,这并没有关系;查找过程首先从 p.f
的引用开始,所以函数中的 this
指向p
。也就是说,因为f
是作为p
的方法调用的,所以它的this
指向了p
。
5、作为构造函数
当一个函数用作构造函数时(使用new关键字),它的this
被绑定到正在构造的新对象。
虽然构造器返回的默认值是this
所指的那个对象,但它仍可以手动返回其他的对象(如果返回值不是一个对象,则返回this
对象)。
function C(){ this.a = 37; } var o = new C(); console.log(o.a); // 37 function C2(){ this.a = 37; return {a:38}; } o = new C2(); console.log(o.a); // 38,手动设置了返回对象
6、作为DOM事件处理函数
当函数被用作事件处理函数时,它的this
指向触发事件的元素(一些浏览器在使用非addEventListener
的函数动态添加监听函数时不遵守这个约定)。
// 被调用时,将关联的元素变成蓝色 function bluify(e){ console.log(this === e.currentTarget); // 总是 true // 当 currentTarget 和 target 是同一个对象时为 true console.log(this === e.target); this.style.backgroundColor = '#A5D9F3'; } // 获取文档中的所有元素的列表 var elements = document.getElementsByTagName('*'); // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色 for(var i=0 ; i<elements.length ; i++){ elements[i].addEventListener('click', bluify, false); }
关于this问题的一道面试题:
var baz = 0; let foo = { bar:function() { console.log(this,this.baz); return this.baz; }, baz:1 }; let foo2 = { baz:2 }; let a = foo.bar(); //作为对象的方法调用,this指向调用函数的对象,即foo let b = foo.bar.call(foo2); //使用call方法将this绑定到第一个参数对象向,此时,this指向foo2 let fn = foo.bar; console.log(fn); let c = fn(); //let fn创建的对象,此时,fn = function(){...},此时函数执行时,默认指向全局window对象 let d; (function test(){ d = arguments[0]() })(foo.bar); // arguments.callee包括了一个函数的引用去创建一个arguments对象,它能让一个匿名函数很方便的指向本身,即此时this指向arguments类数组对象 console.log(a,b,c,d);
结果如下: