结构函数-深度解析-原型链-JavaScript-类和承袭机制 (结构函数的定义)
01、前言
妇孺皆知,JavaScript是一门面向对象的言语,而结构函数、原型、类、承袭都是与对象密无法分的概念。在咱们日常前端业务开发中,系统和第三方库曾经为咱们提供了大局部须要的类,咱们的关注点更多是在对象的经常使用和数据处置上,而比拟少须要去自定义结构函数和类,对原型的间接接触就更少了。
但是,能深度了解并把握好结构函数、原型、类与承袭,对咱们的代码设计大有裨益,也是作为一名初级前端工程师必无法少的基本功。
本文旨在用最深刻易懂的解释和便捷活泼的代码示例,来彻底捋清对象、结构函数、原型、类与承袭。咱们会以问答对话的方式,层层递进,从结构函数谈起,再引出原型与原型链,剖析类为什么是语法糖,最后再推理出JS的几种承袭方式。
在进入正式篇章之前,咱们可以先尝试思索以下几个疑问:
1.newDate().__proto__==Date.prototype?
2.newDate().constructor==Date?
3.Date.__proto__==Function.prototype?
4.Function.__proto__==Function.prototype?
5.Function.prototype.__proto__==Object.prototype?
6.Object.prototype.__proto__==null?
——思索宰割线——
没错,它们都是true!为啥?听我娓娓道来~
02、结构函数
某IT公司前端研发部,新人小Q和职场混迹多年的老白聊起着结构函数、原型与类的话题。
小Q:结构函数我知道呀,往常newDate(),newPromise()常罕用,Date,Promise不就是结构函数,咱们经过new一个结构函数去创立并前往一个新对象。
老白:没错,这些是系统自带的一些结构函数,那你可以自己写个结构函数吗?
小Q:虽然往罕用的不多,但也难不倒我~
//定义个结构函数functionPerson(name){this.name=name;}//new结构函数,创立对象letperson=newPerson("张三");
小Q:看吧person就是对象,Person就是结构函数,明晰明了!
老白:那我要是单纯写这个方法算不算结构函数?
functionadd(a,b){returna+b;}
小Q:这不是吧,这清楚就是个普通函数啊?
老白:可是它也可以new对象哦!
functionadd(a,b){returna+b;}leta=newadd(1,2);//add{}console.log(a);//trueconsole.log(ainstanceofadd);//objectconsole.log(typeofa);
![深度解析](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
小Q:诶?
老白:其实所谓结构函数,就是普通函数,关键看你要不要new它,但是new是在经常使用的时刻,在定义的时刻咋知道它前面会不会被new呢,所以结构函数只不过是当被用来new时的称说。就像你上方的Person函数,不要new间接运转也是可以的嘛。
functionPerson(name){this.name=name;}Person("张三");
小Q:哦,我懂了,一切函数都可以被new,都可以作为结构函数咯,所谓结构函数只是一种经常使用场景。
老白:嗯嗯,总结得很好,但也不全对,比如箭头函数就不能被new,由于它没有自己的this指向,所以不能作为结构函数。比如上方这样就会报错。
letPerson=(name)=>{this.name=name;};//UncaughtTypeError:Personisnotaconstuctorletperson=newPerson("张三");
小Q:原来如此,那你刚刚Person("张三");,既然没有创立新对象,那外面的this又指向谁了?
老白:这就触及到函数内this指向疑问了,可以便捷总结以下5种场景。
1.经过new调用,this指向创立的新对象;
2.间接当做函数调用,this指向window(严厉形式下为undefined);
functionPerson(name){this.name=name;}//this指向windowPerson("张三");//张三console.log(window.name);
(看吧,不留意的话,不小心把window对象改了都不知道)
3.作为对象的方法调用,this指向该对象;
functionPerson(name){this.name=name;}letobj={Person,};//this指向objobj.Person("张三");//{"name":"张三",Person:f}console.log(obj);
4.经过ly,call,bind方法,显式指定this;
functionPerson(name){this.name=name;}//this指向call的第一个参数Person.call(Math,"张三");//张三console.log(Math.name);
5.箭头函数中没有自己的this指向,取决于高低文:
functionPerson(name){this.name=name;//普通函数,this取决于调用者,即上述的4种状况setTimeout(function(){console.log(this);},0)//箭头函数,this取决于高低文,咱们可以疏忽箭头函数的存在//即同上方this.name=name中的this指向一样setTimeout(()=>{console.log(this);},0)}
小Q:原来this指向都有这么多种状况,好的,小本本记下了,等下就去实验下。
小Q:等下,我从新看了你的newadd(1,2),那a+b=3还被return了呢,这3return到哪去了?
functionadd(a,b){returna+b;}leta=newadd(1,2);
老白:没错,你留意到了,结构函数是不须要return的,函数中的this就是创立并前往的新对象了。
但当new一个有return的结构函数时,假设return的是基本类型,则return的数据间接被放弃。
假设return一个对象,则最终前往的新对象就是return的这个对象,这时原本this指向的对象就会被放弃。
functionPerson(name){this.name=name;//前往的是对象类型returnnewDate();}letperson=newPerson("张三");//前往的是Date对象//SatJul29202316:13:01GMT+0800(中国规范期间)console.log(person);
老白:当然假设要把一个函数的经常使用用途作为结构函数的话,像我刚刚起名add()必需是不规范的,普通首字母要大写,并且最好用名词,像你起的Person就不错。
小Q:新常识get√
要点演绎 |
1.除箭头函数外的一切函数都可以作为结构函数被new |
2.函数内this指向疑问 |
3.结构函数return疑问 |
4.结构函数命名规范 |
03、原型
小Q:都说原型原型,可看了这么久,这代码里也没发生原型呀?
老白:没错,原型是个暗藏的家伙,咱们可以经过对象或许结构函数去拿到它。
//结构函数functionPerson(name){this.name=name;}//对象letperson=newPerson("张三");//经过对象拿到原型(2种方法)letproto1=Object.getPrototypeOf(person);letproto2=person.__proto__;//经过结构函数拿到原型letproto3=Person.prototype;//验证一下//trueconsole.log(proto1==proto2);//trueconsole.log(proto1==proto3);
小Q:可是这个原型是哪来的呀,我代码里也没创立它呀?
老白:当你申明一个函数时,系统就智能帮你生成了一个关联的原型啦,当然它也是一个普通对象,蕴含constructor字段指向结构函数,并且结构函数的prototype属性也会指向这个原型。
当你用结构函数创立对象时,系统又帮你把对象的__proto__属性指向原型。
//结构函数functionPerson(name){this.name=name;}//可以了解为:申明函数时,系统智能口头了上方代码Person.prototype={//指向结构函数constructor:Person}//对象letperson=newPerson("张三");//可以了解为:创立对象时,系统智能口头了上方代码person.__proto__==Person.prototype;
小Q:它们的援用相关,稍微有点绕啊~
老白:没事,我画两个图来示意,愈加明晰点。
(备注:proto只是单纯用来示意原型的一个代名而已,代码中并不存在)
图片
图片
小Q:懂了!
老白:那你说说{}.__proto__和{}.consrtuctor区分是什么?
小Q:让我剖析下,{}其实就是newObject()的一种字面量写法,实质上就是Object对象,那{}.__proto__就是原型Object.prototype,{}.constructor就是结构函数Object,对吧?
老白:没错,只需能熟练把握上方这个图,结构函数,原型和对象这三者的援用相关基本很明晰了。一开局提的1、2题基本也迎刃而解了!
小Q:那这个原型有什么用呢?
老白:一句话总结:当访问对象的属性不存在时,就会去访问原型的属性。
图片
图3
老白:咱们可以经过代码验证下,person对象是没有age属性的,所以person.age前往的其实是原型的age属性值,当原型的age属性扭转时,person.age也会跟着扭转。
functionPerson(name){this.name=name;}//给原型参与age属性Person.prototype.age=18;//对象letperson=newPerson("张三");//18console.log(person.age);//修正原型的age属性Person.prototype.age++;//19console.log(person.age);
小Q:那假设我间接person.age++呢,改的是person还是原型?
老白:这样的话就相当于person.age=person.age+1啦,等号左边的person.age由于对象目前还没age属性,所以拿到的是原型的age属性,即18,而后18+1=19将赋值给person对象。
后续当你再访问person.age时,由于person对象曾经存在age属性了,就不会再检索到原型上了。
这种行为咱们普通称为重写,在这个例子里也形容为:person对象重写了原型上的age属性。
图片
图4
小Q:那这样的话经常使用起来岂不是很乱,我还得很小心的剖析person.age究竟是person对象的还是原型的?
老白:没错,假设你不想发生这种有看法的重写,将原型上的属性设为对象类型不失为一种方法。
functionPerson(name){this.name=name;}//原型的info属性是对象Person.prototype.info={age:18,};letperson=newPerson("张三");person.info.age++;
小Q:我懂了,扭转的是info对象的age属性,person并没有重写info属性,所以person对象自身依然没有info属性,person.info依然指向原型。
老白:没错!不过这样也有个坏处,每一个Person对象都可以共享原型的info,当info中的属性被某个对象扭转了,也会对其余对象形成影响。
functionPerson(name){this.name=name;}Person.prototype.info={age:18,};letperson1=newPerson("张三");letperson2=newPerson("李四");//person1修正infoperson1.info.age=19;//person2也会被影响,打印:19console.log(person2.info.age);
老白:这对咱们代码的设计并不好,所以咱们普通不在原型上定义数据,而是定义函数,这样对象就可以间接经常使用挂载在原型上的这些函数了。
functionPerson(name){this.name=name;}Person.prototype.sayHello=function(){console.log("hello");}letperson=newPerson("张三");//helloperson.sayHello();
小Q:我了解了,数据确实不应该被共享,每个对象都应该有自己的数据好点,但是函数次要,多个对象可以共享同一个原型函数。
老白:所以你知道为啥{}这个对象自身没有任何属性,却可以口头toString()方法吗?
小Q:【豁然开朗】来自它的原型Object.prototype!
老白:不只如此,很多系统自带的结构函数发生的对象,其方法都是挂载在原型上的。比如咱们常罕用的数组方法,你以为是数组对象自己的方法吗?不,是数组原型Array.prototype的方法,咱们可以验证下。
letarray=[];//array对象的push和原型上的push是同一个//打印:trueconsole.log(array.push==Array.prototype.push);//array对象自身没有自己的push属性//打印:falseconsole.log(array.hasOwnProperty("push"));
图片
小Q:【若有所思】
老白:再比如,你随意定义一个函数functionfn(){},为啥它就能fn.call()这样口头呢,它的call属性是哪来的?
小Q:来自它的原型?函数其实是Function的对象,那它的原型就是Function.prototype,实验一下。
functionfn(){}//trueconsole.log(fn.constructor==Function);//trueconsole.log(fn.call==Function.prototype.call);
老白:回答正确。在实践开发中,咱们也可以经过修正原型上的函数,来扭转对象的函数口头。比如说咱们修负数组原型的push方法,加个监听,这样一切数组对象口头push方法时就能被监听到了。
Array.prototype.push=(function(push){//闭包,push是原始的那个push方法returnfunction(...items){//口头push要指定thispush.call(this,...items);console.log("监听push成功,口头一些操作");};})(Array.prototype.push);letarray=[];//打印:监听push成功,口头一些操作array.push(1,2);//打印:[1,2]console.log(array);
老白:不只修正,也可以新增,比如说某些旧版阅读器数组不支持includes方法,那咱们就可以在原型上新增一个includes属性,保障代码中数组对象经常使用includes()不会报错(这也是Polyfill.js的目标)。
//没有includesif(!Array.prototype.includes){Array.prototype.includes=function(){//自己成功includes}}
小Q:又又涨常识了~
老白:原型相关的也说的差不多了,联合刚刚探讨的结构函数,考你一个:手写一个new函数。
小Q:啊啊,揭示一下?
老白:好,咱们便捷剖析一下new都做了什么
小Q:我试试~
functionmyNew(Fn,...args){varobj={__proto__:Fn.prototype,};Fn.apply(obj,args);returnobj;}
小Q:实验经过!
//结构函数functionPerson(name){this.name=name;}//原型Person.prototype.age=18;//创立对象letperson=myNew(Person,"张三");//Person{name:"张三"}console.log(person);//18console.log(person.age);
老白:不错不错,让我帮你再稍微完善一下嘿嘿~
functionmyNew(Fn,...args){//经过Object.create指定原型,愈加合乎规范varobj=Object.create(Fn.prototype);//指定this为obj对象,口头结构函数letresult=Fn.apply(obj,args);//判别结构函数的前往值能否是对象returnresultinstanceofObject?result:obj;}
要点演绎 |
1.对象,结构函数,原型三者的援用相关 |
2.原型的定义,个性及用法 |
3.手写new函数 |
04、原型链
老白:刚刚咱们说当访问对象的属性不存在时,就会去访问原型的属性,那假设原型上的属性也不存在呢?
小Q:前往undefined?
老白:不对哦,原型自身也是一个对象,它也有它自己的原型。所以当访问一个对象的属性不存在时,就会检索它的原型,检索不到就继续往上检索原型的原型,不时检索到根原型Object.prototype,假设还没有,才会前往undefined,这也称为原型链。
图片
小Q:原来如此,所以说一切的对象都可以经常使用根原型Object.prototype上定义的方法咯。
老白:没错,不过有一些原型会重写根原型上的方法,就比如toString(),在Date.prototype,Array.prototype中都会有它们自己的定义。
//[objectObject]console.log({}.toString())//1,2,3console.log([1,2,3].toString())//TueAug01202317:58:05GMT+0800(中国规范期间)console.log(newDate().toString())
小Q:了解了原型链,看回开局的3~6题,如同也不难了。
Date、Function的原型是Function.prototype,第3、4题就解了。
Function.prototype的原型是Object.prototype,第5题也解了。
Object.prototype是根原型,所以它的__proto__属性就为null,第6题也解了。
老白:齐全正确。最后再考你一道和原型链相关的题,手写instanceOf函数。揭示一下,instanceOf的原理是判别结构函数的prototype属性能否在对象的原型链上。
//array的原型链:Array.prototype→Object.prototypeletarray=[];//trueconsole.log(arrayinstanceofArray);//trueconsole.log(arrayinstanceofObject);//falseconsole.log(arrayinstanceofFunction);
小Q:好了嘞~
functionmyInstanceof(obj,Fn){while(true){obj=obj.__proto__;//婚配上了if(obj==Fn.prototype){returntrue;}//抵达原型链的止境了if(obj==null){returnfalse;}}}
检测一下:
letarray=[];//trueconsole.log(myInstanceof(array,Array));//trueconsole.log(myInstanceof(array,Object));//falseconsole.log(myInstanceof(array,Function));
老白:Good!
要点演绎 |
1.原型链 |
2.手写instanceOf函数 |
05、类
小Q:好不容易把结构函数和原型都弄懂,怎样ES6又推出类呀,学不动了T_T。
老白:不慌,类其实只是种语法糖,实质上还是结构函数+原型。
咱们先看一下类的语法,类中可以蕴含有以下4种写法不同的元素。
classPerson{//对象属性a="a";b=function(){console.log("b");};//原型属性c(){console.log("c");}//结构器constructor(){//修正对象属性this.a="A";//新增对象属性this.d="d";}//静态属性statice="e";staticf(){console.log("f");}}
咱们再将这种class语法糖写法恢复成结构函数写法。
functionPerson(){//对象属性this.a="a";this.b=function(){console.log("b");};//结构器this.a="A";this.d="d";}//原型属性Person.prototype.c=function(){console.log("c");};//静态属性Person.e="e";Person.f=function(){console.log("f");};
经过上方一些方法检测,上方的2种写法会失掉雷同的结果。
//Person类实质是个结构函数,打印:functionconsole.log(typeofPerson);//Person的静态属性,打印:econsole.log(Person.e);//可以看到原型属性c,打印:{constructor:ƒ,c:ƒ}console.log(Person.prototype);letperson=newPerson();//可以看到对象属性abd,打印:Person{a:'A',d:'d',b:ƒ}console.log(person);//对象的结构函数就是Person,打印:trueconsole.log(person.constructor==Person);
小Q:所以类只不过是将原本比拟繁琐的结构函数的写法给简化了而已,这语法糖果真甜~
小Q:不过我发现一个疑问,在class写法中的原型属性只能是函数,不能是数据?
老白:没错,这也响应了前面说的,原型上只介绍定义函数,不介绍定义数据,防止不同对象共享同一个数据。
要点演绎 |
1.类的语法 |
2.类恢复成结构函数写法 |
06、承袭
小Q:我又又发现了一个疑问,ES6的class还可以extends另一个类呢,这也是语法糖?
老白:没错,这就是承袭,但是要弄懂ES6的这套承袭是怎样来的,还得从最开局的承袭方式说起。所谓承袭,就是咱们是宿愿子类可以领有父类的属性方法,这和上方谈到的原型的个性有点不约而同。
咱们用一个例子来思索思索,有这么2个类,如何让Cat承袭Animal,使得Cat的对象也有type属性呢?
//父类functionAnimal(){this.type="生物";}//子类functionCat(){this.name="猫";}
小Q:让Animal对象充任Cat的原型!
functionAnimal(){this.type="生物";}functionCat(){this.name="猫";}//指定Cat的原型Cat.prototype=newAnimal();Cat.prototype.constructor=Cat;letcat=newCat();//Cat对象领有了Animal的属性console.log(cat.type);
老白:没错,这是咱们学完原型之后,最直观的一种承袭成功方式,这种承袭又叫原型链式承袭。但是这种承袭方式存在2个缺陷:
functionAnimal(type){this.type=type;}functionCat(type){this.name="猫";}//在这里就曾经创立了Animal对象Cat.prototype=newAnimal();Cat.prototype.constructor=Cat;//创立子类对象时无法向父类结构函数传参letcat=newCat("哺乳生物");//type属性来自原型,被一切Cat对象共享,打印:undefinedconsole.log(cat.type);
小Q:我想到个方法,可以一举处置上方2个缺陷。
在子类结构函数中口头父类结构函数,并且指定口头父类结构函数中的this是子类对象,这样属性就都是属于子类对象自身了,不存在共享。同时在创立子类对象时,也可以给父类结构函数传参了,一箭双雕。
functionAnimal(type){this.type=type;}functionCat(type){//口头父类,显式指定this就是子类的对象Animal.call(this,type);this.name="猫";}letcat=newCat("哺乳生物");//Cat{type:'哺乳生物',name:'猫'}console.log(cat);
老白:这种承袭方式叫结构函数式承袭,确实处置了原型链式承袭带来的疑问,不过这种承袭方式由于没有用到原型,又有发生了2个新的疑问:
functionAnimal(type){this.type=type;}//父类的原型方法Animal.prototype.eat=function(){console.log("吃");};functionCat(type){Animal.call(this,type);this.name="猫";}letcat=newCat("哺乳生物");//没有承袭父类原型的属性方法,打印:undefinedconsole.log(cat.eat);//子类对象不是父类的实例,打印:falseconsole.log(catinstanceofAnimal);
小Q:看来还要再改良,不如我把原型链式和结构函数式这2种承袭方式都用上,让它们互补。
functionAnimal(type){this.type=type;}Animal.prototype.eat=function(){console.log("吃");};//子类结构函数functionCat(type){Animal.call(this,type);this.name="猫";}//父类对象充任子类原型Cat.prototype=newAnimal();Cat.prototype.constructor=Cat;
实验一下,果真一切疑问都处置了。
//可以给父类结构函数传参letcat=newCat("哺乳生物");//子类对象拥用自己属性,而非来自原型,防止数据共享//打印:Cat{type:'哺乳生物',name:'猫'}console.log(cat);//子类对象可以承袭到父类原型的方法,打印:吃cat.eat();//子类对象属于父类的实例,打印:trueconsole.log(catinstanceofAnimal);
老白:十分痴呆,你又道出了第三种承袭方式,组合式承袭。即原型链式+结构函数式=组合式。疑问确实都处置了,但是有没有发现,这种方式口头了2遍父类结构函数。
functionAnimal(type){this.type=type;}Animal.prototype.eat=function(){console.log("吃");};functionCat(type){//第二次口头父类结构函数Animal.call(this,type);this.name="猫";}//第一次性口头父类结构函数Cat.prototype=newAnimal();Cat.prototype.constructor=Cat;
小Q:多口头了一遍,确实不够完美,这怎样搞?
老白:其实关键在Cat.prototype=newAnimal(),你只不过想让子类对象也能承袭到父类的原型,而这里创立了一个父类对象,为啥?说究竟还是应用原型链:子类对象→父类对象→父类原型。
假设咱们不要两边那个"父类对象",而是用一个空对象x交流,让原型链变成:子类对象→空对象x→父类原型,这样也能到达目标,就不用口头那遍没必要的父类结构函数了。
//组合式承袭:创立父类对象做子类原型letanimal=newAnimal();Cat.prototype=animal;//改良:创立一个空对象做子类原型,并且这个空对象的原型是父类原型letx=Object.create(Animal.prototype);Cat.prototype=x;
小Q:妙啊,这回完美了。
functionAnimal(type){this.type=type;}Animal.prototype.eat=function(){console.log("吃");};functionCat(type){Animal.call(this,type);this.name="猫";}//寄生组合式,改良了组合式,少口头了一遍没必要的父类结构函数Cat.prototype=Object.create(Animal.prototype);Cat.prototype.constructor=Cat;
老白:这种承袭方式又叫寄生组合式承袭,相当于在组合式承袭的基础上进一步优化。回忆上方的几种承袭方式的演化环节,原型链式→结构函数式→组合式→寄生组合式,其实就是不时优化的环节,最终咱们才推理出比拟完美的承袭方式。
小Q:那ES6class的extends承袭又是怎样呢?
老白:说究竟就是寄生组合式承袭的语法糖。咱们先看看它的语法。
classAnimal{eat(){console.log("吃");}constructor(type){this.type=type;}}//Cat承袭AnimalclassCatextendsAnimal{constructor(type){//口头父类结构函数,相当于Animal.call(this,type);super(type);//口头完super(),子类对象就有父类属性了,打印:哺乳生物console.log(this.type);this.name="猫";}}
创立对象实验一下:
letcat=newCat("哺乳生物");//子类原型的原型就是父类原型,打印:trueconsole.log(Cat.prototype.__proto__==Animal.prototype);//子类自身领有父类的属性,打印:Cat{type:'哺乳生物',name:'猫'}console.log(cat);
打印的结果展现的个性和寄生组合式是一样的:
个性1可以了解为extends背后里口头了:
Cat.prototype=Object.create(Animal.prototype);Cat.prototype.constructor=Cat;
个性2在于super(),它相当于Animal.call(this),口头super()就是口头父类结构函数,将原本父类中的属性都赋值给子类对象。
在ES6的语法中还要求super()必需在this的经常使用前调用,也是为了保障父类结构函数先口头,防止在子类结构器中设置的this属性被父类结构函数笼罩。
classAnimal{constructor(){//假设不报错,this.name="猫"就被this.name="狗"笼罩了this.name="狗";}}classCatextendsAnimal{constructor(type){this.name="猫";//没有在this经常使用前调用,报错super();}}
小Q:看懂寄生组合式承袭,extends承袭就是小菜一碟呀~
老白:最后再补充一下super的语法,可以子类的静态属性方法中经过super.xx访问父类静态属性方法。
classAnimal{constructor(){}staticnum=1;staticsay(){console.log("hello");}}classCatextendsAnimal{constructor(){super();}//super.num相当于Animal.numstaticcount=super.num+1;statictalk(){//super.say()相当于Animal.say()super.say();}}//2console.log(Cat.count);//helloCat.talk();
super是一个语法糖的不凡关键词,不凡用法,并不指向某个对象,不能独自经常使用,以下状况都是不准许的。
classAnimal{}classCatextendsAnimal{constructor(){//报错let_super=super;//报错console.log(super);}statictalk(){//报错console.log(super);}}
要点演绎
07、总结
本文深化浅出地探讨了JavaScript结构函数、原型、类、承袭的个性和用法,以及它们之间的相关。宿愿看完本文,能协助大家对它们有愈加明晰通透的看法和把握!
javascript原型,原型链 有什么特点
javascript原型,原型链特点:原型链实现了继承。
JS中每个函数都存在有一个原型对象属性prototype。并且所有函数的默认原型都是Object的实例。每个继承父函数的子函数的对象都包含一个内部属性proto。该属性包含一个指针,指向父函数的prototype。若父函数的原型对象的_proto_属性为再上一层函数。在此过程中就形成了原型链。
原型链的作用是用来实现继承,比如我们新建一个数组,数组的方法就是从数组的原型上继承而来的。
var arr = [];
是从arr.__proto__上继承下来的,arr.__proto__也就是。
扩展资料:
中每个函数都存在有一个原型对象属性prototype。并且所有函数的默认原型都是Object的实例。
2.每个继承父函数的子函数的对象都包含一个内部属性_proto_。该属性包含一个指针,指向父函数的prototype。若父函数的原型对象的_proto_属性为再上一层函数。在此过程中就形成了原型链。
3.原型链实现了继承。原型链存在两个问题:a 包含引用类型值的原型属性会被所有实例共享。b 在创建子类型时,无法向超类型的构造函数中传递参数。
参考资料:网络百科-javascript
javascript 中的? :语句 表示什么意思?
javascript 中的?是属性。 例如:var text={x:1,y:2 }, 输出:text.x 那么值就是1 如此而已。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。