通过一个例题彻底理解原型链

今天看到了一个有意思的原型链例题,可以很好的理顺思路。
首先我们来描述一下下面这段代码的思路。构建两个对象,学生和人类。
人类拥有name, age属性,和hi(), walk()函数(通过原型)。
学生通过Student.prototype=Object.create(Person.prototype);原型继承到name和age属性,并且在学生原型下建立hi(),learn()函数。

代码如下:

function Person(name, age) {
this.name = name;
this.age = age;
//this.walk = function() {
//console.log(this.name + "is walking...");
    //}
}
Person.prototype.hi = function() {
console.log("Hi,my name is" + this.name + ",I'm" + this.age + "years old now.");
};
Person.prototype.walk = function() {
console.log(this.name + " is walking...");
};
Person.prototype.LEGS_NUM = 2;
Person.prototype.ARMS_NUM = 2;


function Student(name, age, className) {
Person.call(this, name, age);
this.className = className;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.hi = function() {
console.log("Hi,my name is " + this.name + " ,I'm " + this.age + " years old now,and from " + this.className + ".");
};
Student.prototype.learn = function(subject) {
console.log(this.name + " is learning " + subject + " at " + this.className + ".");
};

//test
var stu1 = new Student("小明", 27, "Class 3,Grade 2");
stu1.hi(); //Hi,my name is 小明,I'm 27 years old now,and from Class 3,Grade 2.
stu1.LEGS_NUM; //2
stu1.walk(); //小明 walking...
stu1.learn("数学"); //小明 learning 数学 at Class 3,Grade 2.

逻辑分析(按照代码 从上到下):

1.Student.prototype=Object.create(Person.prototype);是将Student.prototype的原型继承指向Person.prototype;为什么不使用Student.prototype=Person.prototype呢?因为Student.prototype会有自己独有的方法,譬如learn(){},如果改成这句代码,那么添加或修改Student.prototype的属性方法后,Person.prototype的属性和方法也会跟着被修改了,因为这两者已经指向了同一个对象。
2. var stu1 = new Student(“小明”, 27, “Class 3,Grade 2”);
构造了一个对象stu1,并且生成了一个原型空间,stu1指向Student.prototype。
3. stu1.hi(); stu1.LEGS_NUM; stu1.learn(“数学”); 这三个都是调用Student原型的方法和函数。
4. stu1.walk(); 这里显示结果为小明 walking… ,我们思考一个问题,起初walk函数被Person.prototype拥有,那么stu1对象调用的是Student原型继承过来的walk函数还是一步一步向上查询到的Person.prototype的walk函数。
所以我们再通过console.log(stu1);看一下,结果如图。
这里写图片描述

证明调用的是Person.prototype的walk函数。所以说,原型下创建的方法并不会被继承。

通过以上思考,我们结合原型链图来更好理解。


这里写图片描述

下面再加入几行代码。

            Student.prototype.x = 101;
            console.log(stu1.x); //101
            Student.prototype = {
                y: 2
            };
            console.log(stu1.y); //undefined
            console.log(stu1.x); //101

分析:
1. 首先是给学生原型加了个属性x=101.
2. 然后我们新建一个学生原型,并且加入属性y=2.
3. 分别通过原来的对象调用x,y.不难理解,x在自己的学生原型属性中,还是101,但是y是存在了新分配的Student原型中,stu1对象并没有指向它。所以访问stu2.y,首先在stu2对象本身寻找,没有,去新分配的原型找,发现也没有,这时候只能返回undefined。

那么好,接下来我们再来几行代码,创建一个stu2指向新的Student原型。

    var stu2 = new Student("韩梅梅", 3, "Class 6 ,Grade 6");
    console.log(stu2.y);//2
    console.log(stu2.hi()); //报错  stu2.hi is not a function
    stu2.walk(); //报错  stu2.walk is not a function

分析1.因为新建了一个原型,所以对象stu2并没有指向本来的Student原型,所以stu2.y得到了新原型的属性。
2.从stu2.hi和stu2.walk的报错结果来看,我们可以得出关键结论,当我们新建一个原型,构造函数内的建立方法可以继承过来,而通过下面代码的方法新建不会跟随继承。

Person.prototype.hi = function() {
console.log("Hi,my name is" + this.name + ",I'm" + this.age + "years old now.");
};

3.当下面代码注释取消后,walk函数成功调用。结果为 韩梅梅 is walking…
论证了上面的结论。

//this.walk = function() {
//console.log(this.name + "is walking...");
    //}

下面结合最终原型链图来理解一下。
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_38735649/article/details/81272578