var person = new Object();
person.name = "Mike";
person.age = "20";
person.job = "student";
person.sayName = function(){
alert(this.name);
}
var person = {
name: "Mike",
age: 20,
job: "student",
sayName: function(){
alert(this.name);
}
}
person.score = "90";//增加属性
alert(person.score); //90
delete person.score;
alert(person.score);//undefined
ES中有两种属性:数据属性和访问器属性。
数据属性
修改默认属性使用defineProperty(),接收三个参数:属性所在对象,属性名和一个描述符对象。
var person = {};
Object.defineProperty(person,"name",{
configurable: false, //不可删除
writable: false, //变为只读
value: "Mike"
});
alert(person.name);//"Mike"
delete person.name;
person.name = "Amy";
alert(person.name);//"Mike"
一旦将configurable设置为false,则无法再使用defineProperty将其修改为true。
访问器属性
访问器属性不能直接定义,必须使用Object.defineProperty()来定义。
var person = {
_age:20
};
Object.defineProperty(person,"age",{
get: function(){
if(this._age>0){
return true;
}else{
return false;
}
}
});
alert(person.age);//true
在定义getter与setter时不能指定属性的configurable及writable特性。
var person = {};
Object.defineProperties(person,{
_age:{
writeable:true, //数据属性
value:20
},
year:{ //定义了一个访问器属性
get function(){
if(value>0){
return true;
}else{
return false;
}
}
}
});
var descriptor = Object.getOwnPropertyDescriptor(person,"_age");
alert(descriptor.value);//20
alert(descriptor.configurable);//false
alert(typeof descriptor.year);//undefined
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("Mike","20","student");
var person2 = createPerson("Amy","22","engineer");
创建对象都是使用Object的原生构造函数来完成的,所以没有解决对象识别问题
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name); //第一种写法
};
this.sayName = new Function("alert(this.name)"); //第二种写法
}
function sayName() {
alert(this.name);
} //第三种写法,将方法写在外面
var person1 = new Person("Mike","20","student");
var person2 = new Person("Amy","22","engineer");
构造函数问题
构造函数与工厂模式 不同之处:
创建Person新实例经历步骤
person1和person2分别保存着Person的一个不同的实例。都有constructor属性,该属性指向Person。
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
alert(person1 instanceof Person);//true;
alert(person1 instanceof Object);//true;
alert(person2 instanceof Person);//true;
alert(person2 instanceof Object);//true;
var person = new Person("Mike",20,"student"); //当做构造函数使用
person.sayName();//"Mike"
Person("Greg",27,"Doctor"); //作为普通函数调用,添加到window
window.sayName(); //"Grey"
var o = new Object();//在另一个对象的作用域中调用
Person.call(o,"Kristen",25,"Nurse");
o.sayName();//"Kristen"
由于构造函数存在的问题,为此引入原型模式
原型:我们创建每个函数都有一个prototype(原型)属性。它是一个指针,是构造函数中自动创建的对象实例的原型对象。
用途:包含可以由特定类型的所有实例共享的属性和方法。
好处:可以让所有对象实例共享它包含的属性和方法,也就是说,不必在构造函数中定义对象实例的信息,可以直接将信息添加到原型对象中。
function Person(){} //声明一个空函数
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Endineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName();//"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName);//true
新对象这些属性和方法是有所有实例共享的。person1和person2访问的都是同一组属性和同一个sayName()函数。
由图可见,实例与构造函数无直接关系。实例不包含属性和方法,但仍可调用sayName(),通过查找对象属性过程实现。
为实例添加新的与原型同名属性时,会屏蔽原型中的那个属性。delete恢复原型连接。hasOwnProperty()查看访问的是实例属性还是原型属性。in主要是看对象里的属性存不存在。如下
function Person(){} //声明一个空函数
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Endineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(person1.hasOwnProperty("name")); //false 来自原型返回false
console.log("name" in person1); //true in只要通过对象访问到属性就返回true 与上一句结合可以确定属性是原型中的属性
person1.name = "Greg";
alert(person1.name); //"Greg" 来自实例 屏蔽了原型中的同名属性
console.log(person1.hasOwnProperty("name")); //true 来自实例返回true
alert(person2.name);//"Nicholas"来自原型
delete person1.name;
alert(person1.name); //"Nicholas"来自原型 删除原型属性就恢复了对原型的连接
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName" 原型函数属性
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1); //实例对象属性
alert(p1keys); //"name,age"
function Person(){
}
Person.prototype = {
name : "Mike",
age : 29,
job : "student"
sayName : function(){
alert(this.name);
}
};
var friend = new Person();
console.log(friend.constructor == Person); //false
console.log(friend.constructor == Object);//true
1,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。这里的语法,完全重写了默认的prototype对象,重写之后变成一个Object对象实例,constructor属性也就变成新对象的constructor属性,不再指向Person。
2,如果不想因为重写prototype而改变了constructor的指向,可以在重写prototype时加上:constructor : Person,
不过[[Enumerable]]属性会变为true
3 ,让constructor指向Person的操作会带来问题,为此我们重设构造函数(ES5兼容)
Object.defineProperty(Person.prototype,"constructor",{
enumerable: false,
value: Person
});//重设构造函数
var friend = new Person();
Person.prototype.sayHi = function(){
alert("hi");
};
friend.sayHi(); //"hi" 即使friend实例在添加新方法之前创建,也可以访问
重写原型对象后,对实例对象通过 [[prototype]] 访问原型对象的属性方法有影响。
调用new Person()创建对象实例时,实例对象会有一个内部属性_ proto _指向原型对象Person.prototype
1) 如果先用new Person()创建实例,后重写Person.prototype,实例中的 _ proto _ 指向的是函数创建时生成的原型对象。
2)如果先重写Person.prototype,后用new person()创建实例,实例中的_ proto _ 指向的是改写后的新原型对象。
//第一种情况:先创建实例,后重写
function Person(){
} //函数创建 同时创建prototype对象,获得constructor属性
var friend = new Person(); //实例中的 _ proto _ 指向上面函数创建时生成的原型对象。
Person.prototype = {
constructor : Person,
name : "Mike",
age : 29,
job : "student"
sayName : function(){
alert(this.name);
}
}; //重写原型,构造函数指向新的原型对象
friend.sayName(); //error 函数创建时生成的原型无sayName属性
//第二种情况:先重写,后创建实例
function Person(){
}
Person.prototype = {
constructor : Person,
name : "Mike",
age : 29,
job : "student",
sayName : function(){
alert(this.name);
}
};
var friend = new Person();
friend.sayName(); //"Mike"
function Person(){
}
Person.prototype = {
name: "Mike"
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Amy";
console.log(person1.name); //"Amy"
console.log(person2.name); //"Mike"
person1.job = "student";
console.log(person1.job); //"student"
console.log(person2.job); //undefined
2)对于引用值,若有一个对象修改引用值,其他实例会共享这个引用类型,所以会受到影响。由图可知,修改引用值对原型有影响。
function Person(){
}
Person.prototype = {
constructor : Person,
name : "Mike",
age : 29,
job : "student",
friends: ["Amy","Ann"],
sayName : function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
console.log(person1.friends); //"Amy,Ann,Van"
console.log(person2.friends); //"Amy,Ann,Van"
console.log(person1.friends === person2.friends); //true
构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.freiends = ["Amy","Ann"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
};
var person1 = new Person("Nicholas",29,"Software Engineer");
var person2 = new Person("Greg",27,"Doctor");
person1.friends.push("Van");
console.log(person1.friends); //"Amy,Ann,Van"
console.log(person2.friends); //"Amy,Ann"
console.log(person1.friends == person2.friends);//false 它们引用了不同的数组
console.log(person1.sayName == person2.sayName); //true
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if(typeOf this.sayName != "function"){ //只会在sayName()方法不存在时才会将其添加到原型 使用动态模型时,不能使用字面量重新写原型
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friend = new Person("Mike",20,"student");
friends.sayName();
基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
假设我们要创建一个具有额外方法的特殊数组。由于不能直接修改Array构造数组,因此可以使用这个模式。
function SpecialArray(){
var values = new Array();
values.push.apply(values, arguments);
values.toPipedString = function(){
return this.join("|");
}
return values;
}
var colors = new SpecialArray("red", "blue", "green");
console.log(color.toPipedString());//"red|blue|green"
稳妥对象:指没有公共属性,且其方法也不用this对象。适合在安全的环境中,或者防止数据被其他应用程序改动时使用。
与寄生构造模式不同:
1)新创建对象的实例方法不引用this
2)不使用new操作符调用构造函数。
function Person(name, age, job){
//创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
var friend = Person("Mike", 29, "Engineer");
friend.sayName();//"Mike" 除了这个方法,没有其他方法可以访问name值
说明一点:寄生构造函数模式和稳妥构造函数模式所创建的对象与构造函数的原型没有关系,所以不能用instanceof操作符来确定对象。