/**
* @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来定义属性的操作权限
*/