简介据说创建对象有字面量方法、工厂模式和构造函数模式。原来它们都有各自的缺点,那我介绍一下几种创建对象的方式,争取找到一种无痛模式?原型模式下会有一个非常晦涩的内容,大家密切关注,不要翻车。我们创建的每个函数都有一个原型属性,它是一个指向对象的指针(地址),该对象的目的是包含可以由特定类型的所有实例共享的属性和方法。functionPerson(){}Person.prototype.name='李小花';Person.prototype.age='60';Person.prototype.sayName=function(){console.log(this.name);};varperson1=newPerson();person1.sayName();//李晓华varperson2=newPerson();person2.sayName();//在李小华Person的prototype属性中,构造函数,构造函数是一个空函数。新对象的属性和实例是共享的。现在我们需要做一件事,就是了解原型对象。理解原型对象每当创建一个新函数时,都会根据一组特定的规则为该函数创建一个原型属性。该属性指向函数的原型对象。所有的原型都会得到一个constructor属性,这个属性指向prototype属性所在函数的指针。我们通过这个构造函数向原型对象添加其他方法和属性。对象创建后,其原型对象默认只有constructor属性。当构造函数创建一个新实例时,该实例将包含一个指向构造函数原型的指针。尽管在脚本中没有访问[[Prototype]]的标准方法,但除IE外,其他所有工具都支持一种名为_proto_的方法。该属性直接存在于实例和构造函数的原型对象上,而不存在于实例和构造函数之间。你困惑吗?下面我就用一张图来展示一下各个之间对应的护理。好看,我就展示一下我高超的绘画功底。灵魂图已经展示出来了,相信大家都已经看懂了。需要注意的是,虽然这两个实例不包含属性和方法,但是我们可以在实例上调用原型上的方法,这是通过查找对象属性的过程来实现的。拓展一下,由于js不能实例化[[Prototype]],这个关系可以通过isPrototype()方法来判断。console.log(Person.prototype.isPrototypeOf(person1));//true在es5中增加了一个方法Object.getPrototype(),返回[[Prototype]]的值,例如console.log(Object.getPrototypeof(person1)==Person.prototype)//trueconsole.log(Object.getPrototypeof(person1).name)//李晓华但是同样的,IE8以下的浏览器也不支持这个属性。每当代码读取对象的属性时,都会执行搜索。目标是给定名称的属性。搜索从实例本身开始。如果在实例本身中找到该属性,则返回该属性的值。如果没有找到,则继续到指针所指向的原型对象,如果找到,则返回该属性的值。虽然可以通过对象实例访问原型中存储的值,但是无法通过对象覆盖原型中的值。如果我们向实例添加一个与实例原型中的属性同名的属性,该属性将覆盖原型中的该属性。functionPerson(){}Person.prototype.name='李小花';Person.prototype.age='29';Person.prototype.job='班花';Person.prototype.sayName=function(){console.log(this.name);};varperson1=newPerson();varperson2=newPerson();person1.name='张全蛋';console.log(person1.name);//掌权单控制台.log(person2.name);//当李晓华给一个对象添加属性时,这个属性会屏蔽原型对象的同名属性。添加这个属性只会阻止我们访问原型的属性,而不会修改那个属性。使用delete属性允许我们访问原型中的属性。functionPerson(){}Person.prototype.name='李小花';Person.prototype.name='29';Person.prototype.job='班花';Person.prototype.sayName=function(){console.log(this.name);};varperson1=newPerson();varperson2=newPerson();person1.name='张全蛋';console.log(person1.name);//掌权单控制台.log(person2.name);//李晓华deleteperson1.nameconsole.log(person1.name);//李晓华在遍历一个对象的属性时,我们经常需要判断这个属性是来自于原型还是来自于对象的属性。functionPerson(){}Person.prototype.name='李小花';Person.prototype.name='29';Person.prototype.job='班花';Person.prototype.sayName=function(){console.log(this.name);};varperson1=newPerson();varperson2=newPerson();console.log(person1.hasOwnProperty('name'));//falseperson1.name='张全蛋';console.log(person1.hasOwnProperty('name'));//trueconsole.log(person1.name);//张全丹console.log(person2.name);//李晓华删除person1.nameconsole.log(person1.name);//李晓华我们有一个神器,hasOwnProperty方法,只有当属性是实例属性时才返回true。上面我介绍的原型操作符中的in方法如何通过hasOwnProperty方法判断属性是否来自对象属性,那么如何判断属性是否来自原型链呢?下面隆重介绍了in操作符,但是无论属性来自属性还是来自原型链的属性,都会返回true。我们使用这个属性结合hasOwnProperty方法来判断这个属性是否来自于原型链。functionhasPrototypeproperty(obj,name){return!object.hasOwnProperty(name)&&(nameinobj)}functionPerson(){}Person.prototype.name='李晓华';Person.prototype.name='29';Person.prototype.job='Banhua';Person.prototype.sayName=function(){console.log(this.name);};varperson1=newPerson();console.log(hasPrototypeproperty(person,'name'));//trueperson1.name='张全蛋';console.log(hasPrototypeproperty(person,'name'));//false使用for-in循环时,返回的对象是可以访问的,可枚举属性,包括实例中的属性,也包括原型中存在的属性,屏蔽原型中的不可枚举属性也会返回进。你可以通过es5中的Object.keys方法返回一个包含所有可枚举属性的字符串数组。functionPerson(){}Person.prototype.name='李小花';Person.prototype.age='29';Person.prototype.job='班花';Person.prototype.sayName=function(){console.日志(this.name);};varkeys=Object.keys(Person.prototype);console.log(keys);//命名年龄工作sayNamevarp1=newPerson();p1.name='wholeegg';p1.age=20;varp1keys=Object.keys(p1);console.log(p1keys);//很多时候我们不想费心去写name和age,所以我们会简单声明原型模式如下。functionPerson(){}Person.prototype={name:'李小花',age:30,job:'班花',sayName:function(){console.log(this.name)}}Object.defineProperty(Person.prototype,'constructor',{enumerable:false,value:Person})functionPerson(){}varfriend=newPerson();Person.prototype={constructor:Person,name:'李小华',age:30,job:'Banhua',sayName:function(){console.log(this.name)}};friend.sayName();//UncaughtTypeError:friend.sayNameisnotafunction是不是看了这段代码一头雾水,之前白学了?李晓华不应该出口吗?还记得我们之前说过的,创建构造函数会为实例添加一个指向原原型的[[Prototype]]指针,这个代码修改切断了构造函数和原型之间的联系,所以记住,实例中的指针只是指向原型,而不是构造函数。为了增进大家的认识,我必须从我的老本行做起,为大家画出一张流畅的地图。重写原型对象前重写原型对象后原对象的问题那么原对象有没有缺点,如你所愿,当然有缺点,那么原对象有没有缺点,如你所愿,当然有缺点,原型中的很多实例被很多实例共享,很适合做函数,基本值的属性也是可以的。您可以通过向实例添加属性来隐藏原型中的相应属性。对于包含引用类型的值属性而言,问题更为突出。functionPerson(){}Person.prototype={constructor:Person,name:'李小花',friends:['全蛋','大队长']}varperson1=newPerson();varperson2=newPerson();person1.friends.push('dogegg');console.log(person1.friends)//["wholeegg","bigleader","dogegg"]console.log(person2.friends)//["全蛋","大船长","狗蛋"]person1.friends=[1,2,3]console.log(person1.friends)//[1,2,3],切断与prototypePointerconsole.log(person2.friends)//["Quandan","Captain","Dogdan"]业界公认的模式是构造函数模式和原型模式的结合functionPerson(){name:'李晓华',friends:['全蛋','船长']}Person.prototype={constructor:Person,sayName:function(){console.log(1)}}剩余动态原型模式、寄生构造器模式和安全构造器mode很少用到,这里就不介绍了。
