a.封装:将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只考虑接口怎么用,不用考虑内部怎么实现(前面学习的api其实就是一种封装思想)
b.继承:一个对象拥有其他对象的属性和方法
c.多态:一个对象在不同情况下的多种状态
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> /*面向对象三大特征 a.封装:将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只考虑接口怎么用,不用考虑内部怎么实现 b.继承:一个对象拥有其他对象的属性和方法 c.多态:一个对象在不同情况下的多种状态 */ /*多态(了解即可,使用不多):一个对象在不同情况的多种状态 饲养员对象Person : 给动物对象喂养食物 funtion work(animal,food){ animal.eat(food) } 动物对象Animal : 吃东西 eat(food){ } 多态 : 给不同的动物喂养食物,虽然都是调用eat()方法,但是不同的动物会执行不同的eat() */ //示例:饲养员给动物喂食物 //动物 function Animal ( name ) { this.name = name; }; //猫 let cat = new Animal('猫咪'); cat.eat = function ( food ) { console.log ( "喵喵猫" + "我吃了" + food ); }; //狗 let dog = new Animal('小狗狗'); dog.eat = function ( food ) { console.log ( "汪汪汪" + "我吃了" + food ); }; //饲养员 function Person ( name ) { this.name = name; }; Person.prototype.work = function (animal,food ) { //animal接收一个由Anmiaml构造函数生成的实例化对象,调用它的eat方法 //同一对象表现出来不同的状态,就叫做多态 animal.eat(food); }; let p1 = new Person('ikun'); p1.work(cat, '饼干'); p1.work(dog, '翔'); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> /*学习目标 : 继承的三种实现方式 1.混入式继承 : 遍历 父对象 的所有属性值,添加给 子对象 特点:每继承一次,就要执行一次循环 应用场景:单个对象继承 2.替换原型继承 : 将 父对象 作为 子对象构造函数的原型 特点:会丢失原型之前的成员变量 应用场景:多个对象继承 3. 混合式继承 : 混入式 + 替换原型 特点 : 遍历 父对象 所有的属性值,添加给 子对象构造函数 的原型 */ //继承:让一个对象拥有另一个对象的属性和方法 let father = { house:{ address:'北京一环', price:100000000 }, car:{ brand:'劳斯莱斯', price:5000000 } } let son = { name:'ikun', age:30 } //1.混入式 //解决方案:遍历父对象的所有属性值,添加给子对象 //特点:每继承一次,就要执行一次循环 //应用场景 : 单个对象继承 for (let key in father){ father[key] = son[key] } console.log ( son ) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> //2.替换原型 //解决方案:将父对象作为子对象构造函数的原型 //特点:会丢失原型之前的成员变量 //应用场景:多个对象继承 let father = { house:{ address:'北京一环', price:100000000 }, car:{ brand:'劳斯莱斯', price:5000000 } } function Son (name,age ) { this.name = name this.age = age } Son.prototype.sayHi = function(){ console.log('你好') } //让父对象成为子对象构造函数的原型 Son.prototype = father //实例化对象 let son1 = new Son('ikun',30) console.log ( son1 ) let son2 = new Son('班长',20) console.log ( son2 ) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /*学习目标 : 继承的三种实现方式 1.混入式继承 : 遍历 父对象 的所有属性值,添加给 子对象 特点:每继承一次,就要执行一次循环 应用场景:单个对象继承 2.替换原型继承 : 将 父对象 作为 子对象构造函数的原型 特点:会丢失原型之前的成员变量 应用场景:多个对象继承 3. 混合式继承 : 混入式 + 替换原型 特点 : 遍历 父对象 所有的属性值,添加给 子对象构造函数 的原型 */ //3. 混合式(混入+替换原型) //解决方案:遍历父对象所有的属性值,添加给子对象构造函数的原型 let father = { house:{ address:'北京一环', price:100000000 }, car:{ brand:'劳斯莱斯', price:5000000 } } function Son (name,age ) { this.name = name this.age = age } Son.prototype.sayHi = function(){ console.log('你好') } //将父对象的所有属性添加到子对象构造函数的原型中 for(let key in father){ Son.prototype[key] = father[key]; } //实例化对象 let son1 = new Son('ikun',30) console.log ( son1 ) let son2 = new Son('班长',20) console.log ( son2 ) </script> </body> </html>
xxx is not a function
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /* 1.原型链 :每一个对象,都有__proto__指向自身的原型。 而原型也是对象,也有自己的 __proto__指向原型的原型,以此类推形成链式结构,称之为原型链。 2.对象访问原型链中成员的规则 :就近原则 当访问对象的成员,先看自己有没有。有则访问,没有则看原型有没有。原型有则访问, 没有则访问原型的原型,没有则继续往上找。以此类推,一直找到原型链终点 : null. 还没有, 如果是属性 : 则获取undefined 如果是方法 ,则报错xxx is not defined */ //1.构造函数 function Person(name,age){ this.name = name; this.age = age; }; //2.原型对象 Person.prototype.sayHi = function(){ console.log('人生若只如初见,何事秋风悲画扇'); }; Person.prototype.type = '哺乳动物'; //3.实例化对象 let p1 = new Person('又又',18); console.log(p1); //请说出以下代码执行结果 console.log(p1.name);//又又 p1自己有name属性 console.log(p1.type);//哺乳动物 p1自己没有type,但是p1的原型有 console.log(p1.hobby);//undefined p1自己没有hobby,原型也没有 p1.sayHi();// 人生若只如初见,何事秋风悲画扇 p1自己没有这个方法,原型有 // p1.eat();//报错 xxx is not defined p1自己没有这个方法,原型也没有 //为什么不报错? p1自己没有这个方法,原型也没有这个方法,但是原型的原型有 p1.toString(); //查看p1的原型链 console.log(p1.__proto__.constructor);//Person console.log(p1.__proto__ === Person.prototype);//true //查看p1的原型的原型 console.log(p1.__proto__.__proto__.constructor);//Object console.log(p1.__proto__.__proto__ === Object.prototype);//true //查看p1的原型的原型的原型 console.log(p1.__proto__.__proto__.__proto__);//null </script> </body> </html>
//1.Array let arr = new Array(10,20,30); console.log ( arr ); //查看arr的原型 console.log ( arr.__proto__.constructor );//Array console.log ( arr.__proto__ === Array.prototype ); //查看arr的原型的原型 console.log ( arr.__proto__.__proto__.constructor );//Object console.log ( arr.__proto__.__proto__ === Object.prototype );//true
//2.Date let date1 = new Date(); //细节:日期对象直接console.log会转成string,查看对象需要使用console.dir打印 console.dir(date1); console.log ( date1.__proto__ === Date.prototype );//true console.log ( date1.__proto__.__proto__.constructor );//Object console.log ( date1.__proto__.__proto__ === Object.prototype );//true
//3.String let str = new String('123'); console.log ( str ); console.log ( str.__proto__ === String.prototype );//true console.log ( str.__proto__.__proto__.constructor );//Object console.log ( str.__proto__.__proto__ === Object.prototype );//true
//4.界面元素 let div1 = document.getElementById('div1'); let p1 = document.getElementById('p1');
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="div1"></div> <p id="p1"></p> <script> /*查看内置对象的原型链*/ //1.Array let arr = new Array(10,20,30); console.log ( arr ); //查看arr的原型 console.log ( arr.__proto__.constructor );//Array console.log ( arr.__proto__ === Array.prototype ); //查看arr的原型的原型 console.log ( arr.__proto__.__proto__.constructor );//Object console.log ( arr.__proto__.__proto__ === Object.prototype );//true //2.Date let date1 = new Date(); //细节:日期对象直接console.log会转成string,查看对象需要使用console.dir打印 console.dir(date1); console.log ( date1.__proto__ === Date.prototype );//true console.log ( date1.__proto__.__proto__.constructor );//Object console.log ( date1.__proto__.__proto__ === Object.prototype );//true //3.String let str = new String('123'); console.log ( str ); console.log ( str.__proto__ === String.prototype );//true console.log ( str.__proto__.__proto__.constructor );//Object console.log ( str.__proto__.__proto__ === Object.prototype );//true //4.界面元素 let div1 = document.getElementById('div1'); let p1 = document.getElementById('p1'); </script> </body> </html>
本小节知识点
对象
instanceof
构造函数
//1.示例 let arr = [10,20,30]; //数组原型链 arr->Arr.prototype->Object.prototype->null console.log ( arr instanceof Array );//true console.log ( arr instanceof Object );//true //2.示例 //根据instanceof语法:左边Function表示对象,右边Function表示构造函数 //Function原型链 Function对象->Function.prototype->Object.prototype->null console.log ( Function instanceof Function );//true console.log ( Function instanceof Object );//true //3.示例 //根据instanceof语法:左边Object表示对象,右边Object表示构造函数 //Object原型链 Object对象->Function.prototype->Object.prototype->null console.log ( Object instanceof Object );//true console.log ( Object instanceof Function );//true
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /* 1.学习目标 1.1 class关键字作用: 声明一个类函数 * 相当于ES5的构造函数,只是代码的阅读性大大提高 * a. 把构造函数和原型全部写在一个大括号里面,提高代码阅读性 * b. 必须要使用new才可以调用class函数,提高代码规范 1.2 class关键字语法: class 构造函数名{ constructor(){ //(1)这里写构造函数代码 }; //(2)原型里面的方法写在下面 eat(){ }; play(){ }; }; 2.学习路线:对比法 2.1 ES5 构造函数与原型语法 2.2 ES6 构造函数与原型语法 */ //1.ES5得构造函数与原型写法 // //(1)构造函数 // function Person(name,age){ // this.name = name; // this.age = age; // }; // //(2)给原型添加方法 // Person.prototype.eat = function(){ // console.log('大吉大利今晚吃鸡'); // }; // Person.prototype.sayHi = function(){ // console.log('你好,我是黑马李宗盛'); // }; // //(3)实例对象 // let p1 = new Person('ikun',30); // console.log(p1); //2. ES6的构造函数写法 //(1)声明类Person class Person { //(1) 构造函数 : 名字必须叫做 constructor constructor(name, age) { this.name = name; this.age = age; }; //(2) 给原型添加方法 : 方法名(){} sayHi() { console.log('你好,我是黑马李宗盛'); }; eat() { console.log('大吉大利今晚吃鸡'); }; }; /* 细节: class本质其实就是构造函数的另一种写法,本质还是给prototype添加成员 使用了class,一样可以继续使用ES5的prototype */ Person.prototype.type = '人类'; //(3)实例对象 let p1 = new Person('ikun', 30); console.log(p1); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /* 学习目标: extends关键字 1.作用: 继承 2.底层原理: 替换原型继承 */ //(1)声明类Person class Person { //(1) 构造函数 : 名字必须叫做 constructor constructor(name, age) { this.name = name; this.age = age; }; //(2) 给原型添加方法 : 方法名(){} sayHi() { console.log('你好'); }; eat() { console.log('大吉大利今晚吃鸡'); }; }; Person.prototype.type = '人类'; //(3)实例对象 let p1 = new Person('ikun', 30); console.log(p1); /* extends关键字 */ //声明一个类函数 Student 继承于 Person 类函数 //底层原理相当于 Student.prototype.__proto__ = Person.prototype class Student extends Person{ //Student原型添加方法 learn(){ console.log('今天学的很开心'); }; }; let s1 = new Student('班长',20); console.log(s1); s1.learn();//s1自己没有learn,但是原型有 s1.eat();//s1自己没有eat,原型没有eat, 但是原型的原型有eat </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /* 学习目标: super关键字 1.作用: 子类中调用父类的方法 2.底层原理: 函数上下文调用 */ //(1)声明类Person class Person { //(1) 构造函数 : 名字必须叫做 constructor constructor(name, age) { this.name = name; this.age = age; }; //(2) 给原型添加方法 : 方法名(){} sayHi() { console.log('你好'); }; eat() { console.log('大吉大利今晚吃鸡'); }; }; Person.prototype.type = '人类'; //(3)实例对象 let p1 = new Person('kiki', 18); console.log(p1); /* super关键字 : 子类中调用父类的方法 如果在子类中,也写了 constructor函数,则必须要调用 super()否则程序报错 */ /* 声明子类Student 继承 父类 Person */ class Student extends Person{ //子类构造函数 constructor(name,age,score){ /* 注意点:如果子类也写了constructor,则必须要调用super */ super(name,age); this.score = score; } //Student原型添加方法 learn(){ console.log('1.我要吃饭了'); //调用Person原型中的eat() //底层原理: Person.call(this) super.eat(); console.log('3.我开始学习了'); }; }; let s1 = new Student('班长',20,99); console.log(s1); s1.learn();//s1自己没有learn,但是原型有 s1.eat();//s1自己没有eat,原型没有eat, 但是原型的原型有eat </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /* 1.arguments关键字: 获取函数所有实参 * 是一个伪数组 * 只能用于函数 2.场景 : 例如数组push()方法,传多少实参就给数组添加多少元素。底层原理就是使用arguments实现的 */ function fn(){ console.log(arguments) } fn(10,20,30,40) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /* 1.剩余参数(rest参数) : 获取函数剩余的所有实参 * 是一个真数组 * 只能写在函数最后形参位置 2.场景:大多数情况下, rest参数可以替代arguments arguments是老语法,浏览器兼容性好 rest参数:新语法,有些老的浏览器不支持 */ function fn(a,b,...c){ console.log(arguments)//获取所有实参 10 20 30 40 console.log(a,b)//10 20 console.log(c)//[30,40] 获取剩余参数 } fn(10,20,30,40) </script> </body> </html>
1.面向对象三大特征
2.实现继承的几种方式
3.原型链
对象在原型链中的访问规则:就近原则
xxxx is not undefined
d.函数本身也是对象
5.instanceof运算符
对象 instanceof 构造函数
1.js语言是通过什么技术实现面向对象继承的
2.原型链终点是什么