js中this指向是一個難點,花了很長時間來整理和學習相關的知識點。 一、 this this是JS中的關鍵字, 它始終指向了一個對象, this是一個指針; 參考博文: 1. "JavaScript函數中的this四種綁定形式" 2. "this指向及改變this指向的方法" 二、 this顯示綁 ...
js中this指向是一個難點,花了很長時間來整理和學習相關的知識點。
一、 this
this是JS中的關鍵字, 它始終指向了一個對象, this是一個指針;
參考博文:
二、 this顯示綁定和隱式綁定
1. this顯示綁定
含義: 當一個函數沒有明確的調用對象的時候, 也就是單純作為獨立函數調用的時候, 將對函數的this使用預設綁定: 綁定到全局的window對象
在顯式綁定下: 函數將取得在“ 包含對象“ 里的永久居住權, 一直都會” 住在這裡“
1.1 全局函數
function fire() {
console.log(this === window); //fire此時是全局的方法,this指向window
}
fire(); // 輸出true
1.2 函數內嵌套函數
function fire() {
// 我是被定義在函數內部的函數哦!
function innerFire() {
console.log(this === window); //未明確調用對象,this指向window
}
innerFire(); // 獨立函數調用
}
fire(); // 輸出true
innerFire();
示例:
var a = 1;
console.log(this.a); //1 因為此時this指向了window,因而調用1
function fire() {
var a = 2;
console.log(this.a); //1 因為此時this指向了window,因而調用1
function innerFire() {
var a = 3;
console.log(this.a); //1 因為此時this指向了window,因而調用1
}
innerFire();
}
fire(); //輸出1
與作用域的區別: 全局作用域和局部作用域, 去掉this可發現區別
var a = 1;
console.log(a); //1 a在全局作用域
function fire() {
var a = 2;
console.log(a); // 2 fire函數作用域
function innerFire() {
var a = 3;
console.log(a); //3 此時列印輸出a,a在innerFIre作用域。從自身作用域查找變數,未找到才網上查找
}
innerFire();
}
fire();
1.3 對象內層函數內部函數
var obj = {
fire: function () { //此時的fire函數其實用到了隱式綁定
function innerFire() {
console.log(this === window); //未明確調用對象,this指向window
}
innerFire();
}
}
obj.fire(); //輸出 true
示例:
var a = 1;
console.log(this.a); //1 this指向全局變數window
var obj = {
a: 2,
fire: function () {
var a = 3;
console.log(this.a); //2 因為是obj.fire(),調用了fire函數,因為this指向了obj,輸出了obj下的a=2
function innerFire() {
var a = 4;
console.log(this.a); //1 未明確調用對象,this指向window
}
innerFire(); //沒有明確調用的對象
console.log(this.a); //2 this指向obj
}
}
obj.fire();
2、 this隱式綁定
a.隱式綁定
當函數被一個對象“ 包含” 的時候, 我們稱函數的this被隱式綁定到這個對象裡面, 這時候, 通過this可以直接訪問所綁定的對象裡面的其他屬性, 比如下麵的a屬性
在隱式綁定下: 函數和只是暫時住在“ 包含對象“ 的旅館裡面, 可能過幾天就又到另一家旅館住了
var obj = {
a: 1,
fire: function () { //此時函數的this被隱式綁定到了對象obj
console.log(this == obj); // obj中有fire函數,因而預設this指向obj
console.log(this.a); // 1 this.a 相當於obj.a =1
}
}
obj.fire(); // 輸出1
相同的方法:
fire函數並不會因為它被定義在obj對象的內部和外部而有任何區別,
function fire() {
console.log(this.a)
}
var obj = {
a: 1,
fire: fire
}
obj.fire(); // 輸出1
b.動態綁定:
this實在代碼運行期綁定而不是在書寫期
var obj = {
a: 1, // a是定義在對象obj中的屬性 1
fire: function () {
console.log(this.a)
}
}
var a = 2; // a是定義在全局環境中的變數 2
obj.fire(); //1 此時fire的指向時obj
var fireInGrobal = obj.fire; //因為fireInGrobal是全局變數,this對於obj的綁定丟失,綁定到了全局window
fireInGrobal(); // 輸出 2 輸出全局變數啊
var a = 2;
var obj = {
a: 1, // a是定義在對象obj中的屬性
fire: function () {
console.log(this.a)
}
}
function otherFire(fn) { //全局函數,this綁定window
fn();
}
otherFire(obj.fire); // 輸出2 this對於obj的綁定丟失,綁定到了全局this上面
var obj = {
a: 1,
obj2: {
a: 2,
obj3: {
a: 3,
getA: function () { //obj3.getA() this綁定到了obj3當中
console.log(this.a)
}
}
}
}
obj.obj2.obj3.getA(); // 輸出3
三、 this指向
this的指向不是由定義this決定的, 而是隨腳本解析自動賦值的。
1. 全局環境作用域: this在全局環境始終指向window
變數形式
console.log(this === window) // true
console.log(window.alert === this.alert) // true
console.log(this.parseInt("021", 10)) // 21
var fruit = "banana"; // 定義一個全局變數,等同於window.fruit = "banana"
2. 函數環境 作用域: 函數由誰調用, this就指向誰
2.1 非嚴格模式
function fn() {
console.log(this); //window
}
fn() === window; // true;window.fn(),此處預設省略window
2.2 嚴格模式
a 全局環境下, this指向window
"use strict";
this.b = "MDN";
console.log(this == window) // "MDN"
console.log(b) // "MDN"
b 函數環境下, this為undefined
function fn() {
"use strict"; // 這裡是嚴格模式
console.log(this); //window
}
fn() === undefined //true
3 對象中的方法函數調用: 指向 該方法所屬的對象
隱式調用
var obj = {
a: 1,
fn: function () {
return this;
}
}
console.log(obj.fn() == obj); //true 函數被obj調用,指向obj
this動態綁定
var obj = {
a: 1,
fn: function () {
return this;
}
}
console.log(obj.fn()); //1 函數被obj調用,指向obj,輸出obj的a=1
var a = 2;
var newfun = obj.fn; //此時更改this指向為全局變數newfun
newfun(); //2 ,this訪問全局變數a=2
4 在構造函數中: this始終指向新對象
function Person(age, name) {
this.age = age;
this.name = name
console.log(this) // 此處 this 分別指向 Person 的實例對象 p1 p2
}
var p1 = new Person(18, 'zs')
var p2 = new Person(18, 'ww')
5 通過事件綁定的方法: this 指向 綁定事件的對象
oBtn.onclick = function () {
console.log(this); // btn
}
<
button id = "btn" > hh < /button>
6 定時器函數: 因為是非同步操作, this 指向 window
延時函數內部的回調函數的this指向全局對象window( 當然我們可以通過bind方法改變其內部函數的this指向)
我們常見的window屬性和方法有alter, document, parseInt, setTimeout, setInterval, location等等, 這些在預設的情況下是省略了window首碼的。( window.alter = alter)。
6.1 普通定時器
setInterval(function () {
console.log(this); // window
}, 1000);
6.2 定時器嵌套
function Person() {
this.age = 0;
setTimeout(function () {
console.log(this);
}, 3000);
}
var p = new Person(); //3秒後返回 window 對象
6.3 可以改變this指向 - 想見三方法
function Person() {
this.age = 0;
setTimeout((function () {
console.log(this);
}).bind(this), 3000);
}
var p = new Person(); //3秒後返回構造函數新生成的對象 Person{...}
7 自執行函數(匿名函數): 指向全局變數window
(function inner() {
console.log(this); //this ==> window
})();
8 箭頭函數
要點:
a. 箭頭函數的this是在定義函數時綁定的, 不是在執行過程中綁定的
b. 箭頭函數中的this始終指向父級對象
c. 所有 call() / apply() / bind() 方法對於箭頭函數來說只是傳入參數, 對它的 this 毫無影響。
var obj = {
a: 1,
fn: () => {
//箭頭函數中的this始終指向定義時的環境
//箭頭函數中的this始終指向父級對象
console.log(this); //對象內的this調用時一般指向obj,而箭頭函數在創建時就指向了obj的父級對象window
}
}
obj.fn(); //window
四、 更改this指向
每個Function構造函數的原型prototype, 都有方法
call(), apply(), bind()
總結:
a call(), apply()
在特定作用域調用函數
b bind()
會創建一個函數的實例, this會被綁定到bind() 函數
bing() 綁定this, bind()() 調用函數
1. call() 方法
var Person = {
name: "zhangsan",
age: 19
}
function aa(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.name);
}
aa(4, 5); //this指向window--4,5 window 空
aa.call(Person, 4, 5); //this指向Person--4,5 Person{}對象 zhangsan
2. apply() 方法
apply() 與call() 非常相似, 不同之處在於提供參數的方式, apply() 使用參數數組, 而不是參數列表
var Person = {
name: "zhangsan",
age: 19
}
function aa(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.name);
}
aa.apply(Person, [4, 5]); //this指向Person--4,5 Person{}對象 zhangsan
3. bind() 方法
bind() 創建的是一個新的函數( 稱為綁定函數), 與被調用函數有相同的函數體, 當目標函數被調用時this的值綁定到 bind() 的第一個參數上
var Person = {
name: "zhangsan",
age: 19
}
function aa(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.name);
}
aa.bind(Person, 4, 5); //只是更改了this指向,沒有輸出
aa.bind(Person, 4, 5)(); //this指向Person--4,5 Person{}對象 zhangsan
4. 存儲this指向到變數中
var oDiv1 = document.getElementById("div1");
oDiv1.onclick = function () {
var _this = this; //將this儲存在變數中,而且不改變定時器的指向
setTimeout(function () {
console.log(_this); //註意這裡是_this,而不是this-- <div id="div1">點擊</div>
console.log(this); //定時器的指向沒有被改變--仍然是window
}, 1000)
}
五、 改變this指向綜合案例**
1、 為更改this指向
var oDiv1 = document.getElementById("div1");
oDiv1.onclick = function () {
setTimeout(function () {
console.log(this); //點擊時輸出 window對象
}, 1000)
}
<
div id = "div1" > 點擊 < /div>
2、 更改this指向 - bing, call, apply
在定時器外, 在綁定事件中的this肯定指向綁定事件的對象div啊, 用call和apply都行
var oDiv1 = document.getElementById("div1");
oDiv1.onclick = function () {
setTimeout(function () {
console.log(this); // 更改this指向為 <div id="div1">點擊</div>
}.bind(this), 1000)
}
3、 存儲this指向到變數中
var oDiv1 = document.getElementById("div1");
oDiv1.onclick = function () {
var _this = this; //將this儲存在變數中,而且不改變定時器的指向
setTimeout(function () {
console.log(_this); //註意這裡是_this,而不是this-- <div id="div1">點擊</div>
console.log(this); //定時器的指向沒有被改變--仍然是window
}, 1000)
}