前言原型链作为js中的一座大山,是面试过程中必问的问题之一;了解原型链对学习js也很有帮助。定义我们先来看看它的定义。js在对象上查找属性时,首先查找对象本身的属性(即声明时定义的属性)。如果在这些属性中没有找到target属性,那么js会去寻找__proto__对象,如果这个对象不存在,则继续寻找__proto__对象的__proto__对象,直到__proto__为null或者找到target属性。.__proto__的关系链就是我们说的原型链特征其实很简单。你只需要记住以下三点就大功告成了。每个对象都有一个__proto__属性。每个函数都有一个原型属性。该属性的值也是一个对象。每个对象的__protp__都将指向其构造函数的原型。这些特点。要验证第一点,您可以复制我的代码并将其粘贴到浏览器控制台中。让obj={name:"leelei"};console.log(obj.__proto__);让arr=[1,2,3]console.log(arr.__proto__);让str="http://www.leelei.info";console.log(str.__proto__);letnum=100;console.log(num.__proto__);当你全部打印出来的时候,你会发现一个问题,哎,我的str和num不是基本类型吗?对象也有proto属性?这里不得不提到js的隐式转换规则。当你访问基本类型的一些属性或者一些方法被调用的时候,你的基本类型会被js包裹起来,像下面的str.__proto__=>newString(str).__proto__;num.__proto__=>newNumber(num).__原型__;那么这个新的String和新的Number是从哪里来的呢?都是js原生的构造函数。ObjectNumberStringBooleanRegExpErrorFunctionArrayDate一共有以下几种类型。我们现在已经验证了第一点。每个对象(基本类型转成Object)都有__proto__属性验证第二点请将下面代码逐行复制到控制台console.log(Object.prototype);console.log(Number.prototype);console.log(String.prototype);console.log(Boolean.prototype);console.log(RegExp.prototype);console.log(Error.prototype);console.log(Function.prototype);console.log(Array.prototypee);console.log(Date.prototype);如果我们不是构造函数fn(){}console.log(fn.prototype);可以看到我们声明的构造函数和函数都有原型属性,唯一的区别是原来的构造函数在它的原型对象上有一些方法和属性。所以综上所述,我们可以验证第二个特征,每个函数都有一个原型属性来验证第三点,我们只需要判断下一步的__proto__是否指向第二步对应构造函数的原型即可。letobj={name:"leelei"};obj.__proto__===对象.原型;//trueletarr=[1,2,3]arr.__proto__===Array.prototype;//trueletstr="http://www.leelei.info";str.__proto__===String.prototype;//trueletnum=100;num.__proto__===Number.prototype;//true结果已经很明显了,第三个参数也是可以得到的参数从定义开始,我们可以根据定义来体验一下这条链letobj={name:"leelei"};console.log(obj.__proto。的价值);//有这个方法,obj.valueOf();//{name:"leelei"};我们在声明obj的时候可以看到没有这个属性方法,但是我们可以调用。我们打印了obj的__proto__对象,发现这里有这个方法,所以我们可以调用它。这就是定义所说的。当对象本身没有这个属性时,我们会到它的__proto__对象中去发现这里只有一层?多几层会不会调用失败?我们可以写一个构造函数,然后在它的原型对象prototype中添加自定义的方法来进行实验。functionFoo(age){this.age=age};Foo.prototype.say=function(){console.log('hello')};letfoo=newFoo(18);根据特征2,对象的__proto_对象指向构造函数的原型,所以这个是没有问题的foo.say()=>foo.__proto__.say()//hello相当于=>Foo.prototype。say()//你好,我们还能用Object上面的方法吗?当然,foo.valueOf()=>foo.__proto__.valueOf等价于=>Foo.prototype.valueOf,为什么不呢?//如果在第一层找不到,再上一层//别忘了prototype也是一个对象!//所以它还有__proto__属性==>Foo.prototype.__proto__.valueOf等价于==>Object.prototype.valueOf找到了你可能有点好奇为什么这两个相等?Foo.prototype.__proto__===Object.prototype因为Foo.prototype是一个对象,Foo是一个函数//我们显然可以知道Foo的构造函数是FunctionFoo.__proto__===Function.prototype;//但Foo.prototype的构造函数是谁?Foo.prototype.__proto__===其构造函数.prototype;这里我们需要了解一个构造函数的属性,如果你打印了前面步骤中定义的函数的原型值,你会发现它带有一个构造函数属性,当你打印构造函数时,函数本身将是返回,如下图letfn=function(){console.log('hello')};console.log(fn.prototype)//{constructor:?}fn.prototype.constructor===fn//true所以这东西有什么用?只是为了告诉我们构造函数是谁。上面我们说了Foo.prototype.__proto__===其constructor.prototype;那么显然这也等于Foo.prototype.__proto__.constructor===itsconstructor.prototype.constructor因为它的构造函数.prototype.constructor===ItsconstructorcanlaunchFoo.prototype.__proto__.constructor===Itsconstructorconsole.log(Foo.prototype.__proto__.constructor)//?Object(){[nativecode]}所以我们可以发现Foo.prototype的构造函数是Object。需要注意的一点是typeof(Function.prototype);//函数不是对象?!惊讶根据特征2,每个函数都有一个原型属性。由于Function.prototype不是对象而是方法,所以它还有prototype属性,这让Function.prototype.prototype//undefined感到意外!它甚至没有原型属性!不要惊慌,除了这个,其他的都适用。请记住这个特殊情况。小结其实原型链并不复杂,但是再简单再简单,真的很难记住。我们可以随意摆弄一下控制台,按照它的定义,这样更方便我们理解和记忆。谢谢大家,如有错误,请在评论区指出。打个广告,李雷的博客
