最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 01.原型(prototype)和原型链(prototype chain)

    正文概述 掘金(Jason杰森)   2021-06-20   431

    [toc]

    一.原型

    1. 函数的prototype属性(图)

    ** 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)*

    ** 原型对象中有一个属性constructor, 它指向函数对象*

    2. 给原型对象添加属性(一般都是方法)

    ** 作用: 函数的所有实例对象自动拥有原型中的属性(方法)*

     <script type="text/javascript">
          // 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
          console.log(Date.prototype, typeof Date.prototype)
    
          function Fun() {
    
          }
          console.log(Fun.prototype) // 默认指向一个Object空对象(没有我们的属性)
    
          // // 原型对象中有一个属性constructor, 它指向函数对象
          console.log(Date.prototype.constructor === Date)
          console.log(Fun.prototype.constructor === Fun)
    
          // //给原型对象添加属性(一般是方法) ===>实例对象可以访问
          Fun.prototype.test = function () {
            console.log('test()')
          }
          var fun = new Fun()
          fun.test()
        </script>
    

    1.1 prototype(原型)和_ _ proto _ _(隐式)

    在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。

    1.每个函数function都有一个prototype,即显式原型(属性)

    2. 每个实例对象都有一个__proto__,可称为隐式原型(属性)

    3. 对象的隐式原型的值为其对应构造函数的显式原型的值

     // 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
    console.log(Fn.prototype === fn.__proto__) // true
    

    1.2 理解原型的内存图

    <!--
        1. 每个函数function都有一个prototype,即显式原型(属性)
        2. 每个实例对象都有一个__proto__,可称为隐式原型(属性)
        3. 对象的隐式原型的值为其对应构造函数的显式原型的值
        4. 内存结构(图)
        5. 总结:
          * 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
          * 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
          * 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
        -->
        <script type="text/javascript">
          //一.定义构造函数
          function Fn() { // 内部语句: this.prototype = {}
    
          }
          // 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
          console.log(Fn.prototype)
          // 2. 每个实例对象都有一个__proto__,可称为隐式原型
          //二.创建实例对象
          var fn = new Fn() // 内部语句: this.__proto__ = Fn.prototype
          console.log(fn.__proto__)
          // 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
          console.log(Fn.prototype === fn.__proto__) // true
          //三.给原型添加方法
          Fn.prototype.test = function () {
            console.log('test()')
          }
          //四.通过实例调用原型的方法
          fn.test() //test()
        </script>
    

    01.原型(prototype)和原型链(prototype chain)

    1.3 原型的基本使用

    通常我们在使用原型写代码的时候,我们都会这样做:

    ==1.在构造函数里面写属性。==

    ==2.在原型对象上写方法。==

    这样做就避免了浪费内存的问题

    1.4 构造函数和实例对象,原型对象的三角关系

    01.原型(prototype)和原型链(prototype chain)

      /*
        原型:
            1.是构造函数的一个属性,是一个对象,是在我们创建构造函数的时候 系统 自动分配的
               作用是给 构造函数的 实例对象 提供方法
            2.可以通过构造函数.prototype 得到   
    
         总结:
             1. 原型对象 - 作用是给实例对象提供方法
             2. 构造函数.prototype 和 实例对象.__proto__
             3. 有prorotype属性是 构造函数  ,有 __proto__ 属性 就是 实例对象    
        */
        function Person(name, age, gender) {
          this.name = name
          this.age = age
          this.gender = gender
        }
    
        Person.prototype.sayHi = function () {
          console.log('sayHi被调用了');
        }
        // console.dir(Person)
        console.log(Person.prototype);
        // 2.证明 原型的 constructor 属性  就是  构造函数
        console.log(Person.prototype.constructor === Person); //true
    
        let p1 = new Person()
        let p2 = new Person()
    
        // p1.sayHi()
        // p2.sayHi()
        // console.log(p1);
        // console.log(p2);
    
        //1.证明 实例对象的 __proto__ 就是 构造函数的prototype
        // console.log(p1.__proto__ === p2.__proto__); //true
        // console.log(p1.__proto__ === Person.prototype); //true
        // console.log(p2.__proto__ === Person.prototype); //true
    

    01.原型(prototype)和原型链(prototype chain)

    原型其实就是一个在浏览器中,每当创建一个构造函数,就会自动分配好的对象。

    这个对象的作用,就是给实现对象共享方法用的。

    我们可以通过两个方式访问到这个对象:

    1.从构造函数访问原型:构造函数.prototype
    2.从实例对象访问原型:实例对象.__proto__
    

    1.5 原型总结

    1. 原型其实就是一个对象,存在于内存中,我们看不见
    2. 原型可以给构造函数的实例对象提供方法
    3. 构造函数.prototype实例对象.__proto__ 都可以访问到原型对象
    4. prototype属性的是构造函数,有__proto__属性的是实例对象
    5. 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
    6. 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
    7. 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)

    1.6 原型的原理

    原型之所以会比单独的构造函数好,是因为它在内存这的唯一性,于是在它身上的方法也具备唯一性。

    01.原型(prototype)和原型链(prototype chain)

    此时无论我们new多少个实例对象,它们都会在调用方法的时候,沿着它的__proto__属性找到原型对象,然后调用原型对象上的对应的方法,因为原型对象只会有一个,所以方法对应的函数对象也就只会有一个,就解决了浪费内存的问题。

    而此时在内存中,已经在多个对象之前存在这样一个关联关系

    01.原型(prototype)和原型链(prototype chain)

    二.封装一个简单的jQuery

    jQuery中就是使用了原型来实现了很多代码的封装,所以我们利用所学的原型的知识,模仿一下jQuery的代码封装。(这个过程比较复杂,我们可以先不看,在上课的时候再慢慢讲)

    /*    简单的jQuery包含的功能:        1.获取元素  $(选择器)        2.注册事件  jq对象.on()        3.修改样式  jq对象.css()    目的:        jq对象.on(事件类型,处理程序)        jQuery.prototype.on = function(){}*/// 面向对象,为了区分对象,先 写 构造函数function jQuery(selector) {  // 得到一个伪数组  var dom = document.querySelectorAll(selector);// 这是一个 NodeList 伪数组  // 我们自己造一个  for (var i = 0; i < dom.length; i++) {    this[i] = dom[i];  }  //伪数组都要长度  this.length = dom.length;}// 封装css方法jQuery.prototype.css = function (prop, value) {  // css方法有多个用法,可以通过判断 参数个数区分  if (arguments.length === 2) {    // 把 伪数组里面的所有元素都修改    // 方法里面的this,是实例对象    // this是一个伪数组,当然要遍历    for (var i = 0; i < this.length; i++) {      this[i].style[prop] = value;    }  } else if (arguments.length === 1) {    //此时 prop 应该是一个对象,需要从prop里面得到每个键值对    for (var key in prop) {      for (var i = 0; i < this.length; i++) {        this[i].style[key] = prop[key];      }    }  }  // 为了支持链式编程,返回一个jq对象  return this;}// 封装on方法 - 实现注册事件jQuery.prototype.on = function (type, fn) {  // 判断 当前的浏览器 是否支持  addEventListener 方法  // 事件源.addEventListener(事件类型,处理程序)  for (var i = 0; i < this.length; i++) {    if (typeof this[i].addEventListener === 'function') {      this[i].addEventListener(type, fn);    } else {      // ie 的注册事件的方法 attachEvent      this[i].attachEvent('on' + type, fn);    }  }  return this}// 为了简单jQuery的使用,再包一层函数function $(selector) {  return new jQuery(selector);}
    

    三.原型链

    3.1 什么是原型链?

    ==在JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。==这种一级一级的链结构就称为原型链(prototype chain)

    事实上在内存中,存在多个原型对象,多个原型对象之前存在着一个链式关系,这个链式关系我们称为:原型链

    原型链是javascript特意为了实现面向对象的继承而设计的一种对象结构,这样可以解决代码的重复利用的问题。

    当我们把构造函数的原型输出,再展开查看,发现原型对象上面也有__proto__属性,也就是说其实原型对象也是一个实例对象

    function Person(name,age,gender){ }console.log(Person.prototype)
    

    01.原型(prototype)和原型链(prototype chain)

    而原型对象的__proto__属性我们发现它也是一个对象,此时在内存中就至少存在这样一个链式关系

    01.原型(prototype)和原型链(prototype chain)

    我们就称这样的结构关系为原型链

    3.2 原型链的作用

    我们在上方提到,==原型链是javascript专门为了实现继承而设计的==,我们先不管继承是什么,可以先看看它这样设计的用处。

    前方已经学习过,原型对象的作用就为了给实例对象提供方法的,那么也就是说 对象.__proto__ 上面的方法,对象就可以使用。同理,现在在原型对象(对象.__proto__)的上面的__proto__属性所指向的对象,它身上的方法能否被原型对象所使用呢?当然!

    我们先看看在这个原型的原型上面有什么方法

    console.log(Person.prototype.__proto__)
    

    01.原型(prototype)和原型链(prototype chain)

    可以看到在原型的原型身上有一个toString方法,我们尝试调用一下

    console.log(Person.prototype.toString()); // 结果为: [object Object]
    

    可见__proto__属性身上的方法确实是可以被对象所使用的。

    那么这个时候我们思考一个问题: Person构造的函数的实例对象能不能调用这个toString()方法呢?毕竟Person实例的__proto__属性是 Person.prototype,Person.prototype的__proto__属性的方法也相当于是Person.prototype的方法,那么实例对象访问它的__proto__的方法,应该也可以

    let p = new Person()console.log(p.toString()); // 结果: [object Object]
    

    可以看到,toString方法确实通过多个对象之间的 __proto__ 关系被重用了。

    3.3 原型链结构

    结合之前得到的Perosn与其原型之间的关系,我们可以得到一个更加完整的关系图

    01.原型(prototype)和原型链(prototype chain)

    01.原型(prototype)和原型链(prototype chain)

    于是我们可以思考一个问题,Object原型对象上面还有没有__proto__属性呢?

    console.log(Object.prototype.__proto__) // null
    

    也就是说原型链的关系到了Object原型这,再往上就没有了 ———— 原型链的尽头是 null

    01.原型(prototype)和原型链(prototype chain)

    值得注意的是,这套东西不是我们发现的,而是js作者特意为了能够实现代码复用而设计出来的特殊结构,我们只不过是以推导的方式来带领大家学习

    其实原型链就是我们在js中实现继承的基本原理

    3.4 原型链成员访问规则

    原型链末端的对象可以访问上游的对象的方法,其实也是有一定的访问规则的。和作用域类似的,原型链上的成员也遵循**"就近原则"**

    • 读取对象的属性值时:
    1. 当访问一个对象的成员时,如果这个对象有自己的对应成员,优先使用自己的
    2. 当自己没有对应的成员时,会沿着原型链查找,找到一个最近的
    3. 如果直到Object.prototype身上也没有,返回undefined
    • 设置对象的属性值时:==不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值==

    • ==方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上==

         <!--
            1. 读取对象的属性值时: 会自动到原型链中查找
            2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
            3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
          -->
          <script type="text/javascript">
            //例子一:
            function Fn() {
      
            }
            Fn.prototype.a = 'xxx'
            var fn1 = new Fn()
            console.log(fn1.a, fn1) //xxx Fn{}
      
            var fn2 = new Fn()
            fn2.a = 'yyy'
            console.log(fn1.a, fn2.a, fn2) //xxx yyy Fn{a:'yyy'}
      
      
            //例子二:
            function Person(name, age) {
              this.name = name
              this.age = age
            }
            Person.prototype.setName = function (name) {
              this.name = name
            }
            var p1 = new Person('Tom', 12)
            p1.setName('Bob')
            console.log(p1) //{'Bob',12}
      
            var p2 = new Person('Jack', 12)
            p2.setName('Cat')
            console.log(p2) //{'Cat',12}
            console.log(p1.__proto__ === p2.__proto__) // true
      

    3.5 原型链案例分析(==构造函数/原型/实例对象的关系(图解)==)

      <!--
          1. 原型链(图解)
            * 访问一个对象的属性时,
              * 先在自身属性中查找,找到返回
              * 如果没有, 再沿着__proto__这条链向上查找, 找到返回
              * 如果最终没找到, 返回undefined
            * 别名: 隐式原型链
            * 作用: 查找对象的属性(方法)
          2. 构造函数/原型/实体对象的关系(图解)
          3. 构造函数/原型/实体对象的关系2(图解)
    -->
        <script type="text/javascript">
          // console.log(Object)
          //console.log(Object.prototype)
          //console.log(Object.prototype.__proto__)
    
          function Fn() {
            this.test1 = function () {
              console.log('test1()')
            }
          }
          //console.log(Fn.prototype)
          Fn.prototype.test2 = function () {
            console.log('test2()')
          }
    
          var fn = new Fn()
    
          fn.test1() //test1()
          fn.test2() //test2()
          console.log(fn.toString()) //[object object]
          console.log(fn.test3) //undefined
          //fn.test3()
    
    
          /*
          1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
           */
          console.log(Fn.prototype instanceof Object) // true
          console.log(Object.prototype instanceof Object) // false
          console.log(Function.prototype instanceof Object) // true
          /*
          2. 所有函数都是Function的实例(包含Function)
          */
          console.log(Function.__proto__ === Function.prototype)
          /*
          3. Object的原型对象是原型链尽头
           */
          console.log(Object.prototype.__proto__) // null
    

    01.原型(prototype)和原型链(prototype chain)

    3.6 ==构造函数/原型/实例对象的关系(图解)==

    var o1=new Object();
    var o2={ };
    
    01.原型(prototype)和原型链(prototype chain)

    3.7 ==构造函数/原型/实例对象的关系2(图解)==

    function Foo( ){ }
    
    01.原型(prototype)和原型链(prototype chain)

    01.原型(prototype)和原型链(prototype chain)

    四.instanceOf关键字

    1.instanceof是如何判断的?

    表达式: A instanceof B

    instance关键字规则: ==如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false==

    2.Function是通过new自己产生的实例

    4.1 案例一:

    function Foo() {}
    var f1 = new Foo()
    console.log(f1 instanceof Foo) // true
    console.log(f1 instanceof Object) // true
    

    01.原型(prototype)和原型链(prototype chain)

    4.2 案例二:

     console.log(Object instanceof Function) // true
     console.log(Object instanceof Object) // true
     console.log(Function instanceof Function) // true
     console.log(Function instanceof Object) // true
    
     function Foo() {}
     console.log(Object instanceof Foo) // false
    

    01.原型(prototype)和原型链(prototype chain)

    五.面试题

    5.1 面试题一:熟悉以后,就不需要画图,要一眼看懂

     function A() {
    
          }
          A.prototype.n = 1
    
          var b = new A()
    
          A.prototype = {
            n: 2,
            m: 3
          }
    
          var c = new A()
          console.log(b.n, b.m, c.n, c.m) //2 undefined 2 3
    

    01.原型(prototype)和原型链(prototype chain)

    5.2 面试题二:

    function F() {}
    Object.prototype.a = function () {
          console.log('a()')
    }
    Function.prototype.b = function () {
          console.log('b()')
    }
    
    var f = new F()
    f.a()
    f.b()//报错
    F.a()
    F.b()
    

    这道题的关键在于要理解下面的终极原型链图

    01.原型(prototype)和原型链(prototype chain)


    起源地下载网 » 01.原型(prototype)和原型链(prototype chain)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元