精华内容
下载资源
问答
  • 本文实例讲述了javascript 原型与原型链的理解及应用。分享给大家供大家参考,具体如下: javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了。 比如对象A与对象B之间...
  • 一、什么是原型链? 简单回顾下构造函数,原型和实例的关系:  每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针. ...
  • 主要介绍了js对象继承之原型链继承,以实例形式分析了原型链继承的实现方法与注意事项,具有一定参考借鉴价值,需要的朋友可以参考下
  • 本文实例讲述了JS原型和原型链原理与用法。分享给大家供大家参考,具体如下: Javascript语言的继承机制一直很难被人理解。 它没有”子类””父类”的概念,也没有”类”(class)实例”(instance)的区分,...
  • 这篇文章主要介绍了JavaScript原型继承和原型链原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在讨论原型继承之前,先回顾一下关于创建自定义类型的...
  • 主要介绍了一文秒懂JavaScript构造函数、实例、原型对象以及原型链的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 主要介绍了JavaScript作用域、闭包、对象与原型链,结合实例形式总结分析了javascript中变量与函数的作用域、闭包、对象、原形链相关概念、用法及注意事项,需要的朋友可以参考下
  • js 原型链实例解析

    2018-08-24 18:18:52
    分析一下这个函数包含的知识点:立即执行函数、闭包、原型链、继承、封装、call / apply 的应用等等。接下来我将一点点说一下我的理解,如果说的有不对之处,还请大神指教。 先说好处: 1、只定义了一个全局...

    废话不多说,直接上干货!

    var test = (function(){
        var that;
        var obj = function(){
            this.name = "name";
            that = this;
        }
        obj.prototype = {
            aaa: function(){
                console.log("this is aaa!");
                console.log(this,that,test);
            },
            bbb: function(){
                console.log("this is bbb!");
                that.aaa.call(this);
            }
        }
        return new obj;
    })()

    分析一下这个函数包含的知识点:立即执行函数、闭包、原型链、继承、封装、call / apply 的应用等等。接下来我将一点点说一下我的理解,如果说的有不对之处,还请大神指教。

    先说好处:

    1、只定义了一个全局变量,能减少全局污染。

    2、把子函数写进了原型(prototype),方便继承调用。

    3、函数看起来干净整洁,可扩展,可相互调用;

    4、想不出来了……

    从立即执行函数说起:

    立即执行函数都干了什么事?实例化 obj 函数对象,并赋值给了 test 变量,所以实际上是 test 变量实例化了 obj 对象。

    那 new 都干了什么事?简单的说,初始化一个对象、实现继承;

    初始化一个对象我就不说了,就是给一个对象赋予一个预定义好的值;

    实现继承:

    test 变量的值是 new obj 后赋予的,test 继承了 obj 的属性(如:this.name)及 obj.prototype

    怎么验证 test.__proto__ === obj.prototype?改版一下上面的代码

    var obj = function(){
        this.name = "name";
    }
    obj.prototype = {
        aaa: function(){
            console.log("this is aaa!");
        },
        bbb: function(){
            console.log("this is bbb!");
        }
    }
    var test = new obj();
    
    

    运行代码,在控制台测试:

    再说的深一点:obj 是 Function 对象得来的,证明一下:

    再深一点:

    再深一点:

    OK,到头了,原型链的尽头,这也就是我们在 js 中常说的 ”一切皆对象“!  【斜眼笑】

    回归正题:再说下那个 that :

    这个 that 首先实现了闭包(背一下面试题答案:内层函数用到外层函数的变量就是闭包!)

    再加一行代码:(看注释部分)

    var test = (function(){
        var that;
        var obj = function(){
            this.name = "name";
            that = this;
        }
        console.log('我是that:' + that);     //这里是注释
        console.log('我是this:' + this);     //这里是注释
        obj.prototype = {
            aaa: function(){
                console.log("this is aaa!");
            },
            bbb: function(){
                console.log("this is bbb!");
            }
        }
        return new obj;
    })()

    呵呵!

    但是没错,js 是顺序执行的,到输出 that 的时候,that 只是定义的,并没有赋值,所以是 undefined;当然,this 的指向取决于调用函数的对象,目前谁调用了 this ?没有!那就是 window。

    然后,想看 that 是什么鬼,再动动代码:

    var test = (function(){
        var that;
        var obj = function(){
            this.name = "name";
            that = this;
    
            console.log( that );     //这里是注释
            console.log( this );     //这里是注释
        }
        obj.prototype = {
            aaa: function(){
                console.log("this is aaa!");
            },
            bbb: function(){
                console.log("this is bbb!");
            }
        }
        return new obj;
    })()

    看下结果:

    哈哈,有什么发现吗?是不是跟上面输出的 test 一模一样?

    好吧,重新捋一下思路:test 立即执行函数返回了实例化的 obj ,执行了定义的 obj 函数,实现了继承。(说到这里等于没说)那要那个闭包的 that 有何用?别急,看一下 obj.prototype 这个对象里面的函数,再动动代码

    var test = (function(){
        var that;
        var obj = function(){
            this.name = "name";
            that = this;
        }
        obj.prototype = {
            aaa: function(){
                console.log("this is aaa!");
                console.log(this,that,test);
            },
            bbb: function(){
                console.log("this is bbb!");
                that.aaa.call(this);
            }
        }
        return new obj;
    })()
    
    test.aaa();    //这里是注释,一口气输出了 this、that、test 三个东西!

    看下结果:(先提前呵呵!)

    OK,先说 this ,谁调用 aaa 函数?test!那 test 就是它的指向。

    再说 that 

    that 在实例化的时候被赋予了 this ,这个this return 之前,它自己也不知道指向谁,后来(return 之后),它指向了 test ,也就是说 这时候的 that 实际上就是 test。(其实这样说不严谨,在实例化 new 的时候,this 就注定指向了被实例化后的对象,不论这个对象是谁,只是这里多了一步赋值给 that )

    test 本身就不多说了,这个像是递归的调用。

    利用上面的代码再执行:

    test.bbb();

    看下结果:

    这似乎看不出来什么,好吧,那就动动代码

    var test = (function(){
        var that;
        var obj = function(){
            this.name = "name";
            that = this;
        }
        obj.prototype = {
            aaa: function(){
                console.log("this is aaa!");
                this.xixi = "xixixi";
                console.log(this.haha + this.xixi);
            },
            bbb: function(){
                console.log("this is bbb!");
                this.haha = "hahaha";        //我动了
                that.aaa.call(this);
            }
        }
        return new obj;
    })()
    
    test.bbb();    //这里是注释,一口气输出了 this、that、test 三个东西!

    看下结果:

    想必结果大家都知道,但是我想说的是,这里的 call 是怎么运行的,看代码

    var test = (function(){
        var that;
        var obj = function(){
            this.name = "name";
            that = this;
        }
        obj.prototype = {
            aaa: function(){
                // console.log("this is aaa!");
                // this.xixi = "xixixi";
                // console.log(this.haha + this.xixi);
            },
            bbb: function(){
                console.log("this is bbb!");
                this.haha = "hahaha";        //我动了
    
                // that.aaa.call(this);         //我被注释了
    
                console.log("this is aaa!");    //下面是挪过来的
                this.xixi = "xixixi";
                console.log(this.haha + this.xixi);
            }
        }
        return new obj;
    })()
    
    test.bbb();    //这里是注释,一口气输出了 this、that、test 三个东西!

    看看是不是一样的:

    这里 call 的作用就是把 aaa 函数的内容拿过来在 bbb 函数里执行,然后还要用 bbb 函数的 this (毫不留情的那种,霸占了地盘还霸占了夫人【斜眼笑】)哈哈。

    apply 的用法也是一样的,只是两者参数格式不一样而已,call 可以传多个参数,apply 只有两个参数,第二参数是个数组。

     

    OK , 最后照抄大神的代码,用 call 实现一下 jQuery 中的 each 函数

    var each = function(array,fn){
        for(var index = 0; index < array.length; index++ ){
            fn.call(array[index], index, array[index]);
        }
    }
    
    each([2,3,4],function(i,item){
        console.log(i, item);
    });

    好了,今天我就学到这么点,都给大家分享了,有什么不对的地方,还请多多指正。

     

     

     

     

     

    展开全文
  • 本文实例讲述了JavaScript原型与原型链用法。分享给大家供大家参考,具体如下: 一句话说明什么是原型:原型就是一个JavaScript对象,原型能存储我们的方法,构造函数创建出来的实例对象能够引用原型中的方法。 一、...
  • 主要介绍了JavaScript原型链与继承操作,结合实例形式总结分析了javascript原形链与继承的相关概念、使用方法及操作注意事项,需要的朋友可以参考下
  • 本文实例讲述了javascript 原型与原型链的理解。分享给大家供大家参考,具体如下: javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了。 比如对象A与对象B之间,它们...
  • 本文实例讲述了JS学习笔记之原型链和利用原型实现继承。分享给大家供大家参考,具体如下: 原型链 原型链是一种关系,实例对象原型对象之间的关系,关系是通过原型(__proto__)来联系的 实例对象中有__proto__,是...
  • 主要介绍了JavaScript使用原型和原型链实现对象继承的方法,简单讲述了javascript原型与原型链的原理,并结合实例形式详细分析了javascript中对象继承的常见实现技巧,需要的朋友可以参考下
  • 本文实例分析了javascript原型链继承的用法。分享给大家供大家参考。具体分析如下: 代码如下:function Shape(){   this.name = ‘shape’;   this.toString = function(){   return this.name;   }  }  ...
  • 对于js的原型和原型链的理解

    万次阅读 多人点赞 2019-06-23 22:20:23
    一、原型与原型链 JavaScript是一门基于原型的语言,在软件设计模式中,有一种模式叫做原型模式,JavaScript正是利用这种模式而被创建出来 原型模式是用于创建重复的对象,同时又能保证性能,这种类型的设计模式...

    一、原型与原型链

    1. JavaScript是一门基于原型的语言,在软件设计模式中,有一种模式叫做原型模式,JavaScript正是利用这种模式而被创建出来
    2. 原型模式是用于创建重复的对象,同时又能保证性能,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。原型模式的目的是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,也就是说利用已有的一个原型对象,可以快速地生成和原型对象一样的新对象实例
    3. 原型:一个可以被复制(或者叫克隆)的一个类,通过复制原型可以创建一个一模一样的新对象,也可以说原型就是一个模板,在设计语言中更准确的说是一个对象模板
      1)原型是定义了一些公用的属性和方法,利用原型创建出来的新对象实例会共享原型的所有属性和方法
      实例代码:
        // 创建原型
        var Person = function(name){
            this.name = name;
        };
    
        // 原型的方法
       Person.prototype.sayHello = function(){
           console.log(this.name+",hello");
       };
    
       // 实例化创建新的原型对象,新的原型对象会共享原型的属性和方法
       var person1 = new Person("zhangsan");
       var person2 = new Person("lisi");
    
       // zhangsan,hello
       person1.sayHello();
       // lisi,hello
       person2.sayHello();
    

    2)严格模式下,原型的属性和方法还是会被原型实例所共享的
    实例代码:

        // 开启严格模式,原型的属性和方法还是会被原型实例所共享的
       "use strict";
    
        // 创建原型
        var Person = function(name){
            this.name = name;
        };
    
        // 原型的方法
       Person.prototype.sayHello = function(){
           console.log(this.name+",hello");
       };
    
       // 实例化创建新的原型对象,新的原型对象会共享原型的属性和方法
       var person1 = new Person("zhangsan");
       var person2 = new Person("lisi");
    
       // zhangsan,hello
       person1.sayHello();
       // lisi,hello
       person2.sayHello();
    

    3)通过原型创建的新对象实例是相互独立的,为新对象实例添加的方法只有该实例拥有这个方法,其它实例是没有这个方法的
    实例代码:

        // 创建原型
        var Person = function(name){
            this.name = name;
        };
    
        // 原型的方法
       Person.prototype.sayHello = function(){
           console.log(this.name+",hello");
       };
    
       // 实例化创建新的原型对象,新的原型对象会共享原型的属性和方法
       var person1 = new Person("zhangsan");
       var person2 = new Person("lisi");
    
       // zhangsan,hello
       person1.sayHello();
       // lisi,hello
       person2.sayHello();
    
       
       // 为新对象实例添加方法
       // 通过原型创建的新对象实例是相互独立的
       person1.getName = function(){
           console.log(this.name);
       }
    
       // zhangsan
       person1.getName();
       // Uncaught TypeError: person2.getName is not a function
       person2.getName();
    

    4)原型的总结

    • 所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
    • 所有函数都有一个prototype(原型)属性,属性值是一个普通的对象
    • 所有引用类型的__proto__属性指向它构造函数的prototype

    5)函数的原型prototype:函数才有prototype,prototype是一个对象,指向了当前构造函数的引用地址
    6)函数的原型对象__proto__:所有对象都有__proto__属性, 当用构造函数实例化(new)一个对象时,会将新对象的__proto__属性指向 构造函数的prototype
    7)原型对象和函数的原型的关系
    在这里插入图片描述
    说明

    • 所有函数的__proto__都是指向Function的prototype
    • 构造函数new出来的对象__proto__指向构造函数的prototype
    • 非构造函数实例化出的对象或者对象的prototype的__proto__指向Object的prototype
      Object的prototype指向null

    8)所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)
    9)实例的构造函数属性(constructor)指向构造函数 :person1.constructor == Person
    10)原型对象(Person.prototype)是 构造函数(Person)的一个实例
    11)原型的分类:
    隐式原型(_proto_):上面说的这个原型是JavaScript中的内置属性[[prototype]],此属性继承自object对象,在脚本中没有标准的方式访问[[prototype]],但Firefox、Safari和Chrome在每个对象上都支持一个属性_proto_,隐式原型的作用是用来构成原型链,实现基于原型的继承
    显示原型(prototype):每一个函数在创建之后,便会拥有一个prototype属性,这个属性指向函数的原型对象,显示原型的作用是用来实现基于原型的继承与属性的共享
    12)原型的使用方式
    通过给Calculator对象的prototype属性赋值对象字面量来设定Calculator对象的原型
    在赋值原型prototype的时候使用function立即执行的表达式来赋值,可以封装私有的function,通过return的形式暴露出简单的使用名称,以达到public/private的效果

    1. 原型链
      1)原型链:原型链是原型对象创建过程的历史记录,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构
      2)原型设计的问题:当查找一个对象的属性时,JavaScript 会根据原型链向上遍历对象的原型,直到找到给定名称的属性为止,直到到达原型链的顶部仍然没有找到指定的属性,就会返回 undefined
      也可以理解为原型链继承时查找属性的过程是先查找自身属性,当自身属性不存在时,会在原型链中逐级查找
      3)hasOwnProperty 函数:可以用来检查对象自身是否含有某个属性,返回值是布尔值,当属性不存在时不会向上查找对象原型链,hasOwnProperty是 JavaScript 中唯一一个处理属性但是不查找原型链的函数
      4)getOwnPropertyNames 函数:可以获取对象所有的自身属性,返回值是由对象自身属性名称组成的数组,同样不会向上查找对象原型链
      5)原型链的小结
    • 一直往上层查找,直到到null还没有找到,则返回undefined
    • Object.prototype.__proto__ === null
    • 所有从原型或更高级原型中的得到、执行的方法,其中的this在执行时,指向当前这个触发事件执行的对象

    6)JavaScript的原型是为了实现对象间的联系,解决构造函数无法数据共享而引入的一个属性,而原型链是一个实现对象间联系即继承的主要方法

    二、原型与原型链的常见面试题

    1. 谈谈你对原型的理解
      在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象,使用原型对象的好处是所有对象实例共享它所包含的属性和方法

    2. 什么是原型链?原型链解决的是什么问题
      1)原型链解决的主要是继承问题
      2)每个对象拥有一个原型对象,通过 proto 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null(Object.proptotype.__proto__指向的是null)。这种关系被称为原型链(prototype chain),通过原型链一个对象可以拥有定义在其他对象中的属性和方法
      3)构造函数 Parent、Parent.prototype 和 实例 p 的关系如下:(p.__proto__ === Parent.prototype)
      在这里插入图片描述

    3. prototype 和 proto 区别是什么
      1)prototype是构造函数的属性
      2)__proto__是每个实例都有的属性,可以访问 [[prototype]] 属性
      3)实例的__proto__与其构造函数的prototype指向的是同一个对象

    展开全文
  • 原型和原型链面试题

    2021-05-26 16:31:20
    原型和原型链 javascript原型与原型链 每个函数都有一个prototype属性,被称为显示原型 每个实例对象都会有_ _proto_ _属性,其被称为隐式原型 每一个实例对象的隐式原型_ _proto_ _属性指向自身构造函数的显式原型...

    原型和原型链

    javascript原型与原型链
    1. 每个函数都有一个prototype属性,被称为显示原型
    2. 每个实例对象都会有_ _proto_ _属性,其被称为隐式原型
    3. 每一个实例对象的隐式原型_ _proto_ _属性指向自身构造函数的显式原型prototype
    4. 每个prototype原型都有一个constructor(看次chua克特)属性,指向它关联的构造函数。
    原型链

    获取对象属性时,如果对象本身没有这个属性,那就会去他的原型__proto__上去找,如果还查不到,就去找原型的原型,一直找到最顶层(Object.prototype)为止。Object.prototype对象也有__proto__属性值为null。

    展开全文
  • JavaScript高手之路:原型和原型链

    千次阅读 多人点赞 2019-05-30 01:13:28
    原型和原型链是JavaScript进阶重要的概念,尤其在插件开发过程中是不能绕过的知识点,这篇文章就带你去抽丝剥茧的学习这一过程。在写博客过程中,我比较倾向习惯从一个按例开始说起,以此为切入点一点点的进入正题...

    原型和原型链是JavaScript进阶重要的概念,尤其在插件开发过程中是不能绕过的知识点,这篇文章就带你抽丝剥茧的学习这一过程。

    由一个例子开始说起

    在写博客过程中,我比较倾向和习惯从一个按例开始说起,以此为切入点一点点的进入正题,so,我们还是看看JavaScript内置对象Array来做一个数字排序得例子吧:

    		var arr1 = [1, 0, 0, 8, 6];
    		var arr2 = [1, 0, 0, 8, 6, 1, 1];
    
    		arr1.sort(function(n1, n2) {
    			return n1 - n2;
    		});
    
    		arr2.sort(function(n1, n2) {
    			return n1 - n2;
    		});
    
    		console.log(arr1); //[0, 0, 1, 6, 8]
    		console.log(arr2); //[0, 0, 1, 1, 1, 6, 8]
    		console.log(arr1 === arr2);//false
    		console.log(arr1.sort === arr2.sort);//true
    

    本例子定义了2个数组arr1和arr2,并调用sort方法排序,当两个数组排序结束之后,分别输出这俩数组的内容,控制台输出
    arr1 :[0, 0, 1, 6, 8]
    arr2 :[0, 0, 1, 1, 1, 6, 8]

    我知道上诉两行输出并不会引起你的好奇和兴趣,所以这里我想让你把注意力集中输出的第3行和第4行。这里用到JavaScript严格相等操作符===来判断arr1数组和arr2数组是否相等。显然的,arr1和arr2是两个不同的数组,数组长度和元素都不一样,所以控制台输出false。第4行是判断arr1对象和arr2对象的函数sort是否是同一函数,结果输出了true。如果你学过面向对象编程语言,如C++/Java你会发现,这俩对象调用的不是实例方法,而是调用了类方法,什么意思呢?意思是数组arr1和数组arr2是俩不同的对象,但却用了公共的方法,类似于Java中的static方法。

    那么JavaScript是如何做到的呢?先别急,这里先打一个问号。再来看另一个栗子:

    		var arr1 = [1, 0, 0, 8, 6];
    		var arr2 = [1, 0, 0, 8, 6, 1, 1];
    	    
    	    //数组求和方法
    		arr1.getSum = function() {
    			var sum = 0;
    			for(var i = 0; i < this.length; i++) {
    				sum += this[i];
    			}
    			return sum;
    		}
    		console.log(arr1.getSum()); //输出15
    		console.log(arr2.getSum());//控制台报错: Uncaught TypeError: arr2.getSum is not a function
    

    这个栗子还是定义了2个变量arr1和arr2,我只在arr1上面定义了函数getSum(),正如所期望的,对象arr1已经完成了数组的累和。但是变量arr2却报错,原因很简单,因为你的变量arr2没有getSum()方法,那么有没有解决方案,函数只定义一次,然后提供给不同的变量使用呢?答案是有的,请看栗子:

    		var arr1 = [1, 0, 0, 8, 6];
    		var arr2 = [1, 0, 0, 8, 6, 1, 1];
    
    		//将getSum定义为原型方法
    		Array.prototype.getSum = function() {
    			var sum = 0;
    			for(var i = 0; i < this.length; i++) {
    				sum += this[i];
    			}
    			return sum;
    		}
    		console.log(arr1.getSum()); //控制台输出15
    		console.log(arr2.getSum()); //控制台输出17
    

    解决方案就是将实例方法定义为原型方法Array.prototype.getSum,然后对象arr1和arr2就可以共享getSum方法了。为了验证一下这俩对象是否调用了同一个方法,我们写测试代码如下:

    		console.log(arr1 === arr2);    //false
    		console.log(arr1.getSum === arr2.getSum);  //true
    

    通过上面这个例子,我想你大概知道了JavaScript如何声明静态方法了,也知道第一个例子中为什么两个不同如何定义公共方法了,那么这个prototype关键字是什么呢?它有什么用么?

    prototype

    在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

    1. 每个函数上面都有一个属性(prototype)指向了函数的原型对象(Person.prototype)。

    		function Person() {
    		}
    		console.log(Person.prototype);
    

    即使你只定义了一个空函数,也存在一个prototype的属性。

    {constructor: ƒ}
    constructor: ƒ Person()
    arguments: null
    caller: null
    length: 0
    name: "Person"
    prototype: {constructor: ƒ}
    __proto__: ƒ ()
    [[FunctionLocation]]: 数组排序.html:6
    [[Scopes]]: Scopes[1]
    __proto__:
    constructor: ƒ Object()
    hasOwnProperty: ƒ hasOwnProperty()
    isPrototypeOf: ƒ isPrototypeOf()
    propertyIsEnumerable: ƒ propertyIsEnumerable()
    toLocaleString: ƒ toLocaleString()
    toString: ƒ toString()
    valueOf: ƒ valueOf()
    __defineGetter__: ƒ __defineGetter__()
    __defineSetter__: ƒ __defineSetter__()
    __lookupGetter__: ƒ __lookupGetter__()
    __lookupSetter__: ƒ __lookupSetter__()
    get __proto__: ƒ __proto__()
    set __proto__: ƒ __proto__()
    

    例子尽管我们什么都不做,但是浏览器已经在内存中创建了两个对象:Person(函数)和Person.prototype,其中,我们称Person为构造函数,因为我们后面要用到这个函数来new对象,Person.prototype称为Person的原型对象,简称原型。
    在这里插入图片描述
    现在我们给Person构造函数添加属性并使用new方式来创建一个Person()对象,看看浏览器内存发生了什么?

    		function Person(name, age) {
    			this.name = name;
    			this.age = age;
    		}	
    		Person.prototype.showName = function() {
    			return this.name;
    		}
    		var p1 = new Person("SpringChang", 22);
    		console.log(p1.showName());
    

    2. 每个实例上面都有一个隐式原型(proto)指向了函数的原型对象,如本利的p1对象有一个隐式原型也指向了Person.prototype对象。

    请看下图
    在这里插入图片描述
    如图所示,Person构造函数有一个隐式属性prototype指向了他的原型对象Person.prototype,而p1对象也有一个隐式属性__proto__指向了源性对象Person.prototype,而在原型上面我们定义了showName方法。

    3. 实例访问属性或者方法的时候,遵循以为原则:

    1. 如果实例上面存在,就用实例本身的属性和方法。
    2. 如果实例上面不存在,就会顺着__proto__的指向一直往上查找,查找就停止。

    请看下面例子:

    		function Person(name, age) {
    			this.name = name;
    			this.age = age;
    		}	
    		Person.prototype.showName = function() {
    			return "你调用的原型上面的方法";
    		}
    
    		var p1 = new Person("SpringChang", 22);
    		p1.showName = function() {
    			return "你调用的是p2对象上面的方法";
    		}
    		console.log(p1.showName()); //输出:你调用的是p1对象上面的方法
    
    		var p2 = new Person("SpringChang", 22);
    		console.log(p2.showName()); //输出:你调用的原型上面的方法
    

    在这里插入图片描述
    结合图和代码可以看到,原型上面有showName方法,p1对象也有showName方法,那么这时候p1调用的自身的showName方法,所以输出你调用的是p1对象上面的方法。而p2对象没有showName方法,这时候会顺着p2对象的__proto__属性指向的原型找找看没有有没有showName方法,结果找到了,则p2调用的原型上面的方法。如果原型上面也没有对应的方法呢?这时候它会顺着原型的原型去找对应的方法,最终找到Object对象如果还没找到则报undefined,下面我们来验证一下:

    		console.log(p1.showName === p2.showName); //false
    		console.log(p2.sex); //undefined
    

    4. 每个函数的原型对象上面都有一个constructor属性,指向了构造函数本身。

    console.log(Person.prototype.constructor == Person);  //true
    

    在这里插入图片描述
    由此,我们根据这4条规则绘制成了上述的关系图,这里我们可以看出Person的原型Person.prototype有一个属性constructor又指向Person构造函数本身。

    原型链

    上一小节我们提到,对象在寻找某一属性时,如果自身属性没找到就去他对应的原型对象去找。若在原型上面找到对应的属性则停止,否则继续去原型的原型找对应的属性,这样构成了一条原型链。上一节中Person的原型其实还有一属性__proto__,他指向了上一级Object的原型对象。

    console.log(Person.prototype.__proto__ === Object.prototype); //true
    

    这时候来了一个Object对象,它是JavaScript的顶级对象,同样也有自己的原型Object.protoype,这时候Person对象以及它的原型,Object对象已经对应的原型关系如下图所示。
    在这里插入图片描述
    将Object和Person联系起来的关键是Person.prototype的属性__proto__,它指向了Object.prototype,它将两者打通,构成一个链式关系。同时你也看到Object的prototye也指向了Object.prototype,所以console.log(Person.prototype.proto === Object.prototype)输出的额结果是true。

    		function Person(name, age) {
    			this.name = name;
    			this.age = age;
    		}	
    		Person.prototype.showName = function() {
    			return "你调用的原型上面的方法";
    		}
    
    		var p1 = new Person("SpringChang", 22);
    		var p2 = new Person("CSDNER", 23);
    		p2.__proto__ = null;
    
    		console.log(p1.showName());
    		console.log(p2.showName());
    

    现在我们打破了p1对象的原型,它原本指向的是Person的原型,现在我们让他指向null,则控制台会报错。

    Uncaught TypeError: p2.showName is not a function
    

    现在我们再构造另一个对象Animal,然后强制修改p2的原型链,让他指向Animal的原型。

    		function Person(name, age) {
    			this.name = name;
    			this.age = age;
    		}	
    		Person.prototype.showName = function() {
    			return "你调用的原型上面的方法";
    		}
    
    		//定义另一个构造函数
    		function Aminal() {
    		}
    
    		//在Aminal的原型上面定义方法speek方法
    		Aminal.prototype.showName = function(str) {
    			return "我是Aminal的showName";
    		}
    
    
    		var p1 = new Person("SpringChang", 22);
    		var p2 = new Person("CSDNER", 23);
    		p2.__proto__ = Aminal.prototype; //将p2的__proto__指向Aminal的原型
    
    		console.log(p1.showName());  //你调用的原型上面的方法
    		console.log(p2.showName());  //我是Aminal的showName
    

    从上面代码可以看到,p2的showName方法调用的Aminal原型上面的showName,而不再是Person原型上面的showName,如果你还对p2的showName是不是真的是Aminal的showName心存疑虑,那么再来验证一下吧。

    console.log(p2.showName() === Aminal.prototype.showName()); //true
    

    一般来说,我们不建议手动去修改某个对象的原型,这会破坏掉原来的原型链。但是原型却是JavaScript面向对象编程中相当重要的一个点,因为你可以利用它更好的封装一个类。

    更好的封装一个类

    明显的,之前的文章对类的封装还存在缺陷,至少来说还不够完美,咱再来看看之前对类的封装存在什么问题。

    		function Person(name, age) {
    			this.name = name;
    			this.age = age;
    			this.sayHello = function() {
    				console.log("Hello");
    			}
    		}	
    
    		var p1 = new Person("CSDNer", 22);
    		var p2 = new Person("SpringChang", 23);
    		p1.sayHello();  //Hello
    		p2.sayHello();  //Hello
    		console.log(p1.sayHello === p2.sayHello); //false
    

    一般来说,我们抽象的的方法是完成某一个事情的,而通过上诉代码看出这俩对象各自创建了一个方法,浪费内存,其实这些功能可以通过同一函数来完成。那么我们现在可以很容易的想到,这个方法不必定义在构造函数上面,可以将它定义在构造函数的原型上面,而属性则定义在构造函数上。

    		function Person(name, age) {
    			this.name = name; //定义属性
    			this.age = age;   //定义属性
    		}	
    
    		//将Hello方法定义在Person的原型上面
    		Person.prototype.sayHello = function() {
    			console.log("Hello");
    		}
    
    		var p1 = new Person("CSDNer", 22);
    		var p2 = new Person("SpringChang", 23);
    		p1.sayHello();  //Hello
    		p2.sayHello();  //Hello
    		console.log(p1.sayHello === p2.sayHello); //true
    

    构造函数定义属性,原型定义方法,这种组合的有事可以更好的封装一个类。回过头来看开篇文章举的Array例子,它的sort方法就是这么定义的。

    展开全文
  • 我们通过原型方式,解决了多个实例的方法共享问题,接下来,我们就来搞清楚原型(prototype),原型链的来龙去脉。 function CreateObj(uName) { this.userName = uName; } CreateObj.prototype.showUserName = ...
  • 原型链】每个构造函数都有一个对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,如果原型对象等于另一个原型的实例,此时的原型对象将包含一个指向另一个原型的指针,...
  • 在我初学 JS 语言的继承机制原型和原型链的时候,我一直理解不了这种设计机制,再加上之前原有对 Java继承的理解,在学习 JS 继承机制的设计上踩了一个大坑,很多知识点前期都是死记硬背,无法真正的理解它的设计...
  • 深入javascript之原型和原型链

    万次阅读 多人点赞 2018-03-02 21:22:27
    原型和原型链是js中的难点也是重点,明白了原型和原型链会让我们在后面不管是学习还是工作都会更加高效,并且原型和原型链会是面试中必不可少的话题。看完此篇文章一定会让你对原型,原型链有深刻全面的了解。 深入...
  • 原型和原型链详解

    千次阅读 2020-06-04 18:29:03
    原型和原型链怎么来的? 1994年,网景公司(Netscape)发布了Navigator浏览器0.9版,但是刚开始的Js没有继承机制,更别提像同时期兴盛的C++Java这样拥有面向对象的概念。在实际的开发过程中,构造函数内部的属性...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 103,766
精华内容 41,506
关键字:

原型和原型链实例