什么是面向对象
面向对象就是程序中先用对象保存一个事务的属性和功能,再按需访问对象中的属性和功能
面向对象的三大特点:封装,继承,多态。
优点:便于大量数据的维护和使用。
1.封装
创建一个对象,集中保存一个事务的属性和功能。
3种方式:
①用{}直接量创建对象var 对象名 = { 属性名:属性值, ... : .., 方法名: function() { ... , ... } }
②用new Object():
1.先用new Objec();创建一个空对象;
2. 为空对象添加新属性。
js中一切对象的底层都是关联数组。var 对象名 = new Object(); 对象名.属性名 = 属性值; 对象名.方法名 = function() { ... }
③使用构造函数创建对象(反复创建多个相同结构的对象)
1.定义一个构造函数来描述一类对象统一的结构。
2.反复用new调用构造函数,反复创建相同类型的多个对象。
注意:调用构造函数创建对象必须使用new关键字,构造函数中的所有属性前都需要加’ .this ’
new做了4件事情
1.new先创建了一个新的空对象
2.新对象的__prot__属性继承构造函数的prototype属性
3.用新对象调用构造函数
·①:将构造函数中的所有this,零时指向当前正在创建的新对象
·②:通过强行赋值的方式,将构造函数内规定的属性和方法,强行添加new创建的新对象
4.返回本次创建的对象的地址,保存到等号左边的变量中function 构造函数名(形参1, 形参2...) { this.属性名 = 形参1; this.属性名 = 形参2; ... this.方法名 = function { ... } } var 对象名 = new 构造函数名(属性值1, 属性值2, ...);
对象的克隆
1.浅克隆
什么是浅克隆:只克隆第一级属性,如果某个属性又是一个内嵌的子对象,不会进入子对象种克隆子对象的内容。var student = { sname: '张三', sage: 12, intr: function() { console.log('姓名:' + this.sname + '年龄:' + this.sage ); } } // 自定义函数实现浅克隆对象 clone(oldObj) { // 判断传入数据是否为null,为null则退出函数 if(oldObj === null) { return oldObj; } // 判断传入数据是否为‘object’,不是则返回该数据并退出函数 if(typeOf(oldObj) !== 'object') { return oldObj; } // 判断传入的是数组还是对象,是对象则创建一个空对象({}),是数组则创建一个空数组([]) var newObj = Array.isArray(oldObj) ? [] : {}; // 遍历对象(数组),并把原对象(数组)的值分别对应放入新的对象(数组)中 for (var key in oldObj) { newObj[key] = oldObj[key]; } return newObj; } var students = clone(student); console.log(student === students); // 返回false,表示浅克隆成功
2.深克隆
什么是深克隆:即克隆第一级属性,如果某个属性又是一个内嵌的子对象,深克隆会在进入子对象中,继续克隆内嵌子对象的内容。
①递归思路实现深克隆clone(oldObj) { // 判断传入数据是否为null,为null则退出函数 if(oldObj === null) { return oldObj; } // 判断传入数据是否为‘object’,不是则返回该数据并退出函数 if(typeOf(oldObj) !== 'object') { return oldObj; } // 判断传入的是数组还是对象,是对象则创建一个空对象({}),是数组则创建一个空数组([]) var newObj = Array.isArray(oldObj) ? [] : {}; // 遍历对象(数组),并调用函数把对应的值放入新的对象(数组)中 for (var key in oldObj) { newObj[key] = clone(oldObj[key]); } return newObj; } var student = { name: ['tom', 'jim', 'lucy'], class: '一年级', phone: { tom: '123568', jim: '456895', lucy: '589625' } } var ynStudent = clone(student); console.log(student === ynStudent); // false console.log(student.name === ynStudent.namee); // false console.log(student.phone === ynStudent.phne); // false
②JSON方法
var obj1 = { age: 11, name: 'tom' } var obj2 = JSON.parse(JSON.stringify(obj1)) console.log(obj1 === obj2); // false
③jQuery的extend
$.extend( true, object1, object2 ); // 深度拷贝 $.extend( object1, object2 ); // 浅拷贝
④lodash的cloneDeep()
var obj1 = { name: 'tom', age: 11 } var obj2 = lodash.cloneDeep(obj1); console.log(obj1 === obj 2); // false
判断对象是否是数组
1.使用对象的原型对象__proto__跟数组的原型对象Array.prototype对比
var arr= [1, 2, 3]; var obj = {name: 'jim'}; // ①直接使用对象的__proto__ console.log(arr.__proto__ == Array.prototype); // true console.log(obj.__proro__ == Array.prototype); // false ②使用Object.getPrototypeOf()方法获取对象的__proto__ console.log(Object.getPrototypeOf(arr) == Array.prototype); // true console.log(Object.getPrototypeOf(obj) == Array.prototype); // false ③使用Array.prototype.isPrototype()判断 console.log(Array.prototype.isPrototype(arr)); // true console.log(Array.prototype.isPrototype(obj)); // false
2.使用constructor判断
var arr = [1, 2]; var obj = {name: 'jim'}; // ① 直接使用constructor console.log(arr.constructor == Array); // true console.log(obj.constructor == Array); // false ② 使用instanceof判断 console.log(arr instanceof Array); // true console.log(objinstanceof Array); // false
3.使用es6的isArray方法
var arr = [1]; var obj = {age: 1}; console.log(Array.isArray(arr));// true console.log(Array.isArray(obj));// false
2.继承
如果将方法定义在构造函数内,那么每次创建一个新对象,都会财富创建相同的方法副本,会浪费内存,所以,当多个子对象,都要使用一个共同的功能是,都需要用继承的方式来实现。
什么是继承?
父对象中的成员,子对象无需重复创建,可用直接使用,js中都是使用继承原型对象的方式来实现继承的。
什么是原型对象(prototype)?
每种类型中,替将来所有的子对象集中保存共有成员的父对象,只要保存在原型对象中的成员,所有子对象无需重复创建即可使用。
1.原型对象无需我们创建,只要我们定义构造函数,就会自动创建一个原型对象。
2.每个构造函数都自带一个隐藏的prototype属性,指向当前构造函数的原型对象。
3.只能使用强行赋值,为构造函数添加共有成员。构造函数.prototypr.共有成员 = 新值/function() { … }何时继承
1.无需手工设置继承。
2.当用new调用构造函数,创建该类型的新对象是,new的第二步就会自动设置新对象的__proto__隐藏属性指向该构造函数的原型对象,形成继承关系。
3.js规定,只要是从__proto__之处的引用关系,就是继承关系。
4.将来使用子对象访问一个成员时:就近原则,先自己后原型。>function 构造函数名(形参1, 形参2...) { this.属性名 = 形参1; this.属性名 = 形参2; ... } 构造函数名.prototype.方法名 = function() { ... } var 对象名 = new 构造函数名(属性值1, 属性值2, ...); // 调用父对象的方法 对象名.方法名();
利用原型对象为某种类型的所有子对象添加新共有方法
ES标准中的11种内置类型/对象:String、Number、Boolean、Array、Date、RegExp、Math、Error、Function、Object、global。其中除Math和global之外的9种内置类型都包含构造函数和原型对象。
// 所有数组类型添加求和方法 Array.prototype.sum = function() { let total = 0; for (let i = 0; i < this.length; i ++) { total += this.[i]; } return total; } var arr = [1, 2, 3]; console.log(arr.sum()); // 返回 6
自有属性和共有属性
1.自由属性:保存在对象内部,只归当前子对象独有所有。
2.共有属性:保存在原型对象中,归多个子对象共有的属性。
相同点:获取/调用属性的方式完全一样(子对象.属性名)。
不同点:
(1)修改属性值时:①修改子对象中的自有属性:子对象.属性名 = 新值;
(2)修改原型对象中的共有属性:子对象.prototype.属性名 = 新值;
(3)如果使用‘子对象.共有属性名 = 新值’来修改共有属性,则js会自动在子对象中创建一个与原型对象的属性同名的属性,从此,当前子对象的这个共有属性与其他同类型的共有属性将不再保持一致。原型链
由多级父对象逐级继承,形成的链式结构,保存着一个对象可用的所有属性和方法,控制着该对象的属性和方法的使用顺序:就近原则(先自由,后沿原型链向父级查找)。
注意:js只有唯一的顶级父对象Object。
3.多态
1.什么是多态?
同一个函数在不同情况下表现除不同的状态,从父对象继承的东西不一定都是有用的,如果继承的东西没有用,则可以在对象内部自定义一个同名的成员,覆盖父对象中的成员。
2.多态的表现形式
1.重写(override):在子对象中定义一个和父对象中同名的成员,从而覆盖父对象中的同名成员。
// 以对象的toString()方法举例,所有引用类型的顶级父对象都是Object,所以它们都有toString()方法。 var obj = {name: 'jim'}; var arr = [1,2]; var date = new Date(); console.log(obj.toString()); // 输出 [object Object] console.log(arr.toString()); // 输出 '1,2' console.log(obj.toString()); // 输出 "Thu Mar 25 2021 21:45:43 GMT+0800 (中国标准时间)"
自定义继承
设置子对象的原型(__proto__属性)指向新的父对象。
①直接使用子对象的__proto__指向新的父对象;
②使用子对象的构造函数的prototype属性指向新的父对象;注意:使用此方法,应该在创建子对象之前修改,否则,先创建的子对象继承的还是原来的父对象,只有在修改后的子对象才会继承新对象