澄清概念
js中 基于对象 == js 面向对象
js 中没有类class,但是它取了一个新的名字叫 原型对象,因此 类==原型对象 为什么需要面向对象 引出一个问题:
张老头养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄颜色。如果用户输入的小猫名字错误,则显示张老头没有这只猫猫。 猫有三个属性:名字,年龄,颜色
在没有学习面向对象之前,定义变量,用传统的方法去解决。
//张老太养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。
//..解决方法,把猫的属性集中,创建一种新的数据类(原型对象 /类) //用面向对象的方法来解决上面的问题 //这里就是一个Cat类 function Cat(){
}//如果你这样用 //Cat();//函数.
var cat1=new Cat();//类
//这是cat1就是一个对象(实例) cat1.name=\"小白\"; cat1.age=3;
cat1.color=\"白色\";
//从上面的代码我们可以看出
//1.js中的对象的属性可以动态的添加 //2.属性没有限制
类(原型对象)和对象(实例)的区别和联系 通过上面的案例和讲解我们可以看出:
①类(原型对象)是抽象的,概念的,代表一类事物,比如人,猫... ②对象是具体的,实际的,代表一个具体事物
③类(原型对象)是对象实例的模版,对象实例是类的一个个体
类(原型对象)—如何自定义类(原型对象)和对象 1.工厂方法—使用new Object创建对象并添加相关属性 2.使用构造函数来定义类(原型对象) 3.使用prototype
4.构造函数及原型混合方式 5.动态原型方式
目前先讲2.使用构造函数来定义类(原型对象),然后再创建对象实例 基本语法:
function 类名/原型对象名() 创建对象:
var 对象名=new 类名(); ★对象—特别说明
★★1.在js中一切都是对象★★ function Person(){} var a=new Person();
window.alert(a.constructor); //a 对象实例的构造函数 window.alert(typeof a); //a 的类型是什么 var b=\"1234\";
window.alert(b.constructor); //b 对象实例的构造函数 window.alert(typeof b);//b 的类型是什么 var c=1234;
window.alert(c.constructor); //b 对象实例的构造函数 window.alert(typeof c);//b 的类型是什么
//思考: 如何判断一个对象实例是不是Person类型 ? //window.alert(a);
if(a instanceof Person){ window.alert(\"a是Person ok1\"); }
if(a.constructor==Person){ window.alert(\"a是Person ok2\"); }
补充说明:带var和不带var的区别 //全局变量 var abc=89; function test(){ //在函数里,如果你不带var,则表示使用全局的abc变量 //如果你带上var则表示在test()中,定义一个新的函数变量 abc=900; }
test();
window.alert(abc);
访问(使用)对象实例的成员变量 (1)普通方式 对象实例名.属性名; (2)动态访问
对象实例名[\"属性名\"]; function Person(){ }
var p1=new Person(); p1.name=\"顺平\";
window.alert(p1.name); window.alert(p1[\"name\"]); var val=\"na\"+\"me\"; window.alert(p1[val]); 加深理解
为了让大家加深印象,我们定义一个人类(Person)(包括名字,年龄) 我们看看下面的一段代码 function Person(){}; var a=new Person(); a.age=10;
a.name=\"小明\"; var b=a;
b.name=\"小白\";
window.alert(b.age);
window.alert(b.age+\"名字\"+b.name+\"名字\"+a.name); b=null;
window.alert(a.age+\"a名字\"+a.name); window.alert(b.age+\"b名字\"+b.name); a=null;
内存分析,js引擎中也有堆和栈的概念,对象的引用传递
var a=new Person();// new Person()是一个真正的数据,a是指向它的一个引用
深入理解:什么时候才会把堆里的内容回收了呢
对象回收机制(GC),垃圾回收(GC)是怎样确定堆里的内容是垃圾的呢,内存被js引擎管理,js引擎是浏览器的一部分,js引擎维护了一张表
在上图中,
(1)当执行①后,被引用的次数计为1; (2)当执行②后,被引用的次数计为2;
(3)垃圾回收机制(GC)会扫描这张表,发现不能这个堆0x1234不能被回收,已经有两个引用了。 (4)当执行③后,则b被置空,b就不指向堆0x1234了,则(2)断掉,同时会把堆0x1234的被引用次数由2变为1;如果再执行window.alert(b.age);就会报错了,b已经不指向堆0x1234了。
(5)当执行④后,则a被置空,a就不指向堆0x1234了,则(1)断掉,同时会把堆0想234的被引用次数由1变为0,此时此刻堆0x1234里的数据就变为垃圾了,垃圾回收机制(GC)一扫描这个维护表,发现堆0x1234的被引用次数变为0了,立马就把它清掉。 js还提供一种方式:主动释放对象内存
主动删除:delete a.age;直接把age属性删掉了,即使b没有置空,b.age也访问不到了。 delete直接删除掉某个对象的属性
delete 对象名.属性;//这样就会立即释放 对象的这个属性空间 思考题:
在明白对象是如何在内存中存在后,请看下面的题,请问会输出什么信息? var a=new person(); a.age=10; a.name=\"小明\"; var b=a;
document.write(b.name); // 输出小明 b.age=200;
document.write(a.age); // 输出200 this—问题提出
请大家先看一段代码: function Person(){ }
var p1=new Person(); p1.name=\"顺平\"; p1.age=60;
window.alert(p1.name+\" \"+p1.age); var p2=new Person();
window.alert(p2.name);//这里会输出什么?(undefine)
这里我们可以看到window.alert(p2.name);会输出 (undefine)
在实际编程中,我们可能有这样的需求,当我们创建一个对象后,就希望该对象自动的拥有某些属性(比如:当我们创建一个Person对象后,就希望该对象自动拥有name和age属性,这又怎么办?) 使用this来解决: function Person(){ this.name=”abc”; this.age=90; }
var p1=new Person(); var p2=new Person(); p2.name=\"顺平\";
window.alert(p1.name+””+p2.name); 内存图分析
可能有人会这样想问题: //***有些同学可能会这样想: function Person(){
var name=\"abc\"; //私有的,只能在内部使用 var age=900; //私有的,只能在内部使用
this.name2=\"abc2\";//this.name2表示name2这个属性是公开的 this.show=function (){ //这个就是Person类的一个公开的方法
//如果你一定要访问私有属性,则需要定义一个公开方法(特权方法)
window.alert(\"name =\"+name+\" age\"+age);//如果是访问 this.name 属性值,可以通过 window.alert(this.name来访问); show2(); }
function show2(){//这是Person类的一个私有方法,只能在Person类中使用 window.alert(\"show2()\"+name+\" \"+age); } }
var p1=new Person();
window.alert(p1.name+\" \"+p1.age);//错误
window.alert(p1.name+\" \"+p1.age+p1.name2); p1.show();//OK p1.show2(); ★★★★
应该这么去想:test1这个函数,从面向对象的角度看,它应该是属于某个对象的,它就是属于window的。 alert(\"ok\");
window.alert(\"ok\");这两个是一个意思,alert这个函数默认就是window对象。 我们过去调用函数是这样写
test1(); 这个就是前面没有写window而已,它和下面的是一样的。 window.test1();
function Person(){ this.abc=function(){ window.alert(this.v); } }
var p=new Person(); p.v=\"hello\"; p.abc(); 内存分析图
★★★★
在上图中,刚开始创建的时候堆0x1234中什么都没有,然后p.v=\"hello\";,就在堆0x1234中放入了数据,紧接着p.abc();调用函数,调用函数就开辟新栈了,在新栈里面this就被传进去了,此时这个this实际上就等价于p,p是对象,实际上传了地址,this就指向了ox1234,this.v,this是个地址就到堆中寻找,则hello就被输出了。在内存图中分析,就能理解的透彻了。谁调用这个函数,谁就是这个this 记住一句话:
哪个对象实例调用this所在的函数,那么this就代表哪个对象实例. 注意事项
this不能在类定义的外部使用,只能在类定义的方法中使用。在类定义的外部,调用者就变成window了
window.alert(this.v); //在类定义的外部这样使用,此时的this指的是window了,就变成另外一个对象了,它并会把p.v的hello输出,因为在类的外部这个this已经指的是window了
特别说明:
this.属性名 公开属性,可以通过 对象名.属性名 来直接访问。 var 属性名 私有属性,只能通过对象的内部函数来访问。
这里给大家举一个例说明,因为还没讲对象的函数,因此大家可能还不能很好的理解,这里只是给大家一个结论!(后面详细讲解) 对象——成员函数(方法)
比如:我们希望对象不但有属性,还希望他有行为。(行为在程序中要靠函数来体现) ①添加speak 函数,输出 我是一个好人
②添加jisuan 函数,可以计算从 1+..+1000的结果
③修改jisuan 函数,该方法可以接收一个数n,计算从 1+..+n 的结果 ④添加add 成员函数,可以计算两个数的和 function Person(name,age){ //这个就是使用传入的实际参数,去初始化属性 this.name=name; this.age=age; //输出自己的名字,这里this.show就是一个公开的函数,函数名show this.show=function(){ document.write(\"名字=\"+this.name); } //添加jisuan 函数,可以计算从 1+..+1000的结果 this.jisuan=function(){ var res=0; for(var i=1;i<=1000;i++){ res+=i; } return res; } //修改jisuan1 函数,该方法可以接收一个数n,计算从 1+..+n 的结果 this.jisuan1=function(n){ var res=0; for(var i=1;i<=n;i++){
res+=i; } return res; } //添加add 成员函数,可以计算两个数的和 this.add=function(num1,num2){ return num1+num2; } }
var p1=new Person(\"宋江\p1.show();
var p2=new Person(\"林冲\p2.show();
document.write(\"
res=\"+p1.jisuan());
document.write(\"
res1=\"+p1.jisuan1(100)); document.write(\"
res1=\"+p1.add(100,100)); 给一个对象添加(指定)函数的两种方式: 第一种:
function 类名(){ this.属性; }
var 对象名=new 类名(); function 函数名(){ //执行 }
对象名.属性名=函数名; //这样就相当于把 函数赋给 对象名.属性名,此时这个属性就表示一个函数了
对象名.属性名(); //调用 function Person(){ this.name=\"abc\"; this.age=900; }
function show1(){
window.alert(\"hello\"+this.name); }
var p1=new Person(); p1.abc=show1; p1.abc(); 第二种:
对象名.属性名=function 函数名(){ //代码 }
对象名.属性名(实际参数); //调用 function Person(){
this.name=\"abc\"; this.age=900; }
var p1=new Person(); p1.abc= function show1(){
window.alert(\"hello\"+this.name); };
p1.abc(); 第三种:
前面的几种方法有一个问题:那就每个对象独占函数代码,这样如果对象很多,则会影响效率,js设计者,给我们提供另一个方法 原型法:这样多个对象可以共享函数代码,代码: function Dog(){ }
//使用prototype[类]去绑定一个函数给shout Dog.prototype.shout=function(){ window.alert('小狗'); }
var dog1=new Dog(); dog1.shout();
var dog2=new Dog(); dog2.shout();//这里ok 对代码的原理说明
★★★★★从上面可以更加深刻的认识,this,谁调用的this。
给所有对象添加(指定)函数 ①
[html] view plaincopy
1. function Person(){ 2. this.name=\"abc\"; 3. this.age=900;
4. this.abc=function(v1,v2){
5. window.alert(this.name+\" \"+this.age+\" \"+v1+\" \"+v2); 6. } 7. }
8. var p1=new Person();
9. p1.abc(); //输出 abc 900 undefined undefined;虽然没有传入参数,但是函数是可以调
用起来的,只是遇到 v1和v2会undefined
10. p1.abc (\"北京\天津\"); //输出 abc 900 北京 天津 11. var p2=new Person();
12. p2.abc(); //输出 abc 900 undefined undefined 13. p2.abc(\"南京\东京\"); //输出 abc 900 南京 东京 14. //p1 p2 每个对象创建的对象属性和函数是不一样的
②
看看下面的代码,会输出什么呢
[html] view plaincopy
1. function Person(){ 2. this.name=\"abc\"; 3. this.age=900;
4. this.abc=function(v1,v2){
5. window.alert(this.name+\" \"+this.age+\" \"+v1+\" \"+v2); 6. } 7. }
8. var p1=new Person(); 9. p1.name=\"中国\"; 10. p1.abc(\"北京\天津\"); 11. var p2=new Person(); 12. p2.abc(\"南京\东京\");
13. //这里主要是看p1.name会不会把this.name冲掉
demo6.html
[html] view plaincopy
1. 2.
3. 17. 18.
19.★★★★★再加深
再看使用另外一种方法来给类添加方法!
使用原型的方法来添加,这是第四种了。(前面已经讲解了三种了(1).在类定义的内部(2).外部添加如上面的:p1.abc=show1;(3).外部添加如上面的:p1.abc=function show1(){}) 此时就讲到了麻烦事,如在类中定义,this.show=function(){};这种定义方法,在有些时候,老板或项目经理会对你不满意,因为你每创建一个p1,p2,p3...每一个都会有这个show属性,就相当于每个对象实例都有this.show=function(){};这段代码。
内存分析图
如果我们对象很多,就不是件好事,因为每一个对象实例都带有这段代码。 prototype是原型,prototype上绑定的所有都是共享的,独一份的。
结论:前面的三种方法有一个问题:那就是每个对象,独占函数代码,这样如果对象很多,则会影响效率,js设计者,给我们提供了另一个方法 原型法 prototype
补讲:==号的作用
①当 ==的两边都是字符串的时候,则比较内容 相等否。 ②如 ==的两边是数字,则数的大小是否相等
③如 ==的两边是对象 或者是对象函数,则比较地址是否相通。
因篇幅问题不能全部显示,请点此查看更多更全内容