08 (h5*) js第9天--原型、继承
目录:
1:原型和原型链
2:构造函数的原型可以改变
3:原型的最终指向
4:先修改原型指向,在添加方法。
5:实例对象中的属性和原型属性重合,
6:一个神奇的原型链
7:继承
8:原型链
9:利用call,在构造函数中修改原型原型指向
10:组合继承
11:拷贝继承
12:函数声明和函数表达式的区别
13:函数中this的指向
1:原型和原型链
<script> // 1:使用对象---->使用对象中的属性和方法,就需要先有构造函数 // 1.1:构造函数。 function Person (name, age){ this.name = name; this.age = age; this.sayHi = function () { console.log("您好,您真帅"); }; } // 1.2:实例化对象,并初始化 var per = new Person("小明", 20); // 1.3: 如果想要使用一些属性和方法,并且属性的值在每个对象中都是一样的,方法在每个对象中的操作也是一样的,那么,为了共享数据,节约内存空间,是可以把属性和方法通过原型的方法进行赋值 Person.prototype.sex = "男"; Person.prototype.sayHello = function (){ console.log("您怎么可以这么帅呀"); }; console.dir(per); // 实例对象 console.dir(Person); // 构造函数 // 实例对象中的原型__proto__和构造函数中的原型prototype指向是一样的,指向的是原型对象 // 实例对象的原型__proto__原型指向的是 构造函数中的原型prototype,原型对象 // 原型对象中的构造器指向的是原型对象所在的构造函数 // 实例对象和原型对象通过原型来联系的 // 实例对象中的原型__proto__是游览器使用的,不是正规的 // 构造函数中的prototype是原型,是程序员使用的 // 原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的 console.log(per.__proto__ == Person.prototype); </script>
1:实例对象中的原型__proto__和构造函数中的原型prototype指向是一样的,指向的是原型对象
2:实例对象的原型__proto__原型指向的是 构造函数中的原型prototype,原型对象
3:原型对象中的构造器指向的是原型对象所在的构造函数,实例对象和原型对象通过__proto__来联系的
4:原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的
2:构造函数的原型指向可以改变
<script> console.log("======= 11 ======="); // 1:构造函数中的this是实例对象 function Person (age){ this.age = age; console.log(this); } // 2:原型对象中的方法中的this是实例对象 Person.prototype.eat = function (){ console.log(this); console.log("人吃了吗?"); }; var per = new Person(20); per.eat(); console.log("======= 22 ======="); // 3:学生 function Student (){ }; // 原型的指向是可以改变的 // 构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(__proto__)指向也会发生改变 // 原型的指向是可以改变的 // 实例对象和原型对象之间的关系是通过__proto__原型来联系起来的,这个关系就是原型链 Student.prototype = new Person(20); var stu = new Student(); stu.eat(); </script>
1: 原型的指向是可以改变的
2: 构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(__proto__)指向也会发生改变
3: 实例对象和原型对象之间的关系是通过__proto__原型来联系起来的,这个关系就是原型链
3: 原型的最终指向
<script> // 1: 只要是对象就有__proto__. // 2: 只要有__proto__,他就指向某个构造函数的prototype、 function Person() { } Person.prototype.eat=function () { console.log("吃东西"); }; var per=new Person(); console.dir(per); console.dir(Person); //实例对象中有__proto__原型 //构造函数中有prototype原型 //prototype是对象 //所以,prototype这个对象中也有__proto__,那么指向了哪里 //实例对象中的__proto__指向的是构造函数的prototype //所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype //Person的prototype中的__proto__的指向 //console.log(Person.prototype.__proto__); //per实例对象的__proto__------->Person.prototype的__proto__---->Object.prototype的__proto__是null console.log(per.__proto__==Person.prototype); console.log(per.__proto__.__proto__==Person.prototype.__proto__); console.log(Person.prototype.__proto__==Object.prototype); console.log(Object.prototype.__proto__); </script>
1:实例对象per的__proto__指向的是构造函数Person的prototype对象。
2:prototype是一个对象。
3:prototype的__proto__指向的是某个构造函数的prototype对象。 某个指的是:NSobject。
4:NSObject的原型对象prototype对象,他的__proto__指向的是null。
4:先修改原型指向在添加方法。
<script> function Person (age){ this.age = age; }; Person.prototype.eat = function () { console.log("人在吃饭"); }; function Student(sex){ this.sex = sex; }; // 1: 修改原型指向 Student.prototype = new Person(10); // 2: 添加原型方法,要想问原型添加新方法,一定要再原型指向修改后添加原型方法。 Student.prototype.sayHi = function (){ console.log("您好呀,学生!!"); }; var stu = new Student("男"); console.dir(stu); </script>
1:先添加原型方法,在修改原型指向。
2:先修改原型指向,在添加原型方法。方法添加在实例对象中。
5:实例对象和原型对象中的熟悉重名问题
实例对象访问这个属性,应该先从实例对象中找,找到了就直接用,找不到就去指向的原型对象中找,找到了就使用,找不到呢?undefined
<script> function Person(age,sex) { this.age=age; this.sex=sex; } Person.prototype.sex="女"; var per=new Person(10,"男"); console.log(per.sex); //因为JS是一门动态类型的语言,对象没有什么,只要点了,那么这个对象就有了这个东西,没有这个属性,只要对象.属性名字,对象就有这个属性了,但是,该属性没有赋值,所以,结果是:undefined console.log(per.fdsfdsfsdfds); console.log(fsdfdsfds); //实例对象访问这个属性,应该先从实例对象中找,找到了就直接用,找不到就去指向的原型对象中找,找到了就使用,找不到呢?===== //通过实例对象能否改变原型对象中的属性值?不能 //就想改变原型对象中属性的值,怎么办?直接通过原型对象.属性=值;可以改变 Person.prototype.sex="哦唛嘎的"; per.sex="人"; console.log(per.sex); console.dir(per); </script>
6: 一个神奇的原型链
<body> <div id="dv"></div> <script> //原型链:实例对象和原型对象之间的关系,通过__proto__来联系 var divObj=document.getElementById("dv"); console.dir(divObj); //divObj.__proto__---->HTMLDivElement.prototype的__proto__--->HTMLElement.prototype的__proto__---->Element.prototype的__proto__---->Node.prototype的__proto__---->EventTarget.prototype的__proto__---->Object.prototype没有__proto__,所以,Object.prototype中的__proto__是null </script> </body>
1:首先从f3中找age,之后在f2中找age,最后在f1中找age,如果没有就是undefined。
7:继承(通过原型来修改原型执行实现继承)
<script> /* * * 面向对象编程思想:根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,要想实现这个需求,就要创建对象,要想创建对象,就应该显示有构造函数,然后通过构造函数来创建对象.,通过对象调用属性和方法来实现相应的功能及需求,即可 * 首先JS不是一门面向对象的语言,JS是一门基于对象的语言,那么为什么学习js还要学习面向对象,因为面向对象的思想适合于人的想法,编程起来会更加的方便,及后期的维护.... * 面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是JS不是面向对象的语言,所以,JS中没有类(class),但是JS可以模拟面向对象的思想编程,JS中会通过构造函数来模拟类的概念(class) * * * 小明,小红,小丽,小白,小花 都是人 * 共同的特征和行为 * 特征--->属性 * 行为---方法 * * 面向对象的特性:封装,继承,多态 * * 封装:就是包装 * 一个值存储在一个变量中--封装 * 一坨重复代码放在一个函数中--封装 * 一系列的属性放在一个对象中--封装 * 一些功能类似的函数(方法)放在一个对象中--封装 * 好多相类似的对象放在一个js文件中---封装 * * 继承: 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承 * 继承也是为了数据共享,js中的继承也是为了实现数据共享 * * 原型作用之一:数据共享,节省内存空间 * 原型作用之二:为了实现继承 * * 继承是一种关系: * * 父类级别与类级别的关系 * * 例子: * * 小杨--->人, 姓名, 有钱, 帅, 有功夫--降龙十八掌 * 小杨子-->人, * 继承: * 姓氏----继承 * 外表----继承 * 财产----继承 * 功夫---继承 * * * 人: 姓名, 性别, 年龄 ,吃饭, 睡觉 * * 学生类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 学习行为 * 老师类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资,教学行为 * 程序员: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资, 敲代码 * 司机类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资 开车 * * * 动物类别: 体重, 颜色, 吃 * 狗类别: 体重,颜色, 吃, 咬人 * 二哈类别: 体重,颜色, 吃, 咬人 逗主人开心,汪汪,你好帅 * * 多态:一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟, * */ //例子: //人,都有姓名,性别,年龄, 吃饭, 睡觉, 玩 //学生,都有姓名,性别,年龄, 成绩, 吃饭, 睡觉, 玩 ,学习的行为 //js中通过原型来实现继承 function Person(name,age,sex) { this.name=name; this.sex=sex; this.age=age; } Person.prototype.eat=function () { console.log("人可以吃东西"); }; Person.prototype.sleep=function () { console.log("人在睡觉"); }; Person.prototype.play=function () { console.log("生活就是不一样的玩法而已"); }; function Student(score) { this.score=score; } //改变学生的原型的指向即可==========>学生和人已经发生关系 Student.prototype=new Person("小明",10,"男"); Student.prototype.study=function () { console.log("学习很累很累的哦."); }; //相同的代码太多,造成了代码的冗余(重复的代码) var stu=new Student(100); console.log(stu.name); console.log(stu.age); console.log(stu.sex); stu.eat(); stu.play(); stu.sleep(); console.log("下面的是学生对象中自己有的"); console.log(stu.score); stu.study(); </script>
8:原型链
1:原型链是实例对象之间的关系,实例对象中__proto__之间的关系。
9:利用call,在构造函数中修改原型指向
父类中的方法无法继承
<script> //解决方案:继承的时候,不用改变原型的指向,直接调用父级的构造函数的方式来为属性赋值就可以了------借用构造函数:把要继承的父级的构造函数拿过来,使用一下就可以了 //借用构造函数:构造函数名字.call(当前对象,属性,属性,属性....); //解决了属性继承,并且值不重复的问题 //缺陷:父级类别中的方法不能继承 function Person(name, age, sex, weight) { this.name = name; this.age = age; this.sex = sex; this.weight = weight; } Person.prototype.sayHi = function () { console.log("您好"); }; function Student(name,age,sex,weight,score) { //借用构造函数 Person.call(this,name,age,sex,weight); this.score = score; } var stu1 = new Student("小明",10,"男","10kg","100"); console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score); var stu2 = new Student("小红",20,"女","20kg","120"); console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score); var stu3 = new Student("小丽",30,"妖","30kg","130"); console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score); </script>
10:组合继承
1:构造函数call解决属性继承
2:原型继承解决方法继承。
<script> //原型实现继承 //借用构造函数实现继承 //组合继承:原型继承+借用构造函数继承 function Person(name,age,sex) { this.name=name; this.age=age; this.sex=sex; } Person.prototype.sayHi=function () { console.log("阿涅哈斯诶呦"); }; function Student(name,age,sex,score) { //借用构造函数:属性值重复的问题 Person.call(this,name,age,sex); this.score=score; } //改变原型指向----继承 Student.prototype=new Person();//不传值 Student.prototype.eat=function () { console.log("吃东西"); }; var stu=new Student("小黑",20,"男","100分"); console.log(stu.name,stu.age,stu.sex,stu.score); stu.sayHi(); stu.eat(); var stu2=new Student("小黑黑",200,"男人","1010分"); console.log(stu2.name,stu2.age,stu2.sex,stu2.score); stu2.sayHi(); stu2.eat(); //属性和方法都被继承了 </script>
11:拷贝继承
拷贝继承;把一个对象中的属性或者方法直接复制到另一个对象中
<script> //拷贝继承;把一个对象中的属性或者方法直接复制到另一个对象中 // var obj1={ // name:"小糊涂", // age:20, // sleep:function () { // console.log("睡觉了"); // } // }; // // //改变了地址的指向 // var obj2=obj1; // console.log(obj2.name,obj2.age); // obj2.sleep(); // var obj1={ // name:"小糊涂", // age:20, // sleep:function () { // console.log("睡觉了"); // } // }; // // // var obj2={}; // for(var key in obj1){ // obj2[key]=obj1[key]; // } // console.log(obj2.name); function Person() { } Person.prototype.age=10; Person.prototype.sex="男"; Person.prototype.height=100; Person.prototype.play=function () { console.log("玩的好开心"); }; var obj2={}; //Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是该对象中的属性或者方法 for(var key in Person.prototype){ obj2[key]=Person.prototype[key]; } console.dir(obj2); obj2.play(); </script>
12:函数声明和函数表达式的区别
以后宁愿用函数表达式,都不用函数声明
<script> //函数声明 // // if(true){ // function f1() { // console.log("哈哈,我又变帅了"); // } // }else{ // function f1() { // console.log("小苏好猥琐"); // } // } // f1(); //函数表达式 var ff; if(true){ ff=function () { console.log("哈哈,我又变帅了"); }; }else{ ff=function () { console.log("小苏好猥琐"); }; } ff(); //函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题 //以后宁愿用函数表达式,都不用函数声明 </script>
13:函数中this的指向
<script> /* * * 函数中的this的指向 * * * 普通函数中的this是谁?-----window * 对象.方法中的this是谁?----当前的实例对象 * 定时器方法中的this是谁?----window * 构造函数中的this是谁?-----实例对象 * 原型对象方法中的this是谁?---实例对象 * * * */ //严格模式: // "use strict";//严格模式 // function f1() { // console.log(this);//window // } // f1(); //普通函数 // function f1() { // console.log(this); // } // f1(); //定时器中的this // setInterval(function () { // console.log(this); // },1000); //构造函数 // function Person() { // console.log(this); //对象的方法 // this.sayHi=function () { // console.log(this); // }; // } //原型中的方法 // Person.prototype.eat=function () { // console.log(this); // }; // var per=new Person(); // console.log(per); // per.sayHi(); // per.eat(); //BOM:中顶级对象是window,浏览器中所有的东西都是window的 </script>