this深刻理解

This、call、apply

This:
JavaScript的this老是指向一個對象。javascript

而具體指向哪一個對象是在運行時基於函數的執行環境動態綁定的,而非函數被聲明時的環境。html

this 的綁定和函數聲明的位置沒有任何關係,只取決於函數的調用方式java

this 其實是在函數被調用時發生的綁定,它指向什麼徹底取決於函數在哪裏被調用瀏覽器

在理解 this 是什麼❓首先必需要找到"調用位置",而後判斷符合那種規則。app

當一個函數被調用時,會建立一個"執行上下文環境":函數

  1. 包含函數在哪裏被調用 ( 調用棧 )
  2. 函數調用方法。
  3. 傳入的參數等信息。
  4. this 就是記錄的其中一個屬性,會在函數執行的過程當中用到。

This指向大體能夠分爲以下四類:

  1. 做爲對象的方法調用。
  2. 做爲普通函數調用。
  3. 構造器調用
  4. Function.prototype.callFunction.prototype.apply 調用

一、做爲對象的方法調用

當函數做爲對象的方法被調用時, this 指向該對象oop

// 當函數做爲對象的方法被調用時、this指向該對象
var obj = {
  a:1,
  getA: function(){
    alert(this === obj);//true
    alert(this.a);//1
  }
}

1-一、隱式綁定

另外一條須要考慮的規則是:this

調用位置是 否有上下文 — "對象" ,或者說prototype

是否被某個對象擁有( 包含 )指針

function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
}
obj.foo();
## 1:不管是直接在obj中定義、仍是先定義再添加爲引用屬性,這個函數嚴格來講都不屬於obj對象。
## 2:然而,調用位置會使用obj上下文來引用函數,所以你能夠說函數被調用時obj對象"擁有"或者"包含"了它。
## 3:不管咱們如何稱呼這個模式,當foo()被調用時,它的落腳點確實指向obj對象。當函數引用有上下文對象時,"隱式綁定"規則會把函數調用中的"this"綁定到這個上下文對象。由於調用foo()時this被綁定到obj,因此this.a和obj.a是同樣的
## 4:對象屬性引用鏈中,只有最後一層( 最頂層 )會影響到調用位置

1-二、隱式綁定丟失

最多見的隱式綁定問題:

被"隱式綁定"的函數會丟失綁定對象,也就是說它會應用"默認綁定",從而把this綁定到全局對象或者"undefined"上,取決因而否是"嚴格模式"

解決辦法:使用"顯示綁定"的方案 call(...)和apply(…) 的"硬綁定"模式

// 丟失案例一:堆內存地址的引用
function foo(){
  console.log(this.a);
}
var obj = {
  a:2,
  foo:foo
}
var bar = obj.foo;
var a = "oops,global";
bar();// oops,global
## 雖然bar是obj.foo的一個引用。
## 可是實際上,它引用的是foo函數自己,是foo函數在堆內存空間的地址(複製的是指針的指向)
## 本質上:bar() == foo();
## 所以此時的bar()實際上是一個不帶任何修飾的函數調用,所以應用了默認綁定。

// 丟失案例二:很是常見而且很是出乎意料的狀況(參數傳遞)
function foo(){
    console.log(this.a);
}
function doFoo(fn){
    fn();
}
var obj = {
    a:2,
    foo:foo
}
var a = "oops,global";
doFoo(obj.foo);
## 參數傳遞其實"就是"一種隱式賦值,所以咱們傳入函數時也會被隱式賦值。

引用類型參數傳遞問題

引用類型:引用類型傳遞的是指針的方向

function setName(obj){
    obj.name = 'aaa';
    return obj;
}
var person = new Object();
person.name = 'bbb';
var newPerson = setName(person);
console.log(person.name + ' || ' + newPerson.name);

http://www.cnblogs.com/zareb/p/5699571.html

function setName(obj) {
    obj.name = 'aaa';
    var obj = new Object(); // 若是是按引用傳遞的,此處傳參進來obj應該被從新引用新的內存單元
    obj.name = 'ccc';
    return obj;
}

var person = new Object();
    person.name = 'bbb';

var newPerson = setName(person);
console.log(person.name);
console.log(newPerson.name);

二、做爲普通函數調用 ( this - 默認綁定 )

當函數不做爲對象的屬性被調用時,也就是咱們常說的普通函數方式,此時的 this 老是指向全局對象。在瀏覽器的 JavaScript 裏,這個全局對象是 window 對象。

// 使用普通函數時、其內的this老是指向window
// 在代碼中,getName()是直接使用不帶任何修飾的函數引用進行調用的。所以只能使用"默認綁定",沒法應用其餘規則。
// 若是是嚴格模式( strict mode ),那麼全局對象將沒法使用默認規則,所以this會綁定到"undefined"上
// 案例一:
window.name = 'globalName';
var getName = function(){
  return this.name;
}
console.log(getName());//globalName
// 案例二:引用getName在堆內存中的地址
window.name = 'globalName';
var myObject = {
  name:'ntscshen',
  getName:function(){
    return this.name;
  }
}
var myName = myObject.getName;
console.log(myName());
// 案例三:在事件函數內部、有一個局部的方法。此方法被調用時,方法內部的this指向了window
document.getElementById('div1').onclick = function(){
  console.log(this.id);// 'div1'
  //var _this = this;// 簡單的解決方案
  var callBack = function(){
    //console.log(_this.id);// 'div1'
    console.log(this.id);// 'window'
  }
  callBack();
}
## 沒當你想要把this和"詞法"做用域的查找混合使用時,必定要提醒本身,這是沒法實現的。

三、構造器調用

四、 Function.prototype.callFunction.prototype.apply

4-1:硬綁定

硬綁定的典型應用場景就是:建立一個包裹函數,傳入全部的參數並返回接收到的全部值。

// 案例一:
function foo(){
    console.log(this.a);
}
var obj = {a:2}
var bar = function(){
    foo.call(obj);
};
bar();
setTimeout(bar,100);
bar.call(window);
// 咱們首先建立了函數bar(),並在它的內部手動調用了foo.call(obj),所以強制把foo的this綁定到了obj。不管以後如何調用函數bar,它總會手動在obj上調用foo。這種綁定是一種顯示的強制綁定,所以稱之爲"硬綁定"
// 案例二:
function foo(){
    console.log(this.a,something);
    return this.a + something;
}
function bind(fn, obj){
    return function(){
        return fn.apply(obj, arguments);
    };
}
var obj = {a:2};
var b = bar(3);
console.log(b);
// 因爲硬綁定是一種很是經常使用的模式,因此在ES5中提供了內置的方法Function.prototype.bind,它的用法以下
相關文章
相關標籤/搜索