当前位置:首页 > 数码 > 结构函数-深度解析-原型链-JavaScript-类和承袭机制 (结构函数的定义)

结构函数-深度解析-原型链-JavaScript-类和承袭机制 (结构函数的定义)

admin7个月前 (04-26)数码37
本文深化浅出地探讨了Script结构函数、原型、类、承袭的个性和用法,以及它们之间的相关。宿愿看完本文,能协助大家对它们有愈加明晰通透的看法和把握!

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);
深度解析

小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 如此而已。

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: JS

“结构函数-深度解析-原型链-JavaScript-类和承袭机制 (结构函数的定义)” 的相关文章

JavaScript-彻底把握-中的类型转换-从根底到初级 (javascript指什么)

JavaScript-彻底把握-中的类型转换-从根底到初级 (javascript指什么)

1.什么是类型转换? script是一种弱类型言语,这象征着变量是没有明白类型的,而是由JavaScript引擎在编译时隐式成功。类型转换就是将一种数据类型转换为另一种数据类型,例如: 2...

在名目中辨别经常使用防抖和节流时的思考要素-JS疑问 (在名目中辨别真假)

在名目中辨别经常使用防抖和节流时的思考要素-JS疑问 (在名目中辨别真假)

序文 大家好,我是大澈! 本文约2300+字,整篇阅读大概须要6分钟。 本文关键内容分三局部,第一局部是需求剖析,第二局部是成功步骤,第三局部是疑问详解。 假设您只要要处置...

运行时纷至沓来-全方位JS-运行时盘点-新-JS

运行时纷至沓来-全方位JS-运行时盘点-新-JS

Node.js 是 JavaScript 开发领域的领头羊,但随着时间的推移,新的运行时环境不断涌现,为开发者提供了更多选择。本文将对现有的 JavaScript 运行时进行盘点,探讨它们各自的优...