<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象</title>
</head>
<body>
<script type="text/javascript">
//JavaScript是一种基于对象的语言,但是又不是真正面向对象编程(oop)语言,因为语法中并没有class类
//生成一个原型对象
/*var anminal = {
name : '',
age : ''
}*/
//根据原型对象,生成实例对象
/*var cat ={}//生成一个空的对象
cat.name = '小米';//按照原型对象的属性赋值
cat.age = 13;
var monkey = {};
monkey.name = "小候";
monkey.age = 14;*/
//这种是最简单的封装,但是有2个缺点:一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法可以看出有什么联系。
//写一个函数解决代码重复问题
/*function anminal(name,age){
return {
name : name;
age : color
}
}*/
//生成实例对象,等于在调用函数
/*var cat = anminal('小米',13);
var monkey = anminal('小候',14);*/
//问题:cat和monkey间没有内在的联系,不能反映出它们是同一个原型对象的实例。为了解决这个问题,javaScript提供了一个构造函数Constructor模式,所谓构造函数其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成出实例,并且this变量会绑定在实例对象上。
/*function anminal(name,age){
this.name = name;
this.age = age;
}*/
//生成实例对象
/*var cat = new anminal('小米',13);
var monkey = new anminal('小候',14);*/
//这是cat和monkey自动含有一个constructor属性,并指向它们的构造函数
//console.log(cat.constructor==anminal);//为true
//console.log(monkey.constructor==anminal);//为true
//另外JavaScript还提供了一个叫instanceof运算符,验证原型对象与实例对象之间的关系。
//console.log(cat instanceof anminal);//为true
//console.log(monkey instanceof anminal);//为true
//构造函数虽然好用,但存在浪费内存的问题。例如
/*function anminal(name,age){
this.name = name;
this.age = age;
this.type = "猴子";
this.eat = function(){
alert("吃香蕉");
return 'SKHDFKLF';
};
}*/
//生成实例
/*var cat = new anminal('小米',13);
var monkey = new anminal('小候',14);
alert(cat.type);
cat.eat();*/
//这样写有一个弊端,那就是对于每一个实例对象type属性和eat()方法都是一模一样,每次生成一个实例,都必须为重复的内容多占用一些内存既不环保,更缺乏效率
//console.log(cat.eat == monkey.eat);//为false,this指向当前
//那能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址javaScript规定,结果肯定是可以的,每一个构造函数都有一个prototype属性指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例及成果,我们可以把那些不变的属性和方法,直接定义在prototype对象上
/*function anminal(name,age){
this.name = name;
this.age = age;
}
anminal.prototype.type = "猴子";
anminal.prototype.eat = function (){
alert("吃香蕉");
}
var cat = new anminal('小米',13);
var monkey = new anminal('小候',14);*/
//alert(cat.type);
//cat.eat();
//这时所有实例,其实就是同一个内存地址,指向prototype对象,因此就提高了运行效率。
//console.log(cat.eat == monkey.eat);//true
//javaScript还为prototype提供了验证方法
//1.isPrototypeOf():这个方法用来判断,某个proptotype对象和某个实例之间的关系
//console.log(anminal.prototype.isPrototypeOf(cat));//为true
//console.log(anminal.prototype.isPrototypeOf(monkey));//为true
//2.hasOwnProperty():用来判断一个属性到底是本地属性还是继承prototype对象的属性
//alert(cat.hasOwnProperty("name"));//true
//alert(cat.hasOwnProperty("eat"));//false
//alert(cat.hasOwnProperty("type"));//false
//3.in运算符:用来判断某个实例是否含有某个属性,不管是不是本地属性
/*alert("name" in cat);//true
alert("type" in cat);//true
//in运算符还可以用来遍历某个对象的是所有属性
for(var anminals in cat){
alert("cat["+anminals+"]="+cat[anminals]);
}*/
//综上所述
//Javascript的数据和成员封装很简单。没有类完全是对象操作。纯动态
//Javascript function中的this指针很关键,如果没有的话,那就是局部变量或局部函数。
//Javascript对象成员函数可以在使用时临时声明,并把一个全局函数直接赋过去就好了。
//属性配置——Object.defineProperty
//创建对象
//var obj = Object.create(null);
//设置一个属性
/*Object.defineProperty(
obj,'name',{ value: 'shu xing',
writable: true,
configurable: true,
enumerable: true
}
);*/
//设置多个属性
/*Object.defineProperties(obj,
{
'age' :{
value: 11,
writable: true,
configurable: true,
enumerable: true
},
'sex' :{
value: '女',
writable: true,
configurable: true,
enumerable: true
}
}
);*/
//writable:这个属性的值是否可以改。
//configurable:这个属性的配置是否可以改。
//enumerable:这个属性是否能在for…in循环中遍历出来或在Object.keys中列举出来。
//value:属性值。
/*var ages = 0;
Object.defineProperty(obj,
'ages', {
get: function() {
return age+1;
},
set: function(value) {
age = value;
},
enumerable : true,
configurable : true
}
);
obj.ages = 100;*/ //调用set
//console.log(obj.ages);//调用get输出101(get中加一)
//综上例子get/set访问器,是用一与get/set来取代value,其不能和value一起使用
//利用已有的属性(ages)通过get和set构造新的属性
/*Object.defineProperty(obj,
'birth_year',
{
get: function() {
var d = new Date();
var y = d.getFullYear();
return ( y - this.ages );
},
set: function(year) {
var d = new Date();
var y = d.getFullYear();
this.ages = y - year;
}
}
);
console.log(obj.birth_year);
obj.birth_year = 2000;
console.log(obj.ages);*/
//虽然麻烦,但可以通过defineProperty()可以设置writable,configurable,enumerable等这类的属性配置,还可以动态地为一个对象加属性。比如一些HTML的DOM对象
//列出对象的属性
/*function listProperties(obj){
var newLine = "<br/>";
var names = Object.getOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
var prop = names[i];
document.write(prop + newLine);
//列出对象的属性配置(descriptor)动用getOwnPropertyDescriptor函数。
var descriptor = Object.getOwnPropertyDescriptor(obj,prop);
for(var attr in descriptor){
document.write("..." + attr + ': ' + descriptor[attr]);
document.write(newLine);
}
document.write(newLine);
}
}
listProperties(obj);*/
//查看对象属性配置。上面这个程序可以输出对象的属性和配置等东西,
/*function print (text){
document.write(this.value+'---------'+text+'<br/>');
}
var a = {
value : 10,
print : print
};
var b = {
value : 20,
print : print
}*/
//call和apply函数的差别:参数样子不一样性能不一样,apply的性能要差得多
/*print.call(a,'a');//10---------a
print.call(b,'b');//20---------b
print.apply(a,['a']);//10---------a
print.apply(b,['b']);//20---------b*/
//bind,在bind后,this指针,可能会有不一样,因为JavaScript是动态的
/*var p = print.bind(a);
p('a');//10---------a
p.call(b,'b');//10---------b
p.apply(b,['b']);*///10---------b
//继承和重载
//通过上面的例子,我们可以通过Object,create()来实际继承
var Person = Object.create(null);
Object.defineProperties(Person,
{
'name' : {
value : 'wei wei'
},
'email' : {
value : '835328162@qq.com'
},
'we' : {
value : 'http://cocdvvcvvcv.cn'
}
}
);
Person.sayHello = function (){
var hello = "<p>hello,I am" + this.name +",<br/>"+
"my email is:" + this.email + ",<br/>" +"my we is:"+this.we;
document.write(hello+"<br>");
}
var Student = Object.create(Person);
Student.no = "1234567"; //学号
Student.dept = "Computer Science"; //系
//使用Person的属性
//document.write(Student.name + ' ' + Student.email + ' ' + Student.we +'<br>');
//使用Person的方法
Student.sayHello();
//重载sayHello方法
Student.sayHello = function (person){
var hello = "<p>Hello, I am "+ this.name + ", <br>" +
"my email is: " + this.email + ", <br>" +
"my we is: " + this.we + ", <br>" +
"my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
document.write(hello + '<br>');
}
//再次调用
//Student.sayHello();
//查看Student的属性(只有no、dept重载了sayHello);
//document.write('<p>'+Object.keys(Student)+'<br>');
//通过上面例子 可以做到Person里的属性并没有被真正复制到了Student中,但是我们可以去存取,因为JavaScript用委托实现了这机制这就是Pprototype,Person是Student的Prototype。
//当我们编写的代码需要一个属性的时候,JavaScript的引擎会先看当前的这个对象中是否有这个属性,如果没有,就会查找它的prototype对相关是否有这个属性,并且一直继续下去,直到找到或没有prototype对象,为了证明,我们可以使用Object.getPrototypeOf()来 进行验证
Student.name = 'weiwei';
//document.write('<p>'+Student.name+'</p>');
//document.write('<p>'+Object.getPrototypeOf(Student).name+'</p>');
Student.sayHello = function (person){
Object.getPrototypeOf(this).sayHello.call(this);
var hello = "my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
document.write(hello + '<br>');
}
Student.sayHello();
//你还可以在子对象的函数里调用父对象的函数,就好像C++里的 Base::func() 一样。我们重载hello的方法就可以使用父类的代码
Student.sayHello = function (person){
Object.getPrototypeOf(this).sayHello.call(this);
var hello = "my student no is: " + this. no + ", <br>" +
"my departent is: " + this. dept;
document.write(hello + '<br>');
}
Student.sayHello();
//组合
//首先我们应该定义一个Composition的函数(target是作用于是对象,source是源对象)
function Composition(target,source){
var desc = Object.getOwnPropertyDescriptor;
var prop = Object.getOwnPropertyNames;
var def_prop = Object.defineProperty;
prop(source).forEach(
function(key) {
def_prop(target, key, desc(source, key))
}
)
return target;
}
//就是把source里的属性一个一个拿出来然后定义到target中
//艺术家
var Artist = Object.create(null);
Artist.sing = function() {
return this.name + ' starts singing...';
}
Artist.paint = function() {
return this.name + ' starts painting...';
}
//运动员
var Sporter = Object.create(null);
Sporter.run = function() {
return this.name + ' starts running...';
}
Sporter.swim = function() {
return this.name + ' starts swimming...';
}
Composition(Person, Artist);
document.write(Person.sing() + '<br>');
document.write(Person.paint() + '<br>');
Composition(Person, Sporter);
document.write(Person.run() + '<br>');
document.write(Person.swim() + '<br>');
//Person中有?(输出:sayHello,sing,paint,swim,run)
document.write('<p>' + Object.keys(Person) + '<br>');
//prototype 继承
var plus = function(x,y){
document.write( x + ' + ' + y + ' = ' + (x+y) + '<br>');
return x + y;
};
var minus = function(x,y){
document.write(x + ' - ' + y + ' = ' + (x-y) + '<br>');
return x - y;
};
var operations = {
'+': plus,
'-': minus
};
var calculate = function(x, y,operation){
return operations[operation](x, y);
};
calculate(12, 4, '+');
calculate(24, 3, '-');
var Cal = function(x,y){
this.x = x;
this.y = y;
}
Cal.prototype.operations = {
'+': function(x, y) { return x+y;},
'-': function(x, y) { return x-y;}
};
Cal.prototype.calculate = function(operation){
return this.operations[operation](this.x, this.y);
};
var c = new Cal(4, 5);
Cal.calculate('+');
Cal.calculate('-');
//这就是prototype的用法,prototype 是javascript这个语言中最重要的内容。prototype就是对一对象进行扩展,特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的(这里没有真正的复制,实际只是委托)。上面例子中,我们扩展了实例Cal,让其有了一个operations的属性和一个calculate的方法。
</script>
</body>
</html>