原文地址

本文主要讲述了使用JavaScript创建对象的几种方式,分别是传统的Object构造函数、对象字面量、工厂模式、构造函数模式、原型模式、组合模式,以及es6的class定义类。然后从babel的角度探究es5与es6创建对象的区别。

1.创建对象的几种方式

(1).Object构造函数和对象字面量

在早期js开发中,很多开发者会使用Object构造函数的方式来创建一个对象,通过调用Object构造函数new一个Object对象,然后再给这个对象的每一个属性和方法进行赋值

 var person = new Object();
person.age = 22;
person.name = 'Dolanf';
person.code = function() {
console.log(‘hello world!’);
};

后来出现了对象字面量的写法,由于使用对象字面量创建对象的写法简单直观,所以Object构造函数写法渐渐被对象字面量的写法所取代,对象字面量是通过在一个大括号里面使用键值对的方式表示每一个属性和方法,每一个键值对之间使用逗号隔开

 var person = {
age: 22,
name: 'Dolanf',
code: function() {
console.log('hello world!');
}
}

虽然对象字面量简单直观,但是上面两种方法都存在一个共同的问题:当需要创建很多很多个Person对象的时候,只能一个一个去创建,每一个对象的方法和属性都需要单独写,这使得代码没有丝毫复用性可言,违背了对象封装的特性。于是乎,工厂模式就随之出现了

(2)工厂模式

工厂模式通过将对象的创建封装到一个方法中,再通过在调用该方法时传入参数而实现对象的实例化,解决了以上提到的产生大量重复代码的问题

 function createPerson(age, name) {
var o = new Object();
o.age = age;
o.name = name;
o.code = function() {
console.log('hello world!');
}; return o;
} var person1 = createPerson(11, '小白');
var person2 = createPerson(12, '小黑');

但是工厂模式也存在一个不足,就是通过该方法创建的对象的构造函数全都是Object,没有辨识度。没有办法通过构造函数辨别一个对象到底是Person还是Dog,亦或是Cat。于是乎,为了解决这个问题,就引入了构造函数模式。

(3)构造函数模式

构造函数模式就是通过定义一个function函数,然后通过this给对象的属性和方法进行赋值。当我们实例化对象时,只需在该函数前面加一个new关键字就可以了。

 function Person(age, name) {
this.age = age;
this.name = name;
this.code = function() {
console.log('hello world!');
};
} var person1 = new Person(11, '小白');
var person2 = new Person(12, '小黑');

构造函数模式解决了工厂模式中的对象识别问题,通过:

 console.log(person1 instanceof Person);  // true

可以看出person1能成功被识别为一个Person对象。
但是,构造函数模式也同样存在一个缺点,就是构造函数里的属性和方法在每个对象上都要实例化一遍,包括对象共用的属性和方法,这样就造成了代码的复用性差的问题。所以大多数人会考虑将构造函数模式和原型模式组合起来使用。在这里先介绍一下原型模式。

(4)原型模式

原型模式是通过将所有的属性和方法都定义在其prototype属性上,达到这些属性和方法能被所有的实例所共享的目的。代码如下所示:

 function Person(age, name) {
Person.prototype.age = age;
Person.prototype.name = name;
Person.prototype.code = function() {
console.log('hello world!');
};
} var person1 = new Person();
var person2 = new Person();

当然,这种方法在项目开发中是没有人会使用的,因为当一个对象上的属性改变时,所有对象上的属性也会随之改变,这是非常不切实际的。在这里提及原型模式是为了介绍以下的构造函数+原型组合模式.

(5)构造函数+原型组合模式

组合模式是将构造函数模式和原型模式结合在一起,继承了它们优点的同时又避免了各自的缺点。它将具有各自特点的属性和方法定义在构造函数中,将实例间共享的属性和方法定义在prototype上,成为了在es6出现之前使用最普遍的一种创建对象模式。

 function  Person(age, name) {
this.age = age;
this.name = name;
this.cry = function() {
console.log(name + 'is crying!!! T^T');
}
}
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name);
}
}
var person1 = new Person(11, '小白');
var person2 = new Person(12, '小黑');

(6)class定义类

当然,前面讲的都是浮云,现在大家都用class定义类啦,class的出现就是为了让定义类能更加简单。回到上面的Person构造函数上,我们现在将其改造成使用class定义的方式:

 class Person{
constructor(age, name) {
this.age = age;
this.name = name;
this.cry = function() {
console.log(name + 'is crying!!! T^T');
}
}
sayName() {
console.log(this.name);
}
}
var person1 = new Person(11, '小白');
var person2 = new Person(12, '小黑');

使用class定义类跟上面的构造函数+原型组合模式有一些相似之处,但又有所区别。
class定义的类上有个constructor方法,这就是构造方法,该方法会返回一个实例对象,this代表的就是实例对象,这跟上边的构造函数模式很类似。
此外,class上的方法都是定义在prototype上的,这又跟原型模式有一些相似之处,这个class里的sayName等价于

 Person.protorype.sayName = function() {
console.log(this.name);
}

虽然class定义的类跟es5中的构造函数+原型组合模式很相似,但是他们还是存在不少区别的,下面对比如下:

2.es5与es6定义对象的区别

1)class的构造函数必须使用new进行调用,普通构造函数不用new也可执行。
2)class不存在变量提升,es5中的function存在变量提升。
3)class内部定义的方法不可枚举,es5在prototype上定义的方法可以枚举。

为什么会存在以上这些区别呢?下面使用babel将es6转化成es5看看它的实现过程就知道了。

3.es6中class转化为es5

下面将讲述使用babel将以上的class定义Person类转换成使用es5实现:

es6代码:

 class Person{
constructor(age, name) {
this.age = age;
this.name = name;
this.cry = function() {
console.log(name + 'is crying!!! T^T');
}
}
sayName() {
console.log(this.name);
}
}
var person1 = new Person(11, '小白');
var person2 = new Person(12, '小黑');

使用babel转化成的es5后的代码:

 'use strict'; // es6中class使用的是严格模式

 // 处理class中的方法
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
// 默认不可枚举
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}(); // 对构造函数进行判定
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
} // class Person转换为 es5的function
var Person = function () {
function Person(age, name) {
// 调用了_classCallCheck检查Person是否为构造函数
_classCallCheck(this, Person); this.age = age;
this.name = name;
this.cry = function () {
console.log(name + 'is crying!!! T^T');
};
} // 调用_createClass处理定义在class中的方法。
_createClass(Person, [{
key: 'sayName',
value: function sayName() {
console.log(this.name);
}
}]); return Person;
}(); var person1 = new Person(11, '小白');
var person2 = new Person(12, '小黑');

这里我将转换后的代码格式化并加上了一些注释。
从以上代码可以看出,class主要是通过两个函数实现:_createClass和_classCallCheck。
所以为什么会存在上述区别呢:
1)class的构造函数必须使用new进行调用,普通构造函数不用new也可执行。
class中的constructor会直接转化为function构造函数,然后在function中通过 _classCallCheck的检查该function是否是一个Constructor。因为有_classCallCheck检查必须是instanceof Constructor,所以class必须使用new进行调用。

2)class不存在变量提升,es5中的function存在变量提升。 
class转变成了函数表达式进行声明,因为是函数表达式声明的,所以class不存在变量提升。

3)class内部定义的方法不可枚举,es5在prototype上定义的方法可以枚举。 
class中定义的方法会传入 _createClass中,然后 Object.defineProperty将其定义在Constructor.prototype上。所以class中的方法都是定义在Constructor.prototype上的。
由于defineProperties中的

 descriptor.enumerable = descriptor.enumerable || false;

将属性的 enumerable默认为false,所以class中定义的方法不可枚举。

第一次写博客,内容也是copy原作者,所有代码都有手撸验证过一遍,加深印象。

创建对象—从es5到es6的更多相关文章

  1. ES5与ES6常用语法教程之 ①函数写法、创建对象、导入导出模块方式

    函数写法区别 计算a, b两个数字之和,有返回值 es5 写法 function add(a, b) { return a + b; } es6 写法(箭头函数) let add = (a, b) = ...

  2. ES5与ES6常用语法教程之 ②解构语法糖、声明变量异同

    js常用语法系列教程如下 es5与es6常用语法教程(1) es5与es6常用语法教程(2) es5与es6常用语法教程(3) es5与es6常用语法教程(4) es5与es6常用语法教程(5) es ...

  3. ES5和ES6基本介绍与面向对象的基本思想

    ES6和ES5基本介绍 let  const  关键词定义变量 let 定义变量 特点: let 定义的变量,不会进行预解析  let 定义的变量,与 forEach() 中的变量类似  每次执行都会 ...

  4. React入门 (1)—使用指南(包括ES5和ES6对比)

    前言 本篇会简明扼要的介绍一下React的使用方法.代码会用JSX+ES5和JSX+ES6两种方式实现. React简介 React来自Facebook,于2013年开源.至今不断修改完善,现在已经到 ...

  5. ES5和ES6中对于继承的实现方法

    在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方. 原型链的特点和实现已经在之前的一篇整理说过了,就是 ...

  6. JavaScript面向对象轻松入门之概述(demo by ES5、ES6、TypeScript)

    写在前面的话 这是一个JavaScript面向对象系列的文章,本篇文章主要讲概述,介绍面向对象,后面计划还会有5篇文章,讲抽象.封装.继承.多态,最后再来一个综合. 说实话,写JavaScript面向 ...

  7. JavaScript面向对象轻松入门之封装(demo by ES5、ES6、TypeScript)

    本章默认大家已经看过作者的前一篇文章 <JavaScript面向对象轻松入门之抽象> 为什么要封装? 封装(Encapsulation)就是把对象的内部属性和方法隐藏起来,外部代码访问该对 ...

  8. JavaScript面向对象轻松入门之多态(demo by ES5、ES6、TypeScript)

    多态(Polymorphism)按字面的意思就是"多种状态",同样的行为(方法)在不同对象上有不同的状态. 在OOP中很多地方都要用到多态的特性,比如同样是点击鼠标右键,点击快捷方 ...

  9. JavaScript、ES5和ES6的介绍和区别

    JavaScript由三部分组成: ECMAScript(核心) DOM(文档对象模型) BOM (浏览器对象模型) ES5(ECMAScript第五个版本) strict模式 严格模式,限制一些用法 ...

随机推荐

  1. SQLiteOpenHelper/SQLiteDatabase/Cursor源代码解析

    转载请注明出处:http://blog.csdn.net/y_zhiwen/article/details/51583188 Github地址.欢迎star和follow 新增android sqli ...

  2. 最小生成树模板(poj3625)

    Building Roads Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9360   Accepted: 2690 De ...

  3. ios调用dismissViewController的一个小陷阱

    我们的APP从启动到进入主页面.是通过presentViewController构造了一个ViewController序列,类似于首页 -> 登陆页 -> 启动载入页 -> 主页面 ...

  4. swift 拼图小游戏

    依据这位朋友的拼图小游戏改编 http://tangchaolizi.blog.51cto.com/3126463/1571616 改编主要地方是: 原本着我仁兄的代码时支持拖动小图块来移动的,我參照 ...

  5. Photon + Unity3D 线上游戏开发 学习笔记(四)

    这一节 我们建立 photon Server 端的框架 一个最简单的Photon框架 就包括一个 Applocation 类 和 一个 peer 类,作用例如以下: *  Application 类是 ...

  6. 更改printk打印级别【转】

    本文转载自:http://blog.csdn.net/weed_hz/article/details/8949140 1.查看当前控制台的打印级别 cat /proc/sys/kernel/print ...

  7. 最新昆石VOS2009/VOS3000手机号段导入文件(手机归属地)

    使用2017年4月最新版手机号段归属地制作,支持所有版本的VOS 共360569条记录,兼容所有版本的昆石VOS,包括VOS2009.vos3000.vos5000 导入比较简单.下载后解压到桌面在V ...

  8. 关于MFC控件删除出现“具有该ID的控件已存在”这样的情况的解决方案,详细,网上都没有这么详细的,我是“深受其害”,所以想将详细的方法分享出去。

    网上关于MFC控件删除出现“具有该ID的控件已存在”这样的情况,在网上找了很多关于这方面的东西,但是都不是很全,也不容易弄明白.现在问我直接通过一个项目和图片的形式和大家一块分享一个这个解决方法(如有 ...

  9. grunt的学习和使用

    目前正在编写公司的部分组件,可能一个组件会包含很多js和css,为了项目上使用方便,应该压缩成一个js库,以供开发者使用,同时也可以减少很多http请求,提高页面访问速度.基于此,学习了grunt自动 ...

  10. .Net Core项目上Azure Docker云

    1.找到创建资源-容器-Container Instances 2.安装模板,填写私有映像表的相关信息 3.创建成功,运行测试.