Js是函数式语言,并不是传统的面对对象语言,如Java等等,所以在js中有一些比较独特的面对对象实现方法。
面对对象有多个特点,如封装,继承,多态等,由于js的oop特性并不明显,所以仅叙述继承在js中的实现。
众所周知,js中函数也可以是类,通过new在堆中手动开辟空间就是对象的实例,而且,每个对象都天生有一个指针,指向原型,原型也可以理解为一个对象,可以定义自己的方法,如下图所示,不再赘述,详细可以参考廖雪峰教程。
function Person(name){
this.name = name||‘unnamed‘;
}
function Student(name,grade){
Person.call(this,name);
this.grade = grade||1;
}
Person.prototype.getname = function(){
return this.name;
}
function f(){}
// 令f的原型指向Person的原型
f.prototype = Person.prototype;
// 令student的原型指向f的实例
Student.prototype = new f();
// 重置构造函数为Student
Student.prototype.constructor = Student;
// 必须在改变原型指针后再定义函数,否则无法访问到
Student.prototype.getgrade = function(){
return this.grade;
}
var xm = new Student(‘xm‘,2);
console.log(new f().__proto__) // Person {getname:[Function]}
console.log(new f().__proto__==Person.prototype) // true
console.log(xm.getname()) // xm
console.log(xm.getgrade()) // 2
console.log(xm.__proto__==Student.prototype) // true
console.log(xm.__proto__.__proto__==Person.prototype) // true
上述过程可以用原型链来理解,起初Student的原型链为
new Student() ---> Student.prototype ---> Object ---> null
其中一定要引入新的f函数绕一下的原因是如果暴力引入
Student.prototype = Person.prototype
那么就失去了继承和共享原型的意义,原型链变成了
new Student() ---> Person.prototype ---> Object ---> null
我们希望原型链是
new Student() ---> Student.prototype ---> Person.prototype ---> Object ---> null
这条原型链就很好的共享了Person的原型,如果要创建更多的对象,如工人,就也可以用到Person原型的方法了,十分方便。
对于这些代码,其实有一个最好的理解方式,即将这些指针等等过程理解为内存中的变化即可,以原型链的更改为例
- new student().__proto__指向的是Student.prototype变量,这是静态的,内存中的一个名称
- Student.prototype指针变量含有一个地址,这是动态的,前面通过指向new f()指向了Person.prototype,所以Student.prototype内存其实包含两部分,一部分是本身的原型方法,另一部分指向Person的原型指针