基于JS的对象创建的常用方法及原理分析
序
俗话说“在js语言中,一切都是一个对象”,创建对象的方法有很多,所以今天我们就来整理一下。
最简单的方法
JavaScript创建对象最简单的方法是使用对象的文字形式或对象构造函数
对象的文字形式
var person=new Object();person.name=' jack人。说出name=function () {alert (this。名称)}使用对象构造函数
var person={ name: ' jack说出名字: function () {alert(这个。name)}}有明显的缺点:在创建多个对象时,会出现代码重复,于是“工厂模式”应运而生
工厂模式
通俗地理解工厂模型,工厂:“我创造一个对象,我对整个创造过程负责,但任务完成后,对我来说什么都没有。O(_)O哈哈~”
函数createPerson(name){ var o=new Object();o.name=nameo . SayName=function(){ alert(this . name)} return o } var P1=new createPeer(' jack ');明显的缺点:所有的对象实例都是“对象”类型,这几乎区分了类型!你说分不清类型就分不清。我不相信!然后让我们看看代码:
var p1=新的create person(' jack ');var p2=new create person(' Lucy ');console.log(对象的p1实例);//trueconsole.log(对象的p2实例);//真实你看,就是这个原因;因此,为了解决这个问题,我们采用了“构造函数模式”
构造函数模式
构造器模式,也就是这个函数,我只创建某个类型的对象实例,其他什么都不关心(注意,这里已经有了类型的概念,感觉就是一个小团体)
函数Person(name){ this . name=name;this . SayName=function(){ alert(this . name)} } function Animal(name){ this . name=name;this . say name=function(){ alert(this . name)} } var p1=new Person(' jack ')p1 . say name()/' jack ' var a1=new Animal(' doub ')a1 . say name()//doub ' console . log(Person的P1实例)//true console . log(Animal的A1实例)//true console . log(Animal的P1实例)//false (P1显然不是动物类型,因此是false)console . log(Person的A1实例)//false (a1显然不是人类型,因此也是false)。上面的代码证明了构造器模式确实可以区分对象类型。模型是完美的,但它不是。让我们看看下面的代码:
//然后代码控制台. log (P1。sayname==a1。sayname)//false上面发现什么问题了吗?“p1”的“说出名字”与“a1”的“说出名字”不同。这是什么意思?解释一下‘构造器模式’根本没有‘公共’的概念,每个创建的对象实例都有自己的一套属性和方法,‘属性是私有的’,这个我们可以理解,但是你要自己做一套方法,这有点没必要。
明显的缺点:如上所述,为了解决这个问题,出现了一个新的模式‘原型模式’,简单来说就是一个阶段性的跳跃。让我们看看“原型模式”
原型模式
这里要记住一点:构造函数中的属性和方法不是在每个对象实例之间共享的,它们都是单独设置的;为了实现共享,属性和方法应该存储在构造函数的原型中。这句话是什么意思?下面我们来详细解释一下
构造函数建立后(普通函数也是如此),会自动生成一个‘原型’,构造函数和‘原型’之间是一一对应的关系,此时‘原型’中只有一个‘构造函数’属性(不,显然还有另外一个` __proto__ ',我们先不在这里讨论,以后再说。
这个构造函数是什么?它是一个类似指针的引用,指向原型的构造函数,默认情况下指针必须存在
Console.log (person。prototype . constructor===person)//true刚才说‘prototype’是‘自动生成的’,其实还有另外一种手动生成‘prototype’的方式:
函数Person(name){ this . name=name } Person . prototype={//constructor : Person,Age: 30} console.log (person。原型)//对象{age: 30} console.log(人。prototype . constructor===person)//false tips:为了证明可以为构造函数手动创建‘prototype’,在这里添加‘prototype’
也许你已经注意到了一个问题,这一行代码:
console . log(person . prototype . constructor===person)//false为什么结果是‘false’兄弟,默认生成‘prototype’,然后我们用了另一种方法:手动设置。详细分析手动设置的原理:
1.构造函数的“原型”实际上是一个对象
2.当我们以这种方式设置“原型”时,我们实际上切断了原始的“人物.原型”,然后重新引用另一个对象
3.此时,构造函数可以找到“原型”,但“原型”找不到构造函数
人。prototype={//构造函数: person,//因为构造函数属性,我没有声明,prototype用它来找构造函数,你忘了声明age: 30}4。因此,如果您想显示手动设置构造函数的原型而不丢失它们之间的连接,我们需要这样做:
函数人(名称){this。name=name } person . prototype={ constructor : person。//构造函数一定不要忘记!Age: 30}画外音:“这里,你还没有谈到原型模式是如何实现属性和方法共享的。”别担心,马上开始:
对象实例-构造函数-原型之间是什么关系?
你明白这幅画的意思吗?
1.当一个对象实例访问一个属性时(方法保持不变),如果它本身没有该属性,它将通过` _ _ proto _ _ _ '链在构造函数的prototype '上寻找它。
2.构造函数与原型具有一对一的关系,与对象实例具有一对多的关系,而不是为创建的每个对象实例生成一个“原型”。这是原型模式的核心。结论:在原型上声明属性或方法可以使对象实例共享它们。
那么原型图案完美吗?不,它有以下两个主要问题:
问题1:如果对象实例具有与原型同名的属性或方法,则当访问属性或方法时,实例上的将屏蔽原型上的
函数Person(name){ this . name=name } Person . prototype={ constructor : Person,name : ' Lucy ' } var P1=new Person(' jack ');console . log(P1 . name);//jack问题2:由于原型上的属性和方法在实例之间是共享的,当其中一个对象实例修改原型上的属性(基础值、非引用类型值或方法)时,其他实例也会受到影响
原因是当实例本身的基本值属性与原型同名时,实例会创建该属性并保存起来以备将来使用,但原型上的属性不会被修改;但是,如果属性是引用类型值,比如Array '和Object ',当出现重名时,实例不会复制一个新的供自己使用,或者坚持在实例之间共享,就会出现上图的情况
以上两个问题是原型模式的明显缺点。为了摆脱这些缺点,我们通常采用“构造器模式和原型模式相结合”的组合模式。事实上,在原型模式部分,已经应用了这个模式
将构造器模式和原型模式结合起来
这种模式可以描述为构造模式和原型模式的优点。构造器模式用于定义对象实例的属性或方法,而共享属性或方法则交给原型模式
函数person (name)的属性{this。name=name//instance在构造函数} person中声明。原型={构造函数:人。SayName: function () {//在原型alert(this.name)}中存在共享方法}注意:此模式是目前在ECMAScript中创建自定义类型使用最广泛且得到高度认可的方法
-
下面要介绍的模式是针对不同场景的,而不是说组合使用构造器模式和原型模式有缺点,用这些模式来弥补,事实并非如此
动态原型模式
特性:共享方法在构造函数中被检测和声明,原型不被显示和创建
函数Person(name){ this . name=name;if (typeof this.sayName!=='function') {//检查方法是否存在console.log('sayName方法不存在')person . prototype . say name=function(){ alert(this。name)} } else { console . log(' say name方法已经存在')} } var P1=new Person(' jack//' say name方法不存在' P1 . say name();//既然sayName不存在,那我们就创建它,所以这里输出‘jack’var p2=new Person(‘Lucy’);//“SayName方法已经存在”p2 . SayName();//此时,sayName已经存在,所以输出‘Lucy’。当第一次调用Person的构造函数时,` sayname '方法将被添加到Person . prototype ';书《Javascript高级程序设计》说:使用动态原型模式时,不能用对象文字重写原型。让我们明白:
分析:
1.`p1 '实例已创建。此时,原型没有' sayName '方法,所以我们将为原型添加一个。
2.随后,我们以文字量的形式重写了原型。此时,旧原型没有被破坏,它仍然与‘P1’保持联系
3.下面的例子,也就是这里的‘p2’,与新原型保持联系;因此,“p1”和“p2”有自己的构造函数原型,即使它们的构造函数是相同的
所以请记住:当我们采用动态原型模式时,千万不要以文字量的形式重写原型
寄生构造模式
在理解这个模式之前,让我们思考一个问题:为什么要用‘new’关键字调用构造函数?说代码:
我们发现了什么?如果构造函数没有被' new '方法调用,那么就需要显式地' return ',否则构造函数就没有返回值;但是如果你使用“新的”,就没有这样的问题
现在让我们看看寄生构造函数模式:
函数Person(name){ var o=new Object();o.name=nameo . SayName=function(){ alert(this . name)};返回o}var p1=新人(“杰克”);//与工厂模式唯一的区别是p1.sayName()是用new调用的;//jack,其实new不new也没关系,因为我们已经明确返回了o。
那么寄生构造器模式的应用场景是什么呢?例如,根据《javascript高级程序设计》一书,如果我们想用额外的方法创建一个特殊的数组,那么我们可以这样做:
function SpecialArray () {var值=new Array();Array.prototype.push.apply(值、参数);values . topipedestring=function(){ return this . join(' | ')}返回值} var colors=new SpecialArray(' red ',' blue ',' green ');最后一个重要的警戒点(颜色。topipestring())//‘red | blue | green’是这个模式与构造函数和原型没有联系,也就是说不可能区分实例类型,因为这个模式生成的实例的构造函数和原型都是Object.prototype
健壮的构造器模式
与寄生构造函数相比,这种模式有两个主要区别:
1.创建对象实例的方法不涉及这一点
2.不使用新运算符调用构造函数
根据安全构造函数的要求,以前的Person构造函数可以改写如下:
函数Person(name){ var o=new Object();o . say name=function(){ alert(name)//这其实涉及到闭包的知识,这就引出了私有属性的概念。}返回o}这种模式最适合一些安全的环境(在这些环境中禁止这种和新的)。同样,这种模式与构造函数和原型没有联系。
标签
以上是js中创建对象方式的总结,希望对大家有所帮助
本文对基于JS的对象创建的常用方法和原理的分析,是边肖分享的全部内容,希望能给大家一个参考和支持。
版权声明:基于JS的对象创建的常用方法及原理分析是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。