首页 > 编程知识 正文

浅谈对原型及原型链的理解和感悟,对原型和原型链的理解

时间:2023-05-03 07:21:54 阅读:249004 作者:3122

在开篇我先总结一下原型的作用:
1.数据共享 节约内存内存空间
2.实现继承
注意:函数也是一个对象,对象不一定是函数。(对象有__proto__属性,函数有prototype属性)此处说明,方便大家理解下文。
下面我将举例说明为什么要使用原型
例1:

function Person(name) { this.name = name; this.eat = function () { console.log(this.name+"吃东西"); } this.sleep = function () { console.log(this.name+"睡觉"); }}var p1 = new Person("悲凉的裙子");p1.eat();//悲凉的裙子吃东西p1.sleep();//悲凉的裙子睡觉var p2 = new Person("zjdsj");p2.eat();//zjdsj吃东西p2.sleep();//zjdsj睡觉console.dir(p1);//dir()打印结构console.dir(p2);

每次使用构造函数Person()实例化出对象的时候,就会给每个实例对象的eat()方法和sleep()方法开辟空间。可是当实例化许多对象的时候,就会浪费大量的空间,因为每个实例对象的eat()方法和sleep()的功能都是一样的,所以我们没必要为每个实例对象添加eat()方法和sleep()方法。

这时原型就派上用场了,看下面经过改造的例子:

function Person(name) { this.name = name;}Person.prototype.eat = function () { console.log(this.name+"吃东西");};Person.prototype.sleep = function () { console.log(this.name+"睡觉");}var p1 = new Person("悲凉的裙子");p1.eat();//悲凉的裙子吃东西p1.sleep();//悲凉的裙子睡觉var p2 = new Person("zjdsj");p2.eat();//zjdsj吃东西p2.sleep();//zjdsj睡觉console.dir(p1);console.dir(p2);

 

eat()方法和sleep()被添加到了Person()构造函数的原型(prototype)上了。因此只有一份eat()方法和sleep()方法。当实例对象需要使用这些方法的时候就在自己的__proto__属性中找到并调用这些方法实现相应的功能。

现在我们来捋一下构造函数,实例对象,原型对象之间的关系。

如以下代码和图所示:

Person()构造函数的prototype属性是一个对象,实例对象p1的__proto__属性也是一个对象,并且prototype对象和__proto__对象的指向相同。那么我们再回过头来理解一下为什么添加到原型的方法可以是共享的。因为prototype对象和__proto__对象的指向相同,所以将eat()方法和sleep()添加到Person()构造函数的prototype属性上之后,实例对象就可以通过自己__proto__属性去访问eat()方法和sleep()了。

console.dir(Person);console.dir(p1);console.log(typeof p1.__proto__);//objectconsole.log(typeof Person.prototype);//objectconsole.log(p1.__proto__ === Person.prototype);//true

 

__proto__指向该对象所在的构造函数的原型对象。

实例对象和构造函数之间没用直接的关系。原型对象与实例对象之间用原型(__proto__)关联,这种关系叫做原型链。

我是这样理解原型链的(可能不是很准确)我向我爸要零花钱,我爸也没有钱,那么我就向我奶奶要,奶奶要是也没有,就继续找别人要。

那么原型的指向可以改变吗?答案是可以的。

举个例子:

function Person(name) { this.name = name;}Person.prototype.eat = function () { console.log(this.name+"吃东西");};Person.prototype.sleep = function () { console.log(this.name+"睡觉");}function Student(school) { this.school = school;}Student.prototype.write = function () { console.log("写作业");}Student.prototype = new Person("tzdhm");//改变Student()构造函数的指向,让Student()构造函数的原型对象指向Person的实例对象var s1 = new Student("某某高中");s1.eat();//tzdhm吃东西s1.sleep();//tzdhm睡觉s1.write();//Uncaught TypeError: s1.write is not a function,因为Student()的原型的指向改变,所以找不到write()方法console.dir(Student);console.dir(s1);

__proto__指向该对象所在的构造函数的原型对象。如上图所示:Studend()构造函数的原型(prototype)指向了Person()的实例对象(new Person("tzdhm")),所以Studend()的实例对象s1的__proto__也指向了Person()的实例对象((new Person("tzdhm"))。而实例对象((new Person("tzdhm"))的__proto__指向了其所在的构造函数Person()的原型对象在这个原型对象中,找到了eat()方法和sleep()方法。

从这个例子中,可以发现,利用原型可以实现继承。面向对象的编程语言中有(class)类的概念,但是JavaScript不是面向对象的语言,所以js中没有类(class)(ES6中实现了class),但是js可以模拟面向对象的思想编程,js中通过构造函数来模拟类的概念。

改变原型的指向可以实现方法的继承。借用构造函数继承,主要解决属性的问题

function Person(name) { this.name = name;}Person.prototype.eat = function () { console.log(this.name+"吃东西");};Person.prototype.sleep = function () { console.log(this.name+"睡觉");}function Student(name,school) {//name为父类构造器传参。子类构造器可以添加自己特有的属性school Person.call(this,name);//调用父类构造器Person的属性, this.school = school;}Student.prototype.write = function () { console.log("写作业");}Student.prototype = new Person();//改变Student()构造函数的指向,让Student()构造函数的原型对象指向Person的实例对象var s1 = new Student("zx","某某高中");s1.eat();//tzdhm吃东西s1.sleep();//tzdhm睡觉console.dir(Student);console.dir(s1);

组合继承就是指:将改变原型的指向和借用构造函数两者结合在一起实现继承。

本文章是个人对原型的理解,有不准确的地方,还希望各位大神赐教......

Prewitt

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。