JS 继承

JS 继承

1. 原型链继承
继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一种是接口实现 一种是继承。
而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。
function Box() //被继承的函数叫做超类型(父类,基类)
{
this.name = ‘Lee’;//Box 构造
}
function Desk() //继承的函数叫做子类型(子类,派生类)
{
this.age = 100;// Desk 构造
}

//通过原型链继承,超类型实例化后的对象实例,赋值给子类型的原型属性即可。
//new Box() 会将Box构造中的信息和原型里的信息都交给Desk
//Desk的原型,得到的是Box的构造+原型中的信息。
//
Desk.prototype = new Box(); //Desk继承了Box,通过原型,形成链条 D
//Desk 的prototype属性应该指向 原型中的constructor属性,现在指向了new Box()对象实例。

var desk = new Desk();
console.log(desk.age);//100
console.log(desk.name);//Lee 得到了被继承的属性。

function Table()
{
this.level = ‘AAAAA’;
}
Table.prototype = new Desk(); //继续继承原型链继承。
var table = new Table();
console.log(table.name)//Lee 继承了Box和Desk

如果要实例化Table,那么Desk实例中有age=100,原型中增加相同的属性 age=200,最后结果是多少呢。

function Box()
{
this.name = ‘Lee’;
}
Box.prototype.name = ‘jack’;

function Desk()
{
this.age = 100;
}
Desk.prototype = new Box(); //通过原型链继承。

var desk = new Desk();
console.log(desk.name);//Lee 就近原则 实例有就返回,如果没有就去原型中找

PS: 以上原型链继承还缺少一环,那就是Object,所有的构造函数都是继承自Object。而继承Object是自动完成的,并不需要程序员手动继承。
function Box()
{
this.name = ‘Lee’;
}
Box.prototype.name = ‘jack’;

function Desk()
{
this.age = 100;
}
Desk.prototype = new Box(); //通过原型链继承。

var box =new Box();
var desk = new Desk();

//子类型从属于自己或者它的超类型。
console.log(desk instanceof Object);//true
console.log(desk instanceof Desk);//true
console.log(desk instanceof Box);//true
console.log(box instanceof Desk);//false

PS:在JavaScript里,被继承的函数成为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。 继承也有之前的问题,比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

2. 对象冒充继承(借用构造函数)

为了解决引用共享和超类型无法传参的问题,我们采用一种叫做借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两个问题。

function Box(name,age)
{
this.name = name;
this.age = age;
/*this.family = [‘a’,’b’,’c’];*/
}
Box.prototype.family = ‘家庭’;

function Desk(name,age)
{
Box.call(this,name,age) //对象冒充
}

var desk = new Desk(‘Lee’,100);
console.log(desk.name);//Lee

/*console.log(desk.family);//[‘a’,’b’,’c’]*/
console.log(desk.family)//undefined 使用对象冒充只能继承构造函数里的信息,无法继承原型中的属性。

3.组合模式

借用构造函数虽然解决了刚才两种问题,但是没有原型,复用则无法谈起。所以,我们需要用原型链+借用构造函数的模式,这种模称为组合模式

—————————————–
function Box(name,age)
{
this.name = name;
this.age = age;
this.family = [‘a’,’b’,’c’];
}

//构造函数里的方法,放在构造里,每次实例化,都会分配一个内存地址,造成浪费。所以最好放在原型中,保证多次化只有一个地址。

function Desk(name,age)
{
Box.call(this,name,age)
}

var desk = new Desk(‘Lee’,100);
var desk2 = new Desk(‘Lee’,100);

————————————————————————–

function Box(age)
{
this.name = [‘Lee’,’Jack’,’Hello’];
this.age = age;
}
Box.prototype.run = function()
{
return this.name +this.age;
};
function Desk(age)
{
Box.call(this,age)//对象冒充
}

Desk.prototype = new Box();

var desk = new Desk(100);

console.log(desk.run());//Lee,Jack,Hello100

————————————————————
4. 原型式继承

还有一种继承模式叫:原型式继承;这种继承借助原型并基于已有的对象创建新对象,同时还不必创建自定义类型。

//① 临时中转函数
function obj(o) //o表示将要传递进入的一个对象 传递一个字面量函数
{
function F() {}; //F构造函数是一个临时新建的用来存储传递过来的对象 创建一个构造函数
F.prototype = o; //将o对象实例赋值给F构造的原型对象 把字面量函数赋值给构造函数的原型。
return new F(); //最后返回这个得到的传递过对象的对象实例 把最终返回出 实例化的构造函数
}

//② F.prototype = o 其实就相遇于 Desk.prototype = new Box();

//这是个字面量的声明 相当于 var box = new Box();
var box = { //字面量对象。
name:’Lee’,
family:[‘哥哥’,’妹妹’,’姐姐’]
};

//box1 就等于 new F()
var box1 = obj(box); //传递
console.log(box1.name);//Lee

box1.name = ‘jack’;
console.log(box1.name);//jack

console.log(box1.family)//[‘哥哥’,’妹妹’,’姐姐’]
box1.family.push(‘弟弟’);
console.log(box1.family);//[‘哥哥’,’妹妹’,’姐姐’,’弟弟’]

var box2 = obj(box);
console.log(box2.family);//[‘哥哥’,’妹妹’,’姐姐’,’弟弟’] 引用类型的属性共享了

5.寄生式继承

5.1
寄生式把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。

//① 临时中转函数
function obj(o) //
{
function F() {}; //
F.prototype = o; //
return new F(); //
}

//寄生函数
function create(o)
{
var f = obj(o);
f.run = function()
{
return this.name +’方法’;
};
return f;
}

var box = { //字面量对象。
name:’Lee’,
family:[‘哥哥’,’妹妹’,’姐姐’]
};

var box1 = create(box);
console.log(box1.name); //Lee

5.2 组合式继承是Javascript最常用的继承模式;但是组合模式也有一点小问题,就是超类型在使用过程中会被调用两次;一次是创建子类型的时候,另一次是在子类型构造函数的内部。

function Box(name)
{
this.name = name;
this.arr = [‘哥哥’,’妹妹’,’父母’];
}
Box.prototype.run = function()
{
return this.name;
};
function Desk(name,age)
{
Box.call(this,name);//第二次调用Box
this.age = age;
}
Desk.prototype = new Box();//第一次调用Box

以上代码是之前的组合继承,那么寄生组合继承,解决了两次调用的问题。

6. 寄生式组合继承(比如雅虎框架)
//临时中转函数
function obj(o)
{
function F(){};
F.prototype = o;
return new F();
}

//寄生函数

function create(box,desk)
{
var f = obj(box.prototype);
f.constructor = desk ;//调整原型构造指针
desk.prototype = f;
}

function Box(name,age)
{
this.name = name;
this.age = age;

}
Box.prototype.run = function()
{
return this.name +this.age + ‘运行中…’
};

function Desk(name,age)
{
Box.call(this,name,age);//对象冒充
}

//通过寄生组合继承来实现继承
create(Box,Desk);//这句话用来代替 Desk.prototype = new Box();

var desk = new Desk(‘Lee’,100);
console.log(desk.run())//Lee100运行中…
console.log(desk.constructor)//ƒ Box(name,age){this.name = name;this.age = age;}
</code></pre>

本文来自投稿,不代表微擎百科立场,如若转载,请注明出处:https://www.w7.wiki/develop/2351.html

发表评论

登录后才能评论