javaScript面向对象

/**

 * @authors xubowen ([email protected])

 * @date    2015-04-13 16:31:00

 */

//新建一个类

var  o = new Object();

//添加属性和方法

o.name = "xubowen";

o.age = "20";

o.toString = function(){

    return o.name+","+o.age;

};

//以上 一个简单的对象Apa

//但是看起来很别扭 还可以这样 JSON对象形式

var person = {

    name:"xubowen",

    age:20,

    toString:function(){

        return this.name+","+this.age;

    }//最后一个属性|方法结束的地方什么符号都不要放

};

/**

 * 对象属性的操作

 * 设置只读、不能删除、不可枚举

 * Writable Configurable Enumerable

 */

Object.defineProperty(person,"name",{

        writable:false;//属性不可写 默认true

        configurable:false;//属性不可删除 默认true

        enumerable:false;//属性不可被枚举 默认true

});

/**

 *js的工厂模式

 * 利用Object类自定义属性

 * 缺点是 instance of 不能判断person类

 */

function person(name,age,address){

    var o = new Object();

    o.name=name;

    o.age=age;

    o.address=address;

    o.say=function(){

        return this.name+","+this.age+","+this.address;

    };

}

/**

 * 构造器模式

 *  constructor

 *  按照JAVA的命名方式

 */

function Person(_name,_age,_address){

    this.name = _name ;

    this.age = _age;

    this.address = _address ;

    this.say = function(){

        return this.name+","+this.age+","+this.address;

    };//json式申明此处不能加任何符号 而属性式申明按照正常习惯

}

var p1 = new Person("xu","20","无锡国家软件园Ipark");

var p2 = new Person("liu","21","无锡国家软件园Kpark");

//注意几个点

p1.constructor==Person;//true

p2.constructor==Person;//true  拥有共同的构造器

p1 instanceof Object ;//true

p1 instanceof Person ;//true 所有的类的基类都是Object类

//也可以不用new关键字 作为普通的function 使用

Person("zs","25","wuxi");//这样原函数中的this指向了window,因此相当于给window加了属性和方法

window.name;//zs

/**

 *再新建另一个类 加深对this的理解

 */

function Student(_name,_age,_address){

    //将当前student对象传给Person,那么Person中的this 就是指向 student的实例

    Person.apply(this,arguments);

    this.girlFriend = "Salinna";

}

//创建一个Student实例

var s = new Student("stuName",20,"北京昌平区胡同");

//该s实例具有Person的一切自定义属性和方法 也具有本身的girlFriend属性


/**

 * 对象原形prototype

 * 每个对象都有prototype属性 而且该对象的原形都是指向Object 因此每个对象才会有object的属性和方法

 */

function People(){


}

Object.getPrototypeOf(p);//获取p实例的对象原形->Person(){}==Person.prototype

//对象原形是所有该类的实例所共有的

People.prototype = {

    name:"xubowen",

    age:20;

    address:"江西鹰潭"

};

//给Person类自定对象原形属性 上面其实等价于一个拥有三个属性的匿名类

//然后我们在new Person

var p = new People();

console.info(p.name,p.age,p.address);//xubowen,20,江西鹰潭

var p1 = new People();

    p1.name="liu";

console.info(p.name,p.age,p.address);//liu,20,江西鹰潭

/**

 * 不对prototype进行改变的话prototype默认就是原来的Person(){}

 */

//上面的例子是People没有自己的新属性的例子 然后看下面

function People(_name,_age){ this.name = _name;

    this.age = _age;

}

//然后重写对象原形

People.prototype = {

    name:"people",

    age:20

};

var p = new People("xu",25);

/**

 * 这里new 的时候 会执行构造器中的赋值

 * 会将People.prototype中的属性的值覆盖掉

 * 如果new的时候不传值 new People();也是如此 那么被覆盖之后就是undefined了。

 */

var p1 = new People();

console.log(p1);//属性的值全是undefined

/**

 * 对象原形所携带的参数不属于本身的属性

 * 但是自身对原型属性进行覆盖 属性成了自身的属性了

 * 利用p1.hasOwnProperty("name");来判断是否是自身属性

 */

/**

 * Object.keys();方法

 *  取出所有可枚举的key

 *  Object.prototype中默认都是不可枚举的 只有自定义的才可以遍历

 *   如果自定义的属性覆盖了原自带的属性 则会变成可枚举 Enumerable

 */

for(var key in p){

    console.log(key);

};

/**

 * 小技巧

 *  var arr = [];

 *   var p = new Person("xubwe",20,"无锡");

 *   for(arr[arr.length] in p) ;//获得Person的属性

 */

var k = Object.keys(Person.prototype);//获得Person类中可枚举的key keys|key还是关键字。。不能当变量

/**

 * 再来看 prototype

 */

function Person(){//一个类|函数

}

//此时

var p = new Person();

p instanceof Person;//true

p.constructor==Person;//true

//重写prototype

Person.prototype = {

    name:"person"

};

//此时

var p = new Person();

p instanceof Person;//true

p.constructor==Person;//false 此时的constructor 是Object 因为重写了prototype

//重写时也重写constructor

Person.prototype={

    constructor:Person,//将构造器指向Person

    name:"xubowen"

};

//此时

var p = new Person();

p instanceof Person;//true

p.constructor==Person;//true

//来个完整带属性的

//默认的Person都带有俩个有值的属性

function Person(){

    this.name = "xubowen";

    this.age = "25";

}

//重写原形 增加属性

Person.prototype = {

    constructor:Person,//还是指向Person

    address:"北京",

    money:2000

};

var p = new Person();

console.log(p);//{name: "xubowen", age: "25", constructor: function, address: "北京", money: 2000}

//这样都带有了Person的属性和方法 对  这就是一种对象继承。

//另外我们一般不让constructor可遍历 来修改属性的访问

Object.defineProperty(person.prototype,"constructor",{

    enumerable:false,

    value:Person

});

/**

 * 对于原生对象的原形我们也可以修改或者覆盖起实现方法,当然自定义更是可以

 * 我们对Object的原形新加一个方法和属性

 */

Object.prototype.say=function(){

    return "what's up?";

};

Object.prototype.myname="xubowen";

//我们给Object新增了一个属性和方法

var o = new Object();

o.say();//what's up?

function Student(){


}

var s = new Student();

s.say();//what's up? 这就是明显的继承了Object类

"say" in s;//true 说在s实例中有这个字段

p.hasOwnProperty("say");//fasle 说明这个属性不是本身的

/**

 * 对象原形中的属性是所有引用该对象原形实例所共有的 一旦一个实例改变了原形中的值 其他的也会改变

 * 其实就是每个实例中的原形对象都指向了公共继承的那个 是一种指针 并不是本身所含有的东西

 * 因从我需要把基本属性和公共方法分离开

 */

/****************************************一个重点**********************************************************/

function Person(name,age,address){

    this.name = name;

    this.age = age;

    this.address = address;

}

Person.prototype={

    //每个对象都是有一个length属性,重写prototype之后和constructor一样也要主动申明改造

    say:function(){

        return this.name;

    },

    //一个类似JAVA的equals方法

    equals:function(obj){

        if(obj instanceof Person){

            return this.name===obj.name;//我们没什么属性就这么意思意思

        }else{

            return false;

        }

    }

};

//还差一个constructor

Object.defineProperty(Person.prototype,"constructor",{

    enumerable:false,

    configurable:false,//不可删除

    value:Person

});

//还差一个length

Object.defineProperty(Person.prototype,"length",{

    enumerable:false,//不可枚举

    writable:false,//不可写、

    configurable:false,//不可删除

    value:3//规定死

});

//实例化对象

var p = new Person("xubowen",20,"wuxi");

var k = Object.keys(Person.prototype);//获取Person对象原形中可遍历的属性

for(var key in p){

    console.log(key);

}

/******************************************如上才是一个完整的对象*******************************************/


/*************************************************** 对象的封装************************************/

function Person(_name,_age,_address){

    //使得属性的作用于仅仅在内部 使之私有化 类似java

    var name = _name;

    var age = _age;

    var address = _address;

    //再提供set get方法

    this.setName=function(value){

        name = value;

    };

    this.setAge=function(value){

        age = value;

    };

    this.setAddress=function(value){

        address = value;

    };

    this.getName=function(){

        return name;

    };

    this.getAge=function(){

        return age;

    };

    this.getAddress=function(){

        return address;

    };

}

//下面照旧

Person.prototype = {

    say:function(){

        return this.name;

    },

    equals:function(obj){

        if(obj instanceof Person){

            return this.name===obj.name;

        }else{

            return false;

        }

    }

};

Object.defineProperty(Person.prototype,"constructor",{

    enumerable:false,

    configurable:false,

    value:Person

});

Object.defineProperty(Person.prototype,"length",{

    enumerable:false,

    writable:false,

    configurable:false,

    value:3

});

/********************************************继承*********************************************/

//一个基类

function Base(){

  this.name="base_name";

}

//一个子类

function Child(){

    this.childName= "child_name";

}

//通过重写函数原形来继承Base

Child.prototype=new Base();

var c = new Child();

c.name;//base_name 来自Base类的属性

c.childName;//child_name 来自自己

c instanceof Child;//true

c instanceof Base;//true

/**

 * Child函数原形直接指向了一个Base类的实例 因此Child拥有Base实例所有自己的属性和方法   但是都存放在了函数原形上

 */

/******************************************厉害点的***********************************************/

//定义基类

function Base(){

    this.name="base";

    this.say = function(){

        return "from base";

    };

}

Base.prototype.getName = function(){

    return this.name;

};

//定义子类

function Child(){

    this.name="child";

    this.say = function(){

        return  "from child";

    };

    this.write = function(){

        console.info("this is a message from child");

    };

}

//修改函数原形

Child.prototype = new Base();

//测试

var c = new Child();

c.name;//child

c.getName();//child

c.say();//from child

c.write();//this is a message from child

/**

 * 如果需要重写函数原形中的方法 请在 Child.prototype = new Base(); 之后重写 而且只能使用原形链形式

 * Base.prototype.getName 而不能用Base.prototype={},因为{}也是一个Object

 */

//还有一个问题 因为Base的实例作为Child的函数原形[这种改变和Child的实例化么有关系] 也就是这个Base实例是所有Child实例共享的 我们继续对上面Base新增一个属性

function Base(){

    this.name="base";

    this.money = 20000;

    this.colors = ['red'];

    this.say = function(){

        return "from base";

    };

}

//其他照常 但是Child类中不要覆盖这个money属性

var c= new Child();

c.name;//child

c.getName();//child

c.say();//from child

c.write();//this is a message from child 这些都没变

c.money;//20000

c.colors;//['red']

//然后

c.money=200;//给money赋值为200;

c.colors.push('blue');

var c1 = new Child();

c1.money;//20000 不变

c1.colors;//['red','blue']; 多了一个

//我大致的理解

/**

 * Array是一个Object类,在Base类中,colors对Array进行了地址引用

 * 获取 c1.colors同样也是对相同的Array进行了引用,此时push操作直接改变了Array 从而造成引用地址改变 使得其他实例中的

 * colors也改变。

 * 而money则是一个常量 没有地址引用,因此c1.money赋值只是改变了当前值的引用,而真正要改变money的值

 * 必须用原形链的形式 Child.prototype.money=20;

 */

/********************************************另一种继承**************************************************/

function Base(){

    this.colors = [1,2,3,4];

    this.say = function(){

        return "say what?"

    }

}

Base.prototype.tucao=function(){

    return "this is a message";

};

function Child(){

    this.name="child";

    Base.call(this);//当然 你还可以传递参数Base.apply(this,arguments);

}

var c = new Child();

//此时的c具有了Base类本身的属性和方法,也具有了Child的一切属性和方法 缺点就是 c不具有Base函数原形中的tucao方法

/**

 * 改进

 */

function Base(){

    this.colors=[1,2,3];

    this.say = function(){

        return "say what ?";

    };

}

Base.prototype.tucao=function(){

    return "this is a message";

};

function Child(){

    this.name="child";

    Base.call(this);

}

//到此为止 只继承了Base的属性和方法 Base函数原形中的方法并没有继承到

Child.prototype = new Base();//将Child的函数原形指向Base的实例 以继承Base原形中的方法

/**如果不用 Base.call(this); 那么Base的一切属性和方法都会被添加到Child函数原形中

但是Child.prototype = new Base();也同样会将Base的属性和方法添加到Child的函数原形中 而恰恰是因为之前  Base.call(this);

使得属性覆盖了函数原形中的相同属性 这就是js对象比较好的一点 自身属性会覆盖原形中的属性 而原形中不会覆盖自身属性

以上继承方式是最常用的方式 称为组合继承

/

/***********原形式继承********************/

function object(sup_pro_){

    function F(){}

    F.prototype = sup_pro_;

    return new F();

}

/***************寄生式*********************/

function aobject(sup){

    var clone = object(sup);//得到原形式继承对象

    clone.say = function(){

        return "say what ?";

    };//得到新属性和新方法

}

/***************寄生组合式********************/

function Super(name){

    this.name =name;

    this.colors = [1,2,3,5];

}

//重写原形属性

Super.prototype.sayName = function(){

    return this.name;

};

//子类

function Child(name,age){

    Super.call(this,name);

    this.age = age;

};

//指定函数原形

Child.prototype = new Super();

Child.prototype.constructor = Child;//将构造器指回自己

Child.prototype.sayAge = function(){//新增原形方法

    return this.age;

};

//这样还是回到之前的问题 一个Super.call()会使得Child有了Super的属性

//函数原形指向Super的实例 也会使得Child有Super的属性和方法 这样就会出现两组相同属性

//虽然外面取的时候是优先取自身属性中而不是原形中 但的确是有不妥之处的

//其实Child的函数原形不应该指向Super实例 而应该是指向Super的函数原形 我们利用原形继承来实现这个

//修改上面最后一块

Child.prototype = object(Super.prototype);//单纯的拷贝了一份prototype出来

Child.prototype.constructor = Child;//将构造器指回自己

Child.prototype.sayAge = function(){//新增原形方法

    return this.age;

};

//面向对象就此结束

/*--------------------------------------------总结-------------------------------------------------------/

基本来说 面向对象是没啥难度

只是要优雅的继承 就要有点步骤:

    1.获取父类所有属性-->可以通过调用父类构造器实现

    2.获取父类原形 单独建一个Object对象继承父类的函数原形 在用自己的函数原形指向这个Object类的实例

    以上就分别获取了函数原形和父类基本属性方法

    3.修改自身的构造器指向 跟仔细点通过Object.defineProperty来定义属性的操作权限


    */


你可能感兴趣的:(javaScript面向对象)