1.面向对象三大特征介绍
- 1.封装:将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只考虑接口怎么用,不用考虑内部怎么实现(前面学习的api其实就是一种封装思想)
- 2.继承:一个对象拥有其他对象的属性和方法
- 3.多态:一个对象在不同情况下的多种状态
2.继承的三种实现方式
2.1混入式继承 : 遍历 父对象 的所有属性值,添加给 子对象
- 特点:每继承一次,就要执行一次循环
- 应用场景:单个对象继承
//继承:让一个对象拥有另一个对象的属性和方法
let father = {
house: {
address: '北京一环',
price: 100000000
},
car: {
brand: '劳斯莱斯',
price: 5000000
}
}
let son = {
name: 'ikun',
age: 30
}
//1.混入式
//解决方案:遍历父对象的所有属性值,添加给子对象
//特点:每继承一次,就要执行一次循环
//应用场景 : 单个对象继承
for (let key in father) {
son[key] = father[key]
}
console.log(son)
2.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
console.log(Son.prototype);
//实例化对象
let son1 = new Son('ikun', 30)
console.log(son1)
let son2 = new Son('班长', 20)
console.log(son2)
2.3混合式继承 : 混入式 + 替换原型
- 特点 : 遍历 父对象 所有的属性值,添加给 子对象构造函数 的原型
//父对象
let father = {
house : {
address:'武汉天地',
price : 10000000
},
car:{
brand:'劳斯莱斯幻影',
price:8000000
}
}
//子对象构造函数
function Son(name,age){
this.name = name
this.age = age
}
//默认原型
Son.prototype.eat = function(){
console.log('吃东西')
}
/* 混合式 : 混入式 + 替换原型
* 遍历父对象成员, 添加给子对象构造函数原型
* 特点: 多对象继承,但是不会覆盖默认原型
*/
for(let key in father){
Son.prototype[key] = father[key]
}
let son1 = new Son('爽哥',20)
let son2 = new Son('ikun',30)
console.log(son1,son2)
3.(重点)原型链(框架底层基础)
3.1原型链 :
- 每一个实例对象都有自己的原型,而原型也是对象,也有自己的原型。以此类推,形成链式结构,称之为原型链。
3.2原型链访问规则 :就近原则
- 对象优先访问自己的成员,自己没有就找原型的,如果原型也没有,就找原型的原型。以此类推,直到原型链终点null. 如果还找不到,属性则获取undefined,方法则报错undefined is not a function
- 原型对象里的成员 实例对象可以直接使用 因为实例对象里面有_proto_属性指向原型对象
3.3原型链作用 : 继承
- js语言 通过 原型链 实现面向对象继承
只有对象才有原型,这里一定要把基本数据类型string、number、boolean,和基本包装类型(特殊的引用类型对象)String、Number、Boolean区分开来,不要搞混淆
<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>
4.Array的原型链
//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
5.Date的原型链
//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
6.String对象原型链
//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
7.DOM对象原型链
<div class="box">div盒子</div>
<p class="pp">p标签</p>
<script>
//获取dom对象
let box = document.querySelector('.box')
let pp = document.querySelector('.pp')
</script>
4.instanceof 运算符
- instanceof关键字(运算符) : 检测 构造函数的原型(prototype属性)是否出现在某个实例对象的原型链中
在:true
不在:false - instanceof语法: 实例对象 instanceof 构造函数
- instanceof运算符作用: 限制函数参数数据类型
例如: … not of type … 报错 函数参数数据类型错误
//数组实例对象
// arr->Array.prototype->Object.prototype->null
let arr = [10,20,30]
console.log( arr instanceof Array )//true
console.log( arr instanceof Object )//true
console.log( arr instanceof String )//false
//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
//报错: 参数不是节点
//instanceof关键字一般用在函数中,用于限制参数的数据类型
//例如 appendChild()参数只能是node节点,如果你传了其他类型就会报错。底层就会用到instanceof来检测参数数据类型
let h1 = document.createElement('h1')
h1.innerText = '我是h1标签'
document.body.appendChild(h1)
5.ES6类与继承(class/extends/super)
51.class关键字 :
作用: 声明一个类函数 (相当于ES5的构造函数,只是代码的阅读性大大提高)
- 1.把构造函数和原型全部写在一个大括号里面,提高代码阅读性
- 2.必须要使用new才可以调用class函数,否则会报错 提高代码规范
class关键字语法:
class 构造函数名{
constructor(){
//(1)这里写构造函数代码
};
//(2)原型里面的方法写在下面
eat(){
};
play(){
};
};
class本质其实就是构造函数的另一种写法,底层的原理不变 , 本质还是给prototype添加成员, 使用了class,一样可以继续使用ES5的prototype
5.2extends关键字
-
作用: 类继承
-
语法: class 子类函数 extends 父类函数{ }
-
底层: 替换原型对象的原型继承(不会覆盖子构造函数的原型)
-
- 替换原型:
s1.proto = Person.prototype (会覆盖s1原来的原型)
- 替换原型:
-
- extends关键字替换原型的原型:
s1.proto.proto === Person.prototype (不会覆盖s1原来的原型)
- extends关键字替换原型的原型:
//ES6类函数
class Person{
//1.构造函数
constructor(name,age){
this.name = name
this.age = age
}
//2.原型方法
eat(){
console.log('吃东西')
}
}
//3.实例对象
let p1 = new Person('ikun',30)
console.log( p1 )
/* extends关键字作用: 类继承
语法: class 子类函数 extends 父类函数
底层: 替换原型继承(不会覆盖子构造函数的原型)
我们课堂讲的替换原型: s1.__proto__ = Person.prototype (会覆盖s1原来的原型)
extends关键字替换原型的原型:
s1.__proto__.__proto__ === Person.prototype (不会覆盖s1原来的原型)
*/
class Student extends Person{
//子构造函数原型方法
learn(){
console.log('我今天学的很开心')
}
}
let s1 = new Student('陈爽',20)
console.log(s1)
s1.learn()//调用子类自己的方法
s1.eat()//调用父类方法 (继承:拥有父类所有的成员)
5.3super : 子类中调用父类方法
- (1)应用 : 如果子类要写自己的constructor,则必须要先调用父类的constructor,否则程序会报错
- (2)语法: super( )
应用场景: 写在子类的constructor方法中
class 子类函数 extends 父类函数{
//子类构造函数
constructor(name,age,score){
super(name,age)
this.score = score
}
//子类原型方法
learn(){
}
}
//父类函数:Person
class Person{
//构造函数
constructor(name,age){
this.name = name
this.age = age
}
//原型方法
sayHi(){
console.log('i love you')
}
}
//子类函数:teacher
class Teacher extends Person{
//构造函数
constructor(name,age,className){
//父类方法
super(name,age)
this.className = className
}
//子类原型
learn(){
console.log('一日为师终身为友')
}
}
let t1 = new Teacher('ikun',30,'71期')
console.log(t1)//Teacherage: 30className: "71期"name: "ikun"[[Prototype]]: Person
</script>
6.函数补充
6.1 函数中的关键字: arguments
- 作用:arguments:获取函数所有的实参(伪数组)
- 场景: 例如 arr.push() 底层就是用arguments获取实参
function fn(a,b){
console.log(a,b)//10,20
console.log( arguments )//获取函数所有实参
}
fn(10,20,30,40,50)
6.2.rest参数(剩余参数) : …形参名
- 作用:获取所有的剩余参数(真数组)
- 场景: 绝大多数情况下,剩余参数可以替代arguments
function fn(...b){
console.log(b)//获取所有的剩余参数
console.log( arguments )//获取函数所有实参
}
fn(10,20,30,40,50)
6.33.函数默认参数:
ES5 : 逻辑 或 中断
ES6 : 形参 = 默认值
- 1.函数传参本质是:实参给形参赋值
- 2.函数形参和实参数量可以不一致,但是会按照顺序一一赋值
function fn(a=10,b=20){
//找真 : 左边为真,返回左边的值。反之返回右边的值
// a = a || 10
// b = b || 20
console.log(a)//5
console.log(b)//20
}
fn(5)//底层 fn(5,undefined)
7.总结:
7.1.面向对象三大特征
- a.封装:将某个功能封装到对象或函数中
- b.继承:一个对象拥有另一个对象的所有成员变量(属性和方法)
- c.多态:一个对象在不同情况的多种状态
7.2.实现继承的几种方式
a.混入式继承
- 解决方案:遍历父对象的所有属性值,添加给子对象
- 弊端:每继承一次,就要执行一次循环
- 应用场景:父对象只有一个子对象
b.替换原型
- 解决方案:将父对象作为子对象构造函数的原型
- 弊端:会丢失原型之前的成员变量
- 应用场景:自定义内置对象
c.混合式(混入+替换原型)
- 解决方案:遍历父对象所有的属性值,添加给构造函数的原型
- 应用场景:父对象有多个子对象
7.3.原型链
- 原型链:每一个对象都有原型,原型本身又是对象,所以原型又有原型,以此类推形成一个链式结构,称为原型链
- 对象在原型链中的访问规则:就近原则
- 当访问对象成员变量时,会先从自己的属性中查找,如果有就访问,没有就访问自己原型的,如果原型中没有,则访问原型的原型,以此类推,如果访问到原型链的顶端还是没有,则程序报错
xxxx is not undefined
- 特殊情况:Object.prototype的原型对象是null
- .函数本身也是对象
- 构造函数属于函数
7.4.instanceof运算符
- 语法:
对象 instanceof 构造函数
- 作用:检测构造函数的原型prototype在不在这个对象的原型链上
7.5.js语言是通过什么技术实现面向对象继承的
- 答案:原型链
7.6原型链终点是什么
- 答案:null