工厂模式:
多个类似对象的创建,可以使用工厂模式,但工程模式不知道新创建的对象是什么类型。
构造函数模式:
使用构造函数创建对象:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
};
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1和person2实例对象的constructor都指向Person,因此构造函数可以区分实例为哪种类型。
这是相比工厂模式/构造函数模式所具有的优点
console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // true
只要使用new操作符,调用的就是构造函数。不使用new操作符的就是普通函数。
构造函数的缺点:在构造函数中定义的方法,在每个实例上都不同,并不是共享一个操作方法。
解决方案是,将方法单独定义在构造函数的外部,但是形成的问题是为实例赋予方法的引用的时候,会使用全局的方法的应用,这样会导致全局变量的混乱。
因此可使用原型模式来解决这些问题。
原型模式:
在原型对象上创建属性和方法。
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
console.log(this.name);
};
let person1 = new Person();
person1.sayName(); // "Nicholas"
原型上的属性和方法,都被所有的属性和方法共享。
理解原型:
定义构造方法时,原型对象上就有了constructor属性。指向与之关联构造函数。
注意:构造函数。实例。原型对象,都是不同的对象。
原型层级覆盖:如果在对象的属性找没有找到对应的方法或属性,那么就会去它的原型对象上去找对应的方法或属性。
如果存在同名的属性,那么会覆盖到原型对象上的属性。
原型和in操作符
in操作符:可以通过对象属性名,判断该属性名是否在于对象或者对象的原型上
定义原型的语法:
function Person() {}
Person.prototype = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName() {
console.log(this.name);
}
};
上述方法,会将constructor指向object对象,而它应该指向的是相关的构造函数,因此可以单独设置constructor
}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName() {
console.log(this.name);
}
};
但上述方法又存在了新的问题。这样会创建的一个Enumerable为true的属性。而原生的constructor是不可枚举的。
因此添加以下方式来进行给更改:
function Person() {}
Person.prototype = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName() {
console.log(this.name);
}
};
// 恢复 constructor 属性
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
原型的动态性:
如果先创建了对象,然后再在对象的原型上进行了修改,那么仍然会改变之前已经创建好的对象。
这是因为实例与原型之间就是简简单单的指针。会在原型上找到对应修改的属性。
原生对象原型:
修改原生的String对象
String.prototype.startsWith = function (text) {
return this.indexOf(text) === 0;
};
let msg = "Hello world!";
console.log(msg.startsWith("Hello")); // true
但并不建议这么做。推荐的做法是使用继承。
原型的问题:
原型上包含引用属性值,会存在问题。
继承:
原型链:
将对象原型指针,更改为指向另外一个对象。