物件導向編程思想(前傳)--你必須知道的javascript(轉載)

來源:互聯網
上載者:User

標籤:推薦   selected   body   prototype   object   模式   多個   c#   繼承   

原文地址:http://www.cnblogs.com/zhaopei/p/6623460.html
閱讀目錄 
  • 什麼是鴨子類型
  • javascript的物件導向
  • 封裝
  • 繼承
  • 多態
  • 原型
  • this指向
  • call
  • apply
  • band
  • js中的閉包
  • 什麼是高階函數

在寫物件導向編程思想-設計模式中的js部分的時候發現很多基礎知識不瞭解的話,是很難真正理解和讀懂js物件導向的代碼。為此,在這裡先快速補上。然後繼續我們的物件導向編程思想-設計模式。

什麼是鴨子類型

javascript是一門典型的動態類型語言,也就弱類型語言。
那什麼是鴨子類型:【如果它走起路來像鴨子,叫起來也是鴨子,那麼它就是鴨子】

var 鴨子 = {    走路: function () { },    咕咕咕: function () { }}var 鸚鵡 = {    走路: function () { },    咕咕咕: function () { }}

這隻鸚鵡同樣有“走路”和“咕咕咕”的方法,那在js的世界裡就可以把它當成鴨子。
可以這樣調用:

var 鴨子們 = [];鴨子們.push(鴨子);鴨子們.push(鸚鵡);for (var i = 0; i < 鴨子們.length; i++) {    鴨子們[i].走路();}

所以js的世界沒有抽象和介面,但可以約定“我們都是鴨子”。

javascript的物件導向

javascript不僅是直譯式指令碼語言、動態類型、弱類型語言、函數為一等公民的語言,它還是基於原型的物件導向語言。物件導向三大特性:封裝、繼承、多態,下面我們用js分別實現。

封裝
var Person = (function () {     var sex = "純爺們";    return {        name: "農碼一生",        getInfo: function () {            console.log("name:" + this.name + ",sex:" + sex);        }    };})();


雖然老的js文法沒有提供private等關鍵字,但是我們可以利用閉包來實現私人欄位,達到封裝的目的。

繼承
  • 字面量表示:

    var Person = {name: "農碼一生",getName: function () {    console.log(this.name);}};var obj = Person;obj.getName();

  • 函數構造器:

var Person = function () {    this.name = "農碼一生";    }Person.prototype.getName = function () {    console.log(this.name);}var obj = function () { };obj.prototype = new Person();//obj繼承於Personvar o = new obj();o.getName();//直接調用原型中的getName(類似於C#中的調用父類方法)

多態

對於多態,其實上面的鴨子類型已經表現的很清楚了。

var 鴨子們 = [];鴨子們.push(鴨子);鴨子們.push(鸚鵡);for (var i = 0; i < 鴨子們.length; i++) {    鴨子們[i].走路();//對於鸚鵡來說,它可能是跳著走。對於鴨子來說,它可能左右搖擺著走。這就是多態的表現。}

對於鸚鵡來說,它可能是跳著走。對於鴨子來說,它可能左右搖擺著走。這就是多態的表現。

原型

什麼是原型?在js中是沒有類的,那它怎麼建立對象。在C#中我們可以通過new關鍵字執行個體化一個對象,在js中我們用new關鍵字構造一個原型對象。C#中一切對象繼承於Object,js中一切對象的原型是Object。

var Person = function () {    this.name = "農碼一生";    this.sex = "純爺們";};console.log(Person.prototype);


我們很多時候給一個對象添加方法的時候就是寫在原型上,這是為什嗎?直接寫在對象裡會有問題嗎?下面我們試試:

var Person = function () {    this.name = "農碼一生";    this.sex = "純爺們";    this.getInfo = function () {        console.log("name:" + this.name + ",sex:" + this.sex);    }};


好像並看不出什麼問題。其實不然...

我們發現,每次構造出來的對象中的方法都會去開闢一個空間。但是對象的方法都是一樣的,完全沒有必要。 我們可以把方法放入原型。

這樣一來,不過我們構造多少對象,其方法都是公用的(單例的)。
可是為什麼會這樣呢?
首先,想想原型這個詞,很形象,原本的模型。我們來看一個繼承的例子:

var Person = function () {    this.name = "農碼一生";    this.sex = "純爺們";    this.getInfo = function () {        console.log("name:" + this.name + ",sex:" + this.sex);    }};var Student = function () { };Student.prototype = new Person();//繼承var s1 = new Student();var s2 = new Student();console.log(s1.getInfo === s2.getInfo);


雖然getInfo在Person裡面是直接實現的,但是到了Student的原型(prototype)裡面就是一個Person對象的單例了。也就是說無論構造多少個Student對象其中的getInfo方法都是同一個。
但是,構造多個Person就有多個getInfo方法。所以,我們應該把getInfo方法放入Person的原型中。

var Person = function () {    this.name = "農碼一生";    this.sex = "純爺們";   };Person.prototype.getInfo = function () {    console.log("name:" + this.name + ",sex:" + this.sex);};

我們仔細推敲下這句話“把getInfo方法放入Person的原型中”,Person的原型是Object,那也就是說getInfo方法放到Object裡面去了?
是的,不信請看:

如果原型和原型的原型都實現了同樣的方法呢?我們來猜猜下面會列印哪個版本

var Person = function () {    this.name = "農碼一生"; };var Student = function () { }; Student.prototype = new Person();//繼承var stu = new Student();Student.prototype.getName = function () {    console.log("我的名字:" + this.name);}Person.prototype.getName = function () {    console.log("My name is:" + this.name);}stu.getName();

如果注釋掉中文版呢?

有沒有覺得特神奇,具體原因我們用圖來回答:

從另個一角度說,如果對象實現了原型中已有的方法那就等效於C#中虛方法重寫了。

this指向
var name = "張三";var obj = {    name:"李四",    getName: function(){        console.log(this.name);    }}obj.getName();


這個結果大家應該沒什麼疑問。
接著看下面的:

window.name = "張三";var obj = {    name:"李四",    getName: function(){        console.log(this.name);    }} //obj.getName();window.func = obj.getName;window.func();


暈了沒有?沒關係,告訴大家一個簡單實用的方法:方法是被誰“.”出來的,this就指向的誰。

call

"方法是被誰“.”出來的,this就指向的誰",這個口訣不一定適用所有方法。為什麼這麼說呢?請看下面:

window.name = "張三";var obj = {    name: "李四",    getName: function () {        console.log(this.name);    }}//obj.getName();window.func = obj.getName;window.func.call(obj);


雖然還是window點的,但this已經指向了obj。
因為call可以改變this執行。
這個特性非常有用。比如,我們要編寫一個下拉選中事件。

function func() {    console.log("我點擊了" + $(this).find("option:selected").text());}$("#element1").change(function () {    func.call(this);});$("#element2").change(function () {    func.call(this);});

在寫func方法的時候不用考慮具體是那個下拉框元素。

apply

apply和call區別不大。

function func(age, sex) {    console.log("name:" + this.name + ",age:" + age + ",sex:" + sex);}var obj = {    name: "曉梅"}func.call(obj, "18", "妹子");func.apply(obj,["18","小美女"]);


call和apply第一個參數都是this指向的對象。call第二個和以後的參數對應方法func的參數。而apply的第二個參數是個數組,包含方法的所有參數。

band
function func(age, sex) {    console.log("name:" + this.name + ",age:" + age + ",sex:" + sex);}var obj = {    name: "曉梅"}var func1 = func.bind(obj, "18", "妹子");func1();

和apply、call的區別是,只是改變this指向並不執行。且參數傳入方式和call一樣。

js中的閉包

什麼是閉包?我的理解是存在不能被回收的變數就是閉包。
最常見最大的一個閉包就是全域變數,定義了就不會被銷毀,除非自動設為null。
而我們平時說的和使用的閉包卻非如此,但同樣會產生不會被銷毀的變數。比如我們之前說的私人變數樣本:

var Person = (function () {     var sex = "純爺們";    return {        name: "農碼一生",        getInfo: function () {            console.log("name:" + this.name + ",sex:" + sex);        }    };})();

之所以說它是閉包,那是因為sex這個欄位是永遠不會被銷毀。你想想,如果被銷毀了,那我們調用getInfo的時候豈不是找不到sex欄位了。所以不是不會銷毀,而是不能銷毀。
閉包的作用不僅僅是私人化。我們再來一例:

for (var i = 0; i < 10; i++) {    var t = setTimeout(function () {        console.log(i);    }, 100);}


並不是我們想象的那樣列印0到9。
因為計時器還沒開始迴圈就執行完了。而此時變數i已經是10。
我們可以通過閉包為每次迴圈儲存一個閉包變數。

for (var i = 0; i < 10; i++) {    (function (i) {        var t = setTimeout(function () {            console.log(i);        }, 100);    })(i);}

什麼是高階函數

“高階函數”名字特牛逼。其實我們在js中經常使用。
還是私人變數的例子:

var Person = (function () {     var sex = "純爺們";    return {        name: "農碼一生",        getInfo: function () {            console.log("name:" + this.name + ",sex:" + sex);        }    };})();
  • 當函數做被return時,那麼就是高階函數。
var getInfo = function (callback) {    $.ajax(‘url‘, function (data) {        if (typeof callback === ‘function‘) {            callback(data);        }    });}getInfo(function (data) {    alert(data.userName);});

getInfo在執行的時候,傳入的參數是個函數。

  • 當函數被當成參數傳遞時,那麼這也是高階函數。

 

本文已同步至索引目錄:《設計模式學習》
【demo】:https://github.com/zhaopeiym/BlogDemoCode
【推薦】:深入理解javascript原型和閉包系列

物件導向編程思想(前傳)--你必須知道的javascript(轉載)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.