js中定义对象有多种方法再加上es6的新方法,难免使用时让人摸不着头脑,本篇博文对经典的集中对象定义方法做一次总结,最主要是和es6进行比较加强理解。
es5定义对象:Object构造函数、对象字面量、工厂模式、构造函数模式、原型模式、组合模式。
es6:class定义类。
es5定义对象
对象字面量
使用对象字面量创建对象的写法简单直观,所以定义一个对象的最简单方式就是对象字面量,它是通过在一个大括号里面使用键值对的方式表示每一个属性和方法,每一个键值对之间使用逗号隔开。
var gcc = {
name:"cc",
height:173,
job:"programmer",
do:function(str){
console.log(`${this.name}正在${str}`)
}
}
gcc.do('睡觉!')
我们看下结构:
对象字面量的本质就是利用Object构造函数生成对象
利用Object构造函数
//Object构造函数实现
let gcc0 = new Object();
gcc0.name = "cc";
gcc0.height = 173;
gcc0.job = "porogrammer";
gcc0.do = function(str){
console.log(`${gcc0.name}正在${str}`)
}
gcc0.do("吃饭!");
通过比较两者其实是一回事。
- 优点:定义对象非常简单,适合临时生成对象使用。
- 缺点:当需要创建很多很多个对象的时候,只能一个一个去创建,每一个对象的方法和属性都需要单独写,这使得代码没有丝毫复用性可言,违背了对象封装的特性。
工厂模式
工厂模式通过将对象的创建封装到一个方法中,再通过在调用该方法时传入参数而实现对象的实例化,解决了以上提到的产生大量重复代码的问题。
function CreatGcc(name,height,job){
let gcc = new Object();
gcc.name = name;
gcc.height = height;
gcc.job = job;
gcc.do = function(str){
console.log(`${gcc.name}正在${str}`)
} ;
return gcc;
}
let gcc2 = CreatGcc('cc2','173','programmer');
let gcc3 = CreatGcc('cc3','173','programmer');
gcc2.do("看书");
gcc3.do("画画");
- 优点:相当使用对象字面量方式作为工厂,创造结构相同的Object对象。
- 缺点:由于生成的对象都是Object的构造函数,无法分辨对象类型。所以我们要加入构造函数。
构造函数模式
构造函数模式就是通过定义一个function函数,然后通过this给对象的属性和方法进行赋值。当我们实例化对象时,只需在该函数前面加一个new关键字就可以了。
//构造函数模式
function GccCu(name,height,job){
this.name = name;
this.height = height;
this.job = job;
this.do = function(str){
console.log(`${this.name}正在${str}`)
};
}
let gcc4 = new GccCu("cc4",173,"踢球");
gcc4.do("踢球")
通过截图看到构造函数终于是gccuc了;但这种方式依然存在问题。
- 优点:有自己的构造函数,能够识别出对象类型。
- 缺点:构造函数里的属性和方法在每个对象上都要实例化一遍,包括对象共用的属性和方法,这样就造成了代码的复用性差的问题。原型模式可以解决。
原型模式
原型模式是通过将所有的属性和方法都定义在其prototype属性上,达到这些属性和方法能被所有的实例所共享的目的。
//原型模式
function GccPro(name,height,job){
GccPro.prototype.name = name;
GccPro.prototype.height = height;
GccPro.prototype.job = job;
GccPro.prototype.do = function(str){
console.log(`${this.name}正在${str}`)
};
}
var gcc5 = new GccPro('cc5',173,'programmer');
gcc5.do("看电影")
- 优点:通过原型我们实现了将对象上的所有方法和属性共享,但这样会有很大的问题,看下面示例:
用原型模式生成两个对象 cc5,cc50
var gcc5 = new GccPro('cc5',173,'programmer');
var gcc50 = new GccPro('cc50',173,'programmer');
gcc5.do("看电影")
gcc50.do("看电视")
结果:
除了原型上的方法传入参数不相同之外,cc5的所有属性都被cc50覆盖。
所以此种方法基本上是不能在实际中使用,但是我们可以保留其方法共享的好处,改造其属性私有。
构造函数+原型组合模式
到此定义函数的最佳模式也就诞生了 我们结合构造函数的优点和原型模式的好处。
//组合模式
function Gcc(name,height,job){
this.name = name;
this.height = height;
this.job = job;
}
Gcc.prototype.do= function(str){
console.log(`${this.name}正在${str}`)
};
var gcc6 = new Gcc('cc6',173,'programmer');
var gcc60 = new Gcc('cc60',173,'programmer');
gcc6.do("上班")
gcc60.do("做饭")
通过截图我们发现每个对象实例化后都有自己的私有属性,但是方法都是共享的。
es6通过类实现对象定义
es6 class的出现就是为了更加方便的定义对象。
class GccEs6{
constructor(name,height,job) {
this.name = name;
this.height = height;
this.job = job;
this.sayName = function(str) {
console.log(`我是${this.name},我身高${this.height},我是一名${this.job},我正在${str}`);
}
}
do(str) {
console.log(`我是${this.name},我正在${str}`);
}
}
var gcc100 = new GccEs6('gcc100',173,"programmer");
var gcc200 = new GccEs6('gcc200',173,"programmer");
gcc100.do("codding");
gcc200.do("debugger");
gcc100.sayName("codding");
gcc200.sayName("debugger");
结果:
通过截图发现定义在构造函数里面的方法sayname()实例化之后都是每个对象的私有,定义在构造函数外面的方法是挂在对象的隐式原型上是共享的。do方法等价于
GccEs6.prototype.do= function(str){
console.log(`${this.name}正在${str}`)
};
es5 和es6 定义对象的比较
为了进一步讨论es6定义class模式与es5 function区别我们将es6 class的代码通过babel转为es5
'use strict';
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"); } }
var GccEs6 = function () {
function GccEs6(name, height, job) {
_classCallCheck(this, GccEs6);
this.name = name;
this.height = height;
this.job = job;
this.sayName = function (str) {
console.log('\u6211\u662F' + this.name + '\uFF0C\u6211\u8EAB\u9AD8' + this.height + ',\u6211\u662F\u4E00\u540D' + this.job + '\uFF0C\u6211\u6B63\u5728' + str);
};
}
_createClass(GccEs6, [{
key: 'do',
value: function _do(str) {
console.log('\u6211\u662F' + this.name + '\uFF0C\u6211\u6B63\u5728' + str);
}
}]);
return GccEs6;
}();
// GccEs6.prototype.do= function(str){
// console.log(`${this.name}正在${str}`)
// };
var gcc100 = new GccEs6('gcc100', 173, "programmer");
var gcc200 = new GccEs6('gcc200', 173, "programmer");
gcc100.do("codding");
gcc200.do("debugger");
gcc100.sayName("codding");
gcc200.sayName("debugger");
通过比较我们得出u以下结论;
- class的构造函数必须使用new进行调用,普通构造函数不用new也可执行。
- class不存在变量提升,es5中的function存在变量提升。
- class内部定义的方法不可枚举,es5在prototype上定义的方法可以枚举。