原文地址

本文主要讲述了使用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. 机器学习4logistic回归

    对于线性回归.logistic回归,在以前准备学习深度学习的时候看过一点,当时的数学基础有点薄弱,虽然现在还是有点差,当时看到神经网络之后就看不下去了. 不过这次是通过python对logistic回 ...

  2. [RxJS] exhaustMap vs switchMap vs concatMap

    exhaustMap: It drop the outter observable, just return the inner observable, and it waits until prev ...

  3. /usr/bin/ld: cannot find -lltdl collect2: ld returned 1 exit status make: *** [sapi/cgi/php-cgi] Err

    /usr/bin/ld: cannot find -lltdl collect2: ld returned 1 exit status make: *** [sapi/cgi/php-cgi] Err ...

  4. swift 雨燕 新手教程

    Apple Swift编程语言新手教程 chox 2014-06-03 文件夹 简单介绍 入门 简单值 控制流 函数与闭包 对象与类 枚举与结构 1   简单介绍 今天凌晨Apple刚刚公布了Swif ...

  5. PL/SQL Developer使用技巧、快捷键(转发)

    转发自:https://www.cnblogs.com/linjiqin/archive/2013/06/24/3152538.html PL/SQL Developer使用技巧.快捷键 1.类SQL ...

  6. Java 获取随机日期

    /** * 获取随机日期 * @param beginDate 起始日期 * @param endDate 结束日期 * @return */ public static Date randomDat ...

  7. Yslow on Nodejs server

    1. 目的:用yslow测试某个页面的性能 2. 需求:返回yslow测试后的数据,显示在页面 方法一. nodejs 需要把网址打包为har格式... 方法二. phantomjs 步骤: 1. 安 ...

  8. mysql中DATETIME类型与TIMESTAMP的区别

    1.DATETIME的日期范围是1001--9999年,TIMESTAMP的时间范围是1970--2038年. 2.DATETIME存储时间与时区无关,TIMESTAMP存储时间与时区有关,显示的值也 ...

  9. jquerymobile之collapsible可折叠块标题内容动态显示

    jquery mobile提供了一种可折叠的组件--data-role="collapsible",这种组件可以通过点击折叠块头部来展开/折叠块内的内容,详细组件说明可参考w3cs ...

  10. python 46 边界圆角 、a_img_list标签 、伪类选择器

    一:边界圆角 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <ti ...