原型的做用:1.将公共部分放入原型中,这样构造出的多个实例对象的公共部分只会占用一个公共空间,实现数据共享和节省内存空间
2.经过原型实现继承:构造函数模拟 "类"这个面向对象的概念,JS基于对象,基于构造函数的原型对象实现继承
如何实现继承?
1.改变原型对象的指向:将子类构造函数(B)的prototype指向父类构造函数(A)的一个实例化对象(a),那么这个子类构造函数构造出的实例化对象
(b)就能够访问父类(A)的属性和方法
缺陷:因为B的prototype改变,那么保存在原来的B的prototype里的属性和方法就没法访问了,构造出的b没法得到这些属性和方法
解决方法:先进行原型指向的改变,再定义子类的原型属性和方法,这样子类后定义的原型属性和方法就定义到了父类的实例对象中
var Person = function(name,sex){ this.name = name; this.sex= sex; } Person.prototype.eat = function () { console.log("吃"); }; var Student = function(score){ this.score = score; }; Student.prototype.study = function () { console.log("学习"); }; Student.prototype = new Person("xiaoming","man"); var stu = new Student(66); stu.eat(); //能够调用 stu.study() //报错,因为改变了prototype指向,没法寻找到study这个方法
先改变原型指向,后定义原型方法:app
var Person = function(name,sex){ this.name = name; this.sex= sex; } Person.prototype.eat = function () { console.log("吃"); }; var Student = function(score){ this.score = score; }; Student.prototype = new Person("xiaoming","man"); Student.prototype.study = function () { console.log("学习"); }; var stu = new Student(66); stu.eat(); //能够调用 stu.study(); //能够调用 console.log(Student.prototype); /* name:"xiaoming" sex:"man" study:ƒ () 能够看出后定义的方法写入了Student.prototype即这个new Person实例化对象中了 __proto__:Object 这个隐式原型指向Person.prototype,里面有eat方法 */
若是使用这个方式实现多代继承,那么每一代都须要先改变原型指向,在定义原型属性和方法。函数
若是使用这个方式实现多代继承,那么每一代都须要先改变原型指向,在定义原型属性和方法。
在新的原型链中,本来子代的构造函数的prototype消失,新的prototype便是父代的一个实例化对象。
而子代实例对象的__proto__都指向其父代的这个实例化对象,原型链就成为实例化对象之间的指向关系,直到最高级祖先的构造函数的prototype
仍然存在的问题:改变子代prototype的指向,指向父代的一个实例对象,那么这个实例对象的属性和方法就已经被初始化了,即继承过来的属性和方法是已经肯定的,没法在构建子代实例化对象时从新初始化这些继承下来的属性和方法
2.借用构造函数实现继承
* 利用call方法或者apply方法借用父代的构造函数
* 在子代构造函数中添加
* 父代构造函数.call(this.父代形参列表)
* 同时也要在子代的形参列表中加入父代的形参列表
* 想当于在子代构造函数中也写了父代构造函数中的定义属性和方法的那些代码
* 因此优势:能够在子代构造函数实例化对象时本身初始化父代的属性和方法,再也不是继承到固定的实现和方法
* 因此缺陷:没有在子代的原型和父代原型之间造成原型链,因此没法访问父代原型里的方法和属性
var Person = function (name) { this.name= name; this.say = function () { console.log("Hi,I am" + this.name); }; }; Person.prototype.eat = function () { console.log("吃"); }; Person.prototype.sex = "man"; //父代原型里的属性 var Student= function (score,name) { this.score = score; Person.call(this,name); // Person.apply(this,[name]); }; Student.prototype.test = function () { console.log("考试"); }; var stu1 = new Student(80,"小王"); console.log(stu1.name); stu1.say(); //能够调用父代构造函数里的的属性和方法 console.log(stu1.sex); //undefined stu1.eat(); //报错------>没法经过借用构造函数的方法继承父类原型里的属性和方法
3.组合继承:结合以上两种方法:学习
* 1):改变prototype指向(这时不须要再new父代实例化对象时传入参数),子代和父代之间造成原型链,能够继承父代原型里的属性和方法
* 2):借用父代的构造函数,实现继承父代构造函数内的属性和方法,还能够在实例化子代时本身初始化这些属性和方法
var Person = function (name) { this.name= name; this.say = function () { console.log("Hi,I am" + this.name); }; }; Person.prototype.eat = function () { console.log("吃"); }; Person.prototype.sex = "男"; //父代原型里的属性 var Student= function (score,name) { this.score = score; Person.call(this,name); // Person.apply(this,[name]); }; Student.prototype = new Person(); Student.prototype.test = function () { console.log("考试"); }; var stu2 = new Student(80,"小王"); console.log(stu2.name); stu2.say(); //能够调用父代构造函数里的的属性和方法 console.log(stu2.sex); stu2.eat(); //能够调用父代原型里的属性和方法 console.log(stu2.score); stu2.test(); //固然能够正常调用子代的属性和方法