1. 程式人生 > >js中繼承的幾種方法

js中繼承的幾種方法

繼承就是子能夠用父的東西,下面直接進入正題:

例項層面上的繼承:

1.淺拷貝:

var person={
    name:"cj",
    age:'22',
    add:{
        c:"33",
        d:'44'
    }
}
var prog={
    lang:"javascript"
}
function extend(p,c){
    var c=c || {};
    for(var prop in p){
        c[prop]=p[prop];
    }
}
extend(person,prog);
prog.add.c='66';    
console.log(person.add.c);  //66  ===>是對add的一個引用

這種方式的繼承,缺點很明顯,就是當父物件中的屬性是一個物件或陣列等之類的引用資料型別的時候,改變子也會改變父,因為是對其的一個引用。

2.深拷貝:

var person={
    name:"cj",
    age:'22',
    add:{
        c:"33",
        d:'44'
    }
}
var prog={
    lang:"javascript"
}
//深拷貝,通過遞迴呼叫
function extendDeep(p,c){
   var c=c || {};
   for(var prop in p){
       if(typeof p[prop]=='object'){
            c[prop]=(p[prop].constructor===Array)?[]:{};  //先給子一個容器
            extendDeep(p[prop],c[prop]);
       }else{
            c[prop]=p[prop];
       }
   }
}
extendDeep(person,prog)
console.log(prog);
prog.add.c='66';
console.log(person.add.c);  //33

3.函式式繼承:

function Parent(){
    this.name='abc';
    this.age={
        ming:23
    }
}
function Child(){
    Parent.call(this); //通過call改變this指向,動態給通過Child類new出來的物件新增屬性
    this.add='jia';
}
var c=new Child();
console.log(c.name);
c.name='jjj';
var c2=new Parent()
console.log(c2.age);

4.原型鏈方式的繼承:

var p={
    name:"cj",
}
function myCreate(pp){   
   var ins={};
   function F(){};
   F.prototype=p;
   ins=new F();
   return ins;     
}
var c=myCreate(p);
console.log(c.name)  //cj

myCreate函式需要有兩個功能,一個是繼承父的屬性或者方法,另一個就是它需要返回一個物件。

5.ES5提供的繼承方法:

var p={
    name:"cj",
}
var c1=Object.create(p,{age:{value:100}}); //這裡給c1增加屬性,必須通過這種規範新增
console.log(c1.name,c1.age);  //cj 100

類層面上的繼承:

1.最直接的方式:

function P(){}
function C(){}
C.prototype=P.prototype;  //子的東西暴露給父級了
var c1=new C();
C.prototype.xxx='111';
console.log(c1.xxx); //111
var p1=new P();
console.log(p1.xxx); //111

這種方式直接將子的原型指向父的原型,很明顯違背了繼承的原則,將子的東西暴露給父了,不可取!!!

2.橋接法:

function P(){}
P.prototype.head=1;
function C(){}
C.prototype=new P();
var c=new C();
console.log(c.head);  //1

思想,橋接子的原型,new出來的父級物件肯定是可以訪問父級全部的,將子的原型與其建立關係,這樣就可以拿到父的東西,缺點就是,憑空new 出來一個物件(萬一父級裡面內容龐大),佔用資源,浪費記憶體

3.中間量橋接:

function P(){}
P.prototype.head=1;
function C(){}
function F(){
}
F.prototype=P.prototype;
var f=new F();
C.prototype=f;
var cc=new C();
console.log(cc.head);  //1

通過中間量,讓F的原型等於父的原型,F new出來的物件可以訪問父的原型,只要讓子的原型等於new出來的f物件,這樣子就通過中間量訪問到了父,優點是,F是一個空殼子,不佔資源。

4.ES5自帶方法:

function P(){}
P.prototype.head=1;
function C(){}
C.prototype=Object.create(P.prototype);
var a=new C();
console.log(a.head);  //1

注意一個問題:構造器

//上面幾種方法也一樣,需要手動將構造器指正回來
function P(){};
function C(){};
C.prototype=P.prototype;
console.log(C.prototype.constructor); //ƒ P(){}

下面看一個完整例子(即繼承例項層面上的,又繼承類層面上的):

//先父類
function Person(name,age){
    this.name=name;
    this.age=age;
}
Person.prototype.head=1;
Person.prototype.eat=function(){
	console.log('eating...')
}
//再子類
function programmer(name,age,title){
    Person.apply(this,arguments);
}
//再繼承
programmer.prototype=Object.create(Person.prototype);
programmer.prototype.constructor=programmer;
function createEX(Child,Parent){
    function F(){}
    F.prototype=Parent.prototype;
    Child.prototype=new F();
    Child.prototype.constructor=Child;
}
//最後給子類加屬性或方法
programmer.prototype.language='javascript';
programmer.prototype.work=function(){
    console.log('I am writing code in'+' '+this.language);
}

注意:如果給子類新增屬性或方法寫在了繼承的前面,會導致訪問不到,因為後面重寫了原型

好了,繼承就講到這裡了,最後說幾個常用的方法

function M(name){
    this.name=name;
}
M.prototype.haha=22;
var mm=new M('dd');

//驗證是否是自己的屬性
console.log(mm.hasOwnProperty('name'));  //true
console.log(mm.hasOwnProperty('haha'));  //false,haha是原型上的屬性
//根據物件得到其原型物件
console.log(Object.getPrototypeOf(mm)); //M { haha: 22 }
//原型物件是否是例項物件的原型
console.log(M.prototype.isPrototypeOf(mm)); //true