从零开始学 Web 之 JS 高级(二)原型链,原型的继承

大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......

github:https://github.com/Daotin/Web

微信公众号:Web前端之巅

博客园:

CSDN:https://blog.csdn.net/lvonve/

在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。现在就让我们一起进入 Web 前端学习的冒险之旅吧!

从零开始学 Web 之 JS 高级(二)原型链,原型的继承

一、原型

原型链表示的是实例对象与原型对象之间的一种关系,这种关系是通过__proto__原型来联系的。

1、原型的指向改变

实例对象的原型 __proto__ 指向的是该对象的构造函数中的原型对象 prototype,如果该对象的构造函数的 prototype 指向改变了,那么实例对象中的原型 __proto__ 的指向也会跟着改变。

例如:

Person.prototype = new Cat();

因为 Person.prototype = {}; 可以是一个对象,所以传入另一个对象的实例函数是可以的,这时候 Person 的实例对象可以访问 Cat 原型 prototype 中的属性和方法,而不能再访问自己 Person 中原型的属性和方法了。

2、原型链的最终指向

实例对象的__proto__指向的是构造函数的原型对象 prototype,由于prototype也是个对象,所以也有 __proto__ ,这个 __proto__ 指向的是 Object 的 prototype,而 Object 的 prototype 里面的 __proto__ 指向的是 null。

示例:

function Person() {} var per = new Person(); console.log(per.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null

原型链图示:

从零开始学 Web 之 JS 高级(二)原型链,原型的继承

3、原型指向改变后添加原型方法

先看个案例:问下面程序有问题吗?

function Person() {} Person.prototype.eat = function () {}; function Student() {} Student.prototype.say = function () {}; Student.prototype = new Person(); var stu = new Student(); stu.say();

解答:stu.say(); 会报错。因为 Student 的原型指向变成了 Person 的一个实例对象,Person 的实例对象钟并没有 say 方法,所以报错。

解决办法:在原型指向改变之后再添加原型方法。

function Person() {} Person.prototype.eat = function () {}; function Student() {} Student.prototype = new Person(); Student.prototype.say = function () {}; var stu = new Student(); stu.say();

PS:这个时候就不会报错, Student 添加的原型方法的位置是一个匿名 Person 的实例对象中,这里是一个 Person 的实例对象,不是所有的,所以当你再 new 一个 Person 的实例对象的时候,不会有 say 方法。

4、实例对象和原型对象属性重名问题

当实例对象访问一个属性的时候,会先从实例对象中找,找到了直接使用,找不到再到指向的原型对象中找,找到了使用,还是找不到,则为 undefined。

如何改变原型对象中的属性的值呢?怎么赋值的怎么修改。

如果你使用 对象.属性 = 值 的方式来赋值的话,如果这个属性在实例对象中有的话,改变的是实例对象中属性的值;如果实例对象中没有这个属性的话,则这次修改相当于给该实例对象添加了一个属性,其指向的原型对象中相应的属性的值并没有被改变。

二、原型的继承 1、原型的继承

原型的第二个作用:继承。目的也是节省内存空间。

通过改变子类原型的指向到父类的实例对象,可以实现继承。

案例:

// 父类:人 function Person(name, age) { this.name= name; this.age=age; } Person.prototype.eat = function () { console.log("eat()"); }; // 子类:学生 function Student(sex) { this.sex=sex; } // 子类继承父类只需要改变子类原型的指向到父类的实例对象。 Student.prototype = new Person("Daotin", 18); Student.prototype.study = function () { console.log("study()"); }; var stu = new Student("male"); console.log(stu.name); // Daotin console.log(stu.age); // 18 stu.eat(); // eat()

缺陷1:在改变子类原型对象的指向的时候,属性在初始化的时候就固定了,那么每个子类实例对象的值都固定了。

解决办法:不需要子类原型的指向到父类的实例对象,只需要借用父类的构造函数。

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.eat = function () { console.log("eat()"); }; function Student(name, age, sex) { Person.call(this, name, age); this.sex = sex; } //Student.prototype = new Person("Daotin", 18); Student.prototype.study = function () { console.log("study()"); }; var stu = new Student("Daotin", 18, "male"); console.log(stu.name); console.log(stu.age); console.log(stu.sex); stu.eat(); // 不能访问

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdpgj.html