美文网首页
理解原型和实例的创建

理解原型和实例的创建

作者: SEELE7 | 来源:发表于2017-08-18 23:34 被阅读0次

原型 , 构造模式 , 原型模式 ,组合模式

1.理解原型

在JS中,只要声明一个函数A , 就会对应的产生一个对象 , 这个对象就叫原型对象

试试看

    function A() {
    }
    console.log(A.prototype);
    console.log(A.prototype.constructor);

返回值如下:

定义了一个函数 A , 没有添加任何的属性和方法 , A 的两未定义个属性 却可以返回两个值?

prototype属性返回了一个对象 , constructor属性返回了函数A

prototype是 A 里面的属性 , 指向一个对象,这个对象就叫原型对象 ,

而这个原型对象里 默认情况 储存着 constructor属性和从Object继承而来,这里暂且不表;

constructor 是一个属性 , 存在原型对象里 , 指向了这个函数

比如俩人吵架 , 都用手指头指着对方骂;

而这两个属性就是手指头,构造函数和原型对象就是吵架的人;

手指头是人的一部分,就像这两个属性存在对象的内部是一样的

2.与原型相关的属性和方法

constructor 属性

存在于原型对象中,指向构造函数;

如果重写原型对象则会导致constructor属性不再指向构造函数;

如果需要constructor依旧指向构造函数,可以在重写的时候加上以下代码

Person.prototype = {
       constructor : Person    //让constructor重新指向Person函数
}

[[prototype]]属性

用构造函数创建一个实例对象后,这个对象中会有一个不可访问的属性[[prototype]],这个属性就指向了构造函数的原型对象

Chrome浏览器和火狐浏览器提供这个对这个属性的访问

使用__proto__方法可以访问到原型对象(左右各两个下划线),这个属性存在于被构造函数创建的实例对象中;一般不建议使用

hasOwnProperty() 方法

判断一个属性是否来自对象自身 ;

返回 true表示为对象自身属性;返回false可以判断出存在原型中或属性不存在,所以要如何确定一个属性存在原型里呢?

in操作符

in操作符会从对象的本身开始 , 查找是否有对应的属性 , 在对象自身中没有找到 , 就会沿着原型链开始查找 , 直到找到返回true ,反之,则返回false

3. 两个模型--构造函数模型 和 原型模型

3.1构造函数模型

理论上任何函数都可以作为构造函数,但是一般约定,构造函数的函数名以大写字母开头

比如:

构造函数 : function Person(){ }

当把一个函数作为构造函数,并利用new创建的一个新的实例对象

利用构造函数创建实例 : var p1 = new Person( );

3.1.1构造过程

相关:

实例与构造函数的关系 :

实例对象p1被构造函数Person创建后,P1和Person是没有任何的关系了;

实例与原型 :

  • 实例对象中有一个[[prototype]]属性指向原型对象;
  • 使用new Person()创建多个对象,则多个对象都会同时指向原型对象。
  • 可以手动给这个原型对象添加属性和方法,那么p1,p2,p3...都会共享这些属性和方法
  • 属性和方法的查找会从实例开始 , 沿着原型链查找 , 直到找到属性或方法;
  • 所以给实例对象添加和原型中同名的属性 , 会优先访问实例中的属性
  • 通过实例只能访问到原型的属性或方法,不能修改;
//这段代码将会抛出错误
function Person(name,age) {
    }
    Person.prototype.sex = "男"

    let p1 = new Person();    
    p1.prototype.sex = "女";
    console.log(p1.sex);

3.1.2构造函数的优势

可以传入参数,适用于 每个实例都有的同名但是值不相同的属性

     function Person(name,age) {
         this.name = name;
         this.age = age;
         this.speakName = function (){
            console.log(this.name)
         }
     }
     let p1 = new Person("shark",3);
     let p2 = new Person("dd",4)
     console.log(p1.name,p1);
     console.log(p1.age)
     
     //输出结果
     shark 3
     dd 4

内存模型:


目前来说,每个实例都有了自己的name和age , 但是新的问题也随之而来了


3.1.3构造函数的缺陷

观察内存模型 , 就会发现每个实例中都有一个相同的方法 , 浪费了内存

这不是很OK , 不是我们想要的结果,所以一种新的模型站了出来 - -原型模型

3.2原型模型

3.2.1原型模式的优势

针对以上的问题 , 对代码做出了以下修改

     function Person(name,age) {
         this.name = name;
         this.age = age;
     }
     Person.prototype.speakName = function (){
        console.log(this.name)
      }
     let p1 = new Person("shark",3);
     let p2 = new Person("dd",4)
     console.log(p1.name,p1);
     console.log(p1.age)

利用原型模式的优势 ---共享所有的方法

可以让我们共享speakName这个方法;

这正好解决了构造函数的缺陷 , 每个实例都有自己的名字 , 方法在不浪费内存和性能的情况下共用;


3.14 原型模式的缺陷

那假如使用原型模式添加属性呢?

    function Person() {
    }
    Person.prototype.country = "China";
    Person.prototype.name = "小李";

    var p1 = new Person();
    var p2 = new Person();

场景:

现在有假如两个中国人p1,p2 ,他们的国籍一样,使用原型模式,使得两个人的国籍一致

结合上一个例子说明了:

原型中适合存储大家共有的属性和方法;

不过原型也存在缺陷 :

名字没有办法做到每个人都独一无二 , 不过在之前提到的构造函数模型中 , 正好解决了给不同的实例的相同name赋不同值的情况;

3.4 总结

原型模式适合封装方法和共享的属性,构造方法模式适合封装值不同的属性

如果把这两个模式结合起来,就有了组合模式;

4.组合使用两种模型

组合构造是基于两种模式互补的一种新的构造方法;

总结一下两种模式的优缺点:

优势 缺陷
构造函数模式 属性在对象中都独有一份 对于方法来说,没有必要一人一份
原型模式 方法可以共享 一般属性的值是不同的,不适合共享

不难看出,两者的正好是互补的,所以组合起来使用是最佳的方法;

4.1 组合模式

    //在构造方法内部封装属性
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    //在原型对象内封装方法
    Person.prototype.eat = function (food) {
        alert(this.name + "爱吃" + food);
    }
    Person.prototype.play = function (playName) {
        alert(this.name + "爱玩" + playName);
    }

    var p1 = new Person("李四", 20);
    var p2 = new Person("张三", 30);
    p1.eat("苹果");
    p2.eat("香蕉");
    p1.play("志玲");
    p2.play("凤姐");

虽然完美解决了种模式的缺陷,但是还不够完美

因为 , 代码还不够优雅

4.2 动态原型模式

动态原型模式把所有的属性和方法都封装在构造方法中,而仅仅在需要的时候才去在构造方法中初始化原型,又保持了同时使用构造函数和原型的优点。


<script type="text/javascript">
    //构造方法内部封装属性
    function Person(name, age) {
        //每个对象都添加自己的属性
        this.name = name;
        this.age = age;
        /*
            判断this.eat这个属性是不是function,如果不是function则证明是第一次创建对象,
            则把这个funcion添加到原型中。
            如果是function,则代表原型中已经有了这个方法,则不需要再添加。
            perfect!完美解决了性能和代码的封装问题。
        */
        if(typeof this.eat !== "function"){
            Person.prototype.eat = function () {
                alert(this.name + " 在吃");
            }
        }
    }
    var p1 = new Person("志玲", 40);
    p1.eat();    
</script>

看起来优美多了..但是还是差一丝优美...

4.3 优化

    function Person(opt) {
         this._init(opt)    
    }
    //方法和属性都被封装到了一起,但是属性实际上还是属于构造模式创建的
    //体现了封装性,但是重写Prototype带来了一个小问题
    Person.prototype = {
      //新的原型对象不存在constructor属性,故补齐
         constructor:Person 
      //初始化属性
        _init: function (opt) {
            this.name = opt.name;
            this.age = opt.age;
        },
        eat: function () {
            return "名字:" + this.name
        },
        howOld: function () {
            return "年龄:" + this.age
        },
    };

    var p1 = new Person({
        name: "李四",
        age: 99
    });

前端新人一枚 , 欢迎批评指正~

相关文章

  • 理解原型和实例的创建

    原型 , 构造模式 , 原型模式 ,组合模式 1.理解原型 在JS中,只要声明一个函数A , 就会对应的产生一个对...

  • <Java设计模式>——原型模式:真假猴王难辨

    什么是原型模式 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。简单理解一下哈,就是通过实例拷贝一...

  • javascript中面向对象编程-创建对象之原型模式

    理解名词:对象 原型对象 原型属性 函数 构造函数 实例 对象: Object,创建对象,对象属性方法原型对象:...

  • java设计模式 - 原型模式

    1.定义 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定...

  • 原型模式

    定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要...

  • 高频手写题目

    建议优先掌握: instanceof - 考察对原型链的理解 new - 对创建对象实例过程的理解 call/ap...

  • 设计模式之原型模式

    定义 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。被复制的实例就是“原型”,这个原型是可定制的。...

  • 【Android笔记】设计模式(三)- 原型模式

    1.简述 直接上定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。简单地理解,其实就是当需要创...

  • 设计模式 -- 原型模式 (Prototype Pattern)

    定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。通俗理解:一个对象的产生可以不从零开始,直...

  • 原型模式

    用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,注意深拷贝和浅拷贝

网友评论

      本文标题:理解原型和实例的创建

      本文链接:https://www.haomeiwen.com/subject/jkpzrxtx.html