JS(JavaScript)中的原型和原型链总结———例题详解(附源码)

何为原型及原型链?

什么是原型?
在JS中每个构造器(函数)都一个属性叫prototype,它叫原型,也是个对象,我们叫这个对象为原型对象;而每个对象中有一个属性叫__proto__,它叫隐式原型


什么是原型链?
是一个对象的查找机制,比如查找对象arr中的toString方法,会先在自己的私有属性中找,如果没有,就沿着__proto__去原型对象中找,如果还没有,就继续沿着__proto__去它原型对象中的原型对象中找,直到找到Object中的原型对象(Object原型对象中的__proto__指向null),如果还没找到,那么结果就是undefined;
什么是作用域链?
和原型链如出一辙,是一个EC(执行上下文)中数据的查找机制,现在自己EC中找数据,如果没有就去父函数所对应的上下文中找,如果还没有,就去父函数的父函数的EC中找,知道找到EC(G),如果还是没有,那么就报错;

  • 下图为原型与原型对象的详细分析图
    原型与原型对象
  • 原型练习题1(详细过程)
    原型练习题1
  • 代码执行过程详解;
    80行代码先创建一个Fn构造器; 84行代码,对Fn的原型对象增加一个方法say(是一个函数);
    87行代码创建一个新的Fn的原型对象,此时Fn构造器prototype将指向新的原型对象;
    88行代码,new了一个新的对象f1,有自己的私有属性a=1; 89行代码,给新的原型对象增加一个b方法(也是一个函数);
    92行代码,打印出f1.a,就是打印出f1对象中的a变量,f1对象有a为1,就打印出来1;
    93行代码,执行f1.say()函数,先在对象f1中找say函数,没有,然后沿着__proto__去它的原型对象中找如果还没有就一直沿着__proto__往上一级的原型对象中找,此时在Fn构造器原来的原型对象中找到say函数,此时执行say函数,say函数中的this指向f1对象中的a变量,函数执行后,f1对象中的a变量变为2;
    94行代码,打印出f1.a,为2;
    95行代码,执行f1.b()函数,先在对象f1中找b函数,没有,然后沿着__proto__去它的原型对象中找如果还没有就一直沿着__proto__往上一级的原型对象中找,此时在Fn构造器新的原型对象中找到b函数,此时执行b函数,b函数中的this指向f1对象中的a变量,函数执行后,f1对象中的a变量变为3;
    96行代码,打印出f1.a,为3;

★★★★★如果构造器new了一个新的原型对象(Fn.prototype),此时这个新的原型对象的__proto__还指向原来的Fn的原型对象;但是还有一种情况,如果对构造器的prototype属性进行了修改而不是新建,此时修改后的Fn原型对象的__proto__就是指向Object构造器的原型对象,也就相当于将原来的Fn原型对象覆盖掉了;★★★★★

两种情况


  • 原型练习题2
    原型练习题2
  • 103行代码,把原有的Fn函数当做了对象,此时内部的__proto__指向的是函数Fn的构造器(Function构造器)的原型对象,而它的原型对象里面没有自己设置的方法;
  • 在下面111行代码执行时,先去Fn对象找有没有sum()方法,如果没有就沿着__proto__去原型对象中找,然而,Function构造器的原型对象中没有sum()方法,所以出现报错,//Fn.sum is not a function
  • 原型练习题3
    **原型练习题3**
  • 131行代码,分别new了 三个对象,对于C1,因为没有实参,在执行if语句时候直接跳出,所以没有私有属性,对于C2,因为没有实参,所以拿到的数据为undefined,但是属性名还是name,所以有私有属性(name:undefined),对于C3,因为没有实参,所以形参name为undefined,在构造器中使用了||(或)操作,此时undefined不能确定name,所以私有属性name为join;三个对象的公有属性都为name:tom;
  • 在执行alert时,先找自己的私有属性,如果没有就去找公有属性,所以输出为:Tomundefinedjoin
  • alert中的 + 此时作用是字符串拼接;
  • 原型练习题4(较为复杂)
    代码执行图:
    原型练习题5
    源码(含注释)在这儿:
function Foo(){
        getName = function(){
            console.log(1)
        }
        return this;
    }
    Foo.getName = function(){
        console.log(2)
    }
    Foo.prototype.getName = function(){
        console.log(3)
    }
    var getName = function(){ //预编译时候就得到提升;
        console.log(4)
    }
    function getName(){ //预编译时候就提升;
        console.log(5)
    }
    //去Foo对象中找getName方法,找到了,此时就是2;
     Foo.getName(); //2 
     //直接去EC(G)中找,找到了,输出4;
     getName(); //4
     //先执行Foo()相当于对函数Foo执行了一次,在Foo内部先对方法getName进行执行,此时修改了EC(G)中的getName函数,然后函数返回this,此时this指向的是window,接着寻找window里面的getName函数,输出数值,为1;
     Foo().getName(); //1
     //这个相当于直接执行getName函数,去EC(G)中找,找到了,输出1;
     getName(); //1
     // 这个相当于将Foo.getName()当成一个构造器,然后new一个新的对象,此时Foo.getName()内部有.log(2),在以一个构造器new对象时,构造器中的console.log()会正常执行的,所以此时输出2;然后new Foo.getName()这个整体是一个新的对象,只不过是个空对象;
     new Foo.getName();  //2
     //执行顺序从左到右,相当于先new一个以Foo()为构造器的对象,new出来的就是个空对象,此时这个新对象中的__proto__属性就指向它的构造器的原型对象(Foo.prototype),然后寻找getName方法,自己里面没有,就去构造器的原型对象中找,找到了,输出3;
     new Foo().getName(); //3
     //就是先对Foo()构造器实例化对象,然后去找getName方法,最终在构造器的原型对象(Foo.prototype)中找到输出3,此时再以new Foo().getName()为构造函数实例化对象,此时是一个空对象;
     new new Foo().getName(); //3

猜你喜欢

转载自blog.csdn.net/qq_44830054/article/details/107524692