1.面向对象三大特征介绍
- ? 1.封装:将某个具体功能封装在对象中,只对外部暴露指定的接口,外界在使用的时候,只考虑接口怎么用,不用考虑内部怎么实现(前面学习的api其实就是一种封装思想)
- 2.继承:一个对象拥有其他对象的属性和方法
- 3.多态:一个对象在不同情况下的多种状态
2.继承的三种实现方式
2.1混入式继承 : 遍历 父对象 的所有属性值,添加给 子对象
- 特点:每继承一次,就要执行一次循环
- 应用场景:单个对象继承
let father = {
house: {
address: '北京一环',
price: 100000000
},
car: {
brand: '劳斯莱斯',
price: 5000000
}
}
let son = {
name: 'ikun',
age: 30
}
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原型链访问规则 :就近原则
3.3原型链作用 : 继承
只有对象才有原型,这里一定要把基本数据类型string、number、boolean,和基本包装类型(特殊的引用类型对象)String、Number、Boolean区分开来,不要搞混淆
<script>
function Person(name,age){
this.name = name;
this.age = age;
};
Person.prototype.sayHi = function(){
console.log('人生若只如初见,何事秋风悲画扇');
};
Person.prototype.type = '哺乳动物';
let p1 = new Person('又又',18);
console.log(p1);
console.log(p1.name);
console.log(p1.type);
console.log(p1.hobby);
p1.sayHi();
p1.toString();
console.log(p1.__proto__.constructor);
console.log(p1.__proto__ === Person.prototype);
console.log(p1.__proto__.__proto__.constructor);
console.log(p1.__proto__.__proto__ === Object.prototype);
console.log(p1.__proto__.__proto__.__proto__);
</script>
4.Array的原型链
let arr = new Array(10,20,30);
console.log ( arr );
console.log ( arr.__proto__.constructor );
console.log ( arr.__proto__ === Array.prototype );
console.log ( arr.__proto__.__proto__.constructor );
console.log ( arr.__proto__.__proto__ === Object.prototype );
5.Date的原型链
let date1 = new Date();
console.dir(date1);
console.log ( date1.__proto__ === Date.prototype );
console.log ( date1.__proto__.__proto__.constructor );
console.log ( date1.__proto__.__proto__ === Object.prototype );
6.String对象原型链
let str = new String('123');
console.log ( str );
console.log ( str.__proto__ === String.prototype );
console.log ( str.__proto__.__proto__.constructor );
console.log ( str.__proto__.__proto__ === Object.prototype );
7.DOM对象原型链
<div class="box">div盒子</div>
<p class="pp">p标签</p>
<script>
let box = document.querySelector('.box')
let pp = document.querySelector('.pp')
</script>
4.instanceof 运算符
- instanceof关键字(运算符) : 检测 构造函数的原型(prototype属性)是否出现在某个实例对象的原型链中
在:true 不在:false - instanceof语法: 实例对象 instanceof 构造函数
- instanceof运算符作用: 限制函数参数数据类型
例如: … not of type … 报错 函数参数数据类型错误
let arr = [10,20,30]
console.log( arr instanceof Array )
console.log( arr instanceof Object )
console.log( arr instanceof String )
console.log ( Function instanceof Function );
console.log ( Function instanceof Object );
console.log ( Object instanceof Object );
console.log ( Object instanceof Function );
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(){
};
eat(){
};
play(){
};
};
class本质其实就是构造函数的另一种写法,底层的原理不变 , 本质还是给prototype添加成员, 使用了class,一样可以继续使用ES5的prototype
5.2extends关键字
class Person{
constructor(name,age){
this.name = name
this.age = age
}
eat(){
console.log('吃东西')
}
}
let p1 = new Person('ikun',30)
console.log( p1 )
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(){
}
}
class Person{
constructor(name,age){
this.name = name
this.age = age
}
sayHi(){
console.log('i love you')
}
}
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)
</script>
6.函数补充
6.1 函数中的关键字: arguments
- 作用:arguments:获取函数所有的实参(伪数组)
- 场景: 例如 arr.push() 底层就是用arguments获取实参
function fn(a,b){
console.log(a,b)
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){
console.log(a)
console.log(b)
}
fn(5)
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原型链终点是什么
|