zoukankan      html  css  js  c++  java
  • JavaScript原型模式-理解对象

    一:简述

      

      当初学编程一看到什么什么模式就比较头晕,不过本文我们通过简单的示例代码来说一下js 对象这个话题 ,来看下如何理解这个原型模式。

    二:理解对象

        

      1.简单对象

      js对象中没有java、C#等类的概念。但是在js中 一切皆对象嘛 我们可以这么写一个实例

     1 <!DOCTYPE html>
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
     5     <title></title>
     6 </head>
     7 <body>
     8 
     9     <script>
    10         //创建一个对象
    11         var person = new Object();
    12         //声明属性
    13         person.name = 'hello';
    14         person.age = 29;
    15         person.job = 'Software Engineer';
    16         //声明方法
    17         person.sayName = function () {
    18             alert(this.name);
    19         }
    20     </script>
    21 </body>
    22 </html>

    是不是很像一个class。但这是早期js创建对象的写法,后来出现了对象字面量的写法 简化了上面的写法

      2.对象字面量

      我们来改进上面的写法  

     1 var person = {
     2             //属性
     3             name: 'hello',
     4             age: 29,
     5             job: 'Software Engineer',
     6             //声明方法
     7             sayName: function () {
     8                 alert(this.name);
     9             }
    10         }

    是不是简化了很多,但是上面两种创建的对象如果创建多了会产生大量重复代码 ,所以也就出现了以下第三种模式(又是模式....)

      

      3.工厂模式

      

     1 <!DOCTYPE html>
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5     <title></title>
     6 </head>
     7 <body>
     8 
     9     <script>
    10         //用函数来包装
    11         function createPerson() {
    12             var person = new Object();
    13             //声明属性
    14             person.name = 'hello';
    15             person.age = 29;
    16             person.job = 'Software Engineer';
    17             //声明方法
    18             person.sayName = function () {
    19                 alert(this.name);
    20             }
    21             return person;
    22         }
    23         //调
    24         var person1 = createPerson('tom', 21, 'baidu');
    25         var person2 = createPerson('tony', 31, 'tencent');
    26     </script>
    27 </body>
    28 </html>

    工厂就是把东西加工、包装成一个对象。 其实我们大多数的写法也就是这么个模式 。但是这样我们怎么知道这个对象的类型呢 ?随着js发展又出来一个新模式

      

      4.构造函数模式

      

     1 <!DOCTYPE html>
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5     <title></title>
     6 </head>
     7 <body>
     8 
     9     <script>
    10        
    11 
    12         function Person(name,age,job) {
    13             this.name = name;
    14             this.age = age;
    15             this.job = job;
    16             this.sayName = function () {
    17                 alert(this.name);
    18             }
    19         }
    20 
    21         var pserson1 = new Person('tom', 21, 'baidu');
    22         var pserson2= new Person('tony', 31, 'tencent');
    23     </script>
    24 </body>
    25 </html>

      看着是不是跟对象字面量很像。这个模式跟工厂模式对比来看 

        a.没有显示的创建对象

        b.直接将属性和方法赋值给了this对象

        c.没有return

        d.执行构造函数中的代码(在Person新对象中添加属性和方法)

             

      console.info(person1.constructor == Person);//true
      console.info(person1.constructor == Person);//true

      对象的constructor属性 是用来标识对象类型的 ,可以将他的实例标识为一种特定的类型

      说说这个特殊的函数--构造函数 ,它跟函数的唯一区别就是调用的方式不同(有啥不同啊。。)

      只要是能通过new操作符来调用的函数就可以作为构造函数。(没new的就是普通函数呗O(∩_∩)O)

       

     //我是构造函数
      var pserson1 = new Person('tom', 21, 'baidu');
      //我是普通函数
      person('tony', 31, 'tencent');

    构造函数模式也有自己的问题。每个方法都要在每个实例上重新创建一遍。

    构造函数的另一种定义:  

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body> 
        <script> 
            function Person(name,age,job) {
                this.name = name;
                this.age = age;
                this.job = job;
                //第一种写法
                this.sayName = function () {
                    alert(this.name);
                }
                //第二种写法
                this.sayName = new Function("alert(this.name)")
            }
             
        </script>
    </body>
    </html>

    这样就可以看出person1.sayName 不等于person2.sayName了  因为不同实例的同名函数是不相等的

    但是我们可以这么写:

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body> 
        <script> 
            function Person(name,age,job) {
                this.name = name;
                this.age = age;
                this.job = job; 
                this.sayName = sayName
            }
            function sayName() {
                alert(this.name);
            }
        </script>
    </body>
    </html>

    但是这样虽然是函数指向相同了  但是方法多了 就没有封装性可言了 。

      

      5.原型模式

      我们每个函数都有一个prototype(原型)属性。它是构造函数中(就是new的函数)自动创建的对象实例的原型对象 (就是一new就有了)

    好处:可以让所有对象实例共享它包含的属性和方法(可以直接添)如下

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body> 
        <script> 
            //声明一个空函数(首字母大写)
            function Person() {
    
            }
         //添加属性和方法 Person.prototype.name
    = "tony"; Person.prototype.age=29; Person.prototype.job = 'baidu'; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person(); person1.sayName();//baidu var person2 = new Person(); person2.sayName();//baidu alert(person1.sayName == person2.sayName);//true </script> </body> </html>

    因为新对象的属性和方法都是共享的,所以person1和person2都是访问的同一个sayName函数 

      a.理解原型对象:

      上面说到只要创建一个函数就会自动创建一个prototype属性,这个属性指向的就是函数的原型对象

      Person.prototype.constructor就是Person 

      

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body> 
        <script> 
            //声明一个函数(首字母大写)
            function Person() {
    
            }
            Person.prototype.name = "tony";
            Person.prototype.age=29;
            Person.prototype.job = 'baidu';
            Person.prototype.sayName = function () {
                alert(this.name);
            } 
            var person1 = new Person();
            console.info(person1);
        </script>
    </body>
    </html>
    View Code

     chrome中打印输出结构:

      

    虽然person1不包含任何属性和方法 ,但是我们可以使用person1.sayName() 这就是通过查找对象属性来实现的

    person1就是实例 Person就是原型 当实例中的属性修改后

    person1.name='1234' 不影响实例的属性 person2没修改 name还是tony

    delete person1.name;

    这个可以删除person1的实例属性 从而访问到原型中的值

      (1)我们可以通过hasOwnProperty方法来判断本身是否有这个属性(就是判断自己有没有该属性 返回true、false)  

     1   <script> 
     2         //声明一个函数(首字母大写)
     3         function Person() {
     4 
     5         }
     6         Person.prototype.name = "tony";
     7         Person.prototype.age=29;
     8         Person.prototype.job = 'baidu';
     9         Person.prototype.sayName = function () {
    10             alert(this.name);
    11         } 
    12         var person1 = new Person();
    13         alert(person1.hasOwnProperty('name'));//false
    14         person1.name = '123';//我自己重新赋值  有了该属性
    15         alert(person1.hasOwnProperty('name'));//true
    16         console.info(person1);
    17     </script>

      (2)通过 in操作符来判断(单独使用和在for循环中)

     1 <!DOCTYPE html>
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     5     <title></title>
     6 </head>
     7 <body> 
     8     <script> 
     9         //声明一个函数(首字母大写)
    10         function Person() {
    11 
    12         }
    13         Person.prototype.name = "tony";
    14         Person.prototype.age=29;
    15         Person.prototype.job = 'baidu';
    16         Person.prototype.sayName = function () {
    17             alert(this.name);
    18         } 
    19         var person1 = new Person();
    20         alert(person1.hasOwnProperty('name'));//false
    21         alert('name' in person1) //true
    22         person1.name = '123';//我自己重新赋值  有了该属性
    23         alert(person1.hasOwnProperty('name'));//true
    24         alert('name' in person1) //true
    25        
    26     </script>
    27 </body>
    28 </html>

    可以看到in可以访问到原型中去查找也就返回了都是true  而hasOwnProperty只在本身查找

      (3)我只想看是否在原型中的属性 使用hasPrototypeProperty (在原型中不在实例中的属性)

      (4)像in的用法在ie等浏览器中是有bug的 所以es5提供了 Object.keys() 方法来获取所有枚举属性

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body> 
        <script> 
            //声明一个函数(首字母大写)
            function Person() {
    
            }
            Person.prototype.name = "tony";
            Person.prototype.age=29;
            Person.prototype.job = 'baidu';
            Person.prototype.sayName = function () {
                alert(this.name);
            }
    
            var keys = Object.keys(Person.prototype);
            alert(keys);//name,age,job,sayName
            var person1 = new Person(); 
            person1.name = '123';//我自己重新赋值  有了该属性
            var keys2 = Object.keys(person1.prototype);
            alert(keys2);//name
            
           
        </script>
    </body>
    </html>

    上面原型写法完全可以用对象字面量的形势简写(注意字面量写法中的红字)

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body> 
        <script> 
            //声明一个函数(首字母大写)
            function Person() {
    
            }
            Person.prototype = {
           constructor:Person,//修正构造函数重新指向自己 name:
    "tony", age: 29, job: 'baidu', sayName: function () { alert(this.name); } } //Person.prototype.name = "tony"; //Person.prototype.age=29; //Person.prototype.job = 'baidu'; //Person.prototype.sayName = function () { // alert(this.name); //} </script> </body> </html>

       

    原型模式最大的问题是共享的本性导致的 。里面包含引用类型比如数组 也会跟着共享

    所以原型模式一般不会单独出现

      6.组合使用构造函数模式和原型模式

      构造函数模式用来定义实例属性  

      原型模式用来定义方法和共享的属性 

       这种混合模式是目前es中使用最广泛和认同度最高的一种自定义类型的方法  这也是引用类型的一种默认方式

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
    </head>
    <body>
        <script>
            //声明一个函数(首字母大写)
            function Person(name, age, job) {
                this.name = name;
                this.age = age;
                this.job = job;
                this.friends = ["lili","tonk"];
            }
            Person.prototype = {
                constructor: Person,
                sayName: function () {
                    alert(this.name);
                }
            }
    
            var person1 = createPerson('tom', 21, 'baidu');
            var person2 = createPerson('tony', 31, 'tencent');
    
            person1.friends.push('van');
            console.info(person1.friends);//lili、tonk、van
            console.info(person2.friends);//lili、tonk
            console.info(person1.friends == person2.friends);//false
            console.info(person1.sayName == person2.sayName);//true
        </script>
    </body>
    </html>

      

  • 相关阅读:
    iOS:图片相关(19-05-09更)
    iOS:文字相关(19-01-08更)
    iOS:通信录(完成)(18-01-18更)
    Swift 中的Closures(闭包)详解
    Swift 中的高阶函数和函数嵌套
    Swift API设计原则
    Swift下CoreData的使用
    iOS 使用贝塞尔曲线绘制路径
    iOS CALayer使用
    iOS 多线程的使用
  • 原文地址:https://www.cnblogs.com/DemoLee/p/6605550.html
Copyright © 2011-2022 走看看