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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享这对方法的引用,最大限度的节省了内存。

  1. function Person (name,age) {
  2. this.name = name;
  3. this.age = age;
  4. this.friends = ['cjh','csb'];
  5. }
  6. Person.prototype = {
  7. constructor:Person,
  8. sayName:function () {
  9. console.log(this.name);
  10. }
  11. }
  12. let person1 = new Person('aaa',22);
  13. let person2 = new Person('bbb',26);
  14. person1.friends.push('zxf');
  15. // [ 'cjh', 'csb', 'zxf' ]
  16. console.log(person1.friends);
  17. // [ 'cjh', 'csb' ]
  18. console.log(person2.friends);
  19. // false
  20. console.log(person1.friends === person2.friends);
  21. //true
  22. console.log(person1.sayName === person2.sayName);

我们已经达到目的了,这种构造函数与原型混成的模式,是目前用的比较广泛的。

稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其他方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境会禁止使用this和new),看个例子:

  1. function Person (name) {
  2. let o = new Object();
  3. o.sayName=function(){
  4. console.log(name);
  5. }
  6. return o;
  7. }
  8. let f = Person('cjh');
  9. // cjh
  10. f.sayName();
  11. // undefined
  12. console.log(f.name);

这种其实很少用,就是把函数的参数当作Person的属性,只有函数内部才能访问。

原型链

终于讲到最终BOSS了,原型链可谓难到一大部分刚入门JS的同学(我也是), 原型链主要作用之一就是继承,我们来看个例子:

  1. function SuperType () {
  2. console.log('Super函数被执行')
  3. this.property = true;
  4. }
  5. SuperType.prototype.getSuperValue = function () {
  6. console.log('getSuperValue函数被执行了');
  7. return this.property;
  8. }
  9. function SubType () {
  10. this.subProperty = false;
  11. }
  12. SubType.prototype = new SuperType();
  13. SubType.prototype.getSubValue = function () {
  14. return this.subProperty;
  15. }
  16. let instance = new SubType();
  17. console.log(instance.getSuperValue());

运行结果:

Super函数被执行
getSuperValue函数被执行了
true

这是实现继承的一种基本模式,定义两个类型SuperType和SubType,每个类型都有一个属性和一个方法,要实现subType继承SuperType,通过创建SuperType实例并赋值给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了。

上图来自高级JS程序设计

这里我想说的是,还有种继承的方法,还更安全点,Object.create

上面的运行结果也看出来了,SuperType里面的代码被执行了,这就是new的能力,new的作用其实就是:

  1. //new的时候做了什么
  2. //会执行Base里面的代码
  3. var o1 = new Base();
  4. o1.[[Prototype]] = Base.prototype;
  5. Base.call(o1);

而Object.create做了些什么:

  1. /Object.create = function (o) {
  2. var F = function () {};
  3. //没有执行o函数里面的代码
  4. F.prototype = o;
  5. return new F();
  6. };

所以上面的继承我们可以改为:

  1. function SuperType () {
  2. console.log('Super函数被执行了');
  3. this.property = true;
  4. }
  5. SuperType.prototype.getSuperValue = function () {
  6. console.log('getSuperValue函数被执行了');
  7. return this.property;
  8. }
  9. function SubType () {
  10. this.subProperty = false;
  11. }
  12. SubType.prototype = Object.create(SuperType.prototype);
  13. SubType.prototype.getSubValue = function () {
  14. return this.subProperty;
  15. }
  16. let instance = new SubType();
  17. console.log(instance.getSuperValue());

运行结果:

getSuperValue函数被执行了
undefined

因为没有执行SuperType里面的函数,当这个函数里面要是创建对象和返回对象就会造成内存泄漏,所有this.property没有被声明和赋值,返回undefined,但是继承了所有方法和没有在函数里面声明的属性(在外面声明的):

  1. //这个函数没有被执行到
    function SuperType () {
  2. console.log('Super函数被执行了');
  3. this.property = true;
  4. }
    //这个函数有被执行到
  5. SuperType.prototype.getSuperValue = function () {
  6. console.log('getSuperValue函数被执行了');
  7. return this.property;
  8. }
  9. SuperType.prototype.name = 'cjh';
  10. function SubType () {
  11. this.subProperty = false;
  12. }
  13. SubType.prototype = Object.create(SuperType.prototype);
  14. SubType.prototype.getSubValue = function () {
  15. return this.subProperty;
  16. }
  17. let instance = new SubType();
    //undefined
  18. console.log(instance.property)
    //cjh
  19. console.log(instance.name);
    //[Function: SuperType]

console.log(SuperType.prototype.constructor);

//[Function: SuperType]
console.log(SubType.prototype.constructor);

  1.  

我们看最后两个结果:都是[Function: SuperType],实际上,我们没有改变SubType.prototype的constructor的指向,还记得在JS面向对象(基础篇)里面讲过,默认情况下,所以原型对象都会自动获得一个constructor(构造函数)属性,并且constructor包含一个指向prototype属性所在函数的指针,我们改变了SubType.prototype=SuperType.prototype,所有construtor就自动指向了SuperType。

原型链的问题

  1. function SuperType () {
  2. this.colors = ['red','green'];
  3. }
  4. function SubType () {
  5.  
  6. }
  7. SubType.prototype = new SuperType();
  8. let instance1 = new SubType();
  9. instance1.colors.push("black");
  10. // [ 'red', 'green', 'black' ]
  11. console.log(instance1.colors);
  12. let instance2 = new SubType();
  13. // [ 'red', 'green', 'black' ]
  14. console.log(instance2.colors);

在基本篇的时候讲过用原型模式来创建对象有个很大的问题:就是会共享属性,这个是我们不想看到的,因为每个对象都应该有它自己的一块内存,所有那时我们用组合模式来解决那个问题(就是在构造函数里面定一个各个对象的属性),一样的,现在也可以用组合继承来解决这个问题:

  1. //原型链的组合继承
  2. function SuperType (name) {
      console.log('调用了Supertype');
  3. this.name = name;
  4. this.colors = ['red','green'];
  5. }
  6. SuperType.prototype.sayName = function () {
  7. console.log(this.name);
  8. }
  9. function SubType (name) {
  10. //继承属性
      //给每个实例分配自己的属性地址
  11. SuperType.call(this, name);
  12. }
    //第一次调用SuperType();
  13. SubType.prototype = new SuperType();
    //第二次调用SuperType(); 因为SubType函数里面的SuperType.call();
  14. let instance1 = new SubType('cjh',22);
    //第三次调用SuperType();因为SubType函数里面的SuperType.call();
  15. let instance2 = new SubType('csb',24);
  16. instance1.colors.push('black');
  17. // [ 'red', 'green', 'black' ]
  18. console.log(instance1.colors);
  19. // [ 'red', 'green' ]
  20. console.log(instance2.colors);
  21. // cjh
  22. instance1.sayName();
  23. // csb
  24. instance2.sayName();

这样是可以解决我们的问题,但是再看下结果:

调用了Supertype
调用了Supertype
调用了Supertype
[ 'red', 'green', 'black' ]
cjh
csb

上面的SuperType被执行了三次,但我们就创建了两个对象,第一次纯属多余,要想帮法去掉,在上面讲过我们用new关键字时,JS到时做了什么:

  1. //new的时候做了什么
  2. //会执行Base里面的代码
  3. var o1 = new Base();
  4. o1.[[Prototype]] = Base.prototype;
  5. Base.call(o1);

看到了call这个函数,就是调用Base()并且把o1传进去,但是这里的call没有任何作用,因为我们还没有创建对象时,它就call了。有个终极蛇皮版:

  1. //subType:子类 superType:父类 => subType继承于superType
  2. function inheritPrototype (subType, superType) {
  3. let prototype = Object.create(superType.prototype);
  4. //因为重写了subType的原型而失去的默认的constructor,所以指回subType
  5. prototype.constructor = subType;
  6. subType.prototype = prototype;
  7. }
  8. function SuperType (name) {
  9. console.log('调用了Supertype构造函数');
  10. this.name = name;
  11. this.colors = ['red','blue','green'];
  12. }
  13. SuperType.prototype.sayName = function () {
  14. console.log(this.name);
  15. }
  16. function SubType (name,age) {
  17. console.log('调用了SupType构造函数');
  18. SuperType.call(this, name);
  19. this.age = age;
  20. }
  21. inheritPrototype(SubType, SuperType);
  22. SubType.prototype.sayAge = function () {
  23. console.log(this.age);
  24. }
  25. let instance1 = new SubType('cjh',22);
  26. let instance2 = new SubType('csb',25);
  27. instance1.colors.push('black');
  28. // [ 'red', 'blue', 'green', 'black' ]
  29. console.log(instance1.colors);
  30. // [ 'red', 'blue', 'green' ]
  31. console.log(instance2.colors);
  32. // true
  33. console.log(instance1.sayName === instance2.sayName);
  34. // true
  35. console.log(instance1.sayAge === instance2.sayAge);
  36. //false
  37. console.log(instance1.name === instance2.name);
  38. //
  39. instance1.sayAge();
  40. //
  41. instance2.sayAge();
  42. // cjh
  43. instance1.sayName();
  44. // csb
  45. instance2.sayName();
  46. // true
  47. console.log(instance1 instanceof SubType);
  48. //[Function: SubType],要是没写prototype.constructor = subType;,
  49. //结果就是:[Function: SubType],亲测
  50. // [Function: SubType]
  51. console.log(instance1.constructor);

运行结果:

调用了SupType构造函数
调用了Supertype构造函数
调用了SupType构造函数
调用了Supertype构造函数

现在创建一个对象分别调用一次SuperType和SubType,看上面的结果,不管是父类的方法还是子类的方法是共享的,然后属性却都是自己的内存里面的,这很符合我们的要求,即方便又省内存,到此,就差不多了,有错的话欢迎指正

JS:面向对象(进阶篇)的更多相关文章

  1. Python开发【第七篇】:面向对象 和 python面向对象进阶篇(下)

    Python开发[第七篇]:面向对象   详见:<Python之路[第五篇]:面向对象及相关> python 面向对象(进阶篇)   上一篇<Python 面向对象(初级篇)> ...

  2. Python 面向对象 (进阶篇)

    <Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可 ...

  3. Java面向对象进阶篇(包装类,不可变类)

    一. Java 8的包装类 Java中的8种基本数据类型不支持面向对象的变成机制,也不具备对象的特性:没有成员变量,方法可以调用.为此,Java为这8 种基本数据类型分别提供了对应的 包装类(Byte ...

  4. Java面向对象进阶篇(抽象类和接口)

    一.抽象类 在某些情况下,父类知道其子类应该包含哪些方法,但是无法确定这些子类如何实现这些方法.这种有方法签名但是没有具体实现细节的方法就是抽象方法.有抽象方法的类只能被定义成抽象类,抽象方法和抽象类 ...

  5. Java面向对象进阶篇(内部类)

    一. 概念 大部分时候,类被定义成一个独立的程序单元.有时候把一个类放在另一个类内部定义,这个类被称为内部类,包含内部类的类也被称为外部类. 内部类的主要作用: 内部类提供良好的封装,可以把内部类隐藏 ...

  6. python之路 面向对象进阶篇

    一.字段 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同, 普通字段属于对象 静态字段属于类 class Province: # 静态字段 countr ...

  7. python 面向对象(进阶篇)

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  8. 【转】python 面向对象(进阶篇)

    [转]python 面向对象(进阶篇) 上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 ...

  9. python 面向对象(进阶篇)转载武沛齐

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  10. Python基础—面向对象(进阶篇)

    通过上一篇博客我们已经对面向对象有所了解,下面我们先回顾一下上篇文章介绍的内容: 上篇博客地址:http://www.cnblogs.com/phennry/p/5606718.html 面向对象是一 ...

随机推荐

  1. Oracle高水位线(HWM)及性能优化

    说到HWM,我们首先要简要的谈谈ORACLE的逻辑存储管理.我们知道,ORACLE在逻辑存储上分4个粒度:表空间,段,区和块.    (1)块:是粒度最小的存储单位,现在标准的块大小是8K,ORACL ...

  2. springMvc注册时图形验证码完整代码与详细步骤``````后续更新注册时对密码进行加密

      第一使用 画图软件制作图片 ,文件名就是验证码    ------用户的实体类 import java.util.Date; public class Member {    private in ...

  3. 实用maven笔记三-仓库

    maven管理依赖的一个很重要的基础在于,其维护了收集大量依赖jar包的仓库. maven的仓库分类为本地仓库和远程仓库. 构件在仓库的路径大致为:groupId/artifactId/version ...

  4. GNU Linux 64汇编学习

    函数调用传参: 第一个参数:rdi, 第二个参数:rsi 函数调用栈结构: 返回值 第一个参数 第二个参数 +----------+ rsp-24 | a | +----------+ rsp-16 ...

  5. JSON Web Token (JWT),服务端信息传输安全解决方案。

    JWT介绍 JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑独立的基于JSON对象在各方之间安全地传输信息的方式.这些信息可以被验证和信任,因为它是数字签名的 ...

  6. 【题解】sweet

    题目描述 为了防止糖果被小猫偷吃,John把他的糖果放在了很多的高台上,一个高台可以认为是一段平行于X轴的线段,并且高台的y坐标都是大于0的,每个高台都有左端点和高台的长度,每个高台都有糖果.所有的高 ...

  7. docker--build base image

    通过dockerfile build一个base image,在上面运行一个c程序 首先 1.创建一个目录. 2.然后创建一个c写的小程序,并且gcc编译好. 3.创建一个Dockerfile FRO ...

  8. 《Javascript权威指南》学习笔记之十七:BOM新成就(1)--client存储数据(Storage实现)

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u011043843/article/details/30255899     数据构成了web网站的 ...

  9. java final关键字详解

    final是java中保留关键字,可以声明成员变量.类.方法与本地变量,一旦引用final关键字,将不能再改变这个引用,编译器会检查代码,要是想改变该引用,会报错. final变量? 凡是对成员变量或 ...

  10. 如何通过cmd命令远程重启或远程关闭Windows服务器

    一.想要远程控制服务器,前提条件是远程服务器需要开启IPC$ ,且本地能访问远程服务器445端口 1.开启ipc$ net share IPC$ 2.如果只指定管理员才有执行ipc$的权限 net s ...