创建对象
工厂模式
function createPerson(name){
let person = {};
person.name = name;
person.sayName = function(){
console.log(this.name)
}
return person;
}
let person1 = createPerson("john");
let person2 = createPerson("amy");
每次调用createPerson,都会生成一个包含一个属性和一个方法的对象。但这样新建出的对象无法判断是何种类型,只能判断为Object实例。
构造函数模式
function Person(name){
this.name = name;
this.sayName = function (){
console.log(this.name);
}
}
let person1 = new Person("mike");
let person2 = new Person("james");
这样比工厂模式的好处是,新建出的对象可以确定为特定类型。
但是使用构造函数模式仍然存在一个问题,就是每次new一个对象出来以后,所有的对象方法都是新的Function实例,导致性能损耗。
new操作符做了什么
- 创建一个新对象
- 新对象的[[Prototype]]特性被赋值为构造函数的prototype
- 即this指向新对象
- 执行构造函数内部的代码
- 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
简单的new操作符模拟:
function myNew(myConstructor, ...args) {
let obj = {};
obj.__proto__ = myConstructor.prototype;
let res = myConstructor.apply(obj, args);
let isObject = typeof res === "object";
let isFunction = typeof res === "function";
return isObject || isFunction ? res : obj;
}
原型模式
function Person() {}
Person.prototype.name = "jordan";
Person.prototype.sayName = function () {
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
继承
原型链继承
function Parent() {
this.name = "mike";
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child() {}
Child.prototype = new Parent();
let child = new Child();
child.sayName();
但此方式有两种问题:一,原型中的引用值会被所有实例共享;二,子类型在实例化时不能给父类型的构造函数传参。
经典继承
function Parent() {
this.names = ["john", "jackson"];
}
function Child() {
Parent.call(this);
}
let parent = new Parent();
parent.names.push("luke");
console.log(parent.names);
let child = new Child();
console.log(child.names);
经典继承存在的问题和用构造函数方式新建对象一样,每次新创建实例都会创建新的方法实例。
组合继承
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 二,第二次执行Parent构造函数
this.age = age;
}
Child.prototype = new Parent(); // 一,第一次执行Parent构造函数
Child.prototype.constructor = Child;
let child1 = new Child("jackson", 12);
let child2 = new Child("john", 16);
console.log(child1.name);
console.log(child1.sayName === child2.sayName);
组合继承的方式是使用最为普遍的一种方式。但还存在一个问题,即会调用两次父类的构造函数。
寄生式继承
function createObj(o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log("hi");
};
return clone;
}
寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。但和经典继承一样,每次创建对象都会创建一遍方法。
寄生组合式继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function inheritPrototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Child, Parent);
Child.prototype.sayAge = function () {
console.log(this.age);
};