JS之面向对象

来源:互联网 发布:需要文章源码怎么写 编辑:程序博客网 时间:2024/06/05 03:32

Javascript里边,没有类的概念,可以直接创建一个对象(或通过函数)出来,对象可以有默认成员,后期也可以给对象丰富成员出来。
在js中,所有的内容的对象,一切皆是对象。
Js本身就是多态的,弱类型的

全面理解对象

对象,属性的无序集合。
{}的方式来表示对象。

{属性名: 值,
属性名:值,}
除了属性名和属性值之外,还有一个属性特征

属性的特征
主要包括可修改、可枚举、可删除三个。
Object.defineProperty(cat,”gender”,{
value : “female”, //默认值为undefined
writable : false, //writable表示是否能使用赋值运算符修改其值,默认为true
enumerable : true, //enumerable表示是否可用枚举该属性,通常是指for…in,默认true
configurable : false //configurable表示属性能否被删除,默认是true,并且不能将该值修改回去
});

使用{}定义对象的时候,writable、enumerable和configurable 都为true,也就是可以修改、可以枚举,可以删除。
如果使用defineProperty这个方法来创建对象属性的话,则可以控制器这些值。

<script>var obj={'name':'小环'}//给obj定义一个属性ageObject.defineProperty(obj,'age',{    value:5,    writable:false,    //意味不能重新赋值    enumerable:false,  //意味不能使用for...in来枚举    configurable:false   //意味不能删除他});console.log(obj);obj.age=50;      //不能修改console.log(obj);for(var p in obj)   {    console.log(p,obj[p]);    //不能枚举age}delete(obj.age);     //不能删除console.log(obj);</script>

注意:通常情况下,我们自定义对象的时候,是不用这么来操作的。但是js底层是这么干的。

创建对象

① 字面量方式创建,字面量方式定义对象,有一个专用名称—–json
var obj = {成员名称:值,名称:值,名称:值…};
② 构造函数创建对象
var obj = new 函数名称();
③ Object方式创建对象
var obj = new Object();

<script>//1.字面量方式创建//var obj={成员名称:值,成员名称:值}var cat={name:'kitty','color':'yellow',climb:function(){    console.log('在爬树!')} } console.log(cat.name);       //kitty console.log(cat['color']);   //yellow cat.climb();                 //在爬树!//可以创建一个空的,后期丰富成员var pig={}pig.color='black';pig['addr']='养殖场';pig.hobby=function(){    console.log('love eat!');}console.log(pig);//Objectaddr: "养殖场"color: "black"hobby: //()__proto__: Object//2.构造函数方式创建   var obj=new 函数名称();function Animal(n){    //通过this声明的成员会变成对象的默认成员    var age=5;    this.weight=130;    this.color-n;    this.act=function(){        console.log('跑步!');    }}var dog=new Animal('gray');dog.leg=4;dog['hobby']=function(){    console.log('看家');}console.log(dog);//object方式创建var wolf=new Object();wolf.eye='闪闪发光';console.log(wolf);</script>

{} 双括号表示对象
[] 中括号表示数组
“” 双引号内是属性或值
冒号表示后者是前者的值(这个值可以是字符串、数字、也可以是另一个数组或对象)
所以 {“name”: “Michael”} 可以理解为是一个包含name为Michael的对象
而[{“name”: “Michael”},{“name”: “Jerry”}]就表示包含两个对象的数组

new到底干了什么?
//实际上干了如下几件事情,
1.首先创建一个空对象
2.将this指针指向该对象
3.执行构造器中的代码,为该对象添加属性
4.设置该对象的constructor属性
5.返回该对象,如果显式返回this,效果相同,如果返回其它的值,则new的时候会返回该值。

对象在内存分配

与对象有关系的内存空间:
①. 栈空间
存放的数据大小比较小,一般大小固定的信息适合存放在该空间,例如 整型、boolean、对象的名称(引用)。

②. 堆空间
该空间存储的数据比较多,空间较大,一般数据长度不固定的信息在该空间存放,例如: string、Array、对象实体

③. 数据空间
该空间存放常量、类的静态属性

④. 代码空间
存放函数体代码、方法体代码

对象调用其他函数或其他对象成员

<script>var cat={mingzi:'猫',climb:function(){        console.log(this.mingzi+'在爬树!');    }}var tiger={mingzi:'老虎',act:function()   {        console.log('追动物');    }}//问题:tiger对象要去调用cat中climb方法//解决:为tiger增加一个名字,该名字是climb的引用tiger.pa=cat.climb;   //对象赋值,引用传递tiger.pa();      //老虎在爬树</script>

构造函数与普通函数

区别:
没有区别,就看使用,new就是构造函数,函数()就是普通函数调用。

函数的各种执行方式

① 普通函数调用(包括匿名函数自调用)

② 构造函数执行 new 函数

③ 作为对象的成员方法执行

④ 通过call或apply执行
函数/方法.call(函数内部this指引,实参,实参,实参。。。)
函数/方法.apply(函数内部this指引,[实参,实参,实参。。。])
call和apply使得函数执行的时候可以控制变量污染的风险。
(对象调用其他函数/方法的时候,无需为对象创建新成员)

那么下面介绍下call

<script>function f1(){    console.log('this is Thurday');}f1.call();var cat={mingzi:'波斯猫',climb:function(){        console.log(this.mingzi+'在爬树');    }}var tiger={mingzi:'东北虎',act:function(){        console.log('猛虎扑食');    }}cat.climb.call(tiger);    //东北虎在爬树</script>

获取构造器

构造器:使用什么元素实例化的对象,元素就称为该对象的构造器
var cat = new Animal();
Animal就称为cat对象的构造器。

对象.constructor; //获得构造器

<script>function Animal(){this.age=5;}var dog=new Animal();//获得dog的构造器console.log(dog.constructor);var f1=new Function("name","age",        "console.log(name+'---'+age)");f1('tom',24);    //tom---24</script>

封装性

在javascript里边,封装只体现public、private
构造函数内部声明的局部变量就是私有成员

对象封装,Js中,其实就是使用闭包。

<script>function counter(){    var cnt=0;   //私有属性    function getCnt(){return cnt;}    function setCnt(val){return val;}    return {getCnt:getCnt,setCnt:setCnt}}var f=counter();console.log(f.getCnt());      //0f.setCnt(100);console.log(f.getCnt());      //0</script>

这个代码的延伸—-模块模式

原型继承

/*特殊的__proto__在firefox/chrome中,我们可以通过一个特殊的变量,来访问对象的原型。__proto__*/

在实际的代码中,如何来操作这个原型呢?
此时,就应该使用prototype,但是请注意,它是构造器的(函数),不是对象的。

isPrototypeOf:用来判断是否是另一个对象的原型

<script>//在某些浏览器可以使用console.log(p1.__proto__.isPrototypeof(p1));//在代码中测试console.log(Person.isPrototypeof(p1));</script>

javascript里边:(构造)函数可以继承另外一个对象,构造函数实例化出来的对象除了拥有本身成员还拥有被继承对象的成员。

原型继承关键字:prototype。
函数.prototype.成员名称 = 值; //继承单一成员
函数.prototype = 对象; //继承一个对象
要在实例化对象前面继承

<script>var cat={mingzi:'kitty',climb:function(){        console.log('在爬树!');    }}function Tiger(){    this.color="yellow";    this.leg=4;    this.act=function(){        console.log('猛虎扑食!');    }}//函数继承对象(在实例化对象前面继承)Tiger.prototype=cat;//继承单一成员,后期实例化对象都有该成员Tiger.prototype.eat=function(){    console.log('吃一个动物!');}Tiger.prototype.age=5;var north=new Tiger();console.log(north);</script>

注意

对象 和 单一成员同时继承,需要先继承对象、再继承成员
多个对象 同时继承,最后对象起作用
③ 继承成员名称 与 本身成员名称一致,体现本身成员结果

多个对象同时继承,后者覆盖前者

<script>function Tiger(){    this.color="yellow";    this.leg=4;}var dog={hobby:'看家'};var pig={color:'black'};Tiger.prototype=dog;Tiger.prototype=pig;Tiger.prototype.addr='东北森林';   //先继承对象再继承成员var north=new Tiger();console.log(north);    //体现本身和pig成员</script>

对象访问成员的类型及优先级

对象访问成员类型一共四种,如下图,从上往下优先级依次提高:

<script>//对象访问成员类型和优先级function Cat(){    this.weapon = "伶牙俐齿"; //④继承对象构造函数成员}var kitty = new Cat();kitty.weapon = "走直线";     //③ 继承对象本身成员function Tiger(){       this.color = "yellow";       this.leg = 4;       this.act = function(){            console.log('猛虎扑食');        }    this.weapon = "嘶吼";    //② 构造函数成员}Tiger.prototype = kitty;    //继承对象var north = new Tiger();north.weapon = "大爪子";    //① 对象本身成员console.log(north.weapon);    //大爪子</script>

原型链

什么是原型链
构造函数 继承 一个对象
继承对象也有构造函数,其构造函数还去继承其他对象
其他对象也有构造函数,构造函数还去继承其他对象
其他对象也有构造函数,构造函数还去继承其他对象
如此反复。。。。。
形成了一个继承的链条,称为’原型链’,原型链的顶端是Object

通过原型链,可以使得对象访问到许多继承的成员

对象访问一个成员顺序

① 对象本身获取
② 对象构造函数里边获取
③ 构造函数继承对象本身获取
④ 构造函数继承对象的构造函数里边获取
构造函数继承对象构造函数继承对象的本身获取
构造函数继承对象构造函数继承对象的构造函数里边获取
。。。。。。
直到找到Object成员为止。
有的时候对象访问一个成员,我们本身并没有定义这个成员,其就会通过原型链找到顶端Object的成员进行访问。
例如constructor,hasOwnPrototype,isPrototypeOf等等都是原型链顶端Object的成员

对象遍历及成员属组判断

for(var 成员变量 in 对象){} //遍历对象
对象.hasOwnProperty(成员名称) 判断成员是否是本身的

<script>//遍历对象 及 成员属组判断function Tiger(){     this.color = "yellow";     this.leg = 4;     this.act = function(){             console.log('猛虎扑食');     }     this.weapon = "嘶吼";}Tiger.prototype = {addr:'beijing',weight:230};//继承对象var south = new Tiger();//遍历对象//for(var 成员变量 in  对象){}for(var k in south){//console.log(k+"--"+south[k]);//判断成员是本身的 还是 继承的//对象.hasOwnProperty(成员名称)判断成员是否是本身的,返回boolean     if(south.hasOwnProperty(k)){            console.log("自己的:"+k);       }else{         console.log('继承的:'+k);    }</script>

结果如下:
JS成员属性判断

原型模式缺点
所有的对象,都具备相同的属性。包括简单的属性。
会导致,如果对甲对象进行修改,乙对象也随之更改,这就有问题了。

混合模式

对于属性(简单的属性),采用构造器的方式
对于方法(复杂的属性),采用原型的方式
混合模式= 构造器模式 + 原型模式

<script>//混合模式function Person(name,age){    this.name=name;    this.age=age;}//原型的方式添加方法Person.prototype.say=function(){    return this.name+this.age;}//使用对象var p1=new Person('小强',7);var p2=new Person('小龙',8);console.log(p1.say());    //小强7console.log(p2.say());    //小龙8</script>

混合模式改进-寄生模式

<script>function Person(name,age)   { this.name=name; this.age=age; //做一个判断,如果Person.prototype.say没有定义,就需要定义一个     if(!(typeof Person.prototype.say==='function')){    //以原型的方式添加方法    Person.prototype.say=function(){        return this.name+this.age;    }}}//使用对象var p1=new Person('小强',7);var p2=new Person('小龙',8);console.log(p1.say());    //小强7console.log(p2.say());    //小龙8</script>

原型衍生继承-复制继承

特点(优势):
① 允许同时继承多个对象
② 非常灵活,可以根据实际需要为每个对象复制不同的成员
(之前的原型继承,继承成员无论是否使用都继承给每个对象)
jquery框架里边大量使用复制继承

对象.hasOwnProperty(参数) //判断对象有没有参数的成员

<script>//复制继承function Tiger(){    this.leg=4;    this.color="yellow";}Tiger.prototype.extend=function(obj){    //获得(遍历)obj的每个成员,赋予给当前对象this    //this代表调用的当前对象    for(var k in obj){        //复制继承成员,复制本身没有的成员        if(this.hasOwnProperty(k)===false){            this[k]=obj[k];        }    }}var south=new Tiger();var dog={mingzi:'旺财',age:6,color:'white'};var pig={hobby:'sleep睡觉'};south.extend(dog);      //south把dog成员复制一份south.extend(pig);      //south把pig成员复制一份console.log(south);     //本身成员,dog成员,pig成员</script>

多态

javascript体现多态:
① 在函数内部可以通过arguments关键字进行多态处理,传递个数不相同的参数,通过arguments判断,进而有不同的处理。
② this关键字可以体现多态。其可以任意代表其他对象(call和apply使用)。

参数管理器arguments
形参和实参个数不一样的时候没关系,实参少的话,没有给的形参就是undefined
arguments表示的是实参。可以通过arguments修改传递过来的实参。是一个数组

<script>function sum()   {    console.log(arguments); //[250, 38, 2]    var s=0;    arguments[0]=0;          //改变这个结果也会改变   for(var i=0,len=arguments.length;i<len;++i){            s += arguments[i];    }    return s;}var s1=sum(250,38,2);console.log(s1);          //40</script>

callee关键字
在函数内部使用,代表当前函数的引用(名字),使用arguments.callee(实参);
应用场景:递归,降低代码耦合性

<script>function f1()   {    arguments.callee();     //调用本函数,外面函数名改变,里面不用变}</script>

静态成员

如果一个类里边,有这样的属性,其值不随便发生变化,全部对象都共用一个值,该属性就声明为static静态的,其在内存中无论创建多少个对象(或者说其与具体对象无关)都只占据一份空间。

为什么使用静态成员
① 静态成员归属到类本身,可以明显的加快访问速度(不需要实例化对象,通过类进行访问)。
② 节省内存
一个类,每实例化一个对象,在堆空间都要给每个成员分配内存空间,如果有一个成员$country无论创建多少个对象其的信息值也不发生变化,为了节省内存空间的使用,就把这样的成员设置为“静态成员”。其在内存中就只占据一份内存空间。

在javascript里边如果一个成员是通过(构造)函数直接调用的,就称其为“静态成员”。

<script>function Animal(){    this.age=5;    this.leg=4;}//给Aniaml设置成员Animal.color='black';Animal.run=function(){    console.log('奔跑');}var dog=new Animal();//其中函数对象的成员(color/run)就是'静态成员',一个是静态属性,一个静态方法//两种对象有各自的访问成员console.log(Animal.color);      //blackAnimal.run();                   //奔跑console.log(dog.age);           //5console.log(dog.leg);           //4//注意这2种对象成员不能交叉访问console.log(dog.color);           //underfinedconsole.log(Animal.age);          //underfined</script>

补充

匿名函数自调用

(function(){})() 特点代码没有停顿,立即发生执行,其中里面可以有形参

<script>(function() {    console.log("匿名函数被调用");})();//匿名函数被调用(function(week) {   console.log("今天是"+week);})("星期天");//今天是星期天</script>

单体内置对象

在js中,有两个单体内置对象,
Math
Global

其中Global无法引用。真正要注意的是Math。
所谓单体内置,是指常驻内存的对象,js代码一旦进入执行状态,在内存中就有它们了。所以可以直接使用,根本不需要实例化的过程。

Math是一个数学对象,用于计算的。

常用的一些方法:
Ceil():向上取整
Floor():向下取整
Round():四舍五入
Random():随机,0~1之间的小数。

Global是全局对象,在js中,本身是无法看到(访问)的。
但是如果是在浏览器中使用的话,window就充当了global的角色,它表示全局对象。在nodejs中就可以访问global。

所有全局的内容都是global对象的属性。

所以,我们用的alert函数,setTimeout函数,定义的全局变量,都可以使用window来引用。

包装对象

首先看一个问题:
为什么,普通的字符串可以直接调用字符串函数。
为什么,个普通的字符串,添加属性是无效的。

<script>var s="javascript";console.log(typeof s);    //stringconsole.log(s);           //javascriptconsole.log(s.length);    //10s.name='js';console.log(s.name);      //underfined (思考为什么?)</script>

在js中,有一个包装对象的概念。
对于基本数据类型(数值、字符串、布尔),可以直接调用相应的方法。原因在于在真正调用方法的那一刻,对当前数据做了一个包装。

相当于执行了如下代码:

<script>var s="javascript";//在调用开始的那一刻,使用new得到一个新的对象s1=new String(s);//中间是对s1的操作//..............//调用完毕的那一刻,将这个对象销毁s1=null;</script>

注意两点:
它是一瞬间执行的
在整个过程中,我们是无法干涉的。

包装的过程,有时候称为 装箱 和 拆箱

在js中,基本数据类型都会有对应的包装类型.实际上也就三个:
字符串 String
数值 Number
布尔 Boolean

0 0
原创粉丝点击