理解原型和实例的创建

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

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
    });

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341

推荐阅读更多精彩内容