原型与构造函数总结 二
原型
无论什么时候只要创建了一个新的函数,就会有一组特定的规则为该函数去创建一个prototype属性,这个属性都指向的是原型对象而所有的原型对象都会默认获得一个constructor属性,这个属性包含一个指向prototype属性所在的函数指针People.prototype.constructor指向People---通过这个构造函数可以为原型对象添加其他的属性和方法
案例:
function People(){} People.prototype.name="zhangsan";
People.prototype.age=18;
People.prototype.sex="male"
People.prototype.say=function(){
alert(this.name);
}
var p1=new People();
p1.name="xiaozuo";
alert(p1.name)-----注意这里打印出来的值是指我们给定的值,覆盖了原型中的值,但是并没有改变它
var p2=new People();
alert(p2.name)----这里打印出来的是原型中的值
注意检测 原型是否属于对象
注意我们在打印p1的时候,在打印的过程中因为已经找到了name这个属性,也就是xiaozuo这个值,所以不会再向上找值(最终一直找到原型当中去) 但是p2没有给他赋值也就是说p2本质上来说没有name这个属性的,所以他必须找到一个值并且打印出来,所以一直会找到原型当中去
所以这里的根本原因是当为对象的实例添加了一个属性的时候,这个属性就会屏蔽原型对象中的同名属性,换个角度来说,也就是阻止去找这个默认的属性 哪怕你赋值为null也会去找
People.prototype.name=null;
同样的,当我们给这个属性赋值为null的时候会去找上方的东西嘛? 注意修改两个地方
People.prototype.name="zhangsan" ;
var p1=new People();
p1.name=null;
alert(p1.name)
打印结果为null 也就是说我们新覆盖的值即使为null他也会组织去寻找那个原型中的值
我们可以通过delete这个来取消覆盖
delete p1.name
alert(p1.name)
重要函数hasOwnProperty()---检测一个属性是存在于原型还是实例中
var p1=new People();
p1.name="zhangsan";
alert(p1.hasOwnProperty("name"))----true因为我们为p1给了一个name的属性值
var p1=new People();
//p1.name="zhangsan";
alert(p1.hasOwnProperty("name"))--false
in关键字
使用in关键字检测对象是否拥有某个属性
var p1=new People();
alert(p1.hasOwnProperty("name"))--false我们并没有为p1赋值name属性,但是他会从原型中找到这个值
alert("name" in p1)---true name属性从原型当中而来
枚举取得对象所有的属性
var key=Object.keys(People.prototype)
alert(key)
注意点
var key=Object.keys(People.prototype)
alert(key)
var p1=new People();
p1.name="zhangsan";
p1.age=13;
var keys=Object.keys(p1)
alert(keys)
原型简写
上面案例中使用原型是不是有点麻烦呢?每一个属性都需要People.prototype.age=18; 这么的方式去写,那有没有简单一点的办法呢?
function People(){}
People.prototype={
name:"zhangsan",
age:18,
gender:'male',
say:function(){
alert(this.name)
}
}
在这里我们将People的prototype设置为等于一个以对象字面量形式创建的新对象,和上面的结果是一样的 但是,我们看这样一个东西
function People(){}
People.prototype={
name:"zhangsan",
age:18,
gender:'male',
say:function(){
alert(this.name)
}
}
var zhangsan=new People();
alert(zhangsan instanceof Object)
alert(zhangsan instanceof People)
alert(zhangsan.constructor==People) // 打印结果为false
alert(zhangsan.constructor==Object)
原因,我们这次的写法和之前有所不同,每创建一个函数,就会创建它的prototype对象,这个对象会自动获得constructor属性,而我们这次的写法,实际上完全覆盖了默认的prototype对象,因此constructor也指向了别的东西(Object)
function People(){}
People.prototype={
name:"zhangsan",
age:18,
gender:'male',
say:function(){
alert(this.name)
}
}
var zhangsan=new People();
alert(zhangsan instanceof Object)
alert(zhangsan instanceof People)
alert(zhangsan.constructor==People) // 打印结果为false
alert(zhangsan.constructor==Object)
原因,我们这次的写法和之前有所不同,每创建一个函数,就会创建它的prototype对象,这个对象会自动获得constructor属性,而我们这次的写法,实际上完全覆盖了默认的prototype对象,因此constructor也指向了别的东西(Object)
如果constructor非常重要,则可以这样
function People(){}
People.prototype={
constructor:People,
name:"zhangsan",
age:18,
gender:'male',
say:function(){
alert(this.name)
}
}
var p=new People();
alert(p.constructor)
原型模式
function People(){}
People.prototype={
name:"zhangsan",
age:18,
gender:'male',
say:function(){
alert(this.name)
}
}
var zhangsan=new People();
alert(zhangsan.constructor)
构造器模式
function People(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=new Function("alert(this.name)")
var zhangsan=new People("zhangsan",18,"male");
alert(zhangsan.constructor)
原型动态性:
function People(){}
People.prototype.name="zhangsan";
People.prototype.age=18;
People.prototype.sex="male"
People.prototype.say=function(){
alert(this.name);
}
var p1=new People();
p1.say=function(){
alert("hi")
}
p1.say()
虽然第二个say方法是在p1对象构造出来之后才改写的,但是依然能用 但是:
function People(){}
var p1=new People();
People.prototype={
name:"zhangsan",
age:18,
gender:'male',
say:function(){
alert(this.name)
}
}
p1.say()---报错了,因为原型是咋创建了对象之后才改写的
当然除了我们自定义的对象有原型外,原生态的对象也是有原型的
继承: 关于继承我之前已经讲过一种方式了call继承 那么继承到底有什么好处呢?继承是OO编程中最重要的思想
原型链:
ECMAscript中描述了原型链的概念,并且将原型链作为最主要的继承方式,主要思想是让一个引用对象继承另一个引用对象的属性和方法
使用原型链:
function parentClass() {
this.methoda = function() {
alert("parentClass method");
}
}
function subClass() {
this.methodb = function() {
alert("subClass method");
}
}
subClass.prototype = new parentClass();
var temp = new subClass();
temp.methoda();
temp.methodb();
案例2:
function SuperType(){
this.colors=["red","yellow","green"]
}
function SubType(){}
SubType.prototype=new SuperType();
var v=new SubType();
alert(v.constructor)
原型链的问题1:
function SuperType(){
this.colors=["red","yellow","green"]
}
function SubType(){}
SubType.prototype=new SuperType();
var v=new SubType();
alert(v.colors )
v.colors.push("hello")
var v2=new SubType();
alert(v2.colors )---注意这里,因为继承,所以所有共享实力都使用了同一个原型,原型中会发生改变
原型问题2:没法向父类构造参数中传递参数
所以一般比较少的使用原型链
比较多的使用构造函数继承,最简单的就是之前的call方法了
原型式继承
大师级人物道格拉斯提出了种经典的函数
function object(people){
function F(){}
F.prototype=people;
return new F();
}
和原型区分开来 在函数类不限创建了一个临时的构造函数,然后将传入的对象作为原型,最后返回临时对象的一个新实力,从本质来讲的话object()对传入其中的对象执行了一次复制
ECMAscript通过新增Object.create()方法来规划了原型继承,这个方法和接受两个参数 1:新对象原型的对象2:新对象定义额外属性的对象(可选) 在传入一个参数的情况下Object.create() 和object()方法是一样的 var people={ name:"zhangsan" } var student=Object(people); student.name="lisi"; alert(people.name)// 注意这里,和原型模式是一样的,类型的值始终会被共享出去
案例2:
var people={
name:"zhangsan"
}
var student=Object(people);
student.name="lisi";
alert(student.name);
var student2=Object(people);
student2.name="wangwu";
alert(student2.name);
在没有必要大规模创建构造函数,仅仅让一个对象和另外一个对性保持类似的情况下,原型继承是完全可以胜任的
关于js中继承有很多中方式,这里就不一一列举了