工厂模式
1 2 3 4 5 6 7 8 9 10 11 12 13
| function createPerson(name, age, job) { let obj = new Object(); obj.name = name; obj.age = age; obj.job = job; obj.sayName = function() { console.log(this.name); }; return obj; };
let p1 = createPerson('Katrina', 18, 'student'); console.log(p1);
|
优点
缺点
- 没有解决对象标识问题(即新创建的对象是什么类型)
- 通俗来说,工厂模式中我们直接用
new Object
来创建,所以实例的原型链上只有Object.prototype
对象
构造函数模式
1 2 3 4 5 6 7 8 9 10
| function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); }; }
let p1 = new Person('Katrina', 18, 'student');
|
Person()
构造函数代替了createPerson()
工厂函数,实际上,Person()
内部代码跟createPerson()
基本一样,有如下区别:
- 没有显示地创建对象
- 属性和方法直接付给了this
- 没有return
这就需要我们明白new操作符的机制了:手写原理 | new操作符
优点
1 2 3 4 5 6 7 8 9 10 11
| function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); }; }
let p1 = new Person('Katrina', 18, 'student'); console.log(p1 instanceof Person);
|
缺点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); }; }
let p1 = new Person('Katrina', 18, 'student'); let p2 = new Person('Kate', 20, 'doctor');
console.log(p1.sayName === p2.sayName)
|
在上面这个例子中,p1
和p2
都有sayName
方法,但这两个方法不是同一个sayName
实例
在ECMAScript
中的函数是对象,因此每次定义函数时,都会初始化一个对象
1 2 3 4 5 6 7 8 9
| function Person(name, age, job) { this.name = name; this.age = age; this.job = job;
this.sayName = new Function(console.log(this.name); ) }
|
因为都是做同样一件事,所以定义两个Function实例是没必要的,要解决这个问题,可以把函数定义转移到函数外部
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName; }
function sayName() { console.log(this.name); }
let p1 = new Person('Katrina', 18, 'student'); let p2 = new Person('Kate', 20, 'doctor');
|
这里,sayName()
被定义在了构造函数外部,在构造函数内部,sayName
属性等于全局sayName()
函数
因为这一次sayName
属性中包含的只是一个指向外部函数的指针,所以person1
和person2
共享了定义在全局作用域上的sayName()
函数
原型模式
每个函数都会创建一个prototype
属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法
补课:理解原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Person() {}
Person.prototype.name = "Katrina"; Person.prototype.age = 18; Person.prototype.job = "Student"; Person.prototype.sayName = function() { console.log(this.name); };
let person1 = new Person(); person1.sayName();
let person2 = new Person(); person2.sayName();
console.log(person1.sayName == person2.sayName);
|
优点
缺点
1 2 3 4 5 6 7 8 9 10
| function Person() {} Person.prototype.colors = ['red', 'black', 'yellow'];
let p1 = new Person(); let p2 = new Person();
p1.colors[1] = 'pink';
console.log(p1.colors); console.log(p2.colors);
|
构造函数模式+原型模式
属性写在构造函数模型中,方法写在原型模型中
这样的话,每个实例都有自己实例属性的副本,但同时又共享着对方法的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.colors = ['red', 'black', 'yellow']; }
Person.prototype.sayName = function() { console.log(this.name); };
let p1 = new Person('Katrina', 18, 'student'); let p2 = new Person('Kate', 20, 'doctor');
console.log(p1.sayName == p2.sayName);
p1.colors[1] = 'pink';
console.log(p1.colors); console.log(p2.colors);
|