當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)溯祸,會(huì)創(chuàng)建一個(gè)活動(dòng)記錄(執(zhí)行上下文)肢专。這個(gè)記錄會(huì)包括函數(shù)在哪里被調(diào)用(調(diào)用棧)、函數(shù)的調(diào)用方式焦辅、傳入的參數(shù)等信息博杖。 this 就是這個(gè)記錄的一個(gè)屬性,會(huì)在函數(shù)執(zhí)行的過程中用到筷登。
隨著函數(shù)使用場(chǎng)合的不同剃根,this的值會(huì)發(fā)生變化。但是有一個(gè)總的原則前方,那就是this指的是狈醉,調(diào)用函數(shù)的那個(gè)對(duì)象。
一惠险、作為函數(shù)調(diào)用
函數(shù)直接調(diào)用時(shí)的this
console.log(this); // window
function fn(){
console.log(this);
}
fn(); // 在函數(shù)被直接調(diào)用時(shí)this綁定到全局對(duì)象苗傅。在瀏覽器中,window 就是該全局對(duì)象
內(nèi)部函數(shù)的this
函數(shù)嵌套產(chǎn)生的內(nèi)部函數(shù)的this不是其父函數(shù)班巩,仍然是全局變量
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()
二、 作為構(gòu)造函數(shù)調(diào)用
作為構(gòu)造函數(shù)調(diào)用時(shí),this指向?qū)嵗龑?duì)象
function Cat(name){
this.name = name;
}
Cat.prototype.sayName = function(){
console.log(this.name);
};
var p1 = new Cat('Munchkin');
p1.sayName(); // Munchkin
三逊桦、作為對(duì)象方法調(diào)用
- 隱式綁定眨猎, 直接調(diào)用
cat.fn()
var cat = {
name: 'Munchkin',
fn : function(){
console.log(this);
}
};
cat.fn(); // 指向調(diào)用函數(shù)的對(duì)象 cat
對(duì)象屬性引用鏈中只有上一層或者說最后一層在調(diào)用位置中起作用。
- 常見陷阱 ( “隱式丟失”): 將cat.fn 賦值給其他對(duì)象再調(diào)用
var fn2 = cat.fn;
fn2(); // window
- 將對(duì)象方法作為參數(shù)傳遞給函數(shù)强经,然后調(diào)用函數(shù)
function foo(){
console.log(this.a)
}
function doFoo(fn) {
console.log(fn)
fn(); // <-- 調(diào)用位置!
}
var obj = {
a: 2,
foo: foo
}
var a = "oops, global";
doFoo(obj.foo); // "oops, global"
參數(shù)傳遞其實(shí)就是一種隱式賦值睡陪,因此我們傳入函數(shù)時(shí)也會(huì)被隱式賦值(與上個(gè)例子類似)。
四匿情、 DOM對(duì)象綁定事件
在事件處理程序中this代表事件源DOM對(duì)象(低版本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码秉,返回一個(gè)新函數(shù)逮矛,并且使函數(shù)內(nèi)部的this為傳入的第一個(gè)參數(shù)
var fn3 = obj1.fn.bind(obj1);
fn3();
三者比較
共同點(diǎn)
- 都可以改變 this 指向,第一個(gè)參數(shù)都是希望設(shè)置的this對(duì)象
不同點(diǎn)
- call 和 apply的不同之處在于call方法接收參數(shù)列表转砖,而apply接收參數(shù)數(shù)組; - call 和 apply 都是直接調(diào)用函數(shù)鲸伴,而bind 是返回一個(gè)改變了this 指向的新函數(shù)府蔗,供后面調(diào)用(不是直接調(diào)用)
七、 關(guān)于 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