面向对象与原型5---继承

1.用原型链实现继承 最普通 但是没有办法传参,没有办法共享方法

继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继
承:一个是接口实现,一个是继承。而 ECMAScript
只支持继承,不支持接口实现,而实现
继承的方式依靠原型链完成。

原型链是由原型加对象构造之间的关系结构形成的像一个链条一样

在 JavaScript 里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的
函数称为子类型(子类,派生类)。

//继承,通过原型链实现

function Box() { //Box 构造 被继承的函数叫做超类型(父类)
this.name =
‘Lee‘;
}
function Desk() { //Desk 构造 继承的函数称为子类型(子类,派生类)。
this.age
= 100;
}

var desk = new Desk();

alert(desk.age);   //100

alert(desk.name);   //undefined

//通过原型链继承,超类型实例化后的对象实例,赋值给子类型的原型属性即可

//new Box()会将Box构造里的信息和原型里的信息都交给Desk
Desk.prototype = new Box(); //Desk
继承了 Box,通过原型,形成链条  超类型new出来的东西交给子类型的原型
var desk = new
Desk();
alert(desk.age);
alert(desk.name); // Lee 得到被继承的属性

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

Desk的原型得到的是Box的构造加原型里的信息(图中水平第一个和第二个框),Table的原型又得到了Desk的构造和原型里的信息,所以最终的
 Table得到了上面所有的信息。

如果要实例化 table,那么 Desk 实例中有 age=100,原型中增加相同的属性
age=200,
最后结果是多少呢?
Desk.prototype.age = 200; //实例和原型中均包含 age

alert(table.age);  //输出100  就近原则  实例里面有就返回,没有就去查找原型

从属关系

以上原型链继承还缺少一环,那就是 Obejct,所有的构造函数都继承自 Obejct。而
继承 Object
是自动完成的,并不需要程序员手动继承。

//子类型从属于自己或者他的超类型

alert(desk instanceof Desk);  //true
alert(desk instanceof
Box);  //true

alert(desk instanceof Object);  //true

继承也有之前问题,比如字面量重写原型会中断关系,使用引用类型的原型时,并且子类型还无法给超类型传递参数。

2.第二种继承方法——借用构造函数继承 即对象冒充继承 解决了传参的问题  但是没有解决共享的方法

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

//使用对象冒充

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

}

Box.prototype.family=‘家庭‘;
function Desk(name,age) {
Box.call(this,
name,age); //this代表了Desk本身的类   对象冒充,给超类型传参 因此 Desk就可以冒充Box了
对象冒充只能继承构造里的信息,继承不了原型里的信息

}

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

alert(desk.name);  //输出 Lee   说明Desk冒充了Box

alert(desk.family);  //undefined   原型里的信息继承不过来

//引用类型放到构造函数里就不会共享

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

this.family = [‘哥哥‘,‘姐姐‘,‘妹妹‘];

}

function Desk(name,age){

Box.call(this,name,age);

}

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

desk.family.push(‘弟弟‘);

alert(desk.family);  //输出 哥哥 姐姐 妹妹 弟弟

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

alert(desk2.family);  //输出  哥哥 姐姐 妹妹  引用类型放到构造函数里就不会共享

(参看面向对象与原型4---原型

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

this.family = [‘哥哥‘,‘姐姐‘,‘妹妹‘];

this.run = function(){

}

}

function Desk(name,age){

Box.call(this,name,age);

}

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

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

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

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

this.family = [‘哥哥‘,‘姐姐‘,‘妹妹‘];

}

Box.prototype.run = function(){

  return this.name+this.age;

}

function Desk(name,age){

Box.call(this,name,age);

}

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

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

以上两次实例化保证了一个地址

但是

alert(desk.run());  //not a function  因为通过对象冒充继承不到原型里的东西

3.组合模式实现继承(结合前两种)----最常用

对象冒充 结合 原型链
 解决传参和方法共享的问题
借用构造函数虽然解决了刚才两种问题,第一个:引用保持独立。第二个:可以传参。但没有原型,复用则无从谈起。所以,我们需
要原型链+借用构造函数的模式,这种模式成为组合继承。

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

this.family = [‘哥哥‘,‘姐姐‘,‘妹妹‘];

}

Box.prototype.run = function(){

  return this.name+this.age;

}

function Desk(name,age){  

Box.call(this,name,age);  //对象冒充只继承构造里的

}

Desk.prototype = new Box();  //原型链继承  只继承原型里的

4.原型式继承

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

//临时中转函数

function obj(o){ //o表示将要传递进入的一个对象实例 

  function F(){}  //F构造是一个临时新建的对象,用来存储传递进来的对象

  F.prototype=o;  //将o对象实例赋值给F构造的原型

  return new F();  // 最后返回这个得到传递过来对象的对象实例

}

var box={  //这是字面量的声明方式,相当于 var box=new Box();

name:‘Lee‘,

age:100,

family:[‘哥哥‘,‘姐姐‘,‘妹妹‘]

}

var box1=obj(box);  //box1就等于new出来的F

alert(box1.name);  //Lee

box1.family.push(‘弟弟‘);

alert(box1.family);  //哥哥姐姐妹妹弟弟

var box2=obj(box);

alert(box2.family);  //哥哥姐姐妹妹弟弟 引用类型的属性共享了  因为把box全部放到F.prototype里了

标题说的原型式继承实际上是F继承了box   F.prototype=o 其实就相当于Desk.prototype= new Box()

这种方法其实就是原型链继承 意思是一样的  只不过是改写了方式

5.寄生式继承 =原型式+工厂模式  ,目的是为了封装创建对象的过程

//临时中转函数

function obj(o){ //o表示将要传递进入的一个对象实例 

  function F(){}  //F构造是一个临时新建的对象,用来存储传递进来的对象

  F.prototype=o;  //将o对象实例赋值给F构造的原型

  return new F();  // 最后返回这个得到传递过来对象的对象实例

}

//寄生函数

function create(o){

var f=obj(o);

f.run = function(){

  return this.name;

}

return f;

}

var box={  //这是字面量的声明方式,相当于 var box=new Box();

name:‘Lee‘,

age:100,

family:[‘哥哥‘,‘姐姐‘,‘妹妹‘]

}

var box1=create(box);

alert(box1.name);

alert(box1.run());//Lee

组合式继承是 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);  //调用obj函数

f.constructor= desk;  //调整原型构造指针

desk.prototype =f;

}

function Box(){

  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);

alert(desk.run());  //Lee100

面向对象与原型5---继承,布布扣,bubuko.com

时间: 2024-12-09 10:35:33

面向对象与原型5---继承的相关文章

面向对象的原型与继承

1. 为什么需要原型 构造器创建对象的时候, 实际上会有成员重复 如果使用 构造器 this.方法名 = function .... 方式创建对象. 那么每一个对象 对应的方法就会重复. 解决办法就是让这个方法( 函数 )共享 -> 将函数写到外面, 那么 Person 在初始化对象的时候就不会再创建一个函数了. 只需要将 外面的函数引用 交给对象即可. 缺点: 一个对象可能有 n 多方法. 如果将所有的东西 都放到外面, 与其他库 冲突的几率就会变大. 所以不宜采取该方法. -> 将所有的方

JS面向对象,原型,继承

ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScript没有类的概念,因此它的对象也与基于类的语言中的对象有所不同.var box = new Object();box.name = 'Lee';box.age = 100;box.run = function(){ return this.name + this.age + '运行中...'; //th

js面向对象编程/原型链/继承 —— javascript

目录 js面向对象编程 js原型链 共享方法 原型继承 js面向对象编程 js面向对象编程不同于 java 的类和对象 JavaScript 不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程. js声明的构造函数,类似于普通函数的声明,但又不同, 实例对象时,如果不写new,就是一个普通函数,它返回 undefined. 但是,如果写了new,它就变成了一个构造函数,它绑定的 this 指向新创建的对象, 并默认返回 this,也就是说,不需要在最后写return th

JavaScript 随笔2 面向对象 原型链 继承

第六章 面向对象的程序设计 1.创建对象的几种方式 A)工厂模式 function CreatObj(name,sex,age){ this.name=name; this.sex=sex; this.age=age; } 缺点:虽然可以批量创建对象,却不能知道对象的类型 只知道他是Object类型: B)构造函数 function Person(name,sex){ this.name=name; this.sex=sex; this.sayName=function(){ alert(thi

面向对象之笔记二——————原型与继承

原型与继承 原型 为什么需要原型? 构造器创建对象的时候, 实际上会有成员重复 如果使用 构造器 this.方法名 = function .... 方式创建对象. 那么每一个对象对应的方法就会重复 function Person( name ) { this.name = name; this.sayHello = function() { console.log( '你好,我是 ' + this.name ); }; } var p1 = new Person( 'Hello' ); var

一步步学习javascript基础篇(5):面向对象设计之对象继承(原型链继承)

上一篇介绍了对象创建的几种基本方式,今天我们看分析下对象的继承. 一.原型链继承 1.通过设置prototype指向“父类”的实例来实现继承. function Obj1() { this.name1 = "张三"; } function Obj2() { } Obj2.prototype = new Obj1(); var t2 = new Obj2(); alert(t2.name1); 这里有个明显的缺点就是:(如果父类的属性是引用类型,那么我们在对象实例修改属性的时候会把原型中

JS面向对象与原型

面向对象与原型一.创建对象 1.基本方法 1 var box = new Object(); //创建对象 2 box.name = 'Lee'; //添加属性 3 box.age = 100; 4 box.run = function(){ //添加方法 5 return this.name + this.age + '运行中...'; //this表示当前作用于下的对象 6 }; 7 8 alert(box.run()); 9 10 alert(this.anme); //这里的this代表

js面向对象及原型(javaScript高级程序设计第3版)

一.创建对象 创建一个对象,然后给这个对象新建属性和方法. var box = new Object(); //创建一个Object对象 box.name = 'Lee'; //创建一个name属性并赋值 box.age = 100; //创建一个age属性并赋值 box.run = function () { //创建一个run()方法并返回值 return this.name + this.age + '运行中...'; }; alert(box.run()); //输出属性和方法的值 上面

JavaScript之基础-16 JavaScript 原型与继承

一.JavaScript 原型 原型的概念 - 在JavaScript中,函数本身也是一个包含了方法和属性的对象 - 每个函数中都有一个prototype属性,该属性引用的就是原型对象 - 原型对象是保存共享属性值和共享方法的对象 为对象扩展属性 - 扩展单个对象的成员 - 扩展共享的属性值 - 内存图描述 删除属性 - 可以使用delete关键字删除对象的属性 自由属性与原型属性 - 自有属性:通过对象的引用添加的属性;其它对象可能无此属性;即使有,也是彼此独立的属性 emp1.job = '