js中常见的创建对象的方法

时间:2021-04-07 19:59:00

前两天好好的把高程对象那一块又读了下,顺便写点笔记。补一句:代码都测试过了,应该没有问题的。可以直接拿到控制台跑!

1.工厂模式

function person(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 = person('Nicholas', 29, 'Software Engineer');
var person2 = person('tangyuhui', 30, 'bigboss');

工厂模式解决了创建多个相似对象的问题,但是没有解决对象识别的问题(怎样知道一个对象的类型)。

2.构造函数模式

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
} ;
} var person1 = new Person('Nicholas', 29, 'Software Engineer');
var person2 = new Person('tangyuhui', 30, 'bigboss');

相对于工厂模式,存在不同的地方是:

(1)没有显示地创建对象

(2)直接将属性和方法赋给了this对象

(3)没有return语句

注意:使用构造函数创建新实例,实际上会经历以下四个阶段:

(1)创建一个新对象

(2)将构造函数的作用域赋值给新对象(因此this指向了这个新对象)

(3)执行构造函数中的代码(为这个新对象添加属性)

(4)返回新对象(这里是没有显示地return才会返回新对象)

注意:构造函数的缺点是实例上的方法不是相等的,即不同实例上的同名函数是不相等的。

person1.sayName === person2.sayName   //false

3.原型模式

function Person() {};
Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function() {
alert(this.name);
} ;
var person1 = new Person();
var person2 = new Person();
person1.sayName === person2.sayName //true

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象是保存可以由特定类型的所有实例共享的属性和方法。

好处:可以让所有对象实例共享它所包含的属性和方法。

实例----原型----构造函数之间的关系如下图:

js中常见的创建对象的方法

当然这里有一个更简便的写法:

function Person() {};
Person.prototype = {
constructor: Person,
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function() {
alert(this.name);
}
}

这里会有一个问题,这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true,所以还有另外一种写法:

function Person() {};
Person.prototype = {
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function() {
alert(this.name);
}
} Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value: Person
})

这里有一点要注意:如果要重写原型对象,一定要先重写后实例化,不然实例和原型之间就断了联系。类似下面的代码,会出错的:

function Person() {};
var friend= new Person();
Person.prototype = {
constructor: Person,
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function() {
alert(this.name);
}
} friend.sayName();//Error

原理图如下:

js中常见的创建对象的方法

原型对象代码量是减少了,但是它缺点也是比较突出的,问题就出在引用类型是按引用传递的。看代码

  function Person(){};
Person.prototype = {
constructor: Person,
name:'Nicholas',
age: 29,
job: 'Software Engineer',
friends: ['zhangsan','lisi'],
sayName: function(){
alert(this.name);
}
} var person1 = new Person();
var person2 = new Person();
person1.friends.push('wanger');
console.log(person1.friends); //zhangsan,lisi,wanger
console.log(person2.friends); //zhangsan,lisi,wanger

4.组合使用构造函数模式和原型模式

单独使用原型模式还是有一点问题,创建自定义类型的常见方式,就是使用构造函数模式与原型模式。

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.firends = ['mayun', 'mahuateng']
}
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name)
}
} var person1 = new Person('zhangsan', 29, '厨师');
var person2 = new Person('lisi', 30, '工程师');
person1.firends.push('wangwei'); console.log(person1.firends); //mayun, mahuateng, wangwei
console.log(person2.firends); //mayun, mahuateng
console.log(person1.sayName === person2.sayName); //true

5.动态原型模式

如果想把所有的信息封装在构造函数中,就可以使用该模式。

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName !== 'function') {
Person.prototype.sayName = function(){
alert(this.name);
}
}
} var friend = new Person('zhangsan', 29, '大佬');

6.寄生构造函数模式

function Person(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 friend = new Person('zhangsan', 40, '计生办');
friend.sayName() //zhangsan

这种模式和工厂函数模式很像,只是使用new实例化对象,使用new构造函数实例化函数时会4个过程,如果没有指定返回值时,默认返回的是this,但也可以显示的return。这里就使用了该特性。这种模式不常用,会用到该模式,真的不常用。反正我没怎么用过。

7.稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其他方法也不引用this,稳妥对象最合适在一些安全的环境中或者防止数据被篡改的程序中使用。

function Person(name, age, job) {
var o = new Object();
//定义私有变量和函数 //添加方法
o.sayName = function() {
alert(name);
}
return o;
} var friend = Person('zhangsan', 40, '计生办');
friend.sayName() //zhangsan 只有该方法才能访问到name的值

这里需要注意的是寄生构造函数模式和稳妥构造函数模式创建的实例对象与构造函数没有什么关系。