JS的原型和原型链一直比较难理解。很多初学者,甚至有一定经验的老手,都未必能完全解释清楚。“也许”是半知半解,这部分内容是JS的核心内容。如果你想推进技术,你一定不能对这个概念一知半解。遇到问题靠的是“猜”,却不懂它的规律!只有原型的函数具有原型属性leta={}letb=function(){}console.log(a.prototype)//undefinedconsole.log(b.prototype)//{构造函数:function(){...}}如何解释Object.prototype?其实Object是一个全局对象,也是一个构造函数,全局对象的其他基本类型也是构造函数:}outTypeName(Object)//[objectFunction]outTypeName(String)//[objectFunction]outTypeName(Number)//[objectFunction]为什么只有函数才有原型属性JS通过new生成对象,而只能通过构造函数,每次生成的对象都不一样。有时需要在两个对象之间共享属性。由于JS在设计之初并没有类的概念,JS使用函数的原型来处理需要共享的属性,通过函数的原型来模拟类:当创建一个时使用一个函数,JS会自动给函数加上一个prototype属性,value是一个有构造函数的对象。下面是共享属性原型的栗子:functionPeople(name){this.name=name}People.prototype.age=23//age//创建两个实例letPeople1=newPeople('OBKoro1')letPeople2=newPeople('Bucket')People.prototype.age=24//老一岁console.log(People1.age,People2.age)//2424为什么People1和People2可以访问People.prototype.age?原因是:People1和People2的原型是People.prototype,答案如下:构造函数是什么,做什么的。原型链__proto__和Object.getPrototypeOf(target):对象的原型__proto__是对象实例与其构造函数之间建立的链接,其值为:`构造函数的原型。也就是说:__proto__的值是它对应的原型对象,也就是某个函数的prototypeObject.getPrototypeOf(target)等于__proto__。ES6标准,兼容IE9,主流浏览器均支持。MDN,本文将使用Object.getPrototypeOf(target)来引用__proto__。Don'tuse__proto__again:本段摘自阮一峰-ES6简介。具体分析请点击链接查看__proto__属性。ES6的正文中并没有写,而是写在了附录中。原因是它本质上是一个内部属性,而不是一个正式的外部API,并且由于广泛的浏览器支持才被添加到ES6中。标准明确规定只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新代码最好认为这个属性不存在。所以无论从语义还是兼容性的角度,都不要使用这个属性,应该使用:Object.getPrototypeOf(target)(读操作)、Object.setPrototypeOf(target)(写操作)、Object.create(target)(generateoperation)替换构造函数是什么,它有什么作用《你不知道的js》:在js中,其实并没有所谓的‘构造函数’,只有对函数的‘构造调用’。上面已经提到了构造函数。所谓构造器其实就是通过关键字new调用的函数:letnewObj=newsomeFn()//构造器调用函数在构造/new调用函数时做了什么:创建一个Brandnew对象。这个新对象的原型(Object.getPrototypeOf(target))指向构造函数的原型对象。该函数的this将绑定到新创建的对象。如果函数没有返回另一个对象,那么在new表达式中的函数调用将自动返回这个新对象。我们称这个新对象为构造函数的实例。原型继承就是利用构造函数的特性来调用函数:SubType.prototype=newSuperType();//原型继承:SubType继承SuperTypeSubType.prototype.constructor=SubType//重新指定构造函数寻找构造函数//将SuperType的this和原型的属性和方法挂载到SubType.prototype构造调用。第二点:将新对象的Object.getPrototypeOf(target)指向函数的原型构造调用。第三点:函数的this会绑定到新创建的对象上。(所以父类this声明的属性是所有子类实例共享的)new对象赋值给SubType.prototype原型链是什么看个例子:functionfoo(){}constnewObj=newfoo()//ConstructcallfooreturnAnewobjectconstnewObj__proto__=Object.getPrototypeOf(newObj)//获取newObj的原型对象newObj__proto__===foo.prototype//true验证newObj的原型指向fooconstfoo__proto__=Object.getPrototypeOf(foo.prototype)//获取foo。prototypefoo__proto__===Object.prototype//truefoo.prototype的原型是Object.prototype如果用前面的语法从newObj中找到foo的原型是这样的:newObj.__proto__.__proto__//这个关系是原型链可以用下面三句话来理解:Everyobjecthasaprototypeobject:newObj的原型是foo.prototype。对象的原型也可以继承自其他原型对象:foo.prototype也有它的原型Object.prototype。一层层往复,这种关系就是原型链。一个对象是否在另一个对象的原型链上如果一个对象存在于另一个对象的原型链上,我们可以说:它们是一种继承关系。有两种判断方式,但都是根据构造函数的原型是否在原型链上来判断:instanceof:用于测试构造函数的原型属性是否出现在对象的原型链中的任何地方语法:objectinstanceofconstructorlettest=function(){}lettestObject=newtest();testObjectinstanceoftest//truetest.prototype在testObject的原型链上testObjectinstanceofFunction//falseFunction.prototype不在testObject的原型链上testObjectinstanceofObject//trueObject.prototypeisPrototypeOfontheprototypechainoftestObject:测试一个对象是否存在于另一个对象的原型链上语法:prototypeObj.isPrototypeOf(object)lettest=function(){}lettestObject=newtest();test.prototype.isPrototypeOf(testObject)//truetest.prototype在testObject的原型链上Object.prototype.isPrototypeOf(testObject)//trueObject.prototype在testObject的原型链原型链的末端:Object.prototypeObject.prototype是原型链的端点,所有对象都从该端点继承方法和属性。Object.prototype没有原型对象:constproto=Object.getPrototypeOf(Object.prototype)//null以下是两个验证示例。有疑问的同学可以多写测试用例来确认。字符串原型链的末端:Object.prototype参考前端高级面试题的详细答案lettest='由String函数构造'letstringPrototype=Object.getPrototypeOf(test)//字符串的原型字符串原型===字符串。prototype//trueTheprototypeofthestringistheStringobjectObject.getPrototypeOf(stringPrototype)===Object.prototype//ThetrueStringobject'sprototypeistheendoftheObjectfunction原型链:Object.prototypelettest=function(){}letfnPrototype=Object.getPrototypeOf(test)fnPrototype===Function.prototype//trueTheprototypeisFunction.prototypeObject.getPrototypeOf(Function.prototype)===Object.prototype//true什么是原型链为了?属性查找:如果你试图访问一个对象(实例instance)的一个属性,你会先在对象内部查找该属性,直到找不到为止,然后在对象的原型(instance.prototype)中查找该属性目的。以此类推,我们举个例子来说明一下:lettest='由String函数构造'letstringPrototype=Object.getPrototypeOf(test)//string的原型stringPrototype===String.prototype//truestring的原型是一个StringobjectObject.getPrototypeOf(stringPrototype)===Object.prototype//trueString对象的原型是Object对象当你访问test的一个属性时,浏览器会进行如下搜索:浏览器首先搜索test本身然后搜索它的原型对象:String.prototype最后找到String.prototype:Object的原型对象。一旦原型在原型链上找到该属性,它会立即返回该属性,并停止在原型链上搜索原型。如果找不到原型链上的原型,则返回undefiend。这种搜索机制也解释了为什么字符串有自己的方法:slice/split/indexOf等。准确的说:这些属性和方法是定义在String的全局对象/函数上的。字符串的原型指向String函数的原型。然后通过搜索原型链在String函数的原型中找到这些属性和方法。拒绝查找原型链:hasOwnProperty:表示对象自身的属性中是否有指定的属性指定的属性。lettest={'OBKoro1':'Bucket'}test.hasOwnProperty('OBKoro1');//truetest.hasOwnProperty('toString');//false测试本身没有发现toString这个API是挂载在object上的。在原型上,所有对象都可以使用,API会忽略那些从原型链继承的属性。扩展:实例的属性你知道构造函数的实例对象上有哪些属性吗?这些属性安装在哪里?是什么原因?functionfoo(){this.some='222'letccc='ccc'foo.obkoro1='obkoro1'foo.prototype.a='aaa'}foo.koro='butcher'foo.prototype.test='测试'letfoo1=newfoo()//`foo1`上有哪些属性,这些属性挂载在哪里?foo.prototype.test='test2'//重新分配上面的问题来研究JS的基础知识。很多人都不对,原因是他们没有完全掌握这个,原型链,和功能。想了再看分析:想了再看分析:想了再看分析:想了再看分析:想了再看分析:这。一些安装在foo1对象下。属性查找:foo1.somefoo1.some直接读取foo1的属性。foo1.test,foo1.a:foo1对象的原型根据上面提到的:constructor/new调用函数时,会创建一个新的对象(foo1),foo1的原型(Object.getPrototypeOf(foo1))会自动指向构造函数的原型对象。构造调用将执行该函数,因此foo.prototype.a='aaaaa'也将被执行。就赋值而言,写在foo外面和写在foo里面是一样的。属性搜索:foo1.test,foo1.afoo1本身没有找到,继续搜索foo1原型Object.getPrototypeOf(foo1)找到一个并测试,返回,停止搜索。foo1.obkoro1和foo1.koro:返回未定义的静态属性:foo.obkoro1,foo.koro函数在JS中是一等公民,也是一个对象,用来模拟一个类。这两个属性与foo1无关,是对象foo上的两个属性(类似于函数:arguments/prototype/length等属性),称为静态属性。它们只能通过foo.obkoro1和foo.koro访问。当原型对象发生变化时,原型链下游获取的值也会发生变化。上例中foo1.test的值是多少?foo.prototype.test='test'letfoo1=newfoo()//`foo1`上有哪些属性,这些属性挂载在哪里foo.prototype.test='test2'//重新赋值foo1.test的值为test2,原因是:foo1的原型对象是Object.getPrototypeOf(foo1)中保存的一个指针,指向foo.prototype的内存地址,不是拷贝,每次读取的值都是当前的最新值foo.原型。printfoo1:总结写了好几天了。之前网上有很多图文博客,那些线条来来回回。个人认为还是比较难理解。因此,本文以纯文本方式描述这些概念。相信各位同学仔细阅读后一定有所收获。如果看不懂,建议多看几遍。这部分概念真的很重要!
