当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包括函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。 this 就是这个记录的一个属性,会在函数执行的过程中用到。
随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。
一、作为函数调用
函数直接调用时的this
console.log(this); // window
function fn(){
console.log(this);
}
fn(); // 在函数被直接调用时this绑定到全局对象。在浏览器中,window 就是该全局对象
内部函数的this
函数嵌套产生的内部函数的this不是其父函数,仍然是全局变量
function fn(){
function fn1(){
console.log(this);
}
fn1();
}
fn(); // window
改写一下:
function fn(){
console.log('这是fn')
console.log(this); // window
function fn1(){
console.log('这是fn1')
console.log(this); // window
function fn2() {
console.log('这是fn2')
console.log(this); // 依旧是window
}
fn2 ()
}
fn1();
}
fn();
setTimeout、setInterval 中的this
function fn(){
console.log(this); // window
setTimeout(function(){
console.log(this); // window
}, 200);
}
fn()
二、 作为构造函数调用
作为构造函数调用时,this指向实例对象
function Cat(name){
this.name = name;
}
Cat.prototype.sayName = function(){
console.log(this.name);
};
var p1 = new Cat('Munchkin');
p1.sayName(); // Munchkin
三、作为对象方法调用
- 隐式绑定, 直接调用
cat.fn()
var cat = {
name: 'Munchkin',
fn : function(){
console.log(this);
}
};
cat.fn(); // 指向调用函数的对象 cat
对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
- 常见陷阱 ( “隐式丢失”): 将cat.fn 赋值给其他对象再调用
var fn2 = cat.fn;
fn2(); // window
- 将对象方法作为参数传递给函数,然后调用函数
function foo(){
console.log(this.a)
}
function doFoo(fn) {
console.log(fn)
fn(); // <-- 调用位置!
}
var obj = {
a: 2,
foo: foo
}
var a = "oops, global";
doFoo(obj.foo); // "oops, global"
参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值(与上个例子类似)。
四、 DOM对象绑定事件
在事件处理程序中this代表事件源DOM对象(低版本IE有bug,指向了window)
<body>
<button>click</button>
<script>
let btn = document.querySelector('button');
btn.addEventListener('click', function(e){
console.log(this); // <button>click</button>
var _document = this;
setTimeout(function(){
console.log(this); // window
console.log(_document); // <button>click</button>
}, 200);
}, false);
</script>
</body>
五、使用 call ,apply ,bind 改变this 指向(显式绑定)
Function.prototype.call
fn.call(context, param1, param2...)
Function.prototype.apply
fn.apply(context, paramArray)
Function.prototype.bind
bind,返回一个新函数,并且使函数内部的this为传入的第一个参数
var fn3 = obj1.fn.bind(obj1);
fn3();
三者比较
共同点
- 都可以改变 this 指向,第一个参数都是希望设置的this对象
不同点
- call 和 apply的不同之处在于call方法接收参数列表,而apply接收参数数组; - call 和 apply 都是直接调用函数,而bind 是返回一个改变了this 指向的新函数,供后面调用(不是直接调用)
七、 关于 this 的题目
var name = 'window'
var person1 = {
name: 'person1',
show1: function() {
console.log(this.name)
},
show2: () => console.log(this.name),
show3: function() {
return function() {
console.log(this.name)
}
},
show4: function() {
return () => console.log(this.name)
}
}
var person2 = {
name: 'person2'
}
以下各输出什么:
person1.show1()
person1.show1.call(person2)
person1.show2()
person1.show2.call(person2)
person1.show3()()
person1.show3().call(person2)
person1.show3.call(person2)()
person1.show4()()
person1.show4().call(person2)
person1.show4.call(person2)()
参考:
- 《你不知道的JS》 P82~ P101
网友评论