• 笔记: js构造函数与原型


    @

    构造函数与原型介绍

    1.函数与函数的原型对象(prototype object):

    • 在JavaScript中,创建一个函数A, 浏览器就会在内存中创建一个对象B,而且该函数默认会有一属性 prototype 指向这个对象(即:prototype属性的值)
    • 这个对象B就是函数A的原型对象,简称函数的原型。原型对象B也默认会有一个属性 constructor 指向了这个函数A (即:constructor属性的值是函数A)
    • 凡是以函数A为构造函数而创建的对象A1,A2,A3等等,也都有一个内部的[[prototype]]属性,也指向这个对象B.
      构造函数,原型对象,实例对象之间的三种重要引用:
      (1) 构造函数-->原型对象 (A.prototype-->B)
      (2) 原型对象-->构造函数 (B.constructor-->A)
      (3) 实例对象-->原型对象 (A1.[[Prototype]]/A1._ proto _-->B)

    涉及三种引用的操作

    2.基于三种引用的操作
    如上图,我们基于这三种引用会有许多的操作(修改,替换,删除),我们来进行一个分析总结.

    • 构造函数创建实例的具体过程
      参考资料:
      英文版--new [[Construct]] [[Call]]
      中文版--new [[Construct]] [[Call]]
      new A();
      1.令ref = 构造函数A的引用(地址)
      2.令变量constructor = GetValue(ref): 按照ref找到函数对象A的存储单元
      3.调用constructor的[[Construct]]内部方法:

      • 创建一个新的原生javascript对象obj
      • 按照规范设定好obj对象的一系列内部属性和方法
      • 设置obj的[[Prototype]] (设置obj的原型)
        • 如果A.prototype是一个对象,令obj.[[Prototype]]=A.prototype
        • 如果A.prototype不是一个对象,令obj.[[Prototype]]=Object.prototype
      • 令变量result = A.[[Call]] (其中,this值为obj)
        (就是以obj为this的值,执行A的函数体中的代码,目的是对obj进行初始化)

      总结: 由上面的分析可知,如果在构造函数A的函数体内用this给实例添加的属性,是不会反映到原型上的,属于实例的本身的属性.

    • 三种引用是否可以被更改的测试

      //TEST:三种引用是否都可以修改替换
      function A (){}
      var B = A.prototype;
      var A1 = new A();

      //A.prototype与B.constructor
      console.log(Object.getOwnPropertyDescriptor(A,'prototype'));//可修改
      console.log(Object.getOwnPropertyDescriptor(B,'constructor'));//可修改


      //[[Prototype]]
      console.log('prototype' in A1); //false,内部属性不属于原型属性
      console.log(A1.hasOwnProperty('prototype'));//false,内部属性不属于自身属性
      //只有获取方法,没有手动修改方法


      //__ proto __
      console.log(' __ proto __ ' in A1);
      console.log(A1.hasOwnProperty(' __ proto __ '));//false, __ proto __ 属于原型属性
      console.log(Object.prototype.hasOwnProperty(' __ proto __ '));//true,__ proto __ 定义在Object.prototype上
      console.log(Object.getOwnPropertyDescriptor(Object.prototype, ' __ proto __ '));//configurable:true enumerable:false


      //利用 __ proto __ 间接修改[[prototype]] (不推荐)
      function C() {}
      var D = C.prototype;
      console.log(Object.getPrototypeOf(A1));
      A1. __ proto __ = D; //利用非规范属性 __ proto __ 间接修改[[prototype]]
      console.log(Object.getPrototypeOf(A1));

      总结: __ proto __属性是非标准的,是定义在Object.prototype上的一个暴露实例内部[[prototype]]属性的访问器属性.如果我们考虑到代码的安全和性能,我们可以在代码开始位置用delete Objet.prototype. _ _ proto _ _ 来删除掉.

    • 替换构造函数A的原型--修改A.prototype的值
      预计的影响:
      1.已有实例的原型不变,但无法再用A.prototype添加或修改原型属性
      2.新实例的原型是A.prototype修改后的值,并且可以用A.prototype添加或修改原型属性

      //TEST:构造函数替换原型对象的影响
      function A() {}
      function B() {}
      var A1 = new A();
      var B1 = new B();


      A.prototype.say = function (){
      alert('A链的方法');
      }
      B.prototype.say = function (){
      alert('B链的方法');
      }


      var temp = B.prototype;
      B.prototype = A.prototype;
      A.prototype = temp;


      var A2 = new A();
      var B2 = new B();


      //检测A1 A2 B1 B2 各自的原型链
      A1.say();
      B1.say();


      A2.say();
      B2.say();


      //尝试通过原有构造函数向A1添加原型属性
      A.prototype.say2 = function (){
      alert('仍可以通过A向A1添加原型属性');
      }
      A.prototype.say3 = function (){
      alert('可以通过A向A2添加原型属性');
      }


      alert('say2' in A1);//false,A1.say2方法不存在.不能再通过A向A1添加原型属性
      A2.say3();//添加成功

    • 替换已有实例的原型
      预计影响:
      1.接上了另一条原型链
      2.无法再用A.prototype添加或修改原型属性

      //TEST:已有实例对象修改原型对象的影响
      function A() {}
      function B() {}


      var A1 = new A();
      var B1 = new B();


      A.prototype.say = function (){
      alert('A链的方法');
      }
      B.prototype.say = function (){
      alert('B链的方法');
      }


      //测试是否接到另一条原型链
      var A2 = Object.create(A1);
      A2.say();


      A2.__ proto __ = B1;
      A2.say();


      //测试是否不能再用原来的构造函数添加原型属性
      var A3 = new A();
      A3.__ proto __ = B1;
      A.prototype.say2 = function (){
      alert('仍然可用构造函数添加原型属性');
      }
      A3.say2(); //出错,A3中找不到方法say2()

    • 替换原型的构造函数--A.prototype.constructor
      影响:
      1.只是无法再用A.prototype.constructor获取构造函数A,无法便捷地添加'静态方法'了
      2.仍能正常用A创建实例,用A.prototype添加或修改原型属性

    有关原型及原型链的一些相关方法总结

    1.instanceof运算符
    参考资料:instanceof [[HasInstance]](https://www.ecma-international.org/ecma-262/5.1/)
    instaceof运算符,是将左边对象的原型作为参数,来调用右边函数的[[HasInstance]] (V)内部方法,所得返回值即为运算符的结果.
    [[HasInstance]] (V)方法大致过程:(以A.[[HasInstance]] (V)为例)

    • 1.令V=V.prototype

    • 2.如果V不是null,比较A.prototype与V的值

      • 如果相等,返回true
      • 如果不等,从1重新开始,直到V为null

    //TEST: instanceof原理测试:向上搜索实例的原型链,看由构造函数所指向的原型对象是否在其中
    function A() {}
    var A1 = new A();
    var B = A.prototype;


    console.log(A1 instanceof A);//true


    B.constructor = null;
    console.log(A1 instanceof A);//true


    A.prototype = {};
    console.log(A1 instanceof A);//false

    2.属性遍历

    • 1.自身可枚举一般属性遍历:(enumerable为true) "Object.keys()+循环语句"或"hasOwnProperty()/getOwnPropertyNames()+propertyIsEnumerable()+循环语句"
    • 2.所有可枚举一般属性遍历:for...in循环
    • 3.自身所有一般属性: hasOwnProperty()/getOwnPropertyNames()+循环语句
    • 4.原型链所有一般属性: 用一个循环遍历整个原型链,对里面的每一个原型都应用3的方案

    注:欢迎转载,转载请注明出处

  • 相关阅读:
    Java核心技术卷阅读随笔--第5章【继承】
    Java核心技术卷阅读随笔--第4章【对象与类】
    Python创建虚拟环境
    软件工程实践2017第二次作业
    tf常见的损失函数(LOSS)总结
    python-Parser使用
    论文杂记
    最近看了Light-GCN的项目,记录了一些里面用到的api
    screen笔记
    Graph Convolutional Networks for Text Classification——论文笔记
  • 原文地址:https://www.cnblogs.com/peterzhangsnail/p/10002798.html
Copyright © 2020-2023  润新知