javascript中创建对象的方式及优缺点(一)
1. 简单方式创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // 字面量方式创建对象 var person1 = { name: "xyc" , age: 23, sayHi: function () { console.log(name); } }; // Object方式创建对象 var person2 = new Object(); person2.name = "lxy" ; person2.age = 18; person2.sayHi = function () { console.log(person2.name); } |
虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码,如上面的代码,每创建一个类似的person对象,就会重复上面的写法,代码较为冗余
为了解决这个问题(代码重复
),下面引入工厂模式 ==>
2. 工厂模式创建对象
1 2 3 4 5 6 7 8 9 10 11 12 | function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function (){ alert( this .name); }; return o; } var person1 = createPerson( "Nicholas" , 29, "Software Engineer" ); var person2 = createPerson( "Greg" , 27, "Doctor" ); |
通俗的解释:工厂模式就是利用了函数的封装调用,类比工厂材料==>成品的过程,完成入口参数==>对象的过程,函数可以无数次的生成,因此能够避免上面产生大量重复代码的情况。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)
为解决这个问题(对象识别
),下面引入构造函数模式
3. 构造函数创建对象
3.1 构造函数与普通函数的区别
- 命名规则:构造函数一般是首字母大写,普通函数遵照小驼峰式命名法
- 函数调用:
构造函数:
(1)new fn( )
(2)构造函数内部会创建一个新的对象,即f的实例
(3)函数内部的this指向 新创建的f的实例
(4)默认的返回值是f的实例
普通函数:
(1)fn( )
(2)在调用函数的内部不会创建新的对象
(3)函数内部的this指向调用函数的对象(如果没有对象调用,默认是window)
(4)返回值由return语句决定 -
构造函数的返回值:
有一个默认的返回值,新创建的对象(实例),当手动添加返回值后(return语句):
(1)返回值是基本数据类型-->真正的返回值还是那个新创建的对象(实例)
(2)返回值是复杂数据类型(对象)-->真正的返回值是这个对象123456789101112function
foo() {
var
f2 =
new
foo2();
console.log(f2);
// {a: 3}
console.log(
this
);
// window
return
true
;
}
function
foo2() {
console.log(
this
);
// foo2类型的对象 不是foo2函数
return
{a: 3};
}
var
f1 = foo();
console.log(f1);
// true
3.2 new 操作符作用
使用new操作符调用构造函数会经历下面几个步骤:
(1)创建一个以这个函数为原型的空对象.
(2)将函数的 prototype 赋值给对象的 proto 属性
(3)将对象作为函数的 this 传进去。如果有 return 出来东西是对象的话就直接返回 return 的内容,没有的话就返回创建的这个对象
1 2 3 4 5 6 7 8 9 10 11 | function NewFunc(func){ var ret = {}; if (func.prototype !== null ) { ret.__proto__ = func.prototype; } var ret1 = func.apply(ret, Array.prototype.slice.call(arguments, 1)); if (( typeof ret1 === "object" || typeof ret1 === "function" ) && ret1 !== null ) { return ret1; } return ret; } |
普通函数的作用主要是封装作用,能够在作用域内多处调用而已
3.3 构造函数解决对象识别
创建自定义的构造函数意味着可以通过 instanceof 将它的实例标识为一种特定的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function Person(name,age,job){ this .name = name; this .age = age; this .sayHi = function (){ console.log( this .name); } } var person1 = new Person( 'xyc' , 23); var person2 = new Person( 'lxy' , 22); console.log(person1 instanceof Person); //true console.log(person2 instanceof Person); //true console.log(person1 instanceof Object); //true 因为Person继承自Object,所以这里一样成立. |
3.4 缺陷
构造函数创建对象的方式解决了代码重复
和对象识别
的问题,但是创建的对象中含有方法时,每实例化一个Person,就会产生一个方法,也就是一个对象,每个对象分别占据内存。因此,构造函数创建对象的方式存在内存大量占用的风险
利用原型共享的特性,下面引入原型模式
4. 原型创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function Person(){} Person.prototype = { constructor: Person, name : "Nicholas" , age : 29, job : "Software Engineer" , friends : [ "Shelby" , "Court" ], sayName : function () { alert( this .name); } }; var person1 = new Person(); var person2 = new Person(); person1.sayName(); //"Nicholas" person2.sayName(); //"Nicholas" person1.friends.push( "Van" ); alert(person1.friends); //"Shelby,Court,Van" alert(person2.friends); //"Shelby,Court,Van" alert(person1.friends === person2.friends); //true |
原型创建对象的方式将属性和方法都存在与原型中,也就是说,只要通过这种形式创建的对象都会共享这些属性和对象,相对于方法共享这是我们乐于看到的,但是属性共享让每个实例缺失了“个性”;另外对于引用类型的属性共享时,如上面的例子,多个实例对引用类型的操作会被篡改
。
实例一般都要有属于自己的全部属性
,这也决定了原型创建方式的局限性。下面引入非常经典的对象创建方式
5. 构造函数+原型创建对象(重点)
组合构造函数模式与原型模式:构造函数模式用于定义实力属性,而原型模式用于定义方法和共享的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function Person(name, age, job){ this .name = name; this .age = age; this .job = job; this .friends = [ "Shelby" , "Court" ]; } Person.prototype = { constructor : Person, sayName : function (){ alert( this .name); } } var person1 = new Person( "Nicholas" , 29, "Software Engineer" ); var person2 = new Person( "Greg" , 27, "Doctor" ); person1.friends.push( "Van" ); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true |
这种方式创建的实例对象,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存;另外,这种方式还支持相构造函数传递参数,解决了上面的各种问题!
6. Object.create(ES5)
在ES5之前,只能使用new来实现原型链集成。总而言之Object.create()和字面量对象应该替换new object()方法。
Object.create()可以接收两个参数:提供原型的对象,可选属性对象(这个对象包含对新创建对象的配置)。
总结
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?