1. 程式人生 > >js - 原型繼承和應用

js - 原型繼承和應用

tor for-in循環 cti fun truct 利用 分享圖片 改變 版本

前言:本文大體摘自:https://blog.csdn.net/sysuzhyupeng/article/details/54645430 這位CSDN博主寫的十分的好,邏輯性很強。後面 “如何安全的擴展一個內置對象 ” 是我添加的。順便把這位博主的 詳解js中extend函數call和apply上手分析 摘抄下來。

原型繼承:利用原型中的成員可以被和其相關的對象共享這一特性,可以實現繼承,這種實現繼承的方式,就叫做原型繼承.

原型繼承是js的一種繼承方式,原型繼承是什麽意思呢,其實就是說構造函數和子構造函數,或者說類和子類之間(當然js中不存在類),唯一的繼承傳遞方式是通過原型,而不是其他語言直接通過extends(當然ES6的語法糖出現了extends)。所以你需要手寫prototype。(封裝手寫prototype的方法請看我的另一篇文章詳解js中extend函數) 我們先看一個簡單的例子

具體看例子:

1 function Parent(){
2       this.job = teacher;
3    }
4    Parent.prototype.showJob = function(){
5       alert(this.job);
6    }
7    var child = new Parent();
8    child.showJob();      //‘teacher‘ 

很明顯,這個例子中的child獲得屬性job和一個方法showJob,為什麽會獲得呢? 這時候來看看new Parent()到底做了什麽

1  var obj = { };   //
obj獲得Parent的this引用 2 obj.job = teacher; 3 obj.__proto__ = Parent.prototype; 4 var child = obj;

所以為什麽child獲得了屬性job,是因為他執行了構造函數,child對象上獲得了屬性job。而為什麽child獲得了方法showJob, 是因為對象上有一個隱藏的原型__proto__ ,它指向了Parent.prototype。當我們在對象child上調用方法時,它首先檢查對象自己是否具有這個方法,沒有的話搜索自己的隱藏原型中有沒有這個方法。

所以,原型繼承是什麽,它的本質是改變其他對象的__proto__,或者說讓它豐富起來,以獲得父構造函數或者祖先構造函數的方法,請看代碼。

1  function Parent(){
2    }
3    Parent.prototype.showJob = function(){}
4    function Child(){
5    }
6    Child.prototype = new Parent();
7    Child.prototype.constructor = Child;
8    var grandChild = new Child();

這時候grandChild獲得了showJob方法,因為它的__proto__中有showJob方法,而為什麽它的隱式原型中有這個方法呢,因為new Child()中grandChild.__proto__指向了Child的prototype, 至於為什麽Child的prototype中有showJob方法,因為Child.prototype.__proto__等於Parent.prototype.
至於為什麽有這麽一句

1 Child.prototype.constructor = Child;

這是因為原本Child.prototype中有一個constructor屬性指向Child本身,當執行Child.prototype = new Parent()的時候,Child.prototype.constructor指向了Parent,否則下一次new Child的時候,constructor的指向就會不正確,當然,這個在實際開發中即時漏掉也不會有大問題,因為我們很少會對constructor進行讀寫。
以上代碼還有另一個問題,為什麽我們要把showJob這個方法寫在Parent.prototype上呢,如果寫成如下

1  function Parent(){
2         this.showJob = function(){}
3     }
4     function Child(){
5     }
6     Child.prototype = new Parent();

當然這樣寫也可以,child.prototype對象上有了showJob方法,而不是child.prototype.__proto__,這對於我們原型鏈的繼承並沒有影響。然而這樣寫的方法一多,child.prototype對象上的方法就越多,如果new了多次的話,在內存上會比寫在原型上多一些消耗。

那麽在實際開發中,會怎麽實現面向對象的原型繼承呢。正常在我們拿到需求的時候,如果需求邏輯復雜,且在多個頁面中有相似邏輯的時候,我們就會想到使用面向對象了,因為面向對象解決的就是邏輯的封裝和復用。
假設頁面A,頁面B,頁面C中存在相同邏輯,那麽我們可以封裝父構造函數對象

 1  function Parent(){}
 2    Parent.prototype = {
 3       method1: function(){},
 4       method2: function(){},
 5       ...
 6    }
 7    function A(){      //頁面A
 8       this.A = A;
 9    }
10    A.prototype = new Parent();
11    A.prototype.otherMethod = function(){};
12 
13    var a = new A();   //使用對象
14    a.init...

首先將頁面A,頁面B,頁面C中相同邏輯抽離,相同邏輯可以是同一個ajax請求返回數據,或者是數據格式化等等的相同操作。將這些方法在Parent.prototype中定義,至於A,B,C頁面自己特有的方法,則在如A.prototype中定義。這樣很好地了解決了我們的問題,邏輯清晰,代碼復用性強。 如果在Parent方法參數中加入了回調callback,並且在callback中想調用子函數方法或者屬性,可以參考我另一篇博文call和apply上手分析

hasOwnProperty

hasOwnProperty方法可以檢測一個屬性是存在於實例中,還是存在於原型中。

 1  function Parent(){
 2       this.name = sysyzhyupeng;
 3    }
 4    Parent.prototype.job = teacher;
 5    Parent.prototype.showJob = function(){
 6    }
 7    var parent = new Parent();
 8    parent.hasOwnProperty(name);  // true
 9    parent.hasOwnProperty(job);  // false
10    //方法也可以
11    parent.hasOwnProperty(showJob);  // false 

in

in運算符和hasOwnProperty不同,只要存在在原型上或者對象上就返回true

 1  function Parent(){
 2       this.name = sysyzhyupeng;
 3    }
 4    Parent.prototype.job = teacher;
 5    Parent.prototype.showJob = function(){
 6    }
 7    var parent = new Parent();
 8    name in parent;  // true
 9    job in parent  // true
10    for(_key in parent){
11       console.log(_key);  // ‘name‘, ‘job‘, ‘showJob‘
12    }

在使用for-in循環時,返回的是所有能通過對象訪問的且可枚舉的屬性。所有開發人正常定義的屬性都是可枚舉的,只有在IE8及更早版本除外。

Object.keys

ES5的Object.keys方法可以返回對象上的所有可枚舉屬性(註意只有對象上的,從原型上繼承的沒有)

如何安全的擴展一個內置對象

給內置對象新增成員和方法,叫做擴展內置對象。這種行為是不可取的。因為原生的內置對象本身就有一些方法和屬性,在一個項目中,如果你替換了這些方法,別人就用不到了,容易造成代碼崩潰,不宜維護。

如何安全的擴展一個內置對象呢? 就比如 內置對象Array, 我們既想用它本身的一些方法,又想給它添加一個sayHello()方法。這樣到底怎麽去做呢?

首先我們創建一個對象,讓這個對象繼承內置對象Array,這樣Array數組裏面的方法就可以在我們創建的對象中使用,然後在我們創建的對象添加我們自定義的方法。這樣無論我們怎麽修改自己創建的對象裏面的方法,都不會影響到內置對象

 1        function MyArray() {      //自己定義函數對象MyArray 
 2             //我自己定義的屬性
 3             this.name = "我是一個數組";
 4             this.sayHello = function () {
 5                 console.log("我的sayHello方法");
 6             }
 7         }
 8 
 9         var arr = new Array();     //內置對象Array創建的對象arr
10         
11         MyArray.prototype = arr;  // 替換原型對象 也就是原型對象繼承數組,我的數組中就有了原生數組對象的所有的屬性和方法
12         MyArray.prototype.hello =function () {
13             console.log("大家好,我是自定義對象MyArray的原型中的hello方法");
14         }
15         
16         var myArr = new MyArray();  //myArr這個對象就繼承自arr
17 
18         myArr.push(100);         //原生內置對象Array自帶的push的方法
19         myArr.push(2,3,4,5)
20         console.log(myArr);
21         myArr.sayHello();
22         myArr.hello();
技術分享圖片

js - 原型繼承和應用