面向对象
面向对象
1.面向对象概念
/* 面向对象概念: 一、面相过程:注重解决问题的步骤,分析问题需要的每一步,实现函数依次调用; 二、面相对象:是一种程序设计思想。将数据和处理数据的程序封装到对象中; 三、面相对象特性: 抽象、 继承、封装、多态 优点:提高代码的复用性及可维护性; */ /* 小明去餐厅吃饭: 面向过程: 1.小明去餐厅看菜单点餐吃饭 面向对象: 1.小明(走、看、点餐、吃) 2.餐厅(菜单); 研究对象之间的关系: 小明.走餐厅.菜单 小明.看 小明.点餐 小明.吃; */
2.JS里的对象是什么
// 对象创建 // 1.字面量方式 let str = 'name'; let obj = { // 属性 [str]:"张三", // name:"张三", age:20, /* hobby(){ console.log("喜欢打篮球"); }, */ // 方法 hobby:function(){ console.log("喜欢打篮球") } } // 2.构造函数; // let obj = new Object(); // obj.name = "张三"; // obj.age = 20; // /* obj.hobby = function(){ // console.log("喜欢篮球"); // } */ // obj.hobby = ()=>{ // console.log("喜欢篮球"); // } // obj.hobby(); // console.log(obj); // 3.Object.create();属性方法放在原型上; // let obj = Object.create({ // name:"张三", // age:20, // hobby(){ // console.log("喜欢篮球") // } // }); // console.log(obj); //在__proto__ // 对象的调用 // console.log(obj.name);//属性 // obj.hobby();//方法 console.log( obj["name"] );//属性 // let str = "name"; // // console.log(obj.str); // console.log( obj[str] );
3.工厂模式
// let zhangsan = { // name:"张三", // age:20, // hobby(){ // console.log("喜欢篮球") // } // } // let lisi = { // name:"李四", // age:21, // hobby(){ // console.log("喜欢篮球") // } // } // 函数封装方法 -> 工厂函数 -> 可以理解为:类,注意它不是代表哪一个,而是代表某一'类' function Person(name,age,hobby){ let obj = {};//添加原料 //加工原料 obj.name = name; obj.age = age; obj.hobby = function(){ console.log(hobby); } return obj;//出厂; } /* 总结:工厂模式; 1.创建一个对象 2.为对象添加一些属性,方法 3.然后把对象 return回来 */ let zhangsan = Person("张三",20,"喜欢篮球"); let lisi = Person("李四",21,"喜欢足球"); console.log(zhangsan); console.log(lisi);
4.new运算符
// let str = "";//[],{}... // let str = new String(); /* new 运算符 1.执行函数 2.自动创建一个空对象 3.把空对象和this绑定 4.如果没有返还(return),隐式返回this */ // function test(){ // console.log("test"); // } // test();//执行函数 // new test();//执行函数 -> ()是否需要传参 // new test;//执行函数 // function Test(){ // // let obj = {}; === this; // // return this; // } // new Test(); // 当new配合工厂模式时: function Person(name,age,hobby){ // let obj = {}; === this this.name = name; this.age = age; this.hobby = function(){ console.log(hobby); } // return obj; } let zhangsan = new Person("张三",20,"喜欢篮球"); console.log(zhangsan.name);//属性 zhangsan.hobby();//方法
5.构造函数
/* 构造函数 1.首字大写; 区分构造函数和普通函数 也是为了模拟系统的对象 2.this指向实例化对象; */ function Person(name){ // this.num = 0; this.name = name; this.age = 20; this.hobby = function(){ console.log("喜欢篮球"); } } Person.num = 0;//静态成员~ Person.fu = function() { console.log("fn"); } // new的过程也叫做: 实例化! -> 通过工厂模式通过new运算符改造成构造函数 let zhangsan = new Person("张三"); // 静态属性和方法; (属于类本身的); // 假如想统计一下'类'实例化的次数! Person.num ++; // zhangsan.num ++; // console.log(zhangsan.num);//1 let lisi = new Person("李四"); Person.num ++; // lisi.num ++; // console.log(lisi.num);//1 console.log(Person.num);//2 //但是它们两个都是 1,并不符合我们的需求~ // 所以我们可以把它们统计的时候,认为是我们的类的属性 -> Person.num /* 总结构造函数 1.首字大写. 2.this执行实例化对象 在构造函数里面写的所有的属性和方法都是属于实例化对象的 除了实例化对象的属性和方法,还有一些 静态的属性和方法 它们是属于构造函数(类)自身的! */
6.构造函数性能
function Person(name) { this.name = name; this.age = 20; this.hobby = function() { console.log("喜欢篮球") } } let zhangsan = new Person("张三"); let lisi = new Person("李四"); console.log( zhangsan.hobby === lisi.hobby ); // false 它们是对象:不仅需要值一样,还需要引用(内存地址)一样 // 虽然它们的值是一样的,但是它们在内存里面都是分别开辟了两个地址! // 所以我们当有很多的对象的时候:10000,它们就都开辟了这么多份,就很大程度的占据内存 // JS提供了一个公共的空间去存放我们相同的方法!更好的节约我们的内容~ -> 原型prototype
7.原型
function Person(name) { this.name = name; this.age = 20; // this.hobby = function() { // console.log("喜欢篮球") // } } /* 每一个构造函数,在实例化的过程中,都分为两份!! 1.构造函数 2.公共空间原型; 原型里面的this也是执行实例化对象(new) */ // Person.prototype.hobby = function() { // console.log("喜欢篮球"); // } // Person.prototype.fn = function(){ // console.log("fn"); // } /* 原型的固有属性 系统自动生成的,自己定义好的! */ // console.log( Person.prototype.constructor );//指向构造函数! // Person.prototype.constructor === Person; Person.prototype = { // 注意点:可以在原型中追加方法,但是不要原型里面进行覆盖属性 hobby:function(){ console.log("喜欢篮球"); }, // 这时候的constructor属性是已经被覆盖掉了!! 可以手动添加! constructor:Person } let zhangsan = new Person("张三"); console.log( zhangsan.constructor === Person );//true /* 实例化对象也是由两部分构成的! 1.是它自身的属性和方法 -> 构造函数里面 2.对象也有自己的原型 __proto__ 它和prototype有什么关系呢? 它是一个东西,就表现形式不一样 */ // console.log(zhangsan); // console.log(zhangsan.__proto__ === Person.prototype);//true // let lisi = new Person("李四"); // console.log( zhangsan.hobby === lisi.hobby );//true // 虽然我们自己写的构造函数,使用constructor属性并不是很多,但在系统的函数中使用constructor属性可以进行判断类型!! // let str = new String("abd"); // console.log( str.constructor === String );//true
8.三者关系 原型,构造函数,对象
let temp; function Person(name){ this.name = name; this.age = 20; // temp = this; } Person.prototype.fn = function(){ console.log("fn"); temp = this; } console.log(Person.prototype.constructor===Person); let zhangsan = new Person("张三"); // console.log(zhangsan); // console.log(temp === zhangsan);//true zhangsan.fn(); console.log(temp === zhangsan);//true /* 1.构造函数的原型: 构造函数.prototype 2.原型里面可以有 属性和方法 3.构造函数得到对象: new(实例化) 它也有自身的属性,方法和原型(__proto__) 实例化之后 属性就是 构造函数里面的属性 原型的方法就放到了 __proto__ 里面 4.this都是执行实例化对象 5.constructor指向构造函数 */
9.工厂模式对比构造函数
// 工厂模式 function Person(name){ let obj = {}; obj.name = name; obj.age = 20; obj.fn = function(){ console.log("fn"); } } let zhangsan1 = Person("张三"); // 构造函数 function Person(name){ this.name = name; this.age = 20; } Person.prototype.fn = function(){ console.log("fn..."); } let zhangsan2 = new Person("张三"); /* 为什么构造函数比工厂函数用的更多呢? 1.构造函数有一个原型 原型有公共空间可以放属性和方法,当有多个实例化对象的时候,它原型上面的方法都在公共空间里面,不会重新占用内容 工厂模式,就没有这个功能! 2.创建对象的指向问题 可以通过constructor指向构造函数 在这里可以知道实例化对象是通过那个构造函数进行构造的 所以我们也可以通过constructor进行判断类型! */
10.原型链
// 构造函数 function Foo(name){ this.obj = name; this.age = 20; // this.test = "你好"; } /* 原型也是一个对象: 原型对象->那所以它也有自身的属性和原型! 原型链的形成:因为原型也是一个对象,对象也有自己的原型和属性... 有原型链的存在他们就有一个 查找的规则 就近原则:构造函数->原型->最终原型 都没有就是undefined */ Foo.prototype.fn = function(){ console.log("fn"); } // Foo.prototype.test = "hello"; // Object.prototype.test = "你好2"; let newFoo = new Foo("张三"); console.log(newFoo.test) // let obj = new Object();//每一个对象都是通过Object对象进行 创建的! // console.log(Object.prototype.__proto__);//null -> 所以这里是最终的原型
11.call,apply,bind 修改this指向
function foo(name,age){ console.log(this,"姓名是"+name+"年龄是"+age); } // foo();//window let obj = { name:"张三" } // foo.call(obj,"张三",20); // foo.apply(obj,["张三",20]); foo.bind(obj)("张三",20); /* call(); foo.call(); 第一个参数:改变this指向 其他参数:对应函数参数 apply() foo.apply(); 第一个参数:改变this指向 其他参数:用数组形式传递 bind() foo.bind(obj)(); 第一个括号:改变this指向 其他参数需要在第二个括号进行传入 */
12.继承
构造函数的继承
// 继承 function Dar(name,age){//父类 this.name = name; this.age = age; this.money = "100000"; } function Son(name,age){//子类 // Dar.call(this,name,age); // Dar.apply(this,[name,age]); Dar.bind(this)(name,age); this.sex = "男"; } let zhangsan = new Son("张三",20); console.log( zhangsan.money,zhangsan.sex );
原型的继承
// 继承 function Dar(name,age){//父类 this.name = name; this.age = age; this.money = "100000"; } Dar.prototype.fn = function(){ console.log("fn"); } function Son(name,age){//子类 // Dar.call(this,name,age); this.sex = "男"; } Son.prototype = Dar.prototype;//可以继承,但是有传址问题 Son.prototype.fn = function(){ console.log("重写的fn"); } let zhangsan = new Son("张三",20); // console.log( zhangsan.money,zhangsan.sex ); zhangsan.fn(); let lisi = new Dar("李四",21); lisi.fn();//父类的原型也受到了影响
传值和传址
/* 传址 复杂的数据类型都会涉及到传址问题 复杂的数据类型:除了简单的数据类型,其他的都是复杂数据类型! 基本数据类型:字符串,数字,布尔值,null,undefined. */ // let DadProto = {//假如它是原型->因为原型也是对象!! // name:"张三", // age:20 // } // let SonProto = DadProto;//涉及到了对象的引用关系->就是用到的是一个地址 // SonProto.name = "李四"; // console.log(SonProto.name); // console.log(DadProto.name); // 简单数据类型 : 传值 -> 它们都会重新去开辟一个地址 // let a = 10; // let b = a; // b = 20; // console.log(a);//10 // 如何做到不相互响应呢 -> 深拷贝 // let DadProto = { // name:"张三", // age:20, // fn(){ // console.log("fn..") // }, // test:undefined // } // let SonProto = JSON.parse( JSON.stringify( DadProto ) ); // SonProto.name = "李四"; // console.log(DadProto) // console.log(SonProto) /* 简单的深拷贝: JSON.stringify转成json JSON.parse把json转成字符串 问题: 1.丢失方法,丢失undefined */ // 深拷贝函数 let obj = { name:"张三", age:20, fn(){ console.log("fn..") }, test:undefined, arr:[] } let obj2 = deepCopy(obj); obj2.name = "李四"; console.log(obj2); console.log(obj); function deepCopy(obj){ let newObj = Array.isArray(obj)?[]:{}; for(var key in obj){ if(obj.hasOwnProperty(key)){ if(typeof obj[key] === "object"){ newObj[key] = deepCopy(obj[key]); }else{ newObj[key] = obj[key]; } } } return newObj; }
原型深拷贝继承
function deepCopy(obj){ let newObj = Array.isArray(obj)?[]:{}; for(var key in obj){ if(obj.hasOwnProperty(key)){ if(typeof obj[key] === "object"){ newObj[key] = deepCopy(obj[key]); }else{ newObj[key] = obj[key]; } } } return newObj; } // 继承 function Dar(name,age){//父类 this.name = name; this.age = age; this.money = "100000"; } Dar.prototype.fn = function(){ console.log("fn"); } function Son(name,age){//子类 // Dar.call(this,name,age); this.sex = "男"; } Son.prototype = deepCopy(Dar);//深拷贝方式继承 Son.prototype.fn = function(){ console.log("重写的fn"); } let zhangsan = new Son("张三",20); // console.log( zhangsan.money,zhangsan.sex ); zhangsan.fn(); let lisi = new Dar("李四",21); lisi.fn();
组合继承
// 继承 function Dar(name,age){//父类 this.name = name; this.age = age; this.money = "100000"; } Dar.prototype.fn = function(){ console.log("fn"); } function Son(name,age){//子类 // Dar.call(this,name,age); this.sex = "男"; } let Link = function(){} Link.prototype = Dar.prototype; Son.prototype = new Link(); Son.prototype.constructor = Son; Son.prototype.fn = function(){ console.log("重写的fn"); } let zhangsan = new Son("张三",20); // console.log( zhangsan.money,zhangsan.sex ); zhangsan.fn(); let lisi = new Dar("李四",21); lisi.fn();
成功一定有方法,失败一定有原因

浙公网安备 33010602011771号