一、对象
1. 属性类型
有两种属性:数据属性、访问器属性
用两对方括号括起来的特征值是内部值,不能直接访问
数据属性:包含一个数据值,可以进行读取和写入,有四个特征来描述它的行为:
- [[Configurable]]:能否进行修改属性的特性、通过delete进行删除属性(默认值:true)
- [[Enumberable]]:能否通过for-in循环返回属性(默认值:true)
- [[Writable]]:能否修改属性的值(默认值:true)
- [[value]]:包含属性的数据值(默认值:undefined)
访问器属性:getter、setter函数非严格模式下非必需,严格模式下,缺少会抛出错误。
- getter函数:读取访问器属性且返回有效的值
- setter函数:写入访问器属性,调入时传入新值,负责决定如何处理函数。
同样有四个特征:
- [[Configurable]]:能否进行修改属性的特性、通过delete进行删除属性(默认值:true)
- [[Enumberable]]:能否通过for-in循环返回属性(默认值:true)
- [[get]]:在读取属性时调用的函数(默认值:undefined)
- [[set]]:在写入属性时调用的函数(默认值:undefined)
当属性名前有_记号时,表示该属性只能通过对象方法访问
2. 方法:
Object.defineProperty(属性所在的对象,属性名,修改该属性内容):修改属性默认的特征。
Object.defineProperty(person,"name",{writable:false,value:"Nicholas"});
当设置为不可修改后强行进行修改时,在非严格模式下,修改操作被忽略;在严格模式下,会导致抛出错误
Object.defineProperties(属性所在的对象,修改内容):可以一次定义多个属性。
var book{}; Object.defineProperties(book, { _year:{value:2004}, edition:{value:1}, year:{ get:function(){ return this.year; } set:function(newvalue){ if(newvalue>2005) { this._year=newvalue; edition+=newvalue-2004; } } } } );
Object.getOwnPropertyDescripor(属性所在的对象,所要读取特征值的属性名):可以取得给定属性的特征值,返回值是一个对象。
var descriptor=Object.getOwnPropertyDescripor(book,"_year"); alert(descriptor.value);//2004
二、创建对象
1.工厂模式
用函数来封装以特定接口来创建对象
funtion creatperson(name,age){
var o=new Object();
o.name=name;
o.age=age;
o.sayname=function(){
alert(this.name);
};
return o;
}
var person=creatperson("rose,"18);
缺点:无法得知创建的对象的类型。
2.构造函数模式
可以创建自定义构造函数,从而定义自定义对象类型的属性和方法
构造函数名第一个字母大写
funtion Person(name,age){
this.name=name;
this.age=age;
this.sayname=function(){
alert(this.name);
};
}
var person=new Person("rose,"18);
//将构造函数的作用域赋值给新对象(因此this就指向了这个新对象)
//返回新对象
缺点:每个方法都要在创建对象的时候重新创建一边,没有实现共享。
3.原型模式(重点)
对象
每个函数都有一个prototype原型属性,是一个指针,指向函数的原型对象,该对象包含所有实例共享的属性和方法
function Person(){} Person.prototype={ constructor:Person,//因为重写了,所以需要特意设置 name:"rose", age:18, sayname:function(){ alert(this.name); } };//这种定义方式是对象字面量,重写了原型对象 var person1=new Person(); var person2=new Person(); alert(person1.sayname==person2.sayname);//true //因为Person.prototype中的内容是共享的,所以相同。
原型对象自动获得constructor构造函数属性,也是一个指针,指向prototype所在de函数。
eg:Person.prototype.constructor->Person当调用构造函数创建一个实例后,该实例将包含一个指针属性[[Prototype]],指向构造函数的原型对象
- 当执行一个对象的某个属性时,会先在实例中搜索该属性名,如若没有,则会在原型对象中搜索。
- 当在实例中为对象添加一个属性时,该属性会覆盖住原型对象中的同名属性,如若delete掉该属性,则显示原型对象中的。
function Person(){}; Person.prototype.name="rose"; person1=new Person(); person1.name="jack"; alert(person1.name);//"jack" delete person1.name; alert(person1.name);//"rose"
方法
- isPrototypeOf(对象):如果[[Prototype]]指向构造函数的原型对象,即Person.prototype,则返回true。
- Object.getPrototypeOf(对象):返回[[Prototype]]的值。
- hasOwnProperty(name):在实例中返回true。
alert(Person.prototype.isPrototypeOf(person1));//true alert(Object.getPrototypeOf(person1)==Person.prototype);//true alert(Object.getPrototypeOf(person1).name);//"rose"
原型与in操作符
使用方式:单独使用和在for-in循环中使用。
单独使用:对象能够访问给定属性时返回true,即属性存在于对象(实例和原型对象)中
继续上例,alert("name" in person1);//true
下述函数可以用来确定属性是存在于实例还是原型对象
function hasPrototypeProperty(object,name){ return !object.hasOwnProperty(name)&&(name in object); } alert(hasPrototypeProperty(person,"name")) //在原型对象中返回true,在实例中返回false
for-in循环:返回的是所有能够通过对象访问的、可枚举的属性。
将[[Enumberable]]设置为false的实例属性也会返回,因为规定所有开发人员定义的属性都是可枚举的。Object,keys(对象)方法:返回一个包含所有可枚举属性的字符串数组
var keys=Object.keys(person1); alert(keys);//"name,age"
- 动态性:当在创建一个实例后,如果再全部重写对象原型,则会切断现有原型和之前所有对象实例的联系,即实例引用的是未重写的原型。
缺点:在原型对象中定义的属性方法都是共享的,对于引用类型值的属性,问题比较大。
4.组合使用构造函数和原型模式(最常见)
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性,节省了内存。
5.动态原型模式
所有信息都封装在了构造函数中,在其中可以判断某个属性是有效,来决定是否初始化原型。
function Person(name)
{
this.name=name;
if(typeof this.sayname!="function"){
Person.prototype.sayname=function(){
alert(this.name);
};
}
}
6.寄生构造函数模式(不推荐使用)
创建一个函数,来封装创建对象的代码,再返回新创建的对象,很像构造函数。
7.稳妥构造函数模式(适合安全环境)
实例方法不引用this和不使用new操作符调用构造函数,其余地方和寄生构造函数模式相同。
与上述代码不同的地方:alert(name);
三、继承
只支持实现继承,则继承实际的方法,且依靠原型链
1.原型链
- 再次讲一下 原型对象、构造函数、实例的关系:
每个构造函数都有一个prototype内部指针属性,该属性指向原型对象,原型对象中有一个constructor指针属性,指向构造函数,创建一个实例调用该类型的构造函数,有了构造函数所定义的属性和方法,同时自动有一个内部指针属性[[Prototype]],指向原型对象。 - 让一个原型对象等于另一个类型的实例,此时这个原型对象中包含着指向另一个原型对象的指针,另一个原型对象包含指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例,就构成了实例与原型的链条,即原型链。
function A(){
this.name="rose";
}
A.prototype.getname=function(){
return this.name;
};
function B(){
this.age="18";
}
B.prototype=new A();
B.prototype.getage=function(){
return this.age;
};
var person=new B();
alert(person.getname());//"rose"
//在这个例子中,调用person.getname();会先搜索实例,然后B.prototype,然后A.prototype.
//应用原型链进行操作是,就是一层一层的搜索,直至找到或者到原型链末端
- 所有函数的默认类型都是Object的实例,所以原型链的末端应该是Object,所以上例中,
A.prototype=new Object();
- 上例中person可以是A、B、Object的实例。
- 给原型添加方法的代码一定要放在继承上一层原型的语句之后。
缺点:
* 原先不想被共享的属性定义在构造函数中,但是原型链中,实例是下一层的原型对象,不想被共享的属性变成共享的了。
* 在继承创建实例时不能传递参数。所以不建议单独使用原型链。
2.借用构造函数
- 在子类型构造函数的内部调用超类型构造函数。这样只会在创建B实例的环境下调用A的构造函数,这样就可以每个实例有属于自己的A,B构造函数所定义的的属性。
function B(){
A.call(this);
}
- 也可以继承的时候传递参数。
function A(name){
this.name=name;
}
function B(){
A.call(this,"rose");
this.age=18;
}
var person=new B();
alert(person.name;//"rose"
缺点:构造函数模式存在的问题,没有函数共享、复用,所以也不建议单独使用。
3.组合继承(最常用的继承模式)
- 使用原型链实现对原型属性和方法的继承,通过构造函数来实现实例属性的继承。
function A(name){
this.name=name;
this.colors=["red","blue","green"];
}
A.prototype.getname=function(){
alert(this.name);
};
function B(name,age){
A.call(this,name);
this.age=age;
}
B.prototype=new A();
B.prototype.constructor=A;
B.prototype.getage=function(){
alert(this.age);
};
var person1=new B("rose",18);
person1.colors.push("black");
alert(person1.colors);//"red,blue,green,black"
person1.getname();//"rose"
var person2=new B("jack",19);
alert(person2.colors);//"red,blue,green"
person2.getage();//19
4.原型式继承
- 基于原有的对象创建新对象,即创建原有对象的副本。
function object(o){
function F(){}
F.prototype=o;
return new F();
}
var person={name:"rose",colors=["red","green"]};
var person1=object(person);//创建person的一个副本
person1.name="jack";//根据需求对得到的对象加以修改
- Object.creat(对象,修改添加内容);当只有第一个参数时,等同于object()。
var person1=Object.creat(person,{name:{value:"jack"}});
5.寄生式继承
- 创建一个封装函数,内部进行继承,和修改添加属于对象自己的属性和方法,再返回这个对象。
function creat(person){
var person1=object(person);
person1.sayhi=function(){
alert("hi");
};
return person1;
}
var person1=creat(person);
person1.sayhi();//"hi"
6.寄生组合式继承
- 通过构造函数来继承属性,原型链来继承方法。
- 本质上就是使用寄生式继承来继承原型,然后再将结果指定给子类型的原型
function inherit(B,A){
var prototype=object(A.prototype);//创建对象,即new A()
prototype.constructor=B;//增强对象
B.prototype=prototype;//指定对象
}