JavaScript 中的对象及创建对象
// 每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型,也可以是自定义类型
eg:
var person = new Object();
person.name = "YangChuanKai";
person.age = 22;
person.job = "Student";
person.sayName = function(){
alert(this.name);
};
//对象字面量语法:
var person = {
name: "YangChuanKai";
age: 22;
job: "Student";
sayName: function(){
alert(this.name);
}
};
//ECMAScript有两种属性:数据属性和访问器属性
1、数据属性:数据属性包含一个数据值的位置,在这个位置可以读取和写入值。4个特性:
[[Configurable]] : 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为访问器属性,前例默认true。
[[Enumerable]] : 表示能否通过for-in循环返回属性,true.
[[Writeable]] : 表示能否修改属性的值,true.
[[Value]] : 包含这个属性的数据值,默认值为undefined.
要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法,参数:(属性所在对象,属性名,描述符)
var person = {};
Object.defineProperty(person."name",{
writeable: false,
value: "kai"
});
alert(person.name); //kai
person.name = "yue";
alert(person.name); //kai,属性值不可修改
//调用Object.defineProperty()时,若不指定,前三个特性的默认值都是false;
2、访问器属性:不包含数据值,包含一对get,set函数,读取访问器属性时调用get函数,写入访问器属性时调用set函数.4个特性:
[[Configurable]],[[Enumerable]] 同上;
[[Get]]:在读取属性时调用的函数,默认值undefined.
[[Set]]:写入属性时调用的函数,默认值undefined.
访问器属性也不能直接定义,必须使用Object.defineProperty()定义:
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
3、定义多个属性:Object.defineProperties():
var book = {};
Object.defineProperties(book,{
_year:{
value : 2004
},
edition:{
value : 1
},
year:{
get: function(){
return this._year;
},
set: function(){
if(newValue > 2004){
this._year = newValue;
thi.edition += newValue - 2004;
}
}
}
});
//读取属性的特性:Object.getOwnPropertyDescriptor(),参数:属性所在对象,要读取其描述符的属性名称
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
alert(descriptor.value);
alert(descriptor.configurable); //false;
alert(typeof descriptor.get); //"undefined"/"function"
/************************************************************************************************************************************/
一、创建对象:
使用Object构造函数或对象字面量创建的对象的缺点:使用一个接口创建很多对象,产生大量重复代码。
1、工厂模式:
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;
}
//工厂模式解决了创建多个相似对象的问题,但却没有解决对象识别的问题,即如何知道一个对象的类型
/****************************************************************************************************************************/
2、构造函数模式:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("kai",22,"Student");
var person2 = new Person("yue",22,"Student");
//person1,person2分别保存着Person的一个不同实例,这两个对象都有一个constructor属性,该属性指向Person:
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
//创建自定义构造函数,将来可将它的实例标示为一种特定的类型
* 构造函数也是函数,不存在定义构造函数的特殊语法,任何函数只要通过new来调用,就可作为构造函数
//当构造函数使用
var person = new Person("kai",22,"Software Engineer");
person.sayName();
//当普通函数使用
Person("kai",22,"Software Engineer"); //添加到window
window.sayName();
//在另一个对象的作用域中调用
var o = new Object();
Person.call(o,"kai",22,"Student");
o.sayName();
* 构造函数的问题:每个方法(sayName)都要在每个实例上重新创建一遍,so:
//对象就是函数,so
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName; //指向函数的指针.也可在此new一个对象(函数)
}
function sayName(){
alert(this.name); //多亏了this
}
var person1 = new Person("jie",22,"Doctor");
var person2 = new Person("Greg",23,"Teacher");
新的问题: 在全局作用域中定义的函数实际上只能被某个对象调用,让全局作用域名不副实;
如果对象要定义很多方法,就要定义很多全局函数,就没有封装性了,so:
/******************************************************************************************************************************/
3、原型模式:
我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象。这个对象的用途是包含可以由特定类型的所有实例共享
的属性和方法。不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中,让所有对象实例共享它所包含的属性和方法。
function Person(){ // 构造函数变为空函数
}
Person.prototype.name = "yue";
Person.prototype.age = 22;
Person.prototype.job = "Presenter";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName();
var person2 = new Person();
person2.sayName();
alert(person1.sayName == person2.sayName); //true
//Person.prototyp指向原型对象,Person.prototype.constructor又指回了Person,在实现中无法访问到[[Prototype]],可利用函数测试
alert(person.prototype.isPrototypeOf(person1)); //true
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"yue"
//如果在实例中创建了一个属性与原型中相同,就在实例中创建该属性,该属性会屏蔽原型中的那个属性: (先搜索实例,再搜索原型)
function Person(){}
person.prototype.name = "yue";
Person.prototype.age = 22;
Person.prototype.job = "Presenter";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "kai"; // Note
alert(person1.name); //"kai" : 来自实例
alert(person2.name); //"yue" : 来自原型
//使用delete可以删除实例属性:
delete person1.name;
alert(person1.name); //"yue" : 来自原型
//hasOwnProperty(),检测一个属性是存在于实例中还是原型中,实例中返回true
person2.name = "love";
alert(person2.name); //"love"来自实例
alert(person2.hasOwnProperty("name")); //true
delete person2.name;
alert(person2.hasOwnProperty("name")); //false
***原型与in操作符:单独使用in时,会在通过对象能够访问给定属性时返回true,不管是在原型中还是实例中;在for-in循环中使用
alert("name" in person1); // true
//确定属性存在于实例中还是原型中:
function hasPrototypeProperty(object,name){
return !Object.hasOwnProperty(name) && (name in object);
} // return true,属性在原型中
使用for-in循环时,返回的是所有能够通过对象访问的可枚举的属性,包括实例原型中的属性.
Object.keys()接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
var p1 = Object.keys(person1); //只返回实例属性
var p2 = Object.keys(person.prototype); //返回原型中属性
//想得到所有实例属性,无论是否可枚举,可用Object.getOwnPropertyNames()
***用对象字面量来重写原型对象:
function person(){}
Person.prototype = {
//constructor : person,
name : "yue",
age : 22,
job : "Presenter";
sayName : function(){
alert(this.name);
}
];
//但如此一来,constructor属性不再指向Person,而指向Object
var friend = new Person();
alert(friend.constructor == Person); // false
alert(friend.constructor == Object); // true
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
//可如注释中重设constructor属性,但会导致它的[[Enumerable]]置为true,默认为false
***原型的动态性:对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也如此:
var friend = new Person();
Person.prototype.sayName = function(){
alert("lenovo");
}
friend.sayName() // "lenovo"
//但如果是重写整个原型对象,就不会这样了:
function Person(){}
var friend = new Person();
Person.prototype = { //重写原型对象
name : "yue",
age : 22,
job : "Presenter";
sayName : function(){
alert(this.name);
}
};
friend.sayName(); //error!
//重写原型对象切断了现有原型与任何值钱已存在的对象实例之间的联系,它们引用的仍然是最初的原型
***原生对象的原型:
String.prototype.startsWith = function(text){ //给原生对象原型添加方法,不建议
return this.indexOf(text) == 0;
}
var msg = "Hello World";
alert(msg.startsWith("Hello")); //true
***原型对象的问题:
1.省略了为构造函数传递初始化参数,结果所有实例默认都将取得相同的属性值
2.原型对象的共享本性,对于包含引用类型值的属性来说,问题比较突出:
function Person(){}
Person.prototype = {
constructor: Person,
name : "yue",
age : 22,
job : "Presenter";
friends : ["Shell","Java"], //Note
sayName : function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Qt");
alert(person1.friends); //"Shell,Java,Qt"
alert(person2.friends); //"Shell,Java,Qt"
alert(person1.friends == person2.friends); //true
//因此,很少单独使用原型模式
/*****************************************************************************************************************************/
4、组合使用构造函数模式和原型模式: //常见
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性:
function Person(name, age, job){ //构造函数
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shell","Java"];
}
Person.prototype = { //原型模式
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("baidu",22,"boss");
var person2 = new person("google",23,"bigBoss");
person1.friends.push("Soft");
alert(person1.friends); //"Shell,Java,Soft"
alert(person2.friends); //"Shell,Java"
alert(person1.friends == person2.friends); //false;
alert(person1.sayName == person2.sayName); //true;
/******************************************************************************************************************************/
5、动态原型模式:
function Person(name,age,job){
//属性
this.name = name;
this.age = age;
this.job = job;
//方法: 方法不存在时添加
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friend = new Person("360","23","Engineer");
friend.sayName(); //360
/*****************************************************************************************************************************/
6、寄生构造函数模式:
function Person(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 friend = new Person("computer",3,"work");
friend.sayName(); //"computer"
//这个模式可以再特殊的情况下用来为对象创建构造函数,假设想要创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数:
function SpecialArray(){
var values = new Array(); //创建数组
values.push.applay(values,arguments); //添加值
values.toPipedString = function(){ //添加方法
return this.join("|");
}
return values; //返回数组
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString()); //"red|blue|green"
//使用其他模式时,不要使用该模式(返回的对象与构造函数或构造函数的原型属性之间没有关系,instanceof没用)
/******************************************************************************************************************************/
7、稳妥构造函数模式:
//新创建对象的实例方法不引用this;不使用new调用构造函数:
function Person(name, age, job){
var o = new Object(); //创建要返回的对象
//... 添加私有变量和函数定义
o.sayName = function(){ //添加方法
alert(name);
};
return o; //返回对象
} //除了使用sayName()外,没有其他方法访问name
var friend = Person("Kugou",21,"SoftWare");
friend.sayName(); //"Kugou"
//变量person中保存的是一个稳妥对象