面向对象 [记录]
面向对象 VS 面向过程
过程:流程式写法 - 到哪步该干啥
对象:所有程序执行都是从对象出发
[] {} null
var a = new Image() | object()
对象拥有属性和方法
啥时候用|好处:(功能|方法)
01 所有功能都从对象出发以达到一个对象实现某种功能的效果
(比如JQ都是从对象出发 JS,JQ互不污染)
02 面向对象可支持扩展(继承)
var a = new object(); a.name='XXX'; a.show = function(){}; function peo(){return obj;} peo('xxx') --> 里面this指window(peo内部) new peo('xxx') --> 在函数执行前+new 01 在函数内部自然会产生一个对象并this指向这个对象 02 函数默认返回产生的对象 this指向 function a(){alert(this);} var k = a(); --> window --> k = undefined var k = new a(); --> obj --> k = obj (默认返回) // new a() 代码相当于 // var obj = {}; // alert(this); // return obj; // 相当于多了2条 不用写 默认return 构造函数 function peo(k){ this.name = k; this.fn = function(){}; }
工厂模式 (构造函数就是工厂模式 | 工厂=加工东西) = 简化后的构造函数 (默认要有原料 默认要出工厂) 原料(var obj=new object()) --> 加工(obj.name='xxx') --> 出厂(return obj)
通常构造函数第一个字母大写 Peo{a=10;} obj1.fn == obj2.fn --> false --> 对象所属对象都不一样 老李家的狗和老王家的狗不一样,obj1和obj2是不同的 对象的赋值和引用 obj1.a == obj2.a --> true !!!!! 比较的时候 对象和函数作比较时 不仅仅要内容一样还要存储位置|地址都要一样 数字,字符串,bool比较 只要内容一样 就好了 new一个对象就要开辟空间去存储
原型 prototype 不用让一样的东西总是去开辟空间存储 只有构造函数有原型(一个函数是不是构造函数是看如何调用的 用new去调用的就是) Peo.prototype.fn = function(){} 公有写原型中 此时 obj1.fn == obj2.fn --> true !!!!! (公有) 私有属性写构造函数里,公共属性写prototype里面,原型的方法里面的this指向的是你执行的时候的对象 谁去执行this就指向谁(obj1执行就指向obj1,obj2执行就指向obj2) 原型好处:节约内存空间 但 this.name 不共享 function Peo(name){this.name = name;} <-- 私有 Peo.prototype.fn = function(){} <-- 公有
方法链 $('#box').css({}).click().animation() --> 这个就是 function Peo(name,age){ this.name = name; this.age = age; } Peo.prototype.fn1 = function(){!!!重点 return this;} Peo.prototype.fn2 = function(){!!!重点 return this;} p1 = new Peo('xxx',22); p1.fn1().fn2(); 需要p1.fn1()是一个对象 那么fn1和fn2里面 return this
包装对象 对于非对象的数据类型是由包装对象产生的 是对象的: 数组 var arr = new Array(); var arr = [1,2,3,4]; arr.push <-- push 在构造函数原型中(prototype中) 那么 Array.prototype.push = function(){} 那么push就被重构了,没了 写类似push的方法 function(){ var argu = arguments; for (var i = 0; i < argu.length; i++) { this[this.length]=argu[i]; } } 非对象: 字符串 对象才能.点 | 对象才能.方法 var str = 'qwer' str.charAt(2); str拥有string函数--包装对象 string.prototype.charAt = function(){} Array 和 String 差别: string只有.点方法才出来 之后消失 +自定义属性 str.number = 10; alert(str.number) --> undefined 解释:因为它不是个对象 去原型prototype找,因为他不是个对象, 那么去原型找就会产生一个对象(产生一个叫包装对象的东西),number=10, 和string相关联去访问 string.prototype后对象销毁 每次产生新的包装对象 str.number --> 产生对象 number=10 -指向-> string.prototype --> 对象死亡 number 不是string的 | 执行完包装对象立马死亡 alert(str.number) str.number --> 产生对象(包装对象) --> string.prototype 这里两个包装对象不相等 给非对象.点number(不存在) --> 就会产生一个包装对象来执行.点操作 --> 操作完之后对象立马死亡 --> alert又会产生一个包装对象 --> 此时你给上一个包装对象赋值,这个对象拿不到赋值,2个对象不一样 上一个对象死亡,用完一次死一次,临时的
原型链 _proto_ 对象和他的构造函数的关系 构造函数a(它是有原型链的a.prototype) --> 创建出对象objA对象 说的是 a.prototype 和 objA 之间的关系 function A(n){this.n=5;} A.prototype.n=10; (!!! A.prototype 是个对象 ) 此时 alert(a1.n) = 5 按原型链去找 --> 自己有n属性吗?有 有了之后就不会再去找 --> 没有则就去构造函数找 --> A.prototype 是个对象 对象有构造函数(假设B) -- 构造函数有原型 [A]构造函数 ---创建出(new)---> objA | [A]有原型 [A.prototype] *** 和objA有关系 *** | [A.prototype]是个对象能.点 | 是对象就有构造函数 假设B [B] | [B.prototype] *** 和 A.prototype有关系 *** | [...] | [object] 一直到祖宗 到根 | [object.prototype] 往下找 一直找到obj都没有则 undefined 如:object.prototype.number = 10; objA --> A.prototype --> B.prototype --> ... --> object.prototype
原型链默认属性 对象原型里面本来就有默认的属性和方法 hasOwnProperty是不是对象自己的属性 返回bool 在原型里的则false 在A(){里面} 它自己本身(即私有的) 如:arr push:false | length: true constructor 属性:值是构造函数 当一个new一个对象的时候,原型默认带有 instanceof 判断构造函数是否一样 function A(){} 方法一:(正确) A.prototype.n = 10; A.prototype.fn = function(){} 方法二: A.prototype = { n: 10, fn = function(){} } 1和2一样的 但是2这么写把原型默认属性覆盖了 new OA OA.hasOwnProperty 是不是对象私有属性 OA.constructor 相当于 A.prototype.constructor = A 弹出它的原型 instanceof 判断对象1的构造函数是不是构造函数1 对象 instanceof 对象 OA instanceof A
继承 儿子继承父亲,用父亲的方法和属性 儿子的改变不会影响父亲 call继承私有属性 function Peo(n){this.n=n;} Peo.prototype.fn1 = function(){alert(this.n);} function PeoAge(n,a){this.a=a;} 通过继承创建一个子类 现在不知道peo里面所有属性 但是我要继承 function PeoAge(n,a){ Peo.call(this,n);(继承|改变了this的指向) 或者把Peo执行一遍 Peo.call(n) 一执行PeoAge就执行了 this.a=a;} Peo(a1,a2,a3) 拓展成 PeoChild(a1,a2,a3,b1) 多了个b1 --> 则把Peo执行一遍 改变了this的指向 --> 抛开一切执行 Peo() this指向window 没有new 自己去找行为window --> this指向改为自己的this 那么Peo.call(this) 那么Peo里的this指向此时call里面的this 此时的this为op(创建出来的对象) var op = new PeoChild(a1,a2,a3,b1) op因为返回的就是this Peo.call(this,+参数(a1,a2,a3)) this,a1,a2,a3 或 var op = new PeoChild([a1,a2,a3],b1) [这里是父亲要的参数] Peo.call 改为 Peo.apply(this,arr); call参数要一个个摆着 apply 参数只要一个数组(放数组里) 要把父亲的原型也继承下来 那么 PeoChild.prototype = Peo.prototype [X] 错误!!! = 为引用 引用关系 儿子的改变不影响父亲 此时 PeoChild.prototype.fn2 = ... 就会影响父亲原型 两边都是对象 = 的时候就是引用 一旦引用 子新增父也会跟着改 那么直接= 为引用 选择不直接= 第一种Clone: 构建个新的对象 把一个对象完全克隆 PeoChild.prototype = new clone(Peo.prototype) 第二种继承: PeoChild.prototype(对象) = Peo.prototype(原型) 对象本身的属性改变能否改变或影响其构造函数原型属性的改变 ---- 不会!!! var a = new Peo() a.x=5; a.fn 也能读取原型属性 那么 --> 中间介质 function Fn(){} --> Fn.prototype = Peo.prototype --> PeoChild.prototype = new Fn(); --> 自身无找原型 var obj = new PeoChild(); --> obj.showName(); --> showName:PeoChild无 找原型 PeoChild.prototype(他是Fn) 无 把Fn它是 Peo.prototype Clone: 对象克隆 var a={aa:10,bb:20,cc:30} 现在要b拥有a所有属性 但b改变不影响a var b=clone(a) function clone(obj){ 代码可以改成new clone 前后去掉即可 + 改this var newObj=new object()/={}; for (var k in obj) { newObj[k]=obj[k]; 这句全是引用,赋值 } return newObj; } 此时 bb.dd=50 alert(a.dd) ----- undefined 不影响了 那么此时a里面有个 qqq:{a:'a',b:'b'} 对象 .clone之后 b.qqq.c='c' alert(a.qqq.c) 会弹出c!!!! clone里面循环一旦读到qqq是一个对象 那么右边是对象了 又变成引用了 不是赋值 要保证 = 是赋值 不管多少层下去 那么只要是个对象就要进行克隆 function clone(obj){ for (var k in obj) { this[k]=obj[k]; obj[k]!!!要进行递归 遇到对象就要进行克隆 } for改为 for (var k in obj) { if(typeof obj[k] == 'object'){ this[k]=clone(obj[k]); } this[k]=obj[k]; } } 递归: 4!=4*3*2*1 拆分为 4!=4*3! 3!=3*2! 2!=2*1! ... 递 ---- 归:到某一点归 x==1 的时候直接返回1 function f(x){ if(x==1){return 1;} return x*f(x-1); } Clone fn: function clone(obj){ for (var k in obj) { this[k]=(typeof obj[k] == 'object')?new clone(obj[k]):obj[k]; } }