今天看到了一个有意思的原型链例题,可以很好的理顺思路。
首先我们来描述一下下面这段代码的思路。构建两个对象,学生和人类。
人类拥有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...");
//}
下面结合最终原型链图来理解一下。