欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

笔记: js构造函数与原型

程序员文章站 2023-10-15 21:03:15
@[toc] 构造函数与原型介绍 1.函数与函数的原型对象(prototype object): 在JavaScript中,创建一个函数A, 浏览器就会在内存中创建一个对象B,而且该函数默认会有一属性 prototype 指向这个对象(即:prototype属性的值) 这个对象B就是函数A的 原型对 ......

目录

@

构造函数与原型介绍

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)
笔记: js构造函数与原型

涉及三种引用的操作

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

  • 构造函数创建实例的具体过程
    参考资料:
    英文版-- [[construct]] [[call]]
    中文版-- [[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运算符
参考资料: [[hasinstance]](v)
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的方案

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