程序員總結:前端面試回顧(1)javascript的面向對象

編程語言 Java JavaScript 程序員 孝道的重要性 孝道的重要性 2017-09-08

一、對象

1.1創建對象

Javascript是一種基於對象(object-based)的語言,你遇到的所有東西幾乎都是對象。

一個簡單的對象創建:

var People = { name : "eavan", age : 24, getName : function(){ alert(this.name); //eavan }}

使用的時候就可以用People.name,獲取People這個對象的name屬性,或者是People.getName()來得到People的name值。

這裡還是要推薦下我自己建的前端學習群:657137906,如果你正在學習前端,小編歡迎你加入,大家都是前端黨,不定期分享乾貨(只有web前端相關的),包括我自己整理的一份2017最新的前端資料和零基礎入門教程,歡迎初學和進階中的小夥伴。

另一種對象創建方式:

var People = new Object();People.name = "eavan";People.age = 24;People.getName = function(){ alert(this.name);} 

這裡用到了new,就順便提一下在使用new的時候發生了什麼,其實在使用new的時候,大致可以認為做了這三件事,看下面的代碼:

var People = {}; //我們創建了一個空對象PeoplePeople.__proto__ = Object.prototype; //我們將這個空對象的__proto__成員指向了Object函數對象prototype成員對象Object.call(People); //我們將Object函數對象的this指針替換成People,然後再調用Object函數

1.2封裝

簡單來說就是對一些屬性的隱藏域暴露,比如私有屬性、私有方法、共有屬性、共有方法、保護方法等等。而js也能實現私有屬性、私有方法、共有屬性、共有方法等等這些特性。

像java這樣的面向對象的編程語言一般會有一個類的概念,從而實現封裝。而javascript中沒有類的概念,JS中實現封裝主要還是靠函數。

首先聲明一個函數保存在一個變量裡面。然後在這個函數(類)的內部通過對this變量添加屬性或者方法來實現對類添加屬相或者方法。

var Person = function(){ var name = "eavan"; //私有屬性 function checkName(){}; //私有方法 this.myName = "gaof"; //對象共有屬性 this.myFriends = ["aa","bb","cc"]; this.copy = function(){} //對象共有方法 this.getName = function(){ //構造器方法 return name; }; }

純構造函數封裝數據的問題是:對像this.copy = function(){}這種方法的創建,其實在執行的時候大可不必綁定到特定的對象上去,將其定義到全局變量上也是一樣的,而且其過程相當於實例化了一個Function,也大可不必實例化這麼多其實幹同一件事的方法。而這個小問題的解決可以用原型模式來解決。

1.3理解原型

在每創建一個函數的時候,都會生成一個prototype屬性,這個屬性指向函數的原型對象。而其是用來包含特定類型的所有實例共享的屬性和方法。所以,直接添加在原型中的實例和方法,就會被所有實例所共享。

同樣還是上面的Person的例子,我們可以為其原型添加新的屬性和方法。

Person.isChinese = true; //類的靜態共有屬性(對象不能訪問)Person.prototype.sex = "man" ; //類的共有屬性Person.prototype.frends = ["gao","li","du"];Person.prototype.isBoy = function(){}; //類的共有方法

原型封裝數據的問題:對綁定在prototype上的引用類型的變量,由於被所有對象所共有,其中某一個對象對該數據進行修改,當別的對象訪問該數據的時候,所訪問到的值就是被修改後的。

比如如下代碼:

var person1 = new Person();person1.frends.push("dd");console.log(person1.frends); //["gao", "li", "du", "dd"]var person2 = new Person();person2.frends.push("ee");console.log(person2.frends); //["gao", "li", "du", "dd", "ee"]

原本希望對person1和person2的friends屬性分別添加新的內容,結果二者的friends屬性居然是“公用”的!

綜上,最常見的方式應該是組合使用構造函數和原型模式,構造函數用於定義實例屬性,原型模式用於定義方法和共享的屬性。

每個類有三部分構成:第一部分是構造函數內,供實例對象化複製用。第二部分是構造函數外,直接通過點語法添加,供類使用,實例化對象訪問不到。第三部分是類的原型中,實例化對象可以通過其原型鏈間接訪問到,也是為所有實例化對象所共用。

在說到對象實例的屬性的時候,我們有一個問題,就是在訪問一個屬性的時候,這個屬性是屬於實例,還是屬於這個實例的原型的呢?

比如還是上面的例子,我們為person2實例增加一個sex屬性,這時候訪問person2的sex屬性時,得到的是我們增加的值。說明為對象實例添加一個屬性的時候,這個屬性就會屏蔽原型對象中保存的同名屬性。

 person2.sex = "woman"; console.log(person1.sex); //man console.log(person2.sex); //woman

這個時候我們可以使用hasOwnProperty()方法來檢測一個屬性是存在於實例中,還是存在於原型中。如果實例中有這個屬性,hasOwnProperty()會返回true,而hasOwnProperty()並不會感知到原型中的屬性。所以可以用這個方法檢測屬性到底是存在於實例中還是原型中。

console.log(person1.hasOwnProperty("sex")); //原型中的屬性,返回falseconsole.log(person2.hasOwnProperty("sex")); //實例中的屬性,返回true

二、繼承

ECMAScript中描述了原型鏈的概念,並將原型鏈作為實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。

2.1 原型鏈繼承

如下代碼:

function Super(){ this.val = true; this.arr = ["a"];}function Sub(){ //...}Sub.prototype = new Super();var sub = new Sub();console.log(sub.val) //true

以上代碼定義了Super和Sub兩個類型,繼承的核心就一句話:Sub.prototype = new Super() 將父類的一個實例賦給子類的原型。這樣子類就能夠使用父類實例所擁有的方法和父類原型中的方法。

這種情況要想給子類添加自己的方法或者是覆蓋父類中某個方法的時候,一定要在放在替換原型語句後面。否則寫在原型上的方法都會丟失。

而且在給子類添加新方法的時候,不能使用字面量的方式添加新方法,這樣會導致繼承無效。

如:

Sub.prototype = new Super();Sub.prototype = { //錯誤的方式 getVal : function(){ //... }}

以上代碼剛剛把Super的實例賦給原型,緊接著又將原信替換成一個對象字面量,導致現在原型包含的是一個Object的實例,並非Super的實例,因此原型鏈被切斷了,Sub和Super已經沒有關係了。

原型鏈的問題:

最主要的問題有兩個:一是由於引用類型的原型屬性會被所有實例所共享,所以通過原型鏈繼承時,原型變成了另一個類型的實例,原先的實例屬性也就變成了現在的原型屬性,如下代碼:

function Super(){ this.friends = ["peng","gao"];}function Sub(){ //...}Sub.prototype = new Super();var sub1 = new Sub();var sub2 = new Sub();sub1.friends.push("du");console.log(sub2.friends); //["peng", "gao", "du"]

這個例子說明的就是上面的問題,子類的所有實例共享了父類中的引用類型屬性。

原型鏈繼承的另一個問題是在創建子類行的實例的時候,沒法向父類的構造函數傳遞參數。

2.2 構造函數繼承

具體實現:

function Super(){ this.val = true; this.arr = ["a"];}function Sub(){ Super.call(this);}var sub = new Sub();console.log(sub.val) //true

這種模式這是解決了原型鏈繼承中出現的兩個問題,它可以傳遞參數,也沒有了子類共享父類引用屬性的問題。

但這種模式也有他的問題,那就是在父類原型中定義的方法,其實是對子類不可見的。

2.3組合繼承

既然上述的兩種方式各有各自的侷限性,將它倆整合到一起是不是會好一點呢,於是就有了組合繼承。

function Super(){ this.val = true; this.arr = ["a"];}function Sub(){ Super.call(this); //{2}}Sub.prototype = new Super(); //{1}Sub.prototype.constructor = Sub; //{3}var sub = new Sub();console.log(sub.val) //true

組合繼承還有一個要注意的地方:

在代碼{3}處,將子類原型的constructor屬性指向子類的構造函數。因為如果不這麼做,子類的原型是父類的一個實例,所以子類原型的constructor屬性就丟失了,他會順著原型鏈繼續往上找,於是就找到了父類的constructor所以它指向的其實是父類。

這種繼承方式是使用最多的一種方式。

這種繼承方式解決了上兩種方式的缺點,不會出現共享引用類型的問題,同時父類原型中的方法也被繼承了下來。

如果要說起有什麼缺點我們發現,在執行代碼{1}時,Sub.prototype會得到父類型的val和arr兩個屬性。他們是Super的實例屬性,只不過現在在Sub的原型上。而代碼{2}處,在創建Sub實例的時候,調用Super的構造函數,又會在新的對象上創建屬性val和arr,於是,這兩個屬性就屏蔽了原型中兩個同名屬性。

2.4寄生組合式繼承

對於上面的問題,我們也有解決辦法,不是在子類原型中多了一份父類的屬性和方法麼,那我原型中就只要父類原型中的屬性和方法,這裡我們引入了一個方法:

function inheritObject(obj){ var F = function(){}; F.prototype = obj; return new F();}

這個方法創建了一個對象臨時性的構造函數,然後將傳入的對象作為這個構造函數的原型,最後返回這個臨時類型的一個新實例。

我們可以設想,如果用這個方法拷貝一份父類的原型屬性給子類,是不是就避免了上面提到的子類原型中多了一份父類構造函數內的屬性。看如下代碼:

function Super(){ this.val = 1; this.arr = [1];}Super.prototype.fun1 = function(){};Super.prototype.fun2 = function(){};function Sub(){ Super.call(this);}var p = inheritObject(Super.prototype); //{1}p.constructor = Sub; //{2}Sub.prototype = p; //{3}var sub = new Sub();

基本思路就是:不必為了指定子類型的原型而調用父類的夠著函數,我們需要的無非就是父類原型的一個副本而已。本質上就是複製出父類的一個副本,然後再將結果指定給子類型的原型。

三、多態

所謂多態,就是同一個方法的多種調用方式,在javascript中,通過arguments對象對傳入的參數做判斷就可以實現多種調用方式。

例子:

function Add(){ function zero(){ return 10; } function one(num){ return 10 + num; }function two(num1, num2){ return num1 + num2;}this.add = function(){ var arg = arguments, len = arg.length; switch (len){ case 0: return zero(); case 1: return one(arg[0]); case 2: return two(arg[0], arg[1]); } }}var A = new Add();console.log(A.add()); //10console.log(A.add(5)); //15console.log(A.add(6, 7)); //13

最後在說幾句:

  1. 厲害程序員相對於普通程序員的優勢在於:

  2. 寫出的代碼更容易排錯,不是高手的代碼就不會錯,而是高手的代碼出了錯容易找。高手的代碼可讀性一定很好,模塊清晰,命名規範,格式工整,關鍵的地方有註釋,出了異常有log,自然容易排錯,即使交給別人去debug也是比較容易的。

  3. 這個話題到這裡就算是說完了,學習web前端的可以加我的群,每天分享對應的學習資料:657137906,歡迎初學和進階中的小夥伴。

如果想看到更加系統的文章和學習方法經驗可以關注我的微信公眾號:‘前端根據地’關注後回覆‘給我資料’可以領取一套完整的學習視頻

相關推薦

推薦中...