【javascript笔记】关于函数的构造函数和prototype
一,构造函数创建对象
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.getName=function(){
return this.name;
}
this.getAge=function(){
return this.age
}
this.getJob=function(){
return this.job;
}
}
//请看这里 咱们新建一个对象
var person1=new Person('nicolas',29,'software engineer');
var person2=new Person('jack',29,'software engineer');
person1.getName(); // nicolas
person2.getName();// jack
到此为止 咱们做一个实验:alert(person1.sayName==person2.sayName) //输出为false.
不是同一个函数对象这似乎和许多强类型语言 比如java和c#类似 声明类的属性 然后设置getset方法去调用
但是 为每一个对象都去初始化这样 “执行同一个动作”的函数对象 是否合乎情理呢,这样我们就自然而然想到了j
ava中的静态关键字,
我们姑且可以将prototype理解为静态的成员或者方法,new的对象是共享的,这样我们就会节约内存?
二,再来看原型模式创建对象
function Person(){
}
Person.prototype.name='nicolas';
Person.prototype.age=29;
Person.prototype.job='software engineer';
Person.prototype.sayName=function(){
return this.name
}
//请看这了 咱们还是新建两个对象
var person1=new Person();
var person2=new Person();
person1.getName();//nicolas
person2.getName();//nicolas</span>
到此为止,咱们仍旧做这个同上的实验 alert(person1.sayName==person2.SayName);//输出为true,是同一个对象重点(理解不到也没关系,楼主也是有点晕)构造函数,原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针 ,而每个实力都包含一个指向原型对象的内部指针。现在我们以第二个声明函数的方式来做个例子:
var person1=new Person();
var person2=new Person();
person1.name="Jack";
alert(person1.name);//Jack
alert(person2.name);//nicolas
这时候大家就会奇怪,不是说原型里面都是共享的吗?为什么person2输出的还是原型设置的nicolas 而不是Jack;请注意:这里我们为person1的name设置了新的值后; "如果我们在实例中添加了一个属性,并且该属性与实例原型中的一个实例重名 ,那我们就是在实例中创建了该属性,换句话说就是:person1.name='Jack';这一句代码表示了这个name是属于实例的属性.它会阻止我们去访问实例的原型中的nicolas";当然我们也可以通过代码delete person1.name; 去删除person1.name这个实例属性,执行了这局代码后:
alert(person1.name);//nicolas 原型属性
javascript里面有很多判断属性是属于实例或者原型的,请大家自行搜索。
三,更加简单的原型语法:以字面量形式创建原型对象
function Person(){ }
Person.prototype={
name:"nicolas",
age:29,
job:"software engineer",
getName:function(){
return this.name;
}
}</span>
第二大点中说过,每创建一个函数就会同时创建他的prototype对象,这个对象也会自动获得constructor属性(指向构造函数的指针);但是这里constructor属性不再指向Person了,这里我们使用的字面量声明方式本质上完全重写了默认的prototype对象,指向构造函数的指针不会指向原来Person,而是Object.
//如果原型指向构造函数的指针真的这么重要,请像下面这样声明:
function Person(){ }
Person.prototype={
constructor:Person,
name:"nicolas",
age:29,
job:"software engineer",
getName:function(){
return this.name;
}
}
//但是这种方式重新设置constructor属性会导致它的[Enumerable]属性设置为true.默认情况下原生的constructor属性是不可枚举的,
//因此如果你是用兼容ECMAScript5的javascript引擎 请像下面这样声明:
function Person(){ }
Person.prototype={
name:"nicolas",
age:29,
job:"software engineer",
getName:function(){
return this.name;
}
}
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false,
value:Person
})
四, 在第三点中提到了用表达式方式声明prototype会或重写默认的prototype对象,这就引出了这一个话题:原型的动态性
<span style="font-size:12px;"> var friend=new Person();
Person.prototype.sayHello=function(){
alert('Hello~')
}
friend.sayHello();//不会报错
//但是 若是这样
function Person(){}
var friend=new Person();
Person.prototype={
...something
sayHello:function(){
alert('Hello~')
}
}
friend.sayHello();//error</span>
用表达式这种方法添加sayHello(),重写了整个prototype 但是当下的friend对象仍然是指向原来的原型,当我们原型整个修改就相当于把原型修改为另外一个对象了,切断了构造函数与最初原型之间的联系。请记住 实例中的指针仅仅指向原型,而不指向构造函数。 相当于当前friend指向的原型 和我们重新写的没有任何关系~五,原型存在的问题
最重要的问题就是 共享的属性若是引用类型,只要任何实例一改动,原型里面的内容也会改动;
function Person(){}
Person.prototype={
name:"nicolas",
job:'software engineer',
age;29,
friends:['sheldon','Lenard']
};
var person1=new Person();
var person2=new Person();
person1.friends.push('Nianlu');
alert(person1.friends);// sheldon,Lenard,Nianlu
alert(person2.friends);// sheldon,Lenard,Nianlu
//再看,他们引用的是否是同一个数组
alert(person1.friends==person2.friends) //true
六,组合使用构造函数和原型模式
//既然两种模式各有优缺点,我们就把两者结合到一起,请看下面的例子
function Person(name,job,age){
this.name=name;
this.job=job;
this.age=age;
this.friends=['Sheldon','Lenard'];
}
Person.prototype={
constructor:Person,
sayHello:function(){
alert('Hello~');
}
}
var person1=new Person('nicolas','software engineer',29);
var person1=new Person('jack','software engineer',29);
person1.friends.push('Kimkadasion');
alert(person1.friends);// sheldon,Lenard,Kimkadasion
alert(person2.friends);// sheldon,Lenard
alert(person1.friends==person2.friends) //false
alert(person1.sayHello==person2.sayHello) //true
结束~