概念
this 是當前函數/當前模塊的運行環境上下文。是一個指針型變量,普通函數中的 this 是在調用時才被綁定確認指向的。
通過不同的 this 調用同一個函數,可以產生不同的結果。
到底如何確認 this 綁定的內容是什麼?
this 綁定的規則
1.默認綁定
function a() {}
a();
函數獨立調用的時候,不帶任何修飾的函數引用.
- 非嚴格模式下 this 指向全局對象
var a = 'hello';
var obj = {
a: 'world',
foo: function () {
console.log(this.a);
},
};
let bar = obj.foo;
bar(); // hello 在調用的時候是直接調用的。所以是指向全局的this
// obj.foo(); // world
- 嚴格模式下,this 指向 undefined,嚴格模式下,不允許指向全局對象。
var a = 'hello';
var obj = {
a: 'world',
foo: function () {
'use strict';
console.log(this.a);
},
};
let bar = obj.foo;
bar(); // 會報錯因為this是指向的undefined
- 普通函數作為參數傳遞的情況,setTimeOut,setInterval, 非嚴格模式下 this 指向全局對象。
var name = 'hello';
var person = {
name: 'world',
sayHi: sayHi,
};
function sayHi() {
console.log(this); // person
setTimeout(function () {
// 指向了全局對象
console.log(this.name); // hello
});
}
person.sayHi();
2.隱式綁定
與默認綁定相反,函數調用的時候有顯式的修飾,
var a = 'hello';
var obj = {
a: 'world',
foo: function () {
console.log(this.a);
},
};
// 誰調用 這個函數(foo)誰就是這個函數的this
obj.foo();
- 在鏈式調用的情況下,this 就近指向
function sayHi() {
console.log(this.name);
}
var person1 = {
name: 'hello',
sayHi: sayHi,
};
var person2 = {
name: 'world',
friend: person1,
};
// 鏈式調用的時候是就近指向
person2.friend.sayHi();
顯式綁定
call apply bind 可以修改函數的 this 指向
call 和 apply 的異同
- 都是改變 this 指向,然後執行原有函數
- 第一個參數都是作為 this 的,綁定到函數體的 this 上,如果不傳參數 fun.cal() , 非嚴格模式下,this 會綁定到全局對象
func.call(this, arg1, arg2, ..., argn);
func.apply(this, [arg1, ..., argn])
// 只有object tostring 可以返回類型
Object.prototype.toString.call(obj) === '[object Array]'
- 如果 call 的第一個參數,傳了數字或者字符串等基本類型 會發生什麼?
得到字符串的對象,數字的對象 [Number: 1] [String: 'lubai'] 等
bind
bind 方法,會創建一個新的函數,
當這個新函數被調用的時候,bind 的第一個參數會作為函數運行時的 this,之後的一系列參數都會在傳遞實參前傳入作為它的函數.
var account = {
name: 'hello',
author: 'world',
subscribe: function (subsc) {
console.log(`${subsc} ${this.name}`);
},
};
account.subscribe('微言'); // 微言 hello
var subsc1 = account.subscribe.bind({ name: 'name', author: 'author' }, 'subsc');
subsc1(); // subsc name (顯式綁定的優先級,比隱式綁定的優先級高)
new
- 創建一個空對象
- 將空對象的\_\_proto\_\_指向原型對象的 prototype
- 以新對象為 this 執行原有的構造函數
- return
function Study(name) {
this.name = name;
}
var study = new Study('hello');
console.log(study.name);
5. this 綁定的優先級
new 綁定 > 顯式(bind, call, apply)綁定 > 隱式綁定(obj.foo()) > 默認綁定(foo())
// 顯式綁定的優先級,比隱式綁定高
function foo(a) {
// console.log(this.a);
this.a = a;
}
var obj1 = {
a: 2,
foo: foo,
};
var obj2 = {
a: 3,
foo: foo,
};
// 隱式綁定
obj1.foo(); // 2
obj2.foo(); //3
// 將this的指向進行強行更改
obj1.foo.call(obj2); // 3
obj2.foo.call(obj1); // 2
function foo(a) {
// console.log(this.a);
this.a = a;
}
var obj1 = {
foo: foo,
};
var obj2 = {};
obj1.foo(2);
console.log(obj1.a); // 2
// 強行把foo的參數指向了obj2上
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3
// new 將obj1指向bar 相當於 bar.a = a, 並不會改變obj1這個對象
var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4
function foo(a) {
this.a = a;
}
var obj1 = {};
// bind 執行的this改變成obj1
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
var barNew = new bar(3);
// new的過程中,以新對象為this運行原來的構造函數 function foo(a) {this.a = a}
// 新對象是 barNew
console.log(obj1.a); // 2
console.log(barNew.a); // 3
箭頭函數
- 箭頭函數沒有arguments
- 箭頭函數沒有構造函數 (本身沒有constructor)
- 沒有自己的this (this的指向是由定義箭頭函數的位置決定的,而普通函數是調用的時候才確定的)
題目練習
-
題目1
var a = 123; function getA() { console.log(this.a) // undefined } getA()
var a = 123;
var obj = {
a: 456,
getA: function() {
// console.log(this) 這裏的this指向obj
function getAA() { // 裏面有獨立的this
console.log(this.a) // undefined
}
getAA() // 默認綁定
}
}
obj.getA() // undefined
-
題目2
var length = 10; function fn() { console.log(this.length) } var obj = { length: 5, method: function(fn) { // this = obj // fn 是一個參數,以函數為參數的 this 指向全局。 fn(); 10 // arguments{0:fn, 1: 1, length:2} 相當於隱式綁定綁定到了fn對象上 arguments[0]() } } obj.method(fn, 1)