//原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,
//每一个对象都会从原型中“继承”属性。
//最近学到的构造函数中原型类似于一个函数的扩展方法。
// 原型和原型链的理解:(面试题)
// 原型:每个函数都有 prototype 属性,该属性指向原型对象;使用原型对象的好处是所有对象实例共享它所包含的属性和方法。
// 原型链:主要解决了继承的问题;每个对象都拥有一个原型对象,通过__proto__ 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。
// 原型的作用:
// 1.数据共享 节约内存内存空间
// 2.实现继承
// 注意:函数也是一个对象,对象不一定是函数。(对象有__proto__属性,函数有prototype属性)此处说明,方便大家理解下文。
// 下面我将举例说明为什么要使用原型
// function Person(name) {
// this.name = name;
// this.eat = function() {
// console.log(this.name + "吃东西");
// }
// this.sleep = function() {
// console.log(this.name + "睡觉");
// }
// }
// var p1 = new Person("小明");
// p1.eat(); //小明吃东西
// p1.sleep(); //小明睡觉
// var p2 = new Person("小利");
// p2.eat(); //小利吃东西
// p2.sleep(); //小利睡觉
// console.dir(p1); //dir()打印结构
// console.dir(p2);
//每次使用构造函数Person实例化出对象的时候,就会给每一个实例化对象的eat和sleep方法开辟空间,当
//方法不是很多的时候不影响性能,但是当方法很多的时候非常占用空间,影响整体性能,
//因为每个实例对象的eat()方法和sleep()的功能都是一样的,
//所以我们没必要为每个实例对象添加eat()方法和sleep()方法。
//现在就需要用到原型的概念来节省空间
// function Person(name) {
// this.name = name;
// }
// Person.prototype.eat = function() {
// console.log(this.name + "吃东西");
// };
// Person.prototype.sleep = function() {
// console.log(this.name + "睡觉");
// }
// var p1 = new Person("小明");
// p1.eat(); //小明吃东西
// p1.sleep(); //小明睡觉
// var p2 = new Person("小利");
// p2.eat(); //小利吃东西
// p2.sleep(); //小利睡觉
// console.dir(p1);
// console.dir(p2);
//此时输出的结果与上面的相同但是打开结构发现,以前实例化对象的每个都包含的eat sleep方法现在
//全都挂载在了Person构造函数的原型上面。所以每次创建实例化对象的时候就直接在原型中找方法了。
// Person()构造函数的prototype属性是一个对象,实例对象p1的__proto__属性也是一个对象,并且prototype对象和__proto__对象的指向相同。
// 那么我们再回过头来理解一下为什么添加到原型的方法可以是共享的。因为prototype对象和__proto__对象的指向相同,所以将eat()方法和sleep()
// 添加到Person()构造函数的prototype属性上之后,实例对象就可以通过自己__proto__属性去访问eat()方法和sleep()了。
//__proto__指向该对象所在的构造函数的原型对象。
// 实例对象和构造函数之间没用直接的关系。 原型对象与实例对象之间用原型(__proto__) 关联, 这种关系叫做原型链。
// 那么原型的指向可以改变吗? 答案是可以的。
// function Person(name) {
// this.name = name;
// }
// Person.prototype.eat = function() {
// console.log(this.name + "吃东西");
// };
// Person.prototype.sleep = function() {
// console.log(this.name + "睡觉");
// }
// function Student(school) {
// this.school = school;
// }
// Student.prototype.write = function() {
// console.log("写作业");
// }
// Student.prototype = new Person("小华"); //改变Student()构造函数的指向,让Student()构造函数的原型对象指向Person的实例对象
// //这里属于原型链继承方式,//原型链继承的优点:可以继承多种属性和参数
// //缺点:无法对父类的属性进行初始化,所有的对象所有的属性都是相同的。
// var s1 = new Student("某某高中");
// s1.eat(); //小华吃东西
// s1.sleep(); //小华睡觉
// s1.write();//此时Student的原型对象已经改变到Person构造函数中
// //只包含了Person构造函数中的eat以及sleep方法
// console.dir(Student);
// console.dir(s1);
//由于原型链继承的缺点所以都是使用组合继承的方式
function People(name, age, gender, height, weight) { //定义一个人类的综合传入参数
this.name = name;
this.age = age;
this.gender = gender;
this.hieght = height;
this.weight = weight;
}
People.prototype.study = function() { //定义一个原型 此时定义的是学习的方法
console.log(this.name + "正在学习")
}
People.prototype.eat = function() { //定义一个原型方法 此时定义的是吃的方法
console.log(this.name + "正在吃炸鸡汉堡")
}
//在创建一个学生类,
function Student(name, age, gender, height, weight) {
People.call(this, name, age, gender, height, weight) //冒充继承
}
Student.prototype.love = function() {
console.log(this.name + "和" + "汉克在谈恋爱");
}
//原型链继承
var s3 = new Student("麦克", "18", "男", "180cm", "90kg"); //在原型链继承之前定义可以输出方法
Student.prototype = new People(); //原型链继承
var s1 = new Student("汤姆", "18", "男", "180cm", "90kg");
console.log(s3.name);
s3.love();
console.log(s1.name);
s1.study();
s1.eat();
//再创建一个教师类
function Teacher(name, age, gender, height, weight) {
People.call(this, name, age, gender, height, weight) //冒充继承
}
Teacher.prototype = new People(); //原型链继承
var s2 = new Teacher("汉克", "15", "女", "170cm", "50kg") //创建一个老师对象
console.log(s2.name, s2.age, s2.hieght, s2.weight, s2.gender); //尝试输出所有参数
s2.study(); //调用学习的方法
s2.eat(); //调用吃的方法