this 是和執行上下文綁定的,也就是說每個執行上下文中都有一個 this。
上圖中 outer 是在變量環境裏面的,爲了方便看
我們都知道 js 在編譯階段 創建執行上下文
在每個執行上下文的變量環境中,都包含了一個外部引用,用來指向外部的執行上下文,我們把這個外部引用稱爲 outer
全局執行上下文中的 this:
全局執行上下文中的 this 是指向 window 對象的。這也是 this 和作用域鏈的唯一交點,作用域鏈的最底端包含了 window 對象,全局執行上下文中的 this 也是指向 window 對象
函數中的this:
function foo(){
console.log(this)
}
foo()
// 也是指向window
那能不能設置執行上下文中的 this 來指向其他對象呢? 肯定是可以的
1、通過函數的 call 方法設置:
let bar = {
myName : "極客邦",
test1 : 1
}
function foo(){
this.myName = "極客時間"
}
foo.call(bar)
console.log(bar) // {myName: "極客時間", test1: 1}
console.log(myName) // myName is not defined
你就能發現 foo 函數內部的 this 已經指向了 bar 對象,
因爲通過打印 bar 對象,可以看出 bar 的 myName 屬性已經由“極客邦”變爲“極客時間”了,
同時在全局執行上下文中打印 myName, myName is not defined
2、通過對象調用方法設置:
要改變函數執行上下文中的 this 指向,除了通過函數的 call 方法來實現外,還可以通過對象調用的方式
var myObj = {
name : "極客時間",
showThis: function(){
console.log(this)
}
}
myObj.showThis() // {name: "極客時間", showThis: ƒ}
使用對象來調用其內部的一個方法,該方法的 this 是指向對象本身的。
var myObj = {
name : "極客時間",
showThis: function(){
this.name = "極客邦"
console.log(this)
}
}
var foo = myObj.showThis
foo()
//你會發現 this 又指向了全局 window 對象。
- 在全局環境中調用一個函數,函數內部的 this 指向的是全局變量 window。
- 通過一個對象來調用其內部的一個方法,該方法的執行上下文中的 this 指向對象本身。
3. 通過構造函數中設置:
function CreateObj(){
this.name = "極客時間"
console.log(this) // CreateObj {name: "極客時間"}
}
var myObj = new CreateObj()
new CreateObj() 過程:
1、首先創建了一個空對象 tempObj;
2、接着調用 CreateObj.call 方法,並將 tempObj 作爲 call 方法的參數,這樣當 CreateObj 的執行上下文創建時,它的 this 就指向了 tempObj 對象;
3、然後執行 CreateObj 函數,此時的 CreateObj 函數執行上下文中的 this 指向了 tempObj 對象;
4、最後返回 tempObj 對象。
嵌套函數中的 this 不會從外層函數中繼承
var myObj = {
name : "極客時間",
showThis: function(){
console.log(this)
function bar(){console.log(this)}
bar()
}
}
myObj.showThis()
//函數 bar 中的 this 指向的是全局 window 對象,
// 而函數 showThis 中的 this 指向的是 myObj 對象
那怎麼實現 bar 的this指向外層 myObj 呢?
var myObj = {
name : "極客時間",
showThis: function(){
console.log(this)
var self = this
function bar(){
self.name = "極客邦"
}
bar()
}
}
myObj.showThis()
console.log(myObj.name)
console.log(window.name)
本質是把 this 體系轉換爲了作用域的體系 。也可以使用 ES6 中的箭頭函數來解決這個問題
var myObj = {
name : "極客時間",
showThis: function(){
console.log(this) // {name: "極客時間", showThis: ƒ}
var bar = ()=>{
this.name = "極客邦"
console.log(this) // {name: "極客邦", showThis: ƒ}
}
bar()
}
}
myObj.showThis()
console.log(myObj.name) // 極客邦
console.log(window.name) // ''
因爲 ES6 中的箭頭函數並不會創建其自身的執行上下文,所以箭頭函數中的 this 取決於它的外部函數
- 當函數作爲對象的方法調用時,函數中的 this 就是該對象;
- 當函數被正常調用時,在嚴格模式下,this 值是 undefined,非嚴格模式下 this 指向的是全局對象 window;
- 嵌套函數中的 this 不會繼承外層函數的 this 值
箭頭函數和普通函數的區別:
箭頭函數和普通函數的this:
1、this 指向不同:
- 普通函數this 指向 爲方法調用的對象,可以通過bind,call,apply,改變this指向
- 箭頭函數比函數表達式更簡潔,箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。bind,call,apply只能調用傳遞參數,不可修改this指向
var obj = {
a: 10,
b: () => {
console.log(this.a); // undefined
console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
},
c: function() {
console.log(this.a); // 10
console.log(this); // {a: 10, b: ƒ, c: ƒ}
}
}
obj.b();
obj.c();
箭頭函數不綁定this,會捕獲其所在的上下文的this值,作爲自己的this值。
任何方法都改變不了其指向
var obj = {
a: 10,
b: function(){
console.log(this.a); //10
},
c: function() {
return ()=>{
console.log(this.a); //10
}
}
}
obj.b();
obj.c()();
箭頭函數通過 call() 或 apply() 方法調用一個函數時,只傳入了一個參數,對 this 並沒有影響。
補充:call,aplly,bind:
它們在功能上是沒有區別的,都是改變this的指向,它們的區別主要是在於方法的實現形式和參數傳遞上的不同。call和apply方法都是在調用之後立即執行的。而bind調用之後是一個函數,需要再調用一次纔行,