@

构造函数与原型介绍

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]](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的方案

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

笔记: js构造函数与原型的更多相关文章

  1. js 构造函数 & 静态方法 & 原型 & 实例方法

    js 构造函数 & 静态方法 & 原型 & 实例方法 ES5 "use strict"; /** * * @author xgqfrms * @licens ...

  2. 深入研究js构造函数和原型

    很快就要从新浪离职了,最近心情比较轻松,抽点空整理一下构造函数和原型的机理. 我们都知道,在经典设计模式中我们最常用的就是工厂模式.构造函数模式.原型模式这几种,听起来‘模式’好像很高大上的样子,实际 ...

  3. 完整原型链详细图解之JS构造函数、原型 原型链、实例化对象

    一.首先说一下什么是构造函数: 构造函数:用来在创建对象时初始化对象.特点:构造函数名一般为大写字母开头:与new运算符一起使用来实例化对象. 举例: function Person(){} //Pe ...

  4. JS构造函数、原型对象、隐含参数this

    This 解析器再调用函数每次都会向函数内部传递一个隐含的参数this,this指向的是一个对象(函数执行的上下文对象) 1.以函数形式调用时,this永远是window. 2.以方法形式调用时,th ...

  5. 一句话总结JS构造函数、原型和实例的关系

    "每个构造函数都有一个原型对象, 原型对象都包含一个指向构造函数的指针, 实例都包含一个指向原型对象的内部指针." --此段话摘自<JavaScript高级程序设计>. ...

  6. Js笔记(对象,构造函数,原型,原型链,继承)及一些不熟悉的语法

    对象的特性: 1.唯一标识性,即使完全不一样的对象,内存地址也不同,所以他们不相等 2.对象具有状态,同一个对象可能处在不同状态下 3.对象具有行为,即对象的状态可能因为他的行为产生变迁 Js直到es ...

  7. JS构造函数的用法和JS原型

    $(function(){ var rec = new Rectangle(5, 10); //alert(rec.width + "*" + rec.height + " ...

  8. JS高级. 02 面向对象、创建对象、构造函数、自定义构造函数、原型

    面向对象的三大特性: 封装 a)  把一些属性和方法装到一个对象里 2.  继承 a)  js中的继承是指:   一个对象没有一些方法和属性,而另一个对象有 把另一个个对象的属性和方法,拿过来自己用, ...

  9. JS基础——构造函数VS原型

    JS是一种基于对象的语言.在使用过程中不免遇到复制对象的问题.但通常我们採用的直接赋值'obj1=obj2'这样的做法会出现数据覆盖问题. 也就是对象引用过程中引用地址一致.导致对象数据被改动的问题. ...

随机推荐

  1. 【转】C# GDAL 配置

    共生成9个dll,如下图: 1.在程序中添加*_csharp.dll四个文件的引用: 2.将剩余的五个文件复制到程序的Debug文件夹中:(如果不复制这五个文件就会出现类似“OSGeo.GDAL.Gd ...

  2. Vue2.0中的系统指令

    v-on注册事件 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...

  3. ES6入门——正则的扩展

    1.RegExp构造函数 在ES5中,RegExp构造函数的参数有两种情况.第一种情况是参数是字符串,这时第二个参数表示正则表达式的修饰符:第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表 ...

  4. HTTP协议(持续更新)

    http请求由三部分组成,分别是:请求行.消息报头.请求正文 HTTP(超文本传输协议)是一个基于请求与响应模式的.无状态的.应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接 ...

  5. ES6学习笔记(一)

    ‘变量’声明 ES6新增两个声明’变量’的关键字,let和const命令. l  let用来声明局部变量 同ES5中var声明的变量不同的是,let声明的变量的作用域范围仅仅是从其声明的地方开始,到其 ...

  6. web.xml配置错误页面,及输出错误信息

    1.需要在web.xml中配置相关信息 <!-- 默认的错误处理页面 --> <error-page> <error-code>403</error-code ...

  7. Kibana基本使用

    现在你已经安装了Kibana,现在你一步步通过本教程快速获取Kibana核心功能的实践经验.学习完本教程,你将: 1.加载案例数据到你安装的Elasticsearch中 2. 定义至少一个索引匹配模式 ...

  8. xise官方网站|xise最新版下载|-xise

    诠释: 1. 破解VIP登陆限制 2.去后门 (自查) 下载地址 :https://pan.baidu.com/s/1eR2rUOM 查毒地址:http://a.virscan.org/a3983f3 ...

  9. 设计模式:状态(State)模式

    设计模式:状态(State)模式 一.前言     状态模式在某些场合中使用是非常方便的,什么叫做状态,如果大家学过<编译原理>就会明白DFA M和NFA M,在确定有限状态机和非确定有限 ...

  10. 035server端并发聊天

    import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # 里面是每个客户端连接执 ...