持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
封装、继承、多态
一、封装
- 封装就是创建一个对象,集中保存现实中一个事物的属性和功能
- 将零散的数据封装进对象结构中,及其便于大量数据的管理维护
- 只要是使用面向对象的思想开发时,第一步都是封装各种各样的对象结构备用
封装方式【一】:直接量 {}
var 对象名 = { // new Object() 的简写
属性名: 属性值,
方法名:function(){}
}
var user = {
name: "hayes",
age: 18,
info: function() {
console.log(`我是 ${name}, ${age} 岁`)
}
}
user.info()
这里如果运行的话,会报错,找不到 info 中的 name 和 age,需要通过 this 来访问
为什么在对象自己的方法内,想使用当前对象自己的另一个属性名,非要加上 this 呢?
我们来看一下这段程序的执行过程:
var user = {:大括号,创建对象- 创建对象之后,对象的这些属性就会进入到对象之中(
name、age、info) info: function(){}:小写的function在底层代表的是new Function。所以这里会创建一个新函数- 这个新函数虽然嵌套的很深,但是它能使用的作用域只有:自己和全局
- 为什么没有对象呢,因为对象的
{}只是一个new Object简写,它不代表任何的作用域(在JS中只有函数才会创建作用域)
- 那么现在位于
info函数中是无法获取到对象的name和age属性的,但是我们可以通过如下的方式获得${user.name}、${user.age}:但是这里属于紧耦合- 另一个方法,我们可以加上
this关键字- 每个函数内自带的
- 专门指向正在调用当前函数的
.前的对象的关键字
this为什么会指向user呢- 我们可以看到这段代码:
user.info(),info函数在调用的时候,它前面有个.,.的前面是user,那么自然函数内的this就指向user了
对象的方法中,只要想使用当前对象自己的属性或其他方法时,都要加
this
封装方式【二】:new Object
var 对象名 = new Object(); // 先创建空对象{}
// 然后强行对空对象中添加新属性和新方法
对象名.属性 = 属性值
对象名.方法 = function() {
...
this.属性名
...
}
为什么有了 {} 大括号模式,还需要 new Object() 模式呢?
其实这揭示了 js语言底层最核心的原理:js 中所有对象底层都是关联数组
注:只有js的对象可以在创建完之后再随意的向里面添加属性和方法
封装方式【三】:构造函数
以上两种方式的问题:
一次只能创建一个对象。如果想创建多个相同结构的对象时,代码就会很多重复------及其不便于将来的维护。
var user1 = {
name: "hay1",
age: 18
}
var user2 = {
name: "hay2",
age: 20
}
可以看出,上面创建的两个对象,属性名是一模一样的,只是值不同。
解决:想反复创建多个相同结构,只是内容不同的对象时,都用构造函数。
构造函数:描述同一类型的所有对象的统一结构的函数。优点是代码重用
1、定义构造函数
function 类型名(形参1,形参2) {
// 将来要加入到新对象中的属性
this.属性名 = 形参1
this.xxx =xxx
this.方法名 = function(){}
}
2、使用构造函数
构造函数需要使用 new 来调用
var 对象名 = new 类型名(实参值1,实参值2)
问题:new 一共干了几件事?
至少做了2件事:
- 创建指定类型的一个新对象
- 同时把实参值传给构造函数的形参变量
3、原理
实参传给形参,形参传给属性,属性通过 this 交给新对象
总结
this 的两种情况
obj.fun():fun中的this指.前的obj对象new Fun():Fun中的this指new创建的新对象
new 的4步
- 创建一个新的空对象
- 让子对象继承构造函数的原型对象
- 调用构造函数,将
this替换为新对象,通过强行赋值方式为新对象添加规定的属性 - 返回新对象地址
二、继承
只要将方法定义放在构造函数中,那么每次 new 时都会执行 function。这样会反复创建相同函数的多个副本,浪费内存。
function Student(name, age) {
this.name = name
this.age = age
this.info = function() {}
}
var hay1 = new Student("hayes1", 18); // 这里hay1.info 的地址(假设):0x123
var hay2 = new Student("hayes2", 20); // 这里hay2.info 的地址(假设):0x345
上述代码在使用构造函数的时候,每次调用都会创建一个新的 info 副本
- 不应该把方法放在构造函数中,否则会重复创建,浪费内存
解决方法:继承
- 如果将来发现多个子对象都要使用相同的功能和属性值时,可以使用继承来解决
什么是继承:父对象中的成员,子对象无需重复创建,就可以直接使用!就像使用自己的成员一样
实现
js 中的继承都是通过原型对象实现的
原型对象:替所有子对象集中保存共有属性值和方法的父对象
只要发现多个子对象都需要使用相同的功能和属性值时,都可将相同的功能和属性值集中定义在原型对象中。
强行赋值
构造函数.prototype.属性名 = 属性值
构造函数.prototype.方法名 = function(){}
function Student(name, age) {
this.name = name
this.age = age
}
Student.prototype.info = function() {}
这样的话,之后使用 Student 作为构造函数,生成的对象都自带 info 函数了
三、多态
同一个函数,在不同情况下表现出不同的状态
- 重载:同一个函数,输入不同的参数,执行不同的逻辑
- 重写:覆盖
重写:
- 在子对象中定义一个和父对象中成员同名的自有成员
- 从父对象继承来的个别成员不好用时,就可以在子对象中定义同名成员,来覆盖父对象中的同名成员
四、总结
简单谈谈你对面向对象的理解:
- 封装:创建对象
- 如果只创建一个对象,则使用
{} - 如果反复创建多个相同结构的对象,则使用构造函数
- 如果只创建一个对象,则使用
- 继承:所有子对象共用的属性值和方法,都要放在构造函数的原型对象中
- 多态:重写:只要觉得从父对象继承来的成员不好用,就可以在子对象中重写同名成员