6.2 创建对象

6.2.1 工厂模式

简单来说,就是使用函数建立了一个盒子,在里边创建了一个对象,然后返回了这个对象。

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');

它解决的问题呢,就是减少了代码量,直接调用函数传参就可以创建对象。但是也有不足,无法识别对象类型,哇,这里刚开始还以为是里边用数组就无法识别了,还在想为啥,哦原来是无法识别createPerson这个类型,对不起打扰了

console.log(person1 instanceof createPerson);  //false

6.2.2 构造函数模式

OK,无法识别,想想之前的Array、String,是不是可以识别?模仿一下吧,于是就有了构造函数模式

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');
console.log(person1 instanceof Person);  //true

不需要显示的创建对象也不需要return,可以识别对象类型。感觉挺不错的,但是每次实例方法都会执行一次函数(函数也是对象),即实例之间并没有共享方法,每个实例都包含各自的Function实例(对应于方法),为了解决这个问题呢,可以把方法写到构造函数外面

function Person(name,age,job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = sayName;
}
function sayName() {
    alert(this.name);
}

但是如果很多方法都写到外面,感觉上就没有封装了,然后,就出现了原型模式。

6.2.3 原型模式

原型模式就是使用构造函数建立一个空壳,之后将所有的属性和方法都添加在原型上

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

实例会共享原型中的所有属性和方法。

构造函数的prototype指针以及实例的[[prototype]]指向原型,原型的constructor指针指向函数。

这里有一个isPrototypeOf方法,当实例的[[prototype]]指针指向原型对象时,会返回true

let person1 = new Person();
console.log(Person.prototype.isPrototypeOf(person1)); //true

ES5定义了一个Object.getPrototypeOf 方法,返回实例指向的原型

console.log(Object.getPrototypeOf(person1) === Person.prototype); //true

实例获取属性和方法时,首先查询自己从构造函数中得到的属性和方法,若没有,查询原型中的实例和方法。

in操作符单独使用和for-in下使用的区别:
for-in返回所有可枚举属性和方法(原型+实例)
in单独使用对所有属性和方法返回true(原型+实例)

console.log('name' in person1); //true
person1.name = 'zz';
console.log('name' in person1); //true
console.log('sayName' in person1); //true

for(let prop in person1) {
    console.log(prop); 
    //name
    // age
    // job
    // sayName
}

若只想得到所有可枚举的实例属性,可以考虑ES5中定义的Object.keys方法
该方法接收一个参数表示对象,以字符串数组的形式返回对应的可枚举实例属性

console.log(Object.keys(person1)); //[]
console.log(Object.keys(Person.prototype)); 
//['name','age','job','sayName']
person1.name = 'zz';
console.log(Object.keys(person1)); //['name']

若返回所有实例属性,不考虑是否可枚举,可以调用Object.getOwnPropertyNames方法

console.log(Object.getOwnPropertyNames(person1)); //[]
console.log(Object.getOwnPropertyNames(Person.prototype));
//[ 'constructor', 'name', 'age', 'job', 'sayName' ]
person1.name = 'zz';
console.log(Object.getOwnPropertyNames(person1)); //['name']

为了简单,也可以使用对象字面量的形式来向原型中添加属性和方法

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

但是这种情况相当于重写了原型,那么它的constructor就不再指向构造函数,而是指向object,如果有需要,可以在对象字面量中添加constructor属性,值设为原构造函数,此时该属性是可枚举的,需要对属性特性进行自定义。
使用对象字面量的话需要先重写原型对象再实例化,否则会使实例无法获取原型中的属性和方法(实例的[[prototype]]指向旧的原型对象)。这里是因为[[prototype]]是在实例化的过程中获得的,之后不会进行更改。
原型模式也不是没有缺点,其中之一是无法进行参数化;最重要的一点就是对于包含引用类型值的属性(eg,数组),实例会共享属性,意味着改变任何一个,都会影响其他实例及原型对象中的值(指向堆中的同一个对象),就此,出现了构造函数模式和原型模式的组合。

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

实例属性在构造函数中定义,原型属性定义共享的属性和方法

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);
	}
}

6.2.5 动态原型模式

书上说这个是为了将构造函数和原型结合在一起,可能是因为我没有其他OO语言的经验,所以对这个感触还真没那么深

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);
		}
	}
}

总的来说,就是在构造函数中加一个判断,初次调用构造函数实例化的时候,由于原型对象中没有添加的属性和方法,所以sayName不存在,满足判断条件,为原型对象添加属性和方法,之后调用的时候,不满足判断条件,无需再次执行。

6.2.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 person1 = new Person('Nicholas',29,'Software Engineer');

原谅我的小脑袋瓜真的没办法理解这个模式的出现,因为我觉得既然有工厂模式,而这个也无法通过instanceof识别类型,感受不到它存在的价值┓( ´∀` )┏(对不起是在下愚钝了)

6.2.7 稳妥构造函数模式

这个主要是为了特殊情况(例如禁止使用this和new)准备的
与寄生构造函数有两点区别:
没有使用this赋值;
没有使用new调用函数

function Person(name,age,job) {
	var o = new Object();
	//定义私有变量和函数
	o.sayName = function() {
		alert(name);
	}
	return o;
}

这种情况下调用person.name会返回undefined
注意的是,这里是定义私有变量(eg.定义的局部变量),而不是o.name = name;
定义的变量应该与参数无关(个人理解),否则会报错,***已声明

还真是脑壳疼,看了好多遍都没有理解透,只好写下来没事看一看
┭┮﹏┭┮

发布了43 篇原创文章 · 获赞 0 · 访问量 304

猜你喜欢

转载自blog.csdn.net/weixin_44774877/article/details/102512077