Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS创建对象的几种方式 #34

Open
conan1992 opened this issue Jun 22, 2020 · 0 comments
Open

JS创建对象的几种方式 #34

conan1992 opened this issue Jun 22, 2020 · 0 comments

Comments

@conan1992
Copy link
Owner

工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程

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");
  • 缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)

构造函数模式

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("Greg", 27, "Doctor");

与createPerson的qubie

  • 没有显式创建对象
  • 直接将属性和方法赋给了 this 对象;
  • 没有return

缺点

  • 每个方法都要在每个实例上重新创建一遍。在前面的例子中,person1 和 person2 都有一个名为 sayName()的方法,但那两个方法不是同一个 Function 的实例。

原型模式

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(); 
person1.sayName(); //"Nicholas" 
var person2 = new Person();
person2.sayName(); //"Nicholas" 
alert(person1.sayName == person2.sayName); //true

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

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实
例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

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

动态原型模式

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("Nicholas", 29, "Software Engineer"); 
friend.sayName();

注意构造函数代码中加粗的部分。这里只在 sayName()方法不存在的情况下,才会将它添加到原
型中。这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化,不需要再做什么修改了。不过要记住,这里对原型所做的修改,能够立即在所有实例中得到反映。因此,这种方法确实可以说非常完美。其中,if 语句检查的可以是初始化之后应该存在的任何属性或方法——不必用一大堆if 语句检查每个属性和每个方法;只要检查其中一个即可。对于采用这种模式创建的对象,还可以使用 instanceof 操作符确定它的类型。
注:使用动态原型模式时,不能使用对象字面量重写原型。前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

我们回顾下 new 的实现步骤:1.首先新建一个对象2.然后将对象的原型指向 Person.prototype3.然后 Person.apply(obj)4.返回这个对象
注意这个时候,回顾下 apply 的实现步骤,会执行 obj.Person 方法,这个时候就会执行 if 语句里的内容,注意构造函数的 prototype 属性指向了实例的原型,使用字面量方式直接覆盖 Person.prototype,并不会更改实例的原型的值,person1 依然是指向了以前的原型,而不是 Person.prototype。而之前的原型是没有 getName 方法的,所以就报错了!

function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('kevin');
var person2 = new Person('daisy');

// 报错 并没有该方法
person1.getName();

// 注释掉上面的代码,这句是可以执行的。
person2.getName();

寄生构造函数模式

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("Nicholas", 29, "Software Engineer"); 
friend.sayName(); //"Nicholas"

假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改 Array 构造函数,因此可以使用这个模式。

function SpecialArray(){ 
 //创建数组
 var values = new Array(); 
 //添加值
 values.push.apply(values, arguments); 
 //添加方法
 values.toPipedString = function(){ 
 return this.join("|"); 
 }; 
 
 //返回数组
 return values; 
} 
var colors = new SpecialArray("red", "blue", "green"); 
alert(colors.toPipedString()); //"red|blue|green"

在这个例子中,我们创建了一个名叫 SpecialArray 的构造函数。在这个函数内部,首先创建了
一个数组,然后 push()方法(用构造函数接收到的所有参数)初始化了数组的值。随后,又给数组实
例添加了一个 toPipedString()方法,该方法返回以竖线分割的数组值。最后,将数组以函数值的形
式返回。接着,我们调用了 SpecialArray 构造函数,向其中传入了用于初始化数组的值,此后又调
用了 toPipedString()方法。
关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属
性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,
不能依赖 instanceof 操作符来确定对象类型。由于存在上述问题,我们建议在可以使用其他模式的情
况下,不要使用这种模式;

稳妥构造函数模式

function Person(name, age, job){ 
 
 //创建要返回的对象
 var o = new Object();
//可以在这里定义私有变量和函数
 //添加方法
 o.sayName = function(){ 
 alert(name); 
 }; 
 
 //返回对象
 return o; 
}

拓展

new模拟实现
参考:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant