理解原型对象:
不管何时,只要建立了新函数,就会根据一组特定的规则为该函数建立一个 prototype属性,这个属性指向函数的原型对象。在默认状况下,全部原型对象都会自动得到一个constructor属性,这个属性包含一个指向prototype属性的所在函数的指针。(重写原型属性会默认取消constructor属性)详细可见文章下图。
建立object实例的方式有三种:对象字面量表示法、new操做符跟object构造函数、(ECMAScript5中的)object.create()函数。下面主要讲的是最为复杂的new操做符跟object构造函数的建立对象实例的方法。函数
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("NIcho",29,"software engineer");
工厂模式的问题
工厂模式虽然解决了建立多个类似对象的问题,可是没有解决对象识别的的问题(即怎样知道一个对象的类型)。测试
建立对象的自有属性的主要方法,每定义一个函数就实例化了一个对象。this
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); } } //将构造的函数Person实例化,传入的参数做为自有属性,而且继承自Person.prototype var person1=new Person(NIcho",29,"software engineer); var person2=new Person(Jhon",26,"software engineer);
Person函数代替了createPerson函数,还有如下不一样之处:spa
咱们在这个例子中建立的对象既是object的实例又是person的实例(全部对象均继承自object。object能够理解为对象的根原型,咱们所用到的全部对象方法的操做,如toString 、substring 、splice等都是继承自object)。prototype
alert(person1 instanceOf Object);//true alert(person1 instanceOf Person);//true
构造函数模式的问题:指针
使用构造函数的主要问题是,每一个方法都要在每一个实例上建立一次。person1和person2都有一个sayName的方法,但两个方法不是同一个Function实例(ECMAScript中的函数是对象,每定义一个函数就是实例化了一个对象)。
若是没有自定义向person原型中添加属性,实例化以后则获得Person自有属性的一个副本,而且继承object(注:person1与person2中sayName()引用位置不一样)。code
经过把函数定义转移到构造函数外来解决:对象
//全局函数 function sayName(){ alert(this.name); }
对应的:继承
this.sayName:sayName;
用于定义实例共享的属性和方法。索引
function Person(){ } Person.prototype.name="Nicho"; Person.prototype.age=29; Person.prototype.jod="software engineer"; Person.prototype.sayName=function(){alert(this.name)}; var person1=new Person(); person1.sayName();//"Nicho" 继承Person.prototype var person2=new Person(); person2.sayName();//"Nicho"继承Person.prototype alert(person1.sayName==person2.sayName);//true 引用位置相同
(注:继承链接存在与实例与构造函数的原型之间而不是实例与构造函数之间)
关于原型上的一些函数:
obj.isPrototypeOf(obj1)//返回true/false
object.getPrototypeOf(obj1)
Obj1.hasOwnProperty();//若是obj1有自有属性则返回true,不然返回false
组合使用构造函数模式和原型模式既能够继承原型对象的属性,又能够设置自由属性。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } Person.prototype={ constructor:Person, sayName:function(){ alert("Hi"); } } var person1=new Person(“Nicho”,29,”software engineer”); //person1自有属性:name aga job;原型属性(来自继承):constructor sayName
代码读取某个对象的某个属性时的搜索方法
搜索首先从对象实例开始,若是实例中找到了给定名字的属性,则返回该属性的值。若是没有找到,则继续搜索指针指向的原型对象,在原型对象中查找给定名字的属性。若是在原型对象中找到了该属性,则返回属性的值,不然一直向上查找给定名字的属性,直到object。若是没有找到则返回undefined。
这就须要咱们注意:虽然咱们能够经过对象实例访问原型中的值,但却不能经过对象实例(注意!!这里说的是经过对象实例而不是在原型中修改属性)来重写原型中的值。若是在实例中添加了一个与实例原型中同名的属性,该属性将会屏蔽原型中的那个属性。
来看下面的的例子:
function Person(){ } Person.prototype.name="Nicho"; Person.prototype.age=29; Person.prototype.jod="software engineer"; Person.prototype.sayName=function(){alert(this.name)}; var person1=new Person(); var person2=new Person(); person1.name="Greg"; alert(person1.name);//Greg-来自实例 alert(person2.name);//Nicho-来自原型; //person1对属性 name的修改并不能修改原型上的相应属性,所以person2继承自原型
person1=new Person()
与 person3=person1
是不一样的:前者是实例化一个对象,后者遵循复制函数(对象引用)的原理var person3=person1;//整个对象的复制即引用相同(指针指针指针!只是一个索引,实际存储都不在指针位置下) person3.name=”newName”;//修改属性名称的值 alert(person1.name);//newName;//另外一个来自此处的引用属性的值会修改;即复制对象原理 alert(person1.isPrototypeOf(person3));//false alert(Person.prototype.isPrototypeOf(person3));//true
var person1=new Person("NIcho",29,"software engineer"); var person2=new Person("HIcho",9,"Hardware engineer"); person1.name=person2.name;//简单的属性的复制,并不是引用同一个属性值 person2.name="NEW";//尝试赋新值 alert(person1.name);//HIcho,说明是值的传递而并不是引用
function superType(){ this.property=true; } superType.prototype.getSuperValue=function(){ return this.property; }; function subType(){ this.subProperty=false; } //继承superType subType.prototype = new superType(); subType.prototype.getSubValue = function(){ return this.subProperty; }; var instance = new subType(); alert((instance.getSuperValue)());//true getSuperValue来自superType.prototype alert(instance.property);//true property来自superType实例
实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于superType的实例的全部属性和方法(包括自有属性和继承属性),如今也存在于subType.prototype中了。
(此时instance.constructor如今指向的是superType,由于subType的原型指向了另外一个对象superType的原型,而这个原型对象的constructor指向的是superType)
注意:
alert(instance instanceOf superType);//true alert(instance instanceOf subType);//true
alert(superType.protoType.isPrototypeOf (instance));//true alert(subType.protoType.isPrototypeOf (instance));//true
原型链的问题:
在经过原型来实现继承时,原型实际上会变成另外一个类型的实例。因而,原先的实例属性也就瓜熟蒂落地变成了如今的原型属性了。
解决原型链继承带来的问题的方法
解决这个问题的技术是借用构造函数,使用最多的继承模式是组合继承。此外还有原型式继承,寄生式继承,寄生组合继承。这里主要讲借用构造函数和组合继承。
在子类型构造函数的内部调用超类型的构造函数。
function superType(){ this.colors=["red","blue","green"]; } function subType(){ //继承了superType superType.call(this);//在新建立的subType实例的环境下调用superType函数 //这样一来,就会在新subType对象上执行superType函数中定义的全部初始化代码 //结果subType的每个实例都会具备本身的color属性的副本了 } var instance1=new subType(); instance1.colors.push("black"); alert(instance1.colors);//red,blue,green,black var instance2=new subType(); alert(instance2.colors);//red,blue,green
思路是使用原型链实现对属性和方法的继承,而经过借用函数来实现对实例属性的继承。
function superType(name){ this.name = name; this.colors = ["red","blue","green"]; } superType.prototype.sayName=function(){ console.log(this.name); } function subType(name,age){ superType.call(this,name); this.age = age; } subType.prototype = new superType(); subType.prototype.constructor = subType; subType.prototype.sayAge = function(){ console.log(this.age); } var instance1 = new subType("Nicho",29); instance1.sayName();//”Nicho” instance1.sayAge();//29
总结:
this.age = age
superType.prptotype.sayName subtype.prototype.sayAge function superType(name){ this.name=name; this.colors=["red","blue","green"]; }
组合式继承的问题
组合式继承是js最经常使用的继承模式,但组合继承的超类型在使用过程当中会被调用两次;一次是建立子类型的时候,另外一次是在子类型构造函数的内部。咱们使用Object.create
作一下改进:
function superType(name){ this.name = name; this.colors = ["red","blue","green"]; } superType.prototype.sayName=function(){ console.log(this.name); } function subType(name,age){ superType.call(this,name); this.age = age; } function inheritPrototype(sup,sub){ const obj = Object.create(sup); obj.constructor = sub; sub.prototype = obj; } // 继承 inheritPrototype(superType,subType);
下面让咱们看下一种状况:重写原型对象(彻底重写而不是简单的添加原型对象的属性)
咱们知道,在调用构造函数是会为实例添加一个prototype指针,而把这个原型修改成另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型而不指向构造函数。
function Person(){}; var friend=new Person(); Person.prototype={ Constructor:Person, Name:“Nicholas”, Age:29, sayName=function(){ alert(“this.name”); } } friend.sayName();//error
重写原型对象以前:
重写原型对象以后:
由图可见:重写原型对象切断了现有原型与任何以前已经存在的对象实例之间的联系,它们引用的还是最初的原型。