【JavaScript】第二十四天 (原型和原型链)

原型和原型链

一、构造函数和原型

1.1构造函数的问题

通过构造函数实例化对象虽然很好用,但是他也存在一些问题,首先一个就是内存浪费的问题。

下面我们来看一个例子

在这里插入图片描述

我们可以 给函数的原型添加函数,这样的话,函数所有的实例化对象都可以使用这个函数,大大减少函数重复占用内存。

1.2 构造函数原型prototype

获取原型的方法:

1.通过对象__proto__来获取
2.通过构造函数的prototype属性来拿到原型

构造函数通过原型分配的函数是所有对象所共享的

javascript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象。

注意这个prototype 也是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不便的方法马志杰定义在prototype 对象上,这样所有对象的实例就可以共享这些方法。

举例

function Star(uname, age) { 
      this.uname = uname; 
      this.age = age; 
}

Star.prototype.sing = function() { 
       console.log('我会唱歌'); 
}
var zsf = new Star('张三丰', 18); 
var zxy = new Star('张学友', 19); 
zsf.sing();//我会唱歌 
zxy.sing();//我会唱歌

1.3对象原型

刚才我们说了,每一个函数都有一个prototype 属性,这个属性就是构造函数的原型,那当函数被实例化以后呢?

构造函数被实例化后的队形都会有一个属性_proto_指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数prototype 原型对象的属性和方法。就是因为对象有__ proto__ 原型的存在。

从而我们知道:__ proto__对象原型和原型对象 prototype是等价的,__ proto__对象原型和原型对象 prototype是等价的,__ proto__对象原型的意义就在于为对象的查找机制提供了方向,或者说一条路线,但是他是一个非标准属性,因此实际开发中,不可以使用这个属性,他只是内部指向原型对象prototype。

在这里插入图片描述

1.4 constructor构造函数

上面我们说了构造函数和对象的原型,在对象的原型(proto)和构造函数(prototype) 原型对象里面都有一个属性 constructor 属性,constructor 我们称为构造函数,因为他指向构造函数本身。

在这里插入图片描述

1.5 原型链

每一个实例对象又有一个proto属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有proto属性,这样一层一层往上找就形成了原型链。

我们看如下图所示:

在这里插入图片描述

1.6 构造函数实例和原型对象三角关系

综上所述:
构造函数的prototype属性指向了构造函数原型对象
实例对象是由构造函数创建的,实例对象的proto属性指向了构造函数的原型对象

构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向 了构造函数

由上面所述内容,我总结构造函数,实例化对象,原型对象三者之间的关系如下图所示:

在这里插入图片描述

继承

面向对象的三个特点就是封装,继承和多态,通过继承,我们可以实现对象和方法的复用,从而提高代
码的利用率,也是代码优化的一种方案。下面我们看一下js的继承是怎么实现的?

2.1 子构造函数继承父构造函数中的属性

上面说了call方法的基本使用,下面我们看一下如何使用call完成函数的继承呢?也就是我们经常所说的
构造函数继承,主要分为三个步骤:
● 先定义一个父构造函数
● 再定义一个子构造函数
● 子构造函数继承父构造函数的属性(使用call方法)

// 1. 父构造函数 
function Father(uname, age) { // this 指向父构造函数的对象实例 
      this.uname = uname; 
      this.age = age; 
}
// 2 .子构造函数 
function Son(uname, age, score) { // this 指向子构造函数的对象实例 3.使用call方式实现子继承父的属性 
       Father.call(this, uname, age); 
       this.score = score; 
 }
var son = new Son('张三丰', 18, 100); 
console.log(son);

2.2 借用原型对象继承方法

当然我们也可以借用原型来继承函数,步骤大致如下所示:

  • 先定义一个父构造函数
  • 再定义一个子构造函数
  • 子构造函数继承父构造函数的属性(使用call方法),把父方法的实例化对象赋给子方法的原型。

代码如下所示:

// 1. 父构造函数 
function Father(uname, age) { // this 指向父构造函数的对象实例 
     this.uname = uname; this.age = age; 
}
Father.prototype.money = function() { 
      console.log(100000); 
};
// 2 .子构造函数 
function Son(uname, age, score) { // this 指向子构造函数的对象实例 
       Father.call(this, uname, age);
       this.score = score; 
} 
// Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型 对象也会跟着一起变化 
Son.prototype = new Father(); // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数 
Son.prototype.constructor = Son; // 这个是子构造函数专门的方法 
Son.prototype.exam = function() { 
    console.log('孩子要考试'); 
}
var son = new Son('张三丰', 18, 100); 
console.log(son);

2.3 组合继承(伪经典继承)

function SuperType (name) { 
      this.name = name 
      this.colors = ["red", "blue", "green"] 
}
SuperType.prototype.sayName = function () { 
      alert(this.name) 
}
function SubType (name, age) { // 继承属性 
      SuperType.call(this, name) // 第二次调用SuperType() 
      this.age = age 
}
//继承方法 

SubType.prototype = new SuperType() // 第一次调用 SuperType() 
Subtype.prototype.sayAge = function () { 
  alert(this.age) 
}

实现思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

注意: 组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,成为js中最常用的继承方式

2.4 ES6继承

先看下如何定义类

class Parent { 
constructor(name,age) { 
this.name = name; 
this.age = age; 
}
getName() { 
return this.name; 
} 
}

类的所有方法都定义在类的prototype属性上面。且class不存在变量提升,如果在class声明之前调用, 会报错。

类的继承

class Child extends Parent { 
constructor(name,age,sex) { 
super(name,age); 
this.sex = sex; 
} 
} 
var child = new Child('xiaoyu',12,'man');

在子类的构造函数中,如果显示声明了constructor,则必须要显示的调用super函数(这一点和Java有 点不一样)。

只有调用super之后,才可以使用this关键字,否则会报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值