1. 程式人生 > >JavaScript繼承

JavaScript繼承

和數 淺拷貝 循環 基本 for 更新 我們 兩個 獨立

1)原型鏈

  ①原型鏈示例

function Shape() {
    this.name = ‘shape‘;
    this.toString = function(){
        return this.name;
    }
}

function TwoDshape () {
    this.name = ‘2D shape‘;
}

function Triangle (side,height) {
    this.name = ‘Triangle‘;
    this.side = side;
    this.height = height;
    this.getArea = function () {
        return this.side * this.height / 2;
    }
}

TwoDshape.prototype = new Shape();
Triangle.prototype = new TwoDshape();   //用new新建對象實體,並賦值覆蓋該對象的原型
TwoDshape.prototype.constructor = TwoDshape;
Triangle.prototype.constructor = Triangle; 

var my = new Triangle(5,10); 
my.getArea();       //25
console.log(my.toString());//繼承的方法,具體步驟(遍歷my對象屬性沒有找到,接著查看my.__proto__所指向的對象,即new TwoDshape()創建的實體,
//依然沒找到,又繼續查找該實體的__proto__所指向的對象,即new Shape()所創建的實體,找到toString方法,並在my對象中被調用,this指向my)


//通過instanceof操作符,我們可以驗證my對象同時是上面三個構造器的實例
my instanceof Shape;          //true
my instanceof  TwoDShape;     //true
my instanceof Triangle;      //true

//我們也可以用其他兩個構造器來創建對象,用new TwoDshape()所創建的對象也可以獲得繼承自Shape()的toString()方法
var td = new TwoDshape();
td.constructor === TwoDshape;   //true;
td.toString();          // 2D shape

var s = new Shape();
s.constructor === shape;    // true;

  

②將共享屬性遷移到原型中去

function Shape(){this.name=‘shape‘}//使用new Shape()新建對象,每個實體都有全新的屬性並占用獨立空間
function Shape(){};Shape.prototype.name=‘shape‘;//屬性移到原型後,使用new新建對象時,不再含自己獨立的這個屬性

  

2)只繼承於原型

Triangle.prototype=Shape.prototype;//減少繼承方法的查詢步驟
Triangle.prototype.name=‘Triangle‘;//修改子對象原型後父對象原型也隨即被改,即再new Shape()新建對象時,新對象name為‘Triangl

  

  ②臨時構造器——new F()

function Shape() {}
Shape.prototype.name = "shape";
Shape.prototype.toString = function () {
    return this.name;
}


function TwoDshape() {}
var F = function () {};
F.prototype = Shape.prototype;
TwoDshape.prototype = new F();
TwoDshape.prototype.constructor = TwoDshape;
TwoDshape.prototype.name = ‘2D shape‘;


function Triangle(side, height) {
    this.side = side;
    this.height = height;
}
var F = function () {};
F.prototype = TwoDshape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = ‘Triangle‘;
Triangle.prototype.getArea = function () {
    return this.side * this.height / 2;
}


var my = new Triangle (5,10);
alert(my.getArea());

//通過這種方法,我們仍然能保持住原型鏈
my._proto_ === Triangle.prototype;              //true
my._proto_.constructor === Triangle;            //true
my._proto_._proto_ === TwoDshape.prototypr;        //true
my._proto_._proto_._proto_.constructor === Shape;_    //true

//並且父對象的屬性不會被子對象覆蓋:
var s = new Shape();
s.name;    // shape

//calling toString()
"I am a" + new TwoDshape();     //I am a 2D shape   
                         

  

3)uber—子對象訪問父對象的方式

function Shape(){}
Shape.prototype.name=‘shape‘;
Shape.prototype.toString=function(){
  var const = this.constructor;
  return const.uber
     ? this.const.uber.toString() + ‘,‘ + this.name
     : this.name;
}
function TwoDShape(){
var F=function(){}
F.prototype=Shape.prototype;
TwoDShape.prototype=new F();
TwoDShape.prototype.constructor=TwoDShape;
TwoDShape.uber=Shape.prototype;
TwoDShape.prototype.name=‘2D shape‘;
function Triangle(side,height){
  this.side=side;
  this.height=height;
}
var F=function(){}
F.prototype=TwoDShape.prototype;
Triangle.prototype=new F();
Triangle.prototype.constructor=Triangle;
Triangle.uber=TwoDShape.prototype;
Triangle.prototype.name=‘triangle‘;
Triangle.prototype.getArea=function(){return this.side*this.height/2};
var my=new Triangle(5,10)
console.log(my.toString());//shape,2D shape,triangle

  

4)將繼承部分封裝成函數

function extend (Child,Parent) {
    var F = function () {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
}


extend(TwoDsgape,Shape);
extend(Triangle,TwoDshape);

  

5)屬性拷貝

/*屬性拷貝執行的是對象原型的逐一拷貝,而非簡單的原型鏈查詢。
  所以需要特別註意的是:
    這種方法僅適用於包含基本數據類型的對象,
    所有的對象類型(包括函數和數組)都是不可復制的,
    因為他們只支持引用傳遞      
*/

function extend2(Child,Parent){
    var p = Parent.prototype;
    var c = Child.prototype;
    for(var i in p){
        c[i] = p[i];
    }
    c.uber = p ;
}

  

6)小心處理引用拷貝

var A=function(){},B=function(){};
A.prototype.stuff=[1,2,3];
A.prototype.name=‘a‘;
extend2(B,A);//讓B繼承A使用方法二
B.prototype.name+=‘b‘;//ab,A.prototype.name依然為a,因為拷貝的是值
B.prototype.stuff.push(4);//此時A和B原型上的stuff同時被修改,因為拷貝的是應用
B.prototype.stuff=[‘a‘,‘b‘,‘c‘]//如果完全重寫事情就不一樣了,A為原來,B為新的

  

7)對象之間的繼承(不使用構造器)

function extendCopy (p) {
    var c = {};
    for (var i in p){
        c[i] = p[i];
    }
    c.uber = p;
    return c;
}

var shape ={
    name = ‘shape‘,
    toString :function () {
        return this.name;
    }
}

var twoDee = extendCopy(shape);
twoDee.name = ‘2D shape‘;
twoDee.toString = function () {
    return this.uber.toString() + ‘,‘ + this.name;
}

//下面我們讓triangle對象繼承一個2D圖形對象
var triangle = extendCopy(twoDee);
triangle.name = ‘Triangle‘
triangle.getArea = function () {
    return this.side * this.height / 2;
}


//使用triangle
triangle.side = 5;
triangle.height = 10;
triangle.getArea();          //25

triangle.toString();        // shape,2D shape,Triangle

  

8)深拷貝(當遇到對象類型時,再次調用拷貝)

function deepCopy(p,c){
    c = c || {};
    for (var i in p){
        if(p.hasOwnProperty(i)){
            if(typeof p[i] === ‘object‘){
                c[i] = Array.isArray(p[i]?[]:{});
                deepCopy(p[i],c[i]);
            }else{
                c[i] = p[i]
            }
        }
    }
    return c;
}

var parent = {
    numbers:[1,2,3],
    letters:[‘a‘,‘b‘,‘c‘],
    obj:{
        prop : 1
    },
    bool : true
};

//我們分別用深拷貝和淺拷貝測試一下,就會發現兩者的不同。
//在深拷貝中,對對象的numbers屬性進行更新不會對原對象產生影響。

var mydeep = deepCopy(parent);
var myshallow = extendCopy(parent);

mydeep.numbers.push(4,5,6);
mydeep.numbers     //[1,2,3,4,5,6]
parent.numbers     //[1,2,3]

myshallow.numbers.push(10);
myshallow.numbers   //[1,2,3,10]
parent.numbers      //[1,2,3,10]

  

9)object()(用object函數來接受父對象,並返回一個以該對象為原型的新對象)

function object(o){
  var n;
  function F(){}
  F.prototype=o;
  n=new F();
  n.uber=o;
  return n;
}//這個函數與extendcopy基本相同

  

10)原型繼承與屬性拷貝的混合應用

function objectplus(o,stuff){
  var n;
  function F(){}
  F.prototype=o;
  n=new F();
  n.uber=o;
  for(var i in stuff){n[i]=stuff[i]}
  return n;
}//兩對象o用於繼承,stuff用於拷貝方法與屬性

  

11)多重繼承(一個對象中有不至一個父對象的繼承)

function multi(){
  var n={},stuff,j=0,len=arguments.length;
  for(j=0;i<len;j++){
    stuff=arguments[j];
    for(var i in stuff){n[i]=stuff[i]}
  }
  return n;
}//內層循環用於拷貝屬性,外層循環用於遍歷多個父對象參數,若有相同屬性後面替代之前

  

12) 寄生式繼承

13)構造器借用

待續...

JavaScript繼承