javascript的面向对象程序设计(一)
JavaScript中没有类的概念,与传统的有类的面向对象程序的语言有所不同;
1.1.理解对象;
创建一个对象最简单的方式,就是创建一个object()实例;然后再为它添加属性和方法;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
var person = new person(); person.name="t"; person.age = 21; person.job = "student"; person.sayName= function (){ alert(this.name); }
用创建对象字面的方式创建;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
var person ={ name:"t", age:20, job:"student", sayName:function(){ alert(this.name); } }
1.2.属性类型;
属性的各种特征;
1.2.1数据属性;[[configurable]],[[Enumerable]],[[Writeable]],[[Value]];
[[configurable]],[[Enumerable]],[[Writeable]]设为true; 而[[Value]]设置为值;
要修改属性的默认属性;Object.defineProperty();接收三个参数:对象名,属性名,描述符对象;
var person = new Object(); Object.defineProperty(person,"name",{
writeable:false,
value:"zht"
}
1.2.2 访问器属性:访问器属性不包含数值,它们包含一对set和get函数,不过这两个函数都不是必须的。
Get:在读取属性时调用的函数;Set:在写入属性时调用的函数;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
var book = { _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){ return this._year; } set:function(newValue){ if(newValue > 2004 ){ this._year =newValue this.edition += newValue - 2004; } } }); book.year = 2005; alert(book.edition) //2
1.3.读取属性的特性;使用Object.getOwnPropertyDesciptor();返回值是一个对象;
2.1创建对象;
虽然object构造函数和对象字面量能够创建对象;使用这样的单一的接口,创建类似的对象时,会造成代码的冗余,所以,我们必须进行代码的封装;
工产模式使我们大家都比较熟悉的创建型设计模式;
考虑到JavaScript无法创建类,开发人员就发明了一种构造函数,用这个函数来封装;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function createPerson(name,age,job){ var o = new object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert( this.name) ; } return o; } var person1 = createPerson(1,1,1); var person2 = createPerson(2,2,2);
2.2构造函数模式;
像object和Array这样的原生构造函数,自己也可以定义自己的构造函数;
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new person(1,1,1); var person2 = new person(2,2,2);
跟上面的例子有些不同:1.没有显示的创建对象;2.直接将属性和方法赋给this对象;3.没有return语句;
用上面的方法创建对象时,会经历以下步骤;1.创建新对象:2.将构造函数的作用域赋给新对象(因此this就指向了新对象);3.执行构造函数的代码(初始化属性)4.返回对象;
person1和person2这两个对象都会有个constructor(构造函数)属性;该属性指向person;
但是对象类型检测 instanceof 会好一些;
2.3将构造函数当做函数;
构造函数与其他函数的区别在于调用的方式不同,任何函数只要通过new来调用,就可以称作是构造函数;
//当做构造函数使用 var person = new person(1,1,1); person.sayName(); //用普通的函数使用; person(1,1,1); //添加到window中l window.sayName();
在用普通的函数使用时,挡在全局作用域调用一个函数时,this对象总是指向Global对象;在浏览器中,就是window对象;因此在第二种方法中,调用函数完时,用window对象
调用属性;
2.4构造函数的问题;
使用构造函数的问题,属性如果是函数的话,就会在每个实例中都要实例这个函数,上述的构造函数也可以这样定义:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = new Function("alert(this.name)");// 与声明函数是等价的; }
解决不同的实例要实例多个同一个函数的方法;把方法写到构造函数之外,但是这种方法也有一种问题,就是,这些方法都成了全局作用域的方法,无法 ,体现出封装性的特征,
还有一点就是,一般情况下不能够,过多的声明全局变量;由此引出原型模式;
2.4 原型模式;
我们创建一个函数,(函数也是对象)就会有一个prototype这个属性,这个属性是一个指针,这个指针指向一个对象,这个对象是通过调用构造函数而创建的那个对象实例的原型
对象;代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function Person(){}; Person.prototype.name = "Nical"; Person.prototype.age = 18; Person.prototype.job = "student"; person.prototype.sayName = function(){ alert(this.name); } var person1 = new Pesron(); person1.sayName();//Nical; var person2 = new Person(); person2.sayName();//Nical; alert(person1.sayName ==person2.sayName) //true
接下来我们理解下原型模式的工作原理;
![](https://images0.cnblogs.com/blog2015/773796/201507/241013257566345.png)
这张图片是对上述代码的解释;
原型对象最初只有constructor这个属性,并且这个属性指向了该构造函数;
我们可以通过对象的实例来访问对象原型的值;但是我们不能通过对象实例来修改对象原型的值;如果我们在对象实例上添加一个跟原型一样的属性,那么当我们访问这个属性时,返回的值是在对象实例上的值,而不是对象原型上的值,这是因为,JavaScript在搜索时,就已经在对象实例上搜索到这个值了,就没有必要在继续往上,想对象原型搜索了;
可以使用hasownproperty()方法来检测这个属性时在对象实例上还是在原型对象上,只有在对象实例上才会返回true;
2.4.2
in和原型;属性是在对象实例上还是在对象原型上只要有这个属性,就会返回true;
alert("name" in person1);
2.4.3 更加简单的创建对象原型的方法;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function Person((){}; Person.prototype ={ name:"zht", age :20; job:"student", sayName:function(){ alert(this.name); } };
这样的创建代码更加简洁,但是跟上述写的对象原型也有所不同,这个写法,相当于重写了一个新的原型对象;这个原型对象constructor属性不指向构造函数,大部分情况下是指向
Object 这个默认的构造函数了。但是用instanceof 检测类型时,还是可以返回true;所以如果constructor这个属性很重要,可以重新特意的将它设置我们的构造函数;从而确保在
访问时能够返回正确的值;但是,以这种方式设置的constructor属性的[[Enumerable]]为true;默认情况下 为false;
2.4.4 原型的动态性;
由于在原型上搜索值是一次性搜索,所以在原型对象做的修改,能够立即在对象实例上反应出来------即使是先创建对象实例 后修改对象原型 也是这样子的;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
var friend = new Person(); Person.prototype .sayHi= function(){ alert("hi"); } friend.sayHi(); //hi
虽然实例是在原型之前创建的,但是在查找属性时,先在实例查找有没这个属性,然后再在原型对象查找,因为在实例和原型对象之间是一个指针而不是一个副本;因此就可以在
原型中找到sayhi属性并保存;
但是如果原型是向上述的重写的话,情况就会不一样了。因为指针指向发生了变化,
注意点,重写了原型对象,就等于切断了原型与任何之前已经存在的对象实例的联系,它们引用的还是最初的原型;
2.4.5 原生对象的模型;
原型模式的重要性,不仅仅体现在自定义对象类型,还体现在,连原生的对象模型,也是采用这种模式定义的;
所有原生引用类型(Object,Array,String ,Function);都在其构造函数的原型上定义了方法。例如,可以在Array.prototype中找到sort方法;
通过原生对象原型的引用,不仅可以取得默认的方法的使用还可以定义新方法;
2.4.6 原型模式的问题;