目录
《JavaScript高级程序设计》学习笔记
ECMA-262把对象定义为:对象是无序属性的集合,其属性可以包含基本值、对象或函数
一、对象
(一)创建对象的两种基本形式
1、创建对象-Object构造函数形式
var person = new Object(); //使用Object构造函数创建对象
person.name = "zhangSan";
person.say = function(){
alert(this.name);
};
person.say();
2、创建对象-对象字面量形式
var person = { //直接使用对象字面量形式创建对象
name:"zhangSan",
say:function(){
alert(this.name)
}
};
person.say();
(二)属性类型
ECMAScript中有两种属性:数据属性、访问器属性。
特性是用来描述属性的行为特征,特性是内部值
- 数据属性包含一个数据值的位置,即可以存放基本数据类型和引用数据类型。
- 访问器属性不包含数据值,包含一对getter和setter函数。用于访问或者设置数据
1、数据属性的4个特性
- [[Configurable]]:是否可通过delete删除属性,能否修改属性的特征或者把属性修改为访问器属性,默认true。
- [[Enumerable]]:表示能否通过for-in循环返回属性,默认true。
- [[Witable]]:能否修改属性的值,默认true。
- [[Value]]:属性的值,默认undefined。
2、访问器属性的4个特性
- [[Configurable]]:是否可通过delete删除属性,能否修改属性的特征或者把属性修改为访问器属性,默认true。
- [[Enumerable]]:表示能否通过for-in循环返回属性,默认true。
- [[Set]]:在写入属性时调用的函数,默认为undefined。
- [[Get]]:在读取属性时调用的函数,默认为undefined。
3、操纵特性
ECMAScript5 的 Object.defineProperty()方法可以操纵特性。该方法接受三个参数:属性所在的参数、属性的名字和一个描述符对象。
- 操纵数据属性特性
//操纵数据属性特性
var person = {};
Object.defineProperty(person,"name",{ //注意第二个参数要加双引号
writable:false,
value:"zhangSan"
})
alert(person.name);// zhangSan
person.name = "li";
alert(person.name);// zhangSan
- 操纵访问器属性特性
//操纵访问器属性特性
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
4、读取属性特性
var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
//使用getOwnPropertyDescriptor()方法读取属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
alert(descriptor.value); //2004
alert(descriptor.configurable); //false
alert(typeof descriptor.get); //"undefined"
var descriptor = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); //undefined
alert(descriptor.enumerable); //false
alert(typeof descriptor.get); //"function"
二、创建对象
(一)工厂模式
使用Object构造函数方式或对象字面量形式在创建多个类似的对象时,会产生大量的重复代码。为了解决重复代码的问题,使用工厂模式创建对象。
function createObject(name,age,job){ //调用一次函数即可创建一个对象
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
p = createObject("xufeng",28,"best");
p.sayName();
缺点:工厂模式创建的对象无法得知对象类型,因为这种新对象并没有类型名字。同时也无法让实例共享属性和方法
(二)自定义构造函数
为了解决工厂模式的缺点,自定义构造函数可以将它的实例标识作为一种特定的类型。
使用自定义构造函数定义:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var p = new Person("小明",20,"student");
var s = new Person("老师",39,"Teacher");
alert(p instanceof Object); //true
alert(p instanceof Person); //true,对象视为Person类型
自定义构造函数名字以大写字母开头,要使用构造函数创建实例,必须使用new操作符。在创建构造函数时经历以下4步(重要):
- 创建一个新对象
- 将函数内的作用域赋予新对象(this指向新对象)
- 执行构造函数的代码(为新对象添加属性)
- 返回新对象
缺点:每个通过构造函数创建的对象不能共享属性和方法。如上述的sayName ()方法,调用一次Person构造函数便生成一次函数实例。
(三)原型模式
1、原型模式
- 每个函数均有一个prototype(原型)属性,该属性指向函数的原型对象。该对象的用途是可以包含由特定类型的所有实例共享的属性和方法。所有的原型对象会自动获得constructor属性,回指向函数。
- 每个实例均有一个内部属性[[Prototype]],该属性指向其构造函数的原型对象。
function Person(){};//自动创建Person的原型对象
使用函数的prototype(原型)属性让对象之间可以共享属性和方法,解决自定义构造函数无法共享属性和方法的缺点。
每个构造函数和实例无直接关系。而构造函数的实例有内部属性[[Prototype]]指向构造函数的原型对象。
function Person(){};
//将需共享的属性和方法放入构造函数的原型对象,实现共享
Person.prototype.name = "zhangSan";
Person.prototype.age = 28;
Person.prototype.sayName = function(){
alert(this.name);
};
var p1 = new Person();
var p2 = new Person();
alert(p1.sayName == p2.sayName); //true
p1、p2共享Person构造函数原型对象内的属性name、age,和方法sayName。
缺点:原型模式会共享原型对象中所有的属性和方法。由于共享机制,若属性为引用类型,则实例之间可能会共享属性的修改,即实例之间的属性值会相互影响。
function Person(){};
Person.prototype.name = "zhangSan";
Person.prototype.friends = ["li","wang","xu"];
Person.prototype.age = 30;
Person.prototype.sayName = function(){
alert(this.name);
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push("niu")
//修改p1的属性会影响p2的属性
alert(p2.friends);//li,wang,xu,niu
2、原型对象和实例的操纵
每个实例均有一个内部属性[[Prototype]],该属性指向其构造函数的原型对象,但所有实现无法访问内部属性[[Prototype]]。
(1)isPrototypeOf
使用isPrototypeOf()方法判断对象之间是否存在原型关系,若存在则返回true。
alert(Person.prototype.isPrototypeOf(p1)); //true
(2)Object.getPrototypeOf
ECMAScript5增加了一个新方法Object.getPrototypeOf(),该方法返回实例的[[Prototype]]的值。
alert(Object.getPrototypeOf(p1).name); //zhangSan
(3) hasOwnProperty
hasOwnProperty()检查一个属性是否存在于实例中。
function Person(){};
Person.prototype.name = "zhangSan";
Person.prototype.age = 28;
Person.prototype.sayName = function(){
alert(this.name);
};
var p1 = new Person();
p1.age = 30;
alert(p1.hasOwnProperty("name"));//false,name不存在于p1实例中
alert(p1.hasOwnProperty("age")); //true,age存在于p1实例中
由于in操作符可以判断该实例是否可以访问某个对象,因此hasOwnProperty()方法配合in操作符可以判断属性是否存在于实例中,还是存在于原型中。
function hasPrototypeProperty(object,name){
//若name不存在与实例中,且in操作符能访问name属性,则name属性存在于原型中
return !obeject.hasOwnProperty(name) && (name in object);
}}
(4) Object.keys
使用Object.keys()可以取得对象上所有可枚举的属性。
function Person(){};
Person.prototype.name = "zhangSan";
Person.prototype.age = 28;
Person.prototype.sayName = function(){
alert(this.name);
};
var p1 = new Person();
p1.age = 30;
alert(Object.keys(Person.prototype));//name,age,sayName
alert(Object.keys(p1));//age
(5)Object.getOwnPropertyName
取得所有属性,其中包括不可枚举属性
function Person(){};
Person.prototype.name = "zhangSan";
Person.prototype.age = 28;
Person.prototype.sayName = function(){
alert(this.name);
};
alert(Object.getOwnPropertyNames(Person.prototype));//constructor,name,age,sayName
(四)组合使用构造函数模式和原型模式
单用原型模式会由于共享机制而发生实例间属性的相互影响。因此可以将需共享的属性和方法放入对象原型中,而将无需共享的属性放入构造函数,实现构造函数模式和原型模式。
function Person(){
//将无需共享的属性放入构造函数
this.name = "zhangSan";
this.friends = ["li","wang","xu"];
this.age = 30;
};
//将需要共享的方法放入原型对象中
Person.prototype.sayName = function(){
alert(this.name);
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push("niu");
//实例间的属性不会相互影响
alert(p2.friends);//li,wang,xu