创建对象的几种方式
在js中,经常看到如下四种创建对象的方法:
var obj = {}; // 对象字面量,跟new Object()一致
var obj1 = new Object(); // new方式创建对象
var obj2 = Object.create(); // 通过Object.create创建
但我们在开发项目过程中,还有另一种常用的创建对象的方式,即通过function方式:
function PObject(name) {
this.name = name;
}
var obj3 = new PObject('weis');
var obj4 = new PObject('smas');
跟强类型语言,比如C#,C++,java一样,通过new function方式实现继承达到方法复用目的,但是这里篇幅主要是讲解对象创建方式,继承方式可以放在后面专门讲解。
new创建对象
通过new创建对象,这个过程做了什么事情?
可以看出,obj3的[[Prototype]]即__proto__==PObject.prototype,况且object对象新增了一个name属性,依此,new主要做了以下步骤:
- 创建对象var o = {};
- 将新对象的隐形原型([[Prototype]])指向构造函数的原型prototype;
- 通过call、apply执行构造函数并将this指向新对象o;
- 返回这个新对象o;
创建一个newFun方法模拟一下new创建的过程:
function PObject(name) {
this.name = name;
}
PObject.prototype.getName = function() {
return this.name;
}
function newFun(func, name) {
var o = {};
o.__proto__ = func.prototype;
func.call(o, name); // 改变this指向
return o;
}
var obj3 = new PObject('weis');
var obj4 = new PObject('smas');
// 通过newFun创建
var obj5 = newFun(PObject, 'new ok');
运行了一下,可以看到obj3和obj5的指向一样,并且都有了name属性,__proto__里有getName了:
new创建对象注意事项
new创建一个对象的时候,请注意了!!!构造函数默认返回this,即return this;我们不用显示的手动写出来,但是当显示写return的时候,可能会改变new的初衷,情况如下:
function PObject(name) {
this.name = name;
return this; // 如果不手动写,js规范默认自己加上去
}
var obj6 = new PObject('weis');
console.log(obj6); // PObject{name:'weis'};
还是返回new创建出来的对象。
function PObject(name) {
this.name = name;
return 1;
}
var obj6 = new PObject('weis');
console.log(obj6); // PObject {name: 'weis'}
return 1竟然返回的还是new创建出来的对象,为什么?因为显示return返回一个number、boolean、string、null、undefined等简单的基本数据类型时,那么返回值仍然为新创建的对象。
function PObject(name) {
this.name = name;
return {
ui: 90
};
// return function() {};
// return [4,5,6];
}
var obj6 = new PObject('weis');
console.log(obj6); // PObject {ui: 90}
如果return一个引用类型或者function时,那么this就会被抛弃,返回值被替换成新的引用或者function。
Object.create创建对象
MDN定义的原话:
Object.create方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
按照我个人理解,就是通过Object.create创建的对象,是通过__proto__原型链查找属性。实例如下:
o对象的属性挂载在__proto__里了。也可以通过模拟Object.create创建对象的过程:
function PObject(name) {
this.name1 = name || 'weis';
}
PObject.prototype.getName = function() {
return this.name1;
}
function ObjCreate(func) {
var f = function() {}; // 新的构造函数
f.prototype = func; // 函数原型为fun参数
return new f();
}
var obj3 = ObjCreate(PObject);
console.log(obj3.name1); // undefined 说明并没有执行PObject构造函数
var obj4 = ObjCreate(new PObject('smas')); // 传入new创建出来的匿名对象;
console.log(obj4);
执行结果
ObjCreate模拟创建的跟Object.create原理一致,ObjCreate(PObject)执行结果可以看的出来,Object.create并没有执行和改变this指向,因此PObject对象上的属性不会继承到Object.create创建的实例中。
总结
- 我们想继承原型方法,不想继承构造函数内部属性时,可以使用Object.create;
- 了解对象创建,有助于我们在实际项目开发中,做功能复用时避免出现不必要的bug;
- 看似简单的知识,也需要很多时间去吃透和理解,我不是一个天赋凌然的人,只能一点一滴去学习和积累。