创建对象—从es5到es6
本文主要讲述了使用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的更多相关文章
- ES5与ES6常用语法教程之 ①函数写法、创建对象、导入导出模块方式
函数写法区别 计算a, b两个数字之和,有返回值 es5 写法 function add(a, b) { return a + b; } es6 写法(箭头函数) let add = (a, b) = ...
- ES5与ES6常用语法教程之 ②解构语法糖、声明变量异同
js常用语法系列教程如下 es5与es6常用语法教程(1) es5与es6常用语法教程(2) es5与es6常用语法教程(3) es5与es6常用语法教程(4) es5与es6常用语法教程(5) es ...
- ES5和ES6基本介绍与面向对象的基本思想
ES6和ES5基本介绍 let const 关键词定义变量 let 定义变量 特点: let 定义的变量,不会进行预解析 let 定义的变量,与 forEach() 中的变量类似 每次执行都会 ...
- React入门 (1)—使用指南(包括ES5和ES6对比)
前言 本篇会简明扼要的介绍一下React的使用方法.代码会用JSX+ES5和JSX+ES6两种方式实现. React简介 React来自Facebook,于2013年开源.至今不断修改完善,现在已经到 ...
- ES5和ES6中对于继承的实现方法
在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方. 原型链的特点和实现已经在之前的一篇整理说过了,就是 ...
- JavaScript面向对象轻松入门之概述(demo by ES5、ES6、TypeScript)
写在前面的话 这是一个JavaScript面向对象系列的文章,本篇文章主要讲概述,介绍面向对象,后面计划还会有5篇文章,讲抽象.封装.继承.多态,最后再来一个综合. 说实话,写JavaScript面向 ...
- JavaScript面向对象轻松入门之封装(demo by ES5、ES6、TypeScript)
本章默认大家已经看过作者的前一篇文章 <JavaScript面向对象轻松入门之抽象> 为什么要封装? 封装(Encapsulation)就是把对象的内部属性和方法隐藏起来,外部代码访问该对 ...
- JavaScript面向对象轻松入门之多态(demo by ES5、ES6、TypeScript)
多态(Polymorphism)按字面的意思就是"多种状态",同样的行为(方法)在不同对象上有不同的状态. 在OOP中很多地方都要用到多态的特性,比如同样是点击鼠标右键,点击快捷方 ...
- JavaScript、ES5和ES6的介绍和区别
JavaScript由三部分组成: ECMAScript(核心) DOM(文档对象模型) BOM (浏览器对象模型) ES5(ECMAScript第五个版本) strict模式 严格模式,限制一些用法 ...
随机推荐
- 输入法InputConnection
/** * The InputConnection interface is the communication channel from an * {@link InputMethod} bac ...
- HDU 4532
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...
- Window下UDP(socket)接和收数据案例
配置QT的环境变量,这台电脑à属性à高级系统设置à高级à环境变量à系统变量àpathàC:\Qt\Qt5.3.0\5.3\mingw482_32\bin;C:\Qt\Qt5.3.0\Tools\ ...
- SQL Server高速导入数据分享
SQL Server高速导入数据,能够尝试的方法例如以下:CTE.OpenRowSet/OpenDataSource.BULK INSERT.bcp.Shell. 以下依次介绍这几种办法. 1.CTE ...
- 【HDU 4870】Rating【DP】
题意:一个人注冊两个账号,初始rating都是0,他每次拿低分的那个号去打比赛,赢了加50分,输了扣100分.胜率为p,他会打到直到一个号有1000分为止,问比赛场次的期望. 题解:因为每次添加分数或 ...
- 回调函数实现类似QT中信号机制(最简单)
1. 定义回调接口类: class UIcallBack{public: virtual void onAppActivated() = 0; virtual void onShowMore() = ...
- spring拦截器和注解处理日志操作
整体思想:通过拦截器拦截所有的请求,处理含有自定义注解的方法,通过request得到需要的参数. 拦截器代码: package com.zktx.platform.log2; import java. ...
- How to do IF NOT EXISTS in SQLite
http://stackoverflow.com/questions/531035/how-to-do-if-not-exists-in-sqlite How about this? INSERT O ...
- 【HDU 3085】 Nightmare Ⅱ
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=3085 [算法] 双向BFS [代码] #include<bits/stdc++.h> ...
- (Go)06. Printf格式化输出、Scanf格式化输入详解
Print.Println .Printf .Sprintf .Fprintf都是fmt 包中的公共方法,在需要打印信息时需要用到这些函数,那么这些函数有什么区别呢? Print: 输出到控制台(不接 ...