面向对象概述
面向对象属于编程思想。重点在于所有的事情考虑都需要以对象的角度出发。与面向对象对应的就是面向过程
前面所使用的对象对他的理解是一种数据类型。对象中可以存在很多的属性,属性又区分名称与值。对象下每个属
性的值 可以是任意数据类型,如果属性值是字符串、数字、布尔、数组、对象就可以表示出一些静止的属性。属
性值也可以使用函数(函数代码是可以运行的),所以当属性值为函数 可以表示出一种动作。所以对象可以表示出静
止的信息也可以表达出行为。所以对象可以用于描述个体信息
例如一辆车就是一个个体,但是车不会凭空产生。会根据设计稿制造出来,设计稿就好比一个模板,设计稿编程语
言来看就是"类" js中可以叫做构造函数(function)。
车是一个个体对象。车的轮子 也是对象。对象与对象直接是存在联系
面向过程:所有的事情都是自己来处理的 。例如做饭 需要自己逐步完成过程
面向对象:使用多个对象,对象与对象直接产生联系,例如做饭 可以找保姆 下达指令
标准面向对象思想是先有类在有对象,类是对很多相似的个体进行抽象化归类所得到的一个模板
面向对象(OOP)特性:封装、继承、多态(多种形态,身份可以切换)
创建对象的四种方式
字面量
let person = {
name:'杜思聪',
age:18,
money:-100000,
run:function(){
console.log(this.name+' running');
}
}
console.log(person.name);
console.log(person.run());
console.log(person)
/*
字面量方式缺陷
1、如果需要一个类似的对象 完整的代码需要在写一遍 麻烦
2、当前对象 无法确定属于哪一种归类的对象
字面量方式创造对象使用频率非常高 在数据交互时,根本不关心对象是什么类型 关心的是数据
*/
内置构造函数
// 在js语言中内置存在很多的构造函数 例如Number、Array、RegExp、Object等 内置中最大的构造函数为Object
let obj = new Object();
obj.name = '刘智鹏';
obj.age = 20;
obj.like = ['js','html','css'];
console.log(obj);
/*
实例化内置的构造函数缺点与字面量方式一样
*/
工厂函数
// 1、使用工厂函数获取对象
// 工厂函数就是一个普通函数 可以返回对象
// function createObject() {
// let obj = new Object();
// obj.name = '刘智鹏';
// obj.age = 20;
// obj.like = ['js', 'html', 'css'];
// return obj
// }
// createObject()
// createObject()
// 2、使用工厂函数传递参数控制对象属性值
function createObject(name, age, like = ['js', 'html', 'css']) {
let obj = new Object();
// 左边表示obj下存在name属性 值为参数name变量的值
obj.name = name;
obj.age = age;
obj.like = like;
return obj
}
console.log(createObject('leo', '31'));
console.log(createObject('zs',18));
自定义构造函数(重点)
1、函数多面性
// 1、函数的多面性
// 1.1、普通函数
function fn(){
console.log('普通函数')
}
fn();
// 1.2、函数也是对象
// fn对象调用say属性并且赋值
fn.say = '能说话';
console.log(fn.say)
// 1.3、函数也是构造函数
console.log(new fn())
2、自定义构造函数使用
// 2、自定义构造函数
// 一个function 在被创造时就已经决定了该如何使用,普通函数一般使用小驼峰命名 构造函数一般使用大驼峰命名
function Person(){
console.log('person构造函数代码被执行')
}
// 构造函数 需要使用必须与new关键字配置 只要实例化对象 构造函数中的代码会自动执行
let p = new Person();
3、new时系统所做的操作
// 3、new时系统所做的操作
function Dog(){
// 1、先以当前构造函数创造一个对象 可以理解为 let obj = new Object
// 2、将this指向创造的对象 可以理解为 let this = obj;
// 3、将构造函数中的代码自动运行起来
// 4、自动将结果返回 return this;
}
let d = new Dog();
console.log(d)
4、构造函数中this
// 4、构造函数中this
function Cat(){
console.log(this)
this.leg = 4;
}
let c = new Cat();
console.log(c);
5、使用构造函数传递参数控制对象
// 5、使用构造函数传递参数
function Car(brand,price){
// 当执行 59行代码 new对象是 构造函数中this 就表示bm对象
this.brand = brand;
this.price = price;
this.start = function(){
console.log(this);
console.log('启动')
}
}
// 在new构造函数传递的参数与普通函数一样也会进行形参赋值
let bm = new Car('bmw','300000');
console.log(bm);
bm.start();
// new 构造函数时如果没有参数 可以省略(),作为构造函数使用 千万别手动的return(可以在属性值为函数的代码中return)
原型
构造函数的缺点
1、测试代码
function Computer(cpu, memerySize, diskSize) {
this.cpu = cpu;
this.memerySize = memerySize;
this.diskSize = diskSize;
this.openBrower = function () {
console.log(this.cpu)
console.log('看电影')
}
}
let c1 = new Computer('i5 7200', 8, 512);
let c2 = new Computer('I5', 16, 512);
2、内存图
构造函数中直接this设置属性为函数时会导致内存空间浪费。
原型
原型三句话
1、每一个构造函数天生具备属性prototype,并且属性值指向的是一个对象(原型对象,也是由系统分配)
2、每一个实例对象天生具备一个属性"__proto__",属性值指向到构造函数的原型(原型被所有的实例对象共享)
3、每一个原型天生具备属性constructor 反向指向到构造函数
原型中三者的关系
function Person(name){
this.name = name;
}
// 现在Person 属于Function的实例对象
// let sum = new Function();
console.log(Person instanceof Function)
console.log(typeof Person)
//只要创建了一个函数系统就自动的分配了原型对象
console.log(Person.prototype);
let p1 = new Person('李四');
console.log(p1)
// 查看对象下__prpto属性
console.log(p1.__proto__)
let p2 = new Person('王五');
// 构造函数的原型 被所有的实例对象共享
console.log(p2.__proto__ == p1.__proto__);
console.log(Person.prototype == p1.__proto__);//true
console.log(Person.prototype.constructor == Person)
//千万别使用对象.__proto__操作到原型 这样虽然功能可以实现 但是不合理 需要操作原型 就使用构造函数来操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EDGZLvNL-1661419310487)(/1661409098337.png)]
原型链
function Dog(){
this.type = 'single dog';
}
// 系统自动为Dog构造函数生成的原型对象
console.log(Dog.prototype);
// Dog的原型对象是系统自动以Object构造函数new出来的对象 所以Dog的原型 也有原型
console.log(Dog.prototype instanceof Object);
// 查看Dog原型的原型 Dog.prototype表示Dog构造函数的原型
console.log(Dog.prototype.__proto__)
// null Dog.prototype.__proto__.__proto__
console.log(Dog.prototype.__proto__.__proto__)
let ls = new Dog();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-agxw1XXJ-1661419310488)(/1661412683546.png)]
对象属性使用规则
访问规则
function Person(leg = 2) {
this.leg = leg;
this.eyes = 2;
}
// 在Person的原型上添加eat方法
Person.prototype.eat = function () {
console.log(this.leg)
console.log('eat')
}
// 原型上添加属性
Person.prototype.zui = 1;
Person.prototype.leg = 2;
let p1 = new Person();
console.log(p1)
//获取当前对象下存在的属性
console.log(p1.eyes);//2
//获取当前对象下不存在 但是原型上存在的属性 先在当前对象下寻找属性 没找到 到原型上寻找(自动),如果找到了就直接使用
console.log(p1.zui);
// 获取当前对象以及对应原型上都不存在的属性 先在当前对象下寻找,没找到 到原型上寻找,原型上也没有 就继续到原型的原型上寻找 找到了就直接用
console.log(p1.toString);
// 顺着原型链都没有找到这个属性最后就是undefined
console.log(p1.abc);//undefined
赋值规则
function Person(name){
this.name = name;
}
Person.prototype.leg = 2;
let p = new Person('lei');
// 修改当前对象下存在的属性
p.name = 'change';
// 修改当前对象下不存在 但是原型上有的属性,
// 当修改对象下的属性时不论这个属性是否存在 永远是修改的当前对象 不可能修改到原型
p.leg = 1;
// 千万不要写下面的代码 要修改原型 就使用构造函数
// p.__proto__.leg = 2;
console.log(p)
总结:
1、当读取对象下的属性时,会按照原型链一层一层寻找,任何一层找到了就直接使用 否则到null都还没找到最后得到undefined 读取原型上的属性时不要手动使用_proto_
2、当修改对象下的属性时不论这个属性是否存在 永远是修改的当前对象 不可能修改到原型
面向对象改造选项卡
1、初版面向对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.container {
width: 600px;
height: 400px;
border: 10px solid #333;
margin: 30px auto;
display: flex;
flex-direction: column;
}
.header {
height: 40px;
display: flex;
line-height: 40px;
}
.header div {
flex: 1;
background-color: hotpink;
text-align: center;
font-size: 36px;
color: white;
}
.header .active {
background-color: orange;
}
.content {
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="active">热卖</div>
<div>精品</div>
<div>推荐</div>
</div>
<div class="body">
<div class="content" style="display: block;">1</div>
<div class="content">2</div>
<div class="content">3</div>
</div>
</div>
</body>
</html>
<script>
function Tab() {
}
// Tab的原型上添加init方法实现特效的初始化
Tab.prototype.init = function(){
// 实现具体逻辑
let headrs = document.querySelectorAll('.header>div');
let contents = document.querySelectorAll('.body>.content');
headrs.forEach((dom,index)=>{
dom.addEventListener('click',function(){
// 将所有的div active样式移除
headrs.forEach(item=>item.className = '');
// 为当前的添加样式
this.className = 'active';
// 将所有的content 设置为隐藏
contents.forEach(content=>content.style.display='none');
// 当前对应的content显示
contents[index].style.display = 'block'
})
})
}
let t = new Tab();
t.init();
</script>
2、面向对象版选项卡
function Tab(headrs,contents) {
// 使用对象下的属性保存 数据
this.headrs = headrs;
this.contents = contents;
// 手动调用初始化 避免 使用者 每次获取到对象还需要手动调用init
this.init();
}
// Tab的原型上添加init方法实现特效的初始化
Tab.prototype.init = function(){
// 此时的this还是表示着Tab的对象
let _this = this;
// 实现具体逻辑
this.headrs.forEach((dom,index)=>{
dom.addEventListener('click',function(){
// 将所有的div active样式移除
_this.headrs.forEach(item=>item.className = '');
// 为当前的添加样式
this.className = 'active';
// 将所有的content 设置为隐藏
_this.contents.forEach(content=>content.style.display='none');
// 当前对应的content显示
_this.contents[index].style.display = 'block'
})
})
}
new Tab(document.querySelectorAll('.header>div'),document.querySelectorAll('.body>.content'));