乐趣区

Javascript原型二

图解原型

function Person() {//TODO}
var p = new Person();

代码执行之前要经历一个预解析的过程。function Person(){}是函数声明,p 也是声明(p 没有画出来),声明在预解析的时候完成。

  • 内存情况

所以,在代码开始运行时,构造函数 Person 在内存中已经存在了,所以代码运行起来第一句话是要执行 var p = new Person();
程序开始执行

  • 执行 new 创建对象
  • 执行构造函数 Person 初始化对象(给对象添加属性)
  • 内存情况

  • 赋值给变量 p

  • 针对构造函数来说,Person 类型对象是构造函数的实例对象
  • 针对构造函数来说,神秘对象是构造函数的原型属性
  • 针对 Person 类型对象来说,神秘对象是 Person 类型对象的原型对象

/* 接上面的代码 */
Person.prototype.good = function() {console.log("好");}
p.good(); // 好

p表示的对象默认连接到 Person.prototypePerson 类型对象中是不存在 good 方法的,但是 p 也可以访问到 good 方法,由此可见,当当前对象中不存在某属性或方法时,会去神秘对象中去查找。所以可以说,当前对象(Person 类型对象 )继承自神秘对象;也可以说,当前的实例对象,继承自其原型对象,这就是 原型继承

  • 原型继承

为什么使用原型

    • 为什么属性一般不放在原型上

      • 属性表示对象的特征,是一种对象特有的东西,对象不一样,属性也应该不一样。但如果放到原型上,那么就会被所有对象共享
    • 为什么方法可以放在原型上

      • 属性是对象特有的,但是这个对象的行为应该是一样的。比如人和人是不一样的,但是人都会走路,吃饭,这就是行为,每个人都是一样的,所以可以将其放在原型中复用
    • 如何修改原型
      1、利用对象的动态特性

      Student.prototype.sleep = function(){}

      2、利用直接替换

      /* 构造函数.prototype.xxxx = vvvv */
      /* 直接将原型进行了替换 */
      Student.prototype = {sleep: function(){}
          study: function(){}
      }
    • 第一种方式与第二种方式最大的不同是,第二种方式又创建出了一个对象,将原有的原型对象进行了替换,所以现在内存中又两个对象;第一种做法是直接在原来的对象上添加属性

    分析以下题目

    function Person() {}
    
    Person.prototype.func = function () {console.log("11111");
    };
    
    var p1 = new Person();
    
    Person.prototype = {func: function () {console.log("2222");
        }
    };
    
    var p2 = new Person();
    p1.func();
    p2.func();
    • 预解析构造函数

    • 构造函数原型属性赋值

    • var p1 = new Person();

    • 原型属性重新赋值,首先执行等号右边的语句,也就是在内存中创建了一个对象

    • var p2 = new Person();
    • 此时构造函数的原型属性已经指向了匿名对象,所以新创建的 Person 类型的对象的原型对象也指向匿名对象

    • 从图可以得知,p1.func() 执行结果是1111p2.func() 执行结果是 2222
    • 总结:只要对象创建出来了,即使原型属性被重新赋值,那么也不会影响已经创建好的对象的功能
    退出移动版