這裏來個倒敘,先說一下this的幾種狀況指向:javascript
下面開始:php
我本身結合網上的定義,給出了this的定義:java
函數中this指向了執行時,調用它的而且離它最近的對象
不過這個定義只適用於大部分狀況:數組
先看一段代碼:markdown
function fn() {
console.log(this);
}
fn();//window
若是你知道在全局做用域中定義函數變量和函數實際是在window變量上添加屬性和方法的話,那麼上面的代碼就很好理解了,上面的代碼至關於:app
function fn() {
console.log(this);
}
window.fn();//window
最後調用這個函數的對象是window對象,因此this就指向了window,再看:dom
var obj = {
fn: function() {
console.log(this);
}
}
obj.fn(); //this指向obj,至關於window.obj.fn();
上面的代碼咱們能夠看到是window的obj對象調用了fn這個函數,因此定義的時候咱們強調是離它最近的調用對象,這裏obj離得近,this就指向了obj。函數
定義時還強調了是執行時,是什麼意思呢?接着看代碼:ui
function fn() {
console.log(this)
}
var obj = {
fn: fn } obj.fn(); //this指向obj
定義時,函數是在全局中定義的,可是執行時咱們是利用obj來調用,它就指向了obj,若是還不太肯定,咱們再看:this
var obj = {
fn: function() {
console.log(this)
}
}
var fn = obj.fn;
fn(); //this指向了window
這下相信了吧,下面介紹幾種比較特殊的狀況:
匿名函數具備全局性,因此this指向的是window。
(function(){ console.log(this); //window })()
var obj = {
fn: function() {
(function(){
console.log(this);
})()
}
}
obj.fn(); //window
上面第二段代碼中,儘管是obj調用了fn函數,可是在fn函數中的匿名函數仍然具備全局性,因此this仍然指向window。
普通調用是什麼意思呢?就是形如:fn();不是做爲某個對象的方法。上面有段代碼:
var obj = {
fn: function() {
console.log(this);
}
}
obj.fn(); //this指向obj
咱們稍微修改一下:
var obj = {
fn: function() {
function innerFn() {
console.log(this);
}
innerFn();
}
}
obj.fn(); //輸出window
是否是有些懵逼了,儘管innerFn是在obj的fn函數中被調用的,可是它的做用域鏈上活動對象只有innerFn和全局自己(ES6之前,JavaScript做用域只有函數域),我猜想,在利用obj.fn()調用的時候,JavaScript內部是作了this指向處理的,而普通調用就指向了全局。
有人可能會問,若是我要調用外層中的this怎麼辦?一般咱們會使用一個變量來保存this,例如:
var obj = {
fn: function() {
var self = this;
function innerFn() {
console.log(self);
}
innerFn();
}
}
obj.fn(); //輸出obj
setTimeout(function(){
console.log(this); //window
}, 1000)
var obj = {
fn: function() {
console.log(this);
}
}
setTimeout(obj.fn, 1000) //輸出window
setTimeout回調函數你能夠看作是下面這樣:
var obj = {
fn: function() {
console.log(this);
}
}
var callback = obj.fn;
//在設定時間後執行回調函數
callback();
上面你可能就很熟悉了,調用fn函數的是全局對象,因此指向了window對象,若是你想改變定時器中函數this的指向,可使用bind函數:
var obj = {
fn: function() {
console.log(this);
}
}
setTimeout(obj.fn.bind(obj), 1000) //輸出obj
在JavaScript中咱們能夠這樣綁定一個事件:
<div id="div">這是一個div元素</div>
function doClickDiv(e) {
//to do something
console.log(this);
}
var oDiv = document.getElementById('div');
//綁定點擊事件
oDiv.addEventListener('click', doClickDiv, false);
當點擊時,輸出時this指向了div這個節點,早期綁定事件的寫法能夠幫助咱們理解:
function doClickDiv(e) {
//to do something
console.log(this);
}
var oDiv = document.getElementById('div');
//綁定點擊事件
oDiv.onclick = doClickDiv;
當點擊div時,會觸發oDiv.onclick函數,至關於oDiv.onclick(),這和使用對象調用是同樣的。
JavaScript中的函數是能夠做爲構造函數的,使用new便可,那麼this在這種狀況下指向是什麼?
function Person() {
this.age = 18;
this.job = 'student';
}
var person = new Person();
console.log(person)//{age: 18, job: 'student'}
咱們能夠知道person是一個實例,因此函數做爲構造函數時,this是指向實例的,在構造函數中實際是這樣的:
function Person() {
//隱藏着的語句
//this = {} 這裏只是簡單說明this是一個對象,它還要關聯Person函數的原型
this.age = 18;
this.job = 'student';
//隱藏着的語句
//return this;
}
var person = new Person();
console.log(person)//{age: 18, job: 'student'}
從上面咱們能夠看出,構造函數隱式return了this,因此person就是this,可是當構造函數有return語句時,this並不必定指向person。
當return返回一個對象時:
function Person() {
this.age = 18;
this.job = 'student';
return {
tip: 'this is an object'
}
}
var person = new Person();
console.log(person)//{tip: "this is an object"}
當return回一個非對象值時:
function Person() {
this.age = 18;
this.job = 'student';
return 1;
}
var person = new Person();
console.log(person)//{age: 18, job: 'student'}
有時咱們須要改變this的指向,就能夠經過這三個方法函數來實現:
call和apply:
var prop = 'window';
function fn() {
console.log(this.prop);
}
var obj1 = {
prop: 'obj1',
fn: function(){
console.log(this.prop);
}
}
var obj2 = {
prop: 'obj2',
fn: function(){
console.log(this.prop);
}
}
fn(); // 'window'
obj1.fn(); // 'obj1'
obj2.fn(); // 'obj2'
fn.call(obj1) // 'obj1'
fn.apply(obj1) // 'obj1'
obj1.fn.call(obj2); // 'obj2'
obj1.fn.apply(obj2); // 'obj2'
從改變this指向來講,call和apply是同樣的,它們兩個函數的區別在於傳參數形式的不一樣:
var obj = {
a: 1,
b: 2,
c: 3
}
function add(a, b, c) {
console.log(this);
return a + b + c;
}
add.call(obj, obj.a, obj. b, obj.c);
add.apply(obj, [obj.a, obj.b, obj.c]);
能夠清楚的看出:call接受的參數是一個一個傳進去的,而apply是傳一個參數數組進去的。這裏插播一個問題:爲何要有call、apply同時存在?其實,有時候改變參數的形式是頗有必要,下面分享一個小技巧,當你要求一個數組的最大值時,你會怎麼作?傳統的作法是用for遍歷一遍數組,而後挑出最大值,可是利用apply你就能夠直接利用js的內置函數:
var arr = [3, 4, 2, 78];
var max = Math.max.apply(Math, arr); //Math.max的用法是Math.max(1, 3, 6, 2) //6
console.log(max); //78
bind:
bind函數的做用是綁定參數,其中第一個參數就是傳入this指向對象,當時undefined或者null時,this指向window,它會返回一個新函數,看代碼;
function add(a,b,c){
return a + b + c;
}
add(1,2,3) //6
var addOne = add.bind(null, 1);
addOne(2, 3) //6,至關於add(1, 2, 3);
bind和call的區別在於call是參入參數並執行函數,而bind是傳入綁定參數,返回一個新函數。