JavaScript繼承
阿新 • • 發佈:2017-06-03
和數 淺拷貝 循環 基本 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繼承