今天又是一个美好的一天耶 ! ✌
文章目录
- 一、面向对象编程之前
- 二、原型 与 原型链 🎁
- 三、new 的原理 🤞
- 四、面向对象的优势
- 五、继承 (构造函数之间的)
- `ps:`
一、面向对象编程之前
在面向对象编程之前,我们是 面向二进制编程 (机器语言, 只有 0和1,难);后来 面向指令编程 (汇编语言),是提前将实现某些常用功能的二进制进行封装成指令;再后来就是 面向自然语言编程 (高级语言)
- 面向过程编程:没有任何封装,纯自然语言,走一步看一步
- 面向函数编程:可能将重复的使用的代码封装成函数,方便调用
- 面向对象编程:将多个函数或变量再次进行封装,更大限度的复用,因为对象存放键值对,所以他提升了 查找速度 和 数据传输速度
面向对象设计 OOD
- 我们在做功能时尽可能将需求拆分成能直接实现的小需求:高内聚,低耦合
- 高内聚:一个需求的所有功能和属性必须从属于一个主体(所有的功能和数据,尽可能从属于一个对象)
- 低耦合:减低功能之间的关系,每个功能可以独立使用或随时被替换 (功能尽可能拆分,依赖关系降到最低)
创建对象
- 单例创建( 字面量方式 ) :如果要得到
一个
对象,每次都需要重新创建
let obj={ }
let obj2={ } - 工厂模式创建 ( 构造函数 ):如果得到
一批
同特征
的对象,可以使用(其实就是构造自定义函数)
定义一个函数,通过new,obj 继承fn属性和方法
let obj = New fn()
// 单例模式 造手机
let obj1 = {
name: "iphone",
color: "red",
show: function() {
console.log("打电话");
}
};
let obj2 = {
name: "iphone",
color: "black",
show: function() {
console.log("打电话");
}
};
// 工厂式 造手机
function Fn(name, color) {
this.name = name;
this.color = color;
this.show = function() {
console.log("打电话");
};
}
let obj3 = new Fn("iphone", "black");
let obj4 = new Fn("iphone", "red");
面向对象的好处: 封装 和 继承 多态
二、原型 与 原型链 🎁
问好兄弟一个问题,如果王思聪去你家吃饭,没带钱,你会让他赊账吗?那肯定是会的呀,毕竟人家老爸是王健林,丝毫不担心他还不上,而且说不定王总一开心,零钱都不用找了…,为什么王建林会让王思聪继承,而不是你呢?那是因为 血缘关系
,别杠,杠就是你对
- 我们有一个函数 Fn , prototype 是函数上一个原型属性,他就像这个血缘关系的凭证
- 比如我们通过 new 的方式得到 obj3 ,当我们访问他的 name 属性时,现在自身找,如果找到就使用
- 如果我们找他的 name2 这个属性时,发现本身并不存在,就会在这个 fn 函数的 prtotype上找,找到了就使用
// 构造函数式创建
function Fn(name, color) {
this.name = name;
this.color = color;
this.show = function() {
console.log("打电话");
};
}
Fn.prototype.name2 = "iphone";
Fn.prototype.show2 = function() {
console.log("拍照");
};
let obj3 = new Fn("black")
// obj3 的值为:
// obj3={
// name:'black'
// }
// obj3.show = function() {
// console.log("打电话");
// };
先说几个概念
- 原型:指原来的类型或模型 ( 百度过来的 ,嘿嘿 ) ,比如 Fn 就是 obj3 的原型
- 原型属性:除了箭头函数,所有函数都会有一个属性prototype,存放实例的公共属性或方法,比如 Fn 的公共属性 name2 和 公共方法 show2
- 原型链:所有对象类型的数据,都会有一个属性proto,他是一个指针,当想要查找这个对象的某个属性时,会先在只身找,找到了就使用,找不到就会在prototype原型上继续查找,直到顶层对象object,这种沿着原型一层层像链条的方式称为原型链
function Fn(){
}
console.log(Fn.prototype)
// Fn 是一个函数,每个函数都有一个 prototype 属性,用来存放公共属性和公共方法
const obj3 = new Fn()
console.log(obj3.proto)
.
obj3 是new出来的对象 ,每个对象都有一个原型链属性_ proto ,指向当前的函数的 prototype
obj3. proto _ == Fn.prototype
proto 是个指针嗷!!
// 王健林
function Fn(name){
this.name=name
}
Fn.prototype.money=100
Fn.prototype.car='法拉利'
// 王思聪
const wsc = new Fn()
console.log(wsc.money) // 100
console.log(wsc.__proto__) //Fn.prototyp
console.log(wsc.__proto__ == Fn.prototype) // true
// 王思聪出门忘记带钱包了,但是售货员毫不犹豫让他赊账,为什么?因为他的父亲是王健林
// 王思聪本身是没钱的,但是他的父亲有 money ,他就可以直接使用
// wsc.proto 指向 Fn.prototype
// 换句话来说,现在自身找,自身没有就找父级(按照原型链找),王思聪继承了王健林所有东西
ps : 这也是通过new调用一个函数时,和普通调用函数的区别 !!!
- 继承 (会沿着原型链查找)
- 节省内存 (Fn 里的每个函数在创建时会生成对应的函数执行上下文,每次通过构建函数创建出来的对象都会创建一次,当然啦,可以放在原型上,大大节省了内存)
function Fn( color) {
this.color = color;
this.show = function() {
console.log("打电话");
};
this.show2 = function() {
console.log("打电话");
};
this.show3 = function() {
console.log("打电话");
};
}
Fn.prototype.show4=function(){
console.log('原型继承')
}
三、new 的原理 🤞
上面说,使用 new 造轮子,实际上时 js 帮我们封装了,那 new 的原理是什么呢,它干了啥?
- 首先它先创建一个新对象
- 修改了函数内部的this指向 ,指向这个新对象(浅拷贝)
- 将新对象的proto指向当前函数的prototype,并执行
- 自动返回这个新对象
要被new 的函数 ,它的函数名最好要大写,以便区分普通函数
四、面向对象的优势
封装
:将多个相关数据和功能封装成对象之后,可以实现方法或对象本身的复用继承
:可以重用父类的方法和变量多态
五、继承 (构造函数之间的)
一个不具备某个功能的对象,通过某种方式,使用了另一个对象的功能,叫做继承 😂
我们今天要说的就是 构造函数 和 构造函数 之间的 继承
通过更改this指向(构造函数继承)
- 例子一 :
function Parent(msg) {
this.msg = msg;
this.show = function () {
console.log(this.msg);
};
}
Parent.prototype.init = function () {
console.log("哈哈哈哈");
};
function Child(msg) {
}
const P = new Parent("hello");
const C = new Child("hi");
C.show();
// 我发现 P 上面有 msg 属性,而且还有 show 方法,
//但是 C 没有,但是 C 就想直接使用 P 的 show !
//这里可以更改 C 的 **this 指向** (apply bind call 都可)
function Child(msg) {
Parent.call(this, msg);
}
const C = new Child("hi");
C.show(); // c 就可以使用p的构造函数内的方法,
C.init(); // 但是他不能继承原型上的属性和方法 !!! 这就是继承的特点,
- 例子二 :
function SendMsg() {
this.s = function () {
console.log("发短信");
};
}
function Game() {
this.g = function () {
console.log("玩游戏");
};
}
function Pic() {
this.p = function () {
console.log("拍照");
};
}
//我现在要生产一个手机,我把手机零件分别给好几家代理商。发现没,第二好处点,多继承!
function Phone(name) {
this.name = name;
SendMsg.call(this);
Game.call(this);
Pic.call(this);
}
const P = new Phone("点赞");
console.log(p);
通过改变this指向,
继承构造函数内的属性和方法,不能继承原型上的属性和方法
,可以实现多继承,简单方便易实现 ✌
修改 原型/原型链 对象
function Parent(msg) {
this.msg = msg;
this.show = function () {
console.log(this.msg);
};
}
Parent.prototype.init = function () {
console.log("哈哈哈哈");
};
const P = new Parent("hello");
//同理我只想继承原型上的方法和属性,我不需要继承构造函数内的方法和属性 ,有办法吗?有!😁😁😁
function Child(msg) {}
const C = new Child("hi");
C.show();
1、修改原型对象继承
// 简单粗暴,直接赋值
Parent.prototype = Child.prototype;
// 但是会有一个很大问题
// 因为这是一个引入值更改,prototype 是一个对象,进行了浅拷贝,改动child 会影响 parent
// 这里要注意深浅拷贝
注意深浅拷贝 😁
2、修改原型链继承
正常的通过构造函数创建的实例的原型链结构
Parent实例的原型链 ——》 Parent 构造函数的原型对象,的原型链 ——》object 构造函数的原型对象,的原型链
P.proto ——》 Parent.prototype, Parent.prototype.proto ——》 object.prototype
// 不是直接操作,而是通过建立关系去操作
Child.prototype = new Parent();
// child实例的原型链 ——》Parent 的实例,的原型链 ——》
// Parent 构造函数的原型对象,的原型链 ——》object 构造函数的原型对象,的原型链
混合继承
- 构造函数继承 + 原型对象继承
- 构造函数继承 + 原型链继承 (不好用,你懂的)
- 这个方式就不多说了,结合一下好了啦
es6提供的继承方式
- 语法层面上的继承
- 但其实就是官方封装了方法
- 官方封装了 构造函数+ 原型链继承
// 定义 class 而不是之前的 function,
// 注意 属性和方法的写法
// 通过 extends 继承,可以使用 super 传递给被继承的
// 这是官方封装好的
class Parent {
constructor(msg) {
this.msg = msg;
}
show() {
console.log(this.msg);
}
}
class Child extends Parent {
constructor(m) {
super(m);
}
}
const P = new Parent("hello");
const C = new Child("hi");
ps:
每种继承方式都有各自的特点! 继承不是完全的复制,如果两个对象所需功能一样,完全没有继承的必要,直接根据一个工厂创建两个对象就 ok 了
你学废了吗?
😂 谢谢阅读,谢谢点赞并关注(没错我就要道德绑架你!)