宝哥软件园

小白谈对JS原型链的理解

编辑:宝哥软件园 来源:互联网 时间:2021-11-01

原型链理解起来有点混乱,网上资料很多。每天晚上睡不着的时候,总喜欢在网上找一些原型链和闭包文章,效果极佳。

不要纠结于那堆术语,它真的帮不了你,除了把你的头拧成一团。大致看一下原型链,想想和代码无关的东西,比如人、怪物、shemale。

1)人是母亲生的,妖是母亲生的。人和妖是客体实例,人和妖是原型。原型也是一个对象,叫做原型对象。

2)男人的母亲和父亲可以生一堆婴儿,恶魔的母亲和父亲可以生一堆婴儿,都是建造者,俗称造人。

3)人可以记录捕捉到的信息,所以人可以找到捕捉到的信息,也就是说可以通过原型对象找到构造函数。

4)人可以有很多他妈的宝宝,但这些宝宝只有一个妈妈,这就是原型的独特性。

5)人是人生的,这种关系叫原型链。

6)原型链不是无限的。当你透过人往上看,你会发现人不是他妈的人,也就是原型链最后指向null。

7)他母亲生的人会和别人长得很像,他母亲生的恶魔会很丑,这叫遗传。

8)你遗传了你妈的肤色,你妈遗传了你妈他妈的肤色,你妈他妈的.这是原型链的继承。

9)如果你没有家,那么你的家就是指你妈妈的家;你妈妈也没有家,所以你的家意味着你妈妈他妈的家.这是原型链的向上搜索。

10)你会继承你妈妈的长相,但你也可以染头发,洗头发,吹头发,这意味着对象的属性可以定制,会覆盖继承的属性。

11)虽然你洗过、剪过、吹过、染过黄头发,但你改变不了妈妈的样子。你妈的弟弟妹妹和你的黄头发没有关系,也就是说对象实例不能改变原型的属性。

12)但是如果你的家被玩火烧了,那就意味着你的家、你母亲的家、你兄弟的家都被烧了,这就是原型属性的共享。

13)你妈妈的外号叫阿珍,你邻居的阿姨叫你阿珍。但是你妈妈的头发从飘柔变成金狮王后,隔壁的阿姨们就改了名字叫你金狮王,这就叫原型的动态性。

14)你妈爱美,去韩国整容,以至于认不出妈妈。即使你妈妈的头发变回软软的,隔壁邻居还是叫你金狮。因为没人认出你妈妈,你妈妈整容后被回收了,这是对原型的整体改写。

尼玛!对你来说够了!不要BB!给我看看密码!

函数Person(name){ this . name=name;} function mother(){ } mother . prototype={//母亲的原型age: 18,home3360 ['北京','上海']};person . prototype=new Mother();//Person的原型是妈妈//用chrome调试工具查看,提供__proto__接口查看原型var P1=new Person(' Jack ');//p 1:‘杰克’;__proto__:18,['北京','上海']var p2=新人(' Mark ');//p 2:‘Mark’;__proto__:18,['北京','上海']P1 . age=20;/* Instance不能改变prototype的基本值属性,就像你洗剪吹染黄头发和你妈妈没有关系p1 instance下增加一个年龄属性的普通操作和prototype没有关系。带var o { };O.age=20。* p1:下面还有一个额外的属性age,__proto__与Mother.prototype相同,age=18。* p2:只有属性名,__proto__与mother . prototype */P1 . home[0]=' Shenzhen ';/*原型中引用类型属性的共享,就像你烧毁了自己的家一样,就是烧毁了整个家庭的家。*这个先说,下面再唠叨怎么样?* P1:‘杰克’,20;__proto__:18,['深圳','上海']* p2:' Mark ';__proto__:18,['深圳','上海']*/p1.home=['杭州','广州'];/*实际上是与p1.age=20相同的运算。用这个理解: var o { };o.house=['big ',' house']* p1:'Jack ',20,['杭州','广州'];__proto__:18,['深圳','上海']* p2:' Mark ';__proto__:18,['深圳','上海']*/delete P1 . age;/*删除用户定义的属性后,原来被覆盖的原型值会再次出现。这就是向上搜索机制,所以它有以下动态* P1:‘杰克’,[‘杭州’,‘广州’];__proto__:18,['深圳','上海']* p2:' Mark ';__proto__:18,['深圳','上海']*/person . prototype . last name=' Jin ';/*重写原型,并将其动态反映到实例中。就在你妈变时髦的时候,你邻居提到你是时髦女人的儿子。*注意这里我们重写了Person的原型,也就是给Mother增加一个lastName属性,相当于Mother.lastName='Jin'*这不是改变Mother.prototype,而是改变不同的层级,效果往往会大不相同。* P1:‘杰克’,[‘杭州’,‘广州’];_ _ proto _ _ : ' jin__proto__:18,['深圳','上海']* p2:' Mark ';_ _ proto _ _ : ' jin__proto__:18,['深圳','上海']*/person . prototype={ age : 28,address: { country: 'USA ',city: ' Washington ' } }var p3=新人(“奥巴马”);/*重写原型!这时,Person的原型完全变成了一个新的对象,这意味着Person改变了他的母亲。*替换为以下理解:var a=10b=a;a=20c=a .所以b保持不变,变成c,所以p3变了,和妈妈无关。* P1:‘杰克’,[‘杭州’,‘广州’];_ _ proto _ _ : ' jin__proto__:18,['深圳','上海']* p2:' Mark ';_ _ proto _ _ : ' jin__proto__:18,['深圳','上海']* p3: '奥巴马';_ _ proto _ _ : 28 { country : ' USA ',city 3360 ' Washington ' } */mother . prototype . no=9527;/*重写原型的原型,并将其动态反映到实例中。就像你妈成了新潮流,你邻居提到你奶奶真的很潮流。*注意,这里我们重写了Mother.prototype,p1p2会变,但是上面的p3和Mother无关,不会影响他。* P1:‘杰克’,[‘杭州’,‘广州’];_ _ proto _ _ : ' jin__proto__:18,['深圳','上海'],9527 * p2:' Mark ';_ _ proto _ _ : ' jin__proto__:18,['深圳','上海'],9527* p3: '奥巴马';_ _ proto _ _ : 28 { country : ' USA ',city 3360 ' Washington ' } */mother . prototype={ car : 2,hobby: ['run ',' walk ']};var p4=新人(“托尼”);/*重写原型的原型!这时,母亲的原型完全变成了一个新的对象!*由于上面的人和母亲已经断开,母亲此时的变化对人没有影响。

* p4:托尼’;_ _ proto _ _ : 28 { country : ' USA ',city 3360 ' Washington ' } */person . prototype=new Mother();//再次绑定var p5=新人('路飞');//此时如果需要应用这些变化,Person的原型将再次绑定到妈妈身上。//p 5:‘路飞’;_ _ proto _ _ : 2,['跑','走'] P1。_ _原型_ _。_ _原型_ _。_ _ proto _ _//null,你说原型链的结尾不为null?妈妈。_ _原型_ _。_ _原型_ _。_ _ proto _ _//null,你说原型链的结尾不为null?看完能基本理解吗?

现在我们来谈谈p1.age=20,p1之间的区别。home=['杭州','广州']和p1.home[0]='深圳'。p1.home[0]='深圳';总结起来就是p1.object.method和p1.object.property的形式

p1 .年龄=20岁;p1.home=['杭州','广州'];这两句话很容易理解。让我们忘记原型,思考如何向普通对象添加属性:

var obj=新对象();obj.name=' xxxobj.num=[100,200];这是一种理解吗?是一样的。

为什么p1.home[0]='Shenzhen '不在p1下创建home数组属性,然后将其第一个位置设置为' Shenzhen '?让我们先忘记这一点,想想上面的obj对象。如果是这样写的:varobj。名称=' XXX ',obj。num=[100,200],能得到想要的结果吗?显然,你得到的只是一个错误。既然obj还没有定义,我们怎么给它添加一些东西呢?同理,p1.home[0]中的home也不是在p1下定义的,所以不可能一步直接定义home[0]。如果您想在p1下创建一个主数组,那么它的写法如下:

P1 . home=[];p1.home[0]='深圳';这不是我们最常用的方法吗?

p1.home[0]='Shenzhen '不直接报错的原因是原型链中有搜索机制。当我们进入p1.object时,原型链的搜索机制是先在实例中搜索对应的值,找不到就在原型中查找,找不到就在下一个原型中查找.直到原型链结束,也就是说,如果没有找到null,它将返回一个undefined。当我们进入p1.home[0]时,也是同样的搜索机制。首先,我们搜索p1,看看是否有任何名为home的属性或方法,然后一步步查找。最后我们在妈妈的原型里找到了,所以修改他就相当于修改妈妈的原型。

总之,p1.home[0]='深圳'相当于妈妈. prototype.home [0]='深圳'。

从上面的分析可以知道,原型链继承的主要问题在于属性的共享。在很多情况下,我们只想共享方法,而不是属性。理想情况下,每个实例都应该有独立的属性。因此,原型继承有以下两种改进方式:

1)组合遗传。

函数母亲(年龄){this.age=年龄;this.hobby=['跑步','足球']} mother . prototype . show age=function(){ console . log(this . age);};函数Person(姓名,年龄){ Mother.call(此,年龄);//第二次执行this.name=name} person . prototype=new Mother();//第一次执行person . prototype . constructor=person;person . prototype . show name=function(){ console . log(this . name);}var p1=新人(' Jack ',20);p1.hobby.push('篮球');//p 1:‘杰克’;__proto__:20,['running ',' football ']var p2=new Person(' Mark ',18);//p 2:‘Mark’;_ _ proto _ _ :18,['跑步','足球']结果是酱紫色:

这里的第一次执行得到person.prototype.age=undefined,Person . prototype . hobby=[' running ',' football'],第二次执行是var p1=new Person('Jack ',20)的时候。去找P1。年龄=20岁,P1。爱好=['跑步','足球'],这就变成了P1。爱好=['跑步','足球','篮球']后推。其实理解这种变化是比较简单的。你可以通过简单的替换得到这个结果。如果感觉理解起来很复杂,试着扔掉脑海中的概念,把自己当成一个浏览器,从上到下执行代码。结果会出来吗?

通过第二次执行原型的构造函数Mother(),我们在对象实例中复制了原型属性的副本,从而实现了与原型属性的分离和独立。小心,你会发现我们第一次叫妈妈()的时候,好像没用。我们能不叫它吗?是的,有以下寄生组合遗传。

2)寄生组合遗传。

函数对象(o){函数F()} { F . prototype=o;返回新的F();}函数inheritage prototype(Person,Mother){ var prototype=object(Mother . prototype);原型.构造者=人;Person.prototype=原型;}函数母亲(年龄){this.age=年龄;this.hobby=['跑步','足球']} mother . prototype . show age=function(){ console . log(this . age);};函数Person(姓名,年龄){ Mother.call(此,年龄);this.name=name}继承原型(人、母亲);person . prototype . show name=function(){ console . log(this . name);}var p1=新人(' Jack ',20);p1.hobby.push('篮球');//p 1:‘杰克’;__proto__:20,['running ',' football ']var p2=new Person(' Mark ',18);//p 2:‘Mark’;_ _ proto _ _ :18,['跑步','足球']结果是酱紫色:

原型中不再有年龄和爱好属性,只有两种方法,这正是我们想要的!

关键点在于对象(o),借用一个临时对象,巧妙地避免调用新的Mother(),然后返回原型为o的新对象实例,从而完成原型链的设置。很迂回,对吧?那是因为我们不能设定人。原型=母亲。直接原型。

总结

-

话虽如此,其实只有一个核心:属性共享和独立控制。当您的对象实例需要独立的属性时,所有实践的本质都是在对象实例中创建属性。如果没有想太多,可以直接在Person中定义自己需要的独立属性来覆盖原型的属性。总之,使用原型继承时,要特别注意原型中的属性,因为它们都涉及到全身。

下面是在js中创建对象的各种方法的简单列表。现在最常用的方法是组合模式。熟悉的同学可以跳到文章结尾,赞一赞。

1)原始模式。

//1.原始模式,对象的字面方式var person={name:' jack ',age: 18,say name 3360 function(){ alert(this。姓名);}};//1.原始模式,对象构造器模式var person=new Object();人名='杰克';person.age=18岁;person . SayName=function(){ alert(this . name);};很明显,我们要批量打造person1、person2……的时候,每次都要敲很多码,对于资深文案来说太多了!然后就是工厂化的大规模生产模式。

2)工厂模式。

//2.工厂模式,定义函数创建对象,函数创建人(姓名、年龄){ var temp=new object();person.name=name人.年龄=年龄;person . SayName=function(){ alert(this . name);};返回温度;}工厂模式为量产,只需简单调用(啪啪啪.).你可以通过指定你的名字和年龄来建造一群婴儿,并解放你的双手。但是,因为工厂在黑箱中运行,所以您无法识别对象是什么类型,是人还是狗(测试的实例是对象)。此外,每次创建人的时候都要创建一个独立的temp对象,代码臃肿。

3)建造师。

//3.构造函数模式,定义一个构造函数的人(姓名、年龄){这个。name=namethis.age=年龄;this . SayName=function(){ alert(this . name);};}var p1=新人(' Jack ',18);//创建p1对象Person('Jack ',18);//属性方法都给了window Object,window.name='Jack ',window.sayName()会输出Jack的构造函数类似于c和JAVA中类的构造函数,很容易理解。此外,人可以被识别为类型(测试的实例是人和对象)。但是所有的实例仍然是独立的,不同实例的方法实际上是不同的功能。这里忘了函数这个词,很容易理解把sayName作为一个对象,也就是说张三的sayName和李四的sayName是不同存在的,但显然我们期望共享一个sayName来节省内存。

4)原型模式。

//4.原型模式,直接定义原型属性函数person () {} person。prototype.name=' jackperson . prototype . age=18;person . prototype . SayName=function(){ alert(this . name);};//4.原型模式,字面定义:函数person () {} person。prototype={name:' jack ',age: 18,say name : function(){ alert(this。姓名);}};var p1=新人员();//name=' Jack ' var p2=new Person();//name='Jack '这里要注意原型属性和方法的共享,即所有实例只引用原型中的属性方法,任何地方的变化都会引起其他实例的变化。

5)混合模式(原型构建)。

//5.原型构造组合模式,功能人(姓名、年龄){这个。name=namethis.age=年龄;} person . prototype={ hobby :[' running ',' football '];sayName:函数(){ alert(this . name);},SayAge : function(){ alert(this . age);}};var p1=新人(' Jack ',20);//p1:'Jack ',20;__proto__: ['跑步','足球'],sayName,sayAgevar p2=new Person('Mark ',18);//p1:'Mark ',18;_ _ proto _ _ : ['running ',' football'],说名字,说方式就是把需要独立性的属性方法放入构造函数中,把可以共享的部分放入原型中,可以最大程度的节省内存,保持对象实例的独立性。

更多资讯
游戏推荐
更多+