专栏首页码客JS 对象式编程

JS 对象式编程

前言

JS作为函数式编程的语言,受其他语言的影响,也可以用对象式编程,一种是用函数模仿对象,另一种是ES6添加的class。

对象字面量

JS中创建对象最原始的方式有两种:

方式一 对象字面量

var person = {
    name: "leon",
    age: "20",
    greeting: function () {
        alert('Hi!');
    }
}

方式二 为Object实例添加属性方法

var person = new Object();
person.name = "leon";
person.age = "20";
person.greeting = function () {
    alert('Hi!');
};
  • 优点:代码简单
  • 缺点: 创建多个对象会产生大量的代码,编写麻烦,且并没有实例与原型的概念。
  • 解决办法:工厂模式。

工厂模式

工厂模式是编程领域一种广为人知的设计模式,它抽象了创建具体对象的过程。JS 中创建一个函数,把创建新对象、添加对象属性、返回对象的过程放到这个函数中,用户只需调用函数来生成对象而无需关注对象创建细节,这叫工厂模式:

function createPerson(name, age) {
  var person = new Object();
  person.name = name;
  person.age = age;
 
  person.greeting = function() {
    alert('Hi!');
  };
  return person;
}
 
var person1 = createPerson("leon", "20");

优缺点

  • 优点:工厂模式解决了对象字面量创建对象代码重复问题,创建相似对象可以使用同一API。
  • 缺点:因为是调用函创建对象,无法识别对象的类型。
  • 解决办法:构造函数

构造函数

JS 中构造函数与其他函数的唯一区别,就在于调用它的方式不同。任何函数,只要通过new 操作符来调用,那它就可以作为构造函数。来看下面的例子:

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.greeting = function () {
        alert('Hi!');
    };
}
var person1 = new Person("leon", "20");
var person2 = new Person("jack", "21");

通过构造函数new一个实例经历了四步:

  1. 创建一个新对象;
  2. 将构造函数内的this绑定到新对象上;
  3. 为新对象添加属性和方法;
  4. 返回新对象(JS 引擎会默认添加 return this;)。

而通过构造函数创建的对象都有一个constructor属性,它是一个指向构造函数本身的指针,因此就可以检测对象的类型啦。:

alert(person1.constructor === Person) //true
alert(person1 instanceof Person) // true

但是仍然存在问题:

alert(person1.greeting == person2.greeting) //false

同一个构造函数中定义了greeting(),而不同实例上的同名函数却是不相等的,意味着这两个同名函数的内存空间不一致,也就是构造函数中的方法要在每个实例上重新创建一次。这显然是不划算的。

优缺点

  • 优点:解决了类似对象创建问题,且可以检测对象类型。
  • 缺点:构造函数方法要在每个实例上新建一次。
  • 解决办法:原型模式。

原型模式

终于讲到了原型模式,JS 中每个构造函数都有一个prototype属性,这个属性是一个指针,指向原型对象,而这个原型对象包含了这个构造函数所有实例共享的属性和方法。

而实例对象中有一个proto属性,它指向原型对象,也就是构造函数.prototype == 原型对象 == 对象._proto_,那么对象就可以获取到原型对象中的属性和方法啦。

同时,所有对象中都有一个constructor属性,原型对象的constructor指向其对应的构造函数。

使用原型,就意味着我们可以把希望实例共享的属性和方法放到原型对象中去,而不是放在构造函数中,这样每一次通过构造函数new一个实例,原型对象中定义的方法都不会重新创建一次。来看下面的例子:

function Person() {
}
 
Person.prototype.name = "leon";
Person.prototype.age = "20";
Person.prototype.greeting = function() {
  alert('Hi!');
};
 
var person1 = new Person();
var person2 = new Person();
alert(person1.name); //"leon"
alert(person2.name); //"leon"
alert(person1.greeting == person2.greeting); //true

优缺点

  • 优点:与单纯使用构造函数不一样,原型对象中的方法不会在实例中重新创建一次,节约内存。
  • 缺点:使用空构造函数,实例 person1 和 person2 的 name都一样了,我们显然不希望所有实例属性方法都一样,它们还是要有自己独有的属性方法。并且如果原型中对象中有引用类型值,实例中获得的都是该值的引用,意味着一个实例修改了这个值,其他实例中的值都会相应改变。
  • 解决办法:构造函数+原型模式组合使用。

另外 JS 中还定义了一些与原型相关的属性,这里罗列一下:

取得实例的原型对象

Object.getPrototypeOf(person1)

判断是不是一个实例的原型对象

Person.prototype.isPrototypeOf(person1)

检测一个属性是否存在于实例中

person1.hasOwnProperty("name")

判断一个属性是否存在于实例和原型中

"name" in person1

构造函数+原型模式

定义

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype = {
    constructor: Person,
    nationality: "China",
    showinfo: function () {
        console.info(this.name);
    }
}

export default Person;

调用

<script type="module">
	import Person from "./person.js";
	let person = new Person("小明", "18");
	person.showinfo();
</script>

上面代码中用对象字面量的形式重写了原型对象,这样相当于创建了一个新的对象,那么它的constructor属性就会指向Object,这里为了让它继续指向构造函数,显式的写上了constructor: Person

这种构造函数与原型模式混成的模式,是目前在 JS 中使用最为广泛的一种创建对象的方法。

class(ES6)

class 相对 function 是后出来的,既然 class 出来了,显然是为了解决 function 在处理面向对象设计中的缺陷而来。

class 作为 ES6 中的重大升级之一,有哪些优点呢?

  1. class 写法更加简洁、含义更加明确、代码结构更加清晰。
  2. class 尽管也是函数,却无法直接调用(不存在防御性代码了)。
  3. class 不存在变量提升。
  4. class 为污染 window 等全局变量(这点很赞啊)。
  5. class 函数体中的代码始终以严格模式执行(新手写的代码可靠性也必须上来了)
  6. 可直接使用 set 和 get 函数。这比 function 要好用多了。
  7. class 内部方法中若涉及到 this,则一定要注意。class 中的 this 永远都不会指向 window。
  8. class 可以从 javascript 中著名的几大类中进行继承:Array、number、string….,显然 function 是做不到的。
  9. class 中有一个对象super,这个对象可以取到父类的方法、构造函数等。
  10. class 中不存在实例方法,class 中定义所有方法都是原型方法。这些方法也都是不可枚举的,使用 for in 这种方式无法遍历到它们。

定义

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    showinfo = () => {
        console.info(this.name + ":" + this.age);
    };
}

export default Person;

调用

<script type="module">
	import Person from "./person.js";
	let person = new Person("小明", "18");
	person.showinfo();
</script>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iTerm2/终端 美化设置

    一直自己调个iTerm2的配色 感觉还挺好看的 但是看网上别人的简直美到无法直视

    剑行者
  • iOS UICollectionView的用法

    UICollectionViewDataSource,UICollectionViewDelegateFlowLayout

    剑行者
  • 实体类的Copy

    剑行者
  • 设计模式

    小胖
  • JavaScript之面向对象学习七(动态原型模式、寄生构造函数模式、稳妥构造函数模式创建自定义类型)

    一、动态原型模式 在面向对象学习六中的随笔中,了解到组合构造函数模式和原型模式创建的自定义类型可能最完善的!但是人无完人,代码亦是如此! 有其他oo语言经验的开...

    郑小超.
  • Jquery 遍历数组之$().each方法与$.each()方法介绍

    $().each() 对于这个方法,在dom处理上用的比较多,如果一个html页面上面有多个checkbox,这时用$().each来处理checkbox是比较...

    郑小超.
  • 知识点梳理

    小胖
  • Java中Json解析

    首先准备一个JSON格式的字符串 * String JsonStr = "{object:{persons:" + "[{name:'呵呵',im...

    用户3030674
  • MYSQL LOCKS 监控

    MYSQL 性能问题中,一定包含 LOCKS 的问题,我想没人反对,但如何监控他,其实说句实话,没有看到特别多的好的解决方法。有两个极端,一个是本身在MYSQL...

    AustinDatabases
  • 你绝对想不到 Kotlin 泛型给反射留下了怎样的坑!

    今天就要给大家展示一段有坑的代码。本文需要你对泛型和反射有相对深入的了解,反正。阅读过程中有任何不适,本人概不负责。:)逃

    bennyhuo

扫码关注云+社区

领取腾讯云代金券