面向对象 面向对象的基本思想 工厂模式 面向对象编程自定义构造函数

    科技2022-08-23  103

    面向对象

    /* 1,什么是面向对象 所谓的面向对象,是一种编程思想,编程思路,代码的书写格式

    之前为了简化代码,我们是将程序封装为函数的形式来调用 函数会有一些小问题: 函数内部定义的局部作用域变量,函数外部无法直接调用 函数调用使用时,会占用大量的系统内存,容易造成数据溢出数据泄露,容易被黑客攻击 函数一般只是封装一些模块化的功能 复杂程序的封装会,封装成其他形式,使用面向对象的方式来封装程序 2,简单介绍面向对象的编程思想 我们之前的编程方式,称为面向过程的编程方式 面向过程和面向对象编程思想的区别 举例生活中的实例 : 中午要吃饺子 面向过程 1,和面 2,和饺子馅 3,包饺子--擀皮,放馅,包饺子 4,烧水 5,水开了煮饺子 6,捞饺子 7,吃饺子 面向对象 1,找一个饺子馆 --- 封装的面向对象的程序,完成制作饺子的过程,我们只要点就可以了 2,点饺子 --- 调用封装的程序 3,做好了吃饺子 4,结账 总结 面向过程 : 自己独立的一步一步的完成程序的定义和执行 面向对象 : 有封装好的面向对象的的程序 直接调用执行就可以了 功能和作用类似于 封装好的函数 但是 封装的语法和思想与函数不同 */ ***// 为什么要面向对象,不是面向字符串等*** // 对象的优点 // 1,对象中,可以定义并且存储多个数据单元以及所有JavaScript支持的数据类型 // const obj = {age:18,name:'张三',fun:()=>{},arr:[1,2,3,]} // 2,对象中,调用具体数据很方便 // 调用数据时,不用考虑数据的顺序 // const obj1 = {age:18,name:'张三'} // const obj2 = {name:'张三',age:18} // 调用 name 和 age 都是不用考虑 数据单元的顺序 // 只要键名/属性 输入正确就可以了 // 3,对象中,可以定义函数,还可以通过函数的this,方便的调用对象本身的数据 // const obj1 = {age:18,name:'张三',fun:function(){ console.log(this.name) }} // const obj2 = {age:18,name:'张三',fun:function(){ console.log(this.name) }} // 调用对象的数据,不用管对象名称 是什么,只要通过this,就可以指向这个对象, // obj1 中的 this,指向的就是obj1 obj2 中的 this,指向的就是obj2 // this.name 分别就是 obj1.name obj2.name <script>

    // 面向对象的基本思想

    // 基本思路就是,创建一个对象,给这个对象,添加上属性和属性值,还有函数等方法 // 之后通过操作这个对象,来完成需要的效果 // 先通过一个函数的方法,来创建对象 function createObj(){ // 创建对象 const obj = {}; // 给对象添加属性和属性值 obj.name = '张三'; obj.age = 18; obj.addr = '北京'; obj.sex = '男'; // 给对象添加方法 obj.funNameAge = function(){ console.log(this.name , this.age); } obj.funNameAddr = function(){ console.log(this.name , this.addr); } obj.funSexAge = function(){ console.log(this.sex , this.age); } obj.funAll = function(){ console.log(this.name, this.sex , this.age, this.addr); } // 返回这个创建好的对象 return obj; } // 调用函数,函数创建对象,并且作为返回值 // 变量中存储的就是函数创建的对象 const obj = createObj(); // 可以通过调用obj中存储的对方的方法,来实现功能 obj.funAll(); </script> <script>

    // 工厂模式

    // 所谓的工厂模式,是一种书写函数的语法形式,语法规范 // 就向工厂中的流水线一样,按照步骤来执行需要的操作 // 步骤1,创建对象 // 步骤2,给对象定义属性和属性值 // 步骤3,给对象添加方法 // 步骤4,定义对象为返回值 // 标准的工厂模式,会有对应的参数 function createDumpling(pi,mian,xian,tioliao){ // 创建包饺子对象 const dumpling = {}; // 给包饺子对象,添加属性 dumpling.pi = pi; dumpling.xian = xian; dumpling.mian = mian; dumpling.tiaoliao = tiaoliao; // 给包饺子对象,添加方法 // 和面 dumpling.huomian = function(){ console.log(this.mian); } // 和饺子馅 dumpling.huoxian = function(){ console.log(this.xian); } // 包饺子 dumpling.bao = function(){ console.log(this.xian,this.pi,this,mian); } // 煮饺子 dumpling.zhu = function(){ console.log('煮饺子了,等着吃吧'); } // 返回包饺子对象 return dumpling; } // 要开始包饺子 // 创建一个执行包饺子功能的对象,并且输入参数 const baojiaozi1 = createDumpling('薄皮' , '白面粉' , '猪肉大葱'); const baojiaozi2 = createDumpling('厚皮' , '玉米面' , '鱼香肉丝'); console.log(baojiaozi1); // 可以调用任意的封装的方法 baojiaozi1.huoxian(); // 调用和饺子馅方法 baojiaozi1.huomian(); // 调用和面方法

    面向对象编程

    // 面向对象编程的优点 // 优点与函数的优点类似 // 高内聚 低耦合 // 高内聚 : 将所有需要的程序,都定义封装在对象内 // 对象中存储所有需要的属性,所有需要的方法 // 低耦合 : 尽量减少特殊程序的执行 // 面向对象编程的特点 // 抽象 --- 通过描述 对象 共有的特点(属性和属性值) , 来形容一个对象 // 这个对象不是一个非常具体事例的内容,是一个抽象化的实例 // 制作饺子,制作包子,制作馅饼,制作馅窝头... // 有皮,有汁,味甜 --- 西瓜 , 橘子 , 桃 , 爆浆蛋糕 , 夹心糖果 ... // 四条腿的,有一个面的 --- 桌子 , 凳子 , 床 ... // // 封装 --- 将所有的程序,都定义在一个对象中

    自定义构造函数

    <script> // 创建对象的方法,有两种 // 字面量 const obj = {} // 构造函数 const obj = new Object() // 这个构造函数,就是JavaScript程序定义好的构造函数,我们直接使用就可以了 // 所谓的构造函数,实际也是一种函数 // 构造函数专门用于生成,定义对象的 // 通过构造函数,生成的对象,称为实例化对象 // 强调: 构造函数,就是一种函数,是专门用于生成对象的函数 // 实例化对象,就是通过构造函数,生成的对象,称为实例化对象 // 构造函数分为两种,一种是JavaScript程序定义好的构造函数,称为 内置构造函数 // 一种是程序员自己定义的构造函数,称为 自定义构造函数 // 构造函数和普通函数的区别 // 1,构造函数一定要和 关键词 new 一起使用 // new 关键词具有特殊的功能, // 会自动的给 构造函数中 定义一个对象,并且返回这个对象 // 我们只要对这个对象设定属性,设定方法就可以了 // 2,构造函数为了和其他函数区别 // 语法规范规定,构造函数,函数名称,第一个字母必须大写,使用大驼峰命名法 // 3,构造函数,给对象定义属性和方法的语法,与一般函数不同 // 实例化对象和普通的对象,没有任何的区别,只是建立方式不同而已 // 自定义构造函数 // 注意:1,函数名称要使用大驼峰命名法 // 2,自定义构造函数中,不要定义对象,和 定义return // new 关键词会执行,如果定义了,会产生冲突 // function CrtObj(name,age,sex,addr){ // 在构造函数中,使用this,来指代对象 // 这个对象,就是我们使用构造函数,生成的实例化对象 // 定义属性 // 给实例化对象,添加name属性,属性值是输入的name参数 this.name = name; this.age = age; this.sex = sex; this.addr = addr; // 定义方法 this.funAll = function(){ console.log(this.name,this.age,this.sex,this.addr ) } this.funNameAge = function(){ console.log(this.name,this.age) } this.funSexAddr = function(){ console.log(this.sex,this.addr ) } } // 通过自定义构造函数来生对象,实例化对象 // 调用执行构造函数时,都必须要和new 关键词一起使用 const obj1 = new CrtObj('张三',18,'男','北京'); console.log(obj1); // 调用 对象/实例化对象 中的方法 obj1.funAll(); obj1.funNameAge(); obj1.funSexAddr(); // 总结 // new 的作用 // 1,在构造函数中,自行创建一个对象,并且返回这个对象 // 2,因为new 关键词,创建了对象,此时,构造函数中的this,才会指向这个对象 // 也就是将来生成的实例化对象 // 3,所有的构造函数中,this的指向,都是将来通过这个构造函数生成的实例化对象 </script>

    proto

    <script> // 自定义构造函数 function CrtObj(name, age, sex, addr) { // 定义属性 this.name = name; this.age = age; this.sex = sex; this.addr = addr; // 定义方法 this.funAll = function () { console.log(this.name, this.age, this.sex, this.addr); } } // 定义了构造函数,可以生成实例化对象 const obj1 = new CrtObj('张三',18,'男','北京'); const obj2 = new CrtObj('李四',19,'女','上海'); const obj3 = new CrtObj('王五',20,'不详','火星'); // 每个实例化对象中,都有属性和方法 // console.log(obj1); // console.log(obj2); // console.log(obj3); // 通过同一个构造函数,生成的实例化对象 // 属性相同,属性值可能不同 // 定义的方法的程序,是相同的 // 但是,如果做一个比较判断,结果是 false // 表示,不同的实例化对象中,定义的是不同的方法/函数 // 不同的方法和函数,会占用过多的内存空间 // 要想办法,让使用同一个构造函数,生成的实例化对象,都是相同的方法 // 原因:每次创建对象,都会在对象上定义一个新的方法,也就是新的函数 // 函数存储时会生成一个独立的存储空间,不同函数有不同的存储空间 // console.log( obj1.funAll == obj2.funAll ); // 解决的方式 // 将构造函数需要定义给实例化对象的方法,定义在函数的 prototype 属性中 function CrtObj2(name, age, sex, addr) { // 定义属性 this.name = name; this.age = age; this.sex = sex; this.addr = addr; } // 在构造函数的 prototype 属性中,来定义实例化对象的方法 CrtObj2.prototype.fun = function(){ console.log(this.name, this.age, this.sex, this.addr); } const obj4 = new CrtObj2('张三',18,'男','北京'); const obj5 = new CrtObj2('李四',19,'女','上海'); // console.log(obj4); // console.log(obj5); // obj4.fun(); // console.log( obj4.fun === obj5.fun ); // 详细解释一下 prototype // 到底 prototype 是什么? // prototype 是每个函数本身就具有的一个特殊的属性 // 可以在这个特殊的属性中,存储 数据和函数 function fun(){} // 向 fun 函数 中的 prototype 中,定义属性 name 属性值 张三 fun.prototype.name = '张三'; fun.prototype.f = function(){console.log(123)}; console.dir( fun ); console.log( fun.prototype.name ); fun.prototype.f(); // 在构造函数中, prototype 的所用和使用方法 // 在构造函数内部,定义实例化对象的属性 // 通过 this关键词,指向实例化对象 function CreFun(name,age){ this.name = name; this.age = age; } // 如果是构造函数,创建实例化对象 // 给实例化对象,添加属性,是通过this方法来添加定义的 // 定义在 prototype 中的属性,就是写在 prototype 中的,不会写在实例化对象中 CreFun.prototype.name2 = '李四'; CreFun.prototype.age2 = 180; // 在 prototype 中定义的方法/函数,也不会定义在实例化对象上 // 只会写在 prototype 中 CreFun.prototype.ff = function(){ console.log('1111'); } // JavaScript中,每一个对象,都有一个 __proto__ // 实例化对象的 __proto__ 就是指向的 构造函数中, prototype 这个属性 // 通过构造函数,生成的实例化对象,这歌实例化对象, __proto__ 存储的地址 // 就是生成这个实例化对象的构造函数的 prototype 的地址 // 实例化对象的 __proto__ 实际上 就是指向 构造函数的 prototype // 在对象中,调用数据,先在对象本身上找,有没有这个属性 // obj6.name 企图调用 obj6中,name属性 // 先在 obj6 对象本身的属性上找 , 如果有就调用本身属性上,对应的数据 // 如果调用的属性,对象本身没有这个属性,会自动去 __proto__ 中寻找 // 如果有,就使用 __proto__ 当中的数据 // obj6.name2 企图调用 obj6中,name2属性 // 实际上,obj6中,本身没有 name2属性 // 去 __proto__ 中 寻找,没有 name2 // 如果有就会使用 name2 的数据 const obj6 = new CreFun('张三',18); console.dir( CreFun ) console.dir(obj6); console.log(obj6.name); console.log(obj6.name2); // 总结 // 函数有一个 属性 叫 prototype // 其中可以定义,存储, 属性属性值 函数名称函数等等数据 // 定义在 prototype 中的内容 就是 函数自己本身的数据 // 对象有一个 属性 叫 __proto__ // 构造函数,在生成实例化对象时,会将自己 prototype 这个空间的地址 // 赋值给 实例化对象 的 __proto__ 来存储 // 实际上 构造函数的 prototype 和 生成的实例化对象的 __proto__ 指向的是同一个 存储空间 // 可以相互调用数据 /* 构造函数 function Fun(){} 只定义在函数 Fun 中的数据 Fun.prototype.name = 'abc'; Fun.prototype.f = function(){}; // 通过 构造函数,生成实例化对象 const obj = new Fun(); 构造函数 Fun 的 prototype 的 空间地址 就赋值给了 实例化对象 obj 的 __proto__ obj 的 __proto__ 和 Fun 的 prototype 指向的是同一个空间地址 通过构造函数,定义给实例化对象的属性,必须使用this,在构造函数中操作 function Fun(){ 通过this方法才是定义给实例化对象的属性和方法 this.name = '张三' this.age = 18 } 只是定义在 构造函数 Fun 中的数据,跟实例化对象没有半毛钱关系 Fun.prototype.name = 'abc'; Fun.prototype.f = function(){}; 当调用 实例化对象 中 f函数时 obj.f() 实例化对象本身中,是没有这个函数的 会继续在 __protop__ 中寻找,是否有这个方法函数 f __protop__ 实际上指向的 就是 构造函数的 prototype 构造函数的 prototype 中,是有这个方法的,那么就可以正常调用 所有通过这个构造函数生成的实例化对象,实际上,本身都没有定义方法和函数 使用时,都是调用生成实例化对象的构造函数,本身prototype中定义的方法 就不会重复生成函数,占用内存空间 */ </script>

    原型对象原型属性原型链

    <script> /* 1,原型对象 每一个函数,天生都有一个 prototype 属性,称为原型对象 是一个专门用来存储数据,函数等内容的空间 2,原型属性 每一个对象,天生都有一个 __proto__ 属性,称为原型属性 实例化对象的原型属性,指向的创建实例化对象的构造函数的 prototype 函数,数组,等,JavaScript中的数据类型 实际存储的形式都是一个对象,都会有 __proto__ 属性 在JavaScript中,可以用对象的形式存储任何数据类型 3,原型链 所谓的原型链,就是所有相互关联的变量,使用 __proto__ 属性串联起来 调用数据数据时,会通过 __proto__ 将所有相互关联的 变量 串联 只要有一个变量中 有相应的属性,就会调用成功 */ // 字面量方式,创建字符串 // let str1 = 'beijing'; // console.dir(str1); // // 构造函数方式,创建字符串 // let str2 = new String(); // console.dir(str2); function fun(){} console.dir(fun); // 实例化对象1 ---> 通过构造函数1创建的 // 构造函数1是 实例化对象2 的方法 // 实例化对象2 ---> 通过构造函数2创建的 // 构造函数2是 实例化对象3 的方法 // 实例化对象3 ---> 通过构造函数3创建的 // 构造函数3 ---> 生成 实例化对象3 ---> 构造函数2 ---> 生成 实例化对象2 ---> 构造函数1 ---> 生成 实例化对象1 // 构造函数本身有 prototype 也有 __proto__ // 实例化对象 有 __proto__ /* 实例化对象1 __proto__ 指向 构造函数1的 prototype 构造函数1本身也有 __proto__ 指向的是 实例化对象2 实例化对象2 __proto__ 指向 构造函数2的 prototype 构造函数2本身也有 __proto__ 指向的是 实例化对象3 实例化对象3 __proto__ 指向 构造函数3的 prototype 最终,所有的构造函数,对象,数组等都会指向 JavaScript中的顶级对象 Object 会在 单例模式中 给大家演示 为什么 实例化对象 本身没有的函数方法, 构造函数上有,为什么可以调用使用 原因就是因为原型链的存在 实例化对象的 __proto__ 指向构造函数的 prototype , 就可以调用使用 构造函数 prototype中定义的方法 */ </script>

    面向对象版选项卡

    <style> *{ margin: 0; padding:0; } ul,ol,li{ list-style: none; } .cont{ width: 800px; height: 600px; border: 5px solid #333; margin: 0 auto; display: flex; flex-direction: column; } .cont ul{ width: 100%; height: 60px; display: flex; } .cont ul li{ flex:1; font-size: 35px; color: #fff; border-left: 2px solid blue; border-right: 2px solid blue; background: hotpink; display: flex; justify-content: center; align-items: center; } .cont ol{ flex:1; position: relative; } .cont ol li{ width: 100%; height: 100%; font-size: 150px; display: flex; justify-content: center; align-items: center; position: absolute; top:0; left:0; background: burlywood; display: none; } /* 按钮标签 哪个标签有这个属性,哪个就显示特殊背景颜色 */ .cont ul li.active{ background: skyblue; color: black; } /* 内容标签 哪个标签有这个属性,哪个就显示 */ .cont ol li.active{ display: flex; } </style> </head> <body> <div class="cont"> <ul> <li name="ulli" class="active">按钮1</li> <li name="ulli" >按钮2</li> <li name="ulli" >按钮3</li> </ul> <ol> <li name="olli" class="active">内容1</li> <li name="olli" >内容2</li> <li name="olli" >内容3</li> </ol> </div> <script> // 面向过程值事件委托形式 // 获取父级div标签对象 // var oDiv = document.querySelector('div'); // // 获取标签对象 // var ullis = document.querySelectorAll('ul li'); // var ollis = document.querySelectorAll('ol li'); // oDiv.onclick = function(e){ // if(e.target.getAttribute('name') === 'ulli'){ // // 1,给所有的li标签,清除样式 // ullis.forEach(function(item,key){ // item.className = ''; // ollis[key].className = ''; // // 给item,也就是ul中的li标签,定义属性 // item.setAttribute('index',key); // }) // e.target.className = 'active'; // ollis[e.target.getAttribute('index')].className = 'active'; // } // } // 面向对象的方法 // 创建一个对象,这个对象中有属性有方法 // 属性是需要操作的标签等 // 方法就是实现选项卡效果的程序 // 定义构造函数 // 参数:需要执行的tab切换的标签对象,是所有需要执行选项卡效果标签的父级 // 面向对象方法1,建立变量,存储this function SetTab1(ele){ // 先要单独,接收存储参数 this.ele = ele; // 通过参数,来获取需要的标签对象 this.ullis = ele.querySelectorAll('ul li'); this.ollis = ele.querySelectorAll('ol li'); // 在构造函数中,定义的this指向的是实例化对象 } // 定义构造函数的方法 SetTab1.prototype.fun = function(){ // 选项卡思路 // 给所有的ul中的li,添加点击事件,点击时,清除所有的ul,ol中li的样式 // 给当前点击的li,添加样式,再给对应的ol中的li,添加样式 // 定义一个变量,专门存储this指向 // 此时的this指向,还是 实例化对象 // oldThis 变量中 存储的就是 实例化对象 let oldThis = this; // 循环遍历 ul中所有的li // 这里的 this.ullis 指向的是 实例化对向中的属性 this.ullis 中存储的数据 // this.ullis this.ollis this.ele 其中的this,应该都是指向实例化对象 this.ullis.forEach(function(item,key){ // 给 li标签添加点击事件 item.addEventListener('click' , function(){ // 在点击事件中,此时this指向的是绑定点击事件的标签 item // 就不是 实例化对象了 // 此时,要想正确调用 实例化对象属性的属性值 // 必须 使用 一个 指向是 实例化对象的内容 // 先循环遍历所有的ul和ol中的li,清除css样式 oldThis.ullis.forEach(function(item2 , k){ item2.className = ''; oldThis.ollis[k].className = ''; }) // 给点击的ul,li添加样式 item.className = 'active'; // 给对应的ol,li添加样式 oldThis.ollis[key].className = 'active'; }) }) } // 面向对象方法2,箭头函数 function SetTab2(ele){ // 先要单独,接收存储参数 this.ele = ele; // 通过参数,来获取需要的标签对象 this.ullis = ele.querySelectorAll('ul li'); this.ollis = ele.querySelectorAll('ol li'); // 在构造函数中,定义的this指向的是实例化对象 } // 定义构造函数的方法 // 这里的function不能改为箭头函数,里面的都改 SetTab2.prototype.fun = function(){ // 将构造函数中,所有的函数,都写成箭头函数 // this 都会指向父级,最终都指向 实例化对象 this.ullis.forEach( (item,key)=>{ // 把普通的函数,改为箭头函数,this指向就是父级程序的this指向 // 也就是 实例化对象了 // 如果不改,this指向的是绑定事件的标签,也就是item item.addEventListener('click' , ()=>{ // 这里的forEach也要改为箭头函数,不然this指向的是window this.ullis.forEach((item2 , k)=>{ item2.className = ''; this.ollis[k].className = ''; }) // 给点击的ul,li添加样式 item.className = 'active'; // 给对应的ol,li添加样式 this.ollis[key].className = 'active'; }) }) } // 获取标签对象 const oDiv = document.querySelector('div'); // 通过构造函数,生成实例化对象 // const tabs1 = new SetTab1(oDiv); // console.log(tabs1) // 调用实例化对象中的方法,来执行程序,实现选项卡效果 // tabs1.fun(); const tabs2 = new SetTab2(oDiv); tabs2.fun() // 特别注意: // 调用构造函数,尤其是封装了方法的构造函数,必须要在定义构造函数之后,来调用 // 如果先调用,可以调用构造函数,也可以生成实例化对象 // 实例化对象中,也有属性 // 但是,对 构造函数 prototype 赋值定义方法,不会提前执行 // 生成的实例化对象,就没有绑定方法 // 面向对象语法,最关键的就是this的使用 // 一定要分清楚,每一个函数中,this的指向 // 常用 方法1: 找变量,存储this指向 // 方法2: 将function,写成 箭头函数,this为父级程序this </script>
    Processed: 0.020, SQL: 9