JavaScript:萬惡的this拿命來(二)

用栗子說this

Bug年年有,今年特別多
對於JavaScript這麼靈活的語言來講,少了this怎麼活!設計模式

function

函數 this

對於沒有實例化的function,咱們稱之爲函數,即沒有用new關鍵字調用的函數,對它們來講,this一概指代全局。

上栗子瀏覽器

var position = "outer";               

function test(){
    position = "inner";           
}

console.log(this.position);     // outer   
test();
console.log(this.position);     // inner

緣由:內部未用var聲明發生「變量提高」,污染全局變量。

構造函數 this

對於被實例化的function,咱們稱之爲構造函數,及使用new關鍵字調用的function,對於它們來講,this會被改變,指向實例。

上栗子函數

var name = 'person';               // 全局this賦上屬性name

function Person(name){
    this.name = name;

    this.say = function () {
        console.log('My name is '+this.name);
    }

    this.show = function () {
        console.log(this);
    }
}


var william = new Person('william'); //經過new關鍵字建立實例,改變函數內部this指向

console.log(this.name);          //person

william.say();                   //My name is william

william.show();                  //Person {name: "william", say: ...


註解:經過這個栗子,咱們能夠看出,經過建立構造函數的實例,使得this的指向改變,指向了實例自己。

原型對象 this

每個函數對象在建立的時候,都會自動添加一個prototype屬性,這個prototype實際上是一個指針,指向這個函數的原型對象。

你能夠經過prototype指針給這個函數對象的原型對象添加屬性,在實例化函數對象後,能夠經過this來訪問原型對象上的屬性。

上栗子this

function Person(){
    console.log(this.name);
}

Person.prototype.name = "william";    //給原型對象上賦name屬性

var person = new Person();            // 經過this.name訪問原型對象上的屬性,打印 "william"

這還不夠,我們爲函數對象直接添加同名,而不在原型對象上添加prototype

function Person(){
    this.name = "Jack";
    console.log(this.name);
}

Person.prototype.name = "william";

var person = new Person();           // 打印 "Jack"

這裏有一個值得注意的地方:設計

當你構造函數中存在和原型對象同名屬性方法時,原型對象中的屬性或方法會被隱藏,你只會訪問到構造函數中的屬性或方法`指針

Object.create this

經過Object.create建立函數對象實例,而不使用new關鍵字,也就是說它不會去調用構造函數

上栗子code

function Person(name){    
    this.name = name;               
    this.showName = function () {
        console.log(this.name + ' in constructor');
    }
} //在構造函數中的屬性和方法,均同名

Person.prototype.name = "jack";
Person.prototype.showName = function () {
    console.log(this.name + ' in prototype');
};
  //在原型對象中的屬性和方法,均同名

var william  = new Person("william");
var jack = Object.create(Person.prototype);

william.showName();  // william in constructor

jack.showName();     // jack in prototype


註解:
 - 使用new關鍵字創造的實例調用了構造函數的屬性和方法
 - 使用Object.create創造的實例調用了原型對象的屬性和方法

原型鏈 this

原型鏈用於實現繼承,當沒有找到須要屬性或方法時,會順着原型鏈向上繼續尋找。

上栗子對象

function Father(){
    this.name = 'father';
}                              //能夠看作"父類"
Father.prototype.showName = function () {
    console.log(this.name);
};

function Son(){
    this.name = 'son';
}                              //能夠看作"子類"
Son.prototype = new Father();  //這步是關鍵!

var mike = new Son();
mike.showName();               // son

註解:
 - 首先要強調的是JavaScript中沒有"父類","子類","繼承"這樣的東西,咱們只是經過原型鏈來實現這樣的設計模式。

 - 經過這一步
     Son.prototype = new Father();
   咱們將"子類"的prototype指針指向了"父類"函數對象

設計模式:繼承

爲何這樣就能實現繼承呢?

 首先要想明白爲何實例能訪問構造函數的原型對象

 在JavaScript中,有一個內部的屬性,在火狐,谷歌瀏覽器中,將這個內部屬性暴露了出來,就是它 __proto__

上栗子(瀏覽器中)繼承

mike.__proto__ // Father {name: "father", showName: function}    
Son.prototype  //Father {name: "father", showName: function}

mike.__proto__ === Son.prototype  // true

經過這個內部屬性,咱們能夠訪問到實例的構造函數的原型對象

再來一枚(瀏覽器中)

mike.__proto__.__proto__      //Father {showName: function}
Father.prototype              //Father {showName: function}

mike.__proto__.__proto__ === Father.prototype  // true

咱們如今經過__proto__訪問到了"父類"的原型對象!
繼承得以實現!

補充一句:

咱們能夠訪問到Son的name屬性的值,卻訪問不到Father的name屬性的值,是由於
 - mike是Son的實例(實例能作什麼...?)
 - Father中的name沒有對外開放,能夠看作是私有屬性

總結

主要探討了
- function做爲函數、構造函數、原型對象時,this的指代狀況
- 擴展了另外一種實例化方式Object.create - 細說了原型鏈原理和實現,模擬了繼承的設計模式

相關文章
相關標籤/搜索