浅谈 OOP JavaScript原型

DanYOU 7年前
   <p>上一章我们谈了构造函数,他的唯一特点就是比较了地址不相同,因为大家知道引用类型是比较的引用。我们来谈谈 <strong>原型。</strong></p>    <h3><strong>原型</strong></h3>    <p>我们每创建一个函数都有一个原型(prototype)属性,这个属性是一个对象,他的特点是 <strong>共享</strong> 。也就是说不用在构造函数中定义对象实例,而是直接将这些添加到原型当中。</p>    <pre>  <code class="language-javascript">function Create () {}    //声明一个构造函数  Create.prototype.a = 'abc';    //在原型中添加属性  Create.prototype.b = 10;  Create.prototype.c = function () {    //在原型中添加方法      return this.a + this.b;  };  var create = new Create();      alert(create.c());    //返回abc10</code></pre>    <p>我们这次再来比较一下原型中方法的地址是否一致:</p>    <pre>  <code class="language-javascript">var create = new Create();  var create1 = new Create();  alert(create.c == create1.c);    //true</code></pre>    <p>是不是还没明白?我们用一张图来告诉大家:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f0d091c2cbded11da87af54491b8cece.jpg"></p>    <p>这个__proto__就相当于指针,指向了原型对象的constructor,而constructor就相当于将构造函数对象和原型对象相关联。</p>    <p>那么我们要用构造函数对象去给重写 <strong>属性或者方法</strong> 会怎么样呢?</p>    <pre>  <code class="language-javascript">var create = new Create();  create.a = 'EFD';  alert(create.a);    //返回EFD</code></pre>    <p>真的将原型对象里面的 <strong>a</strong> 给覆盖了么?并没有:</p>    <pre>  <code class="language-javascript">var create1 = new Create();  alert(create.a);    //返回abc</code></pre>    <p>原型模式的执行过程:</p>    <p>1.先去查找构造函数里面的属性和方法 ,如果有就立即返回。</p>    <p>2.如果构造函数实例里面没有,就去原型里面查找,如果有就立即返回。</p>    <p>因为我们在构造函数添加了属性,所以它会自动去查找,构造函数里面的属性也就立即返回了!</p>    <p>原型的字面量</p>    <p>在原型中,我们也可以使用字面量的方式去创建,可以让属性和方法体现出更好的封装效果。</p>    <pre>  <code class="language-javascript">function Create(a,b){};    //声明一个构造函数  Create.prototype = {    //字面量方式      a:'abc',      b:10,      c:function () {          return this.a + this.b;      }  };</code></pre>    <p>不知道大家有没有发现,我们用字面量的方式是这样的:Create.prototype ={};</p>    <p>大家都知道,用一个{}就等同于new Create();这样,我们就相当于新声明的一个对象,我们原型对象里面的constructor还会指向Create么?</p>    <pre>  <code class="language-javascript">var create = new Create();  alert(create.constructor == Create);    //false  alert(create.constructor == Object);    //true</code></pre>    <p>(我们来解释一下为什么用create.constructor,因为我们打印constructor就会将整个构造函数打印出来,因为上面讲过它是将构造函数对象和原型对象相关联的属性。)</p>    <p>通过上面的例子可以看出,它已经指向了新的实例对象。</p>    <p>constructor的巧妙用法:</p>    <p>我们可以使用constructor来强制指回原来的实例对象:</p>    <pre>  <code class="language-javascript">function Create(a,b){};  Create.prototype = {      constructor:Create,      a:'abc',      b:10,      c:function () {          return this.a + this.b;      }  };</code></pre>    <p>原型对象的重写问题:</p>    <p>大家都知道,构造函数的属性和方法重写是无伤大雅的,但是原型对象中可以重写么?</p>    <pre>  <code class="language-javascript">function Create(a,b){};  Create.prototype = {      constructor:Create,      a:'abc',      b:10,      c:function () {          return this.a + this.b;      }  };    Create.prototype = {      a:'EFD',  };  var create = new Create();  alert(create.c());    //create.c is not a function</code></pre>    <p>不难看出,我们重写了原型会将之前的原型指向切断!!!</p>    <p>原型模式的缺点:</p>    <p>其实它的缺点也是它优点: <strong>共享</strong> 。</p>    <p>我们在字面量里面给原型对象添加一个数组就很容易的看出来了:</p>    <pre>  <code class="language-javascript">function Create(a,b){};  Create.prototype = {      constructor:Create,      a:'abc',      b:10,      c:[第一个,第二个,第三个],      d:function () {          return this.a + this.b + this.c;      }  };  var create = new Create();  create.c.push('第四个');  alert(create.run());    //返回abc10第一个第二个第三个第四个</code></pre>    <p>我们看得出这时候push添加已经生效了,在数组的末尾添加了“第四个”</p>    <p>我们再来实例一个对象就能看得出他的共享问题了:</p>    <pre>  <code class="language-javascript">var create1 = new Create();  alert(create1.run());    //返回abc10第一个第二个第三个第四个</code></pre>    <p>这就是共享问题。下面新实例化一个对象也会将上面添加的字符串给共享到这里来。</p>    <p>这一章就到这里。 <em>欢迎所有阅读文章的人指正错误!</em></p>    <p>Brian Lee</p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000007623594</p>    <p> </p>