面向对象 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];
}
}