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;
定义的变量应该与参数无关(个人理解),否则会报错,***已声明
还真是脑壳疼,看了好多遍都没有理解透,只好写下来没事看一看
┭┮﹏┭┮