前言:基本上是面试必考题!
es6的class如何用es5实现,其实是整理红宝书的东西。
工厂模式
封装了创建对象的细节
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('Tom', 23, 'doctor');
var person2 = createPerson('John', 21, 'engineer');
缺点:没有解决对象识别的问题,即怎样知道一个对象的类型。我个人理解是无法判断某个实例是不是某个类的实例!
构造函数模式
特点:
- 没有显式创建对象
- 直接将属性和方法赋值给this对象
- 没有return
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
}
}
/*
** 又是常考的知识点:new 做了什么
** 1. 创建新对象
** 2. 将构造函数的作用域赋给新对象(this就指向了新对象)
** 3. 执行构造函数中的代码(为这个新对象添加属性)
** 4. 返回对象
*/
var person1 = new Person('Tom', 23, 'doctor');
var person2 = new Person('John', 21, 'engineer');
可以得知,两个对象又一个构造函数属性,都指向Person
person1.constructor == Person // true
person2.constructor == Person // true
/*
* 实际上 实例的构造函数是下面
ƒ Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
}
}
**/
person1 instanceof Person; // true
person2 instanceof Person; // true
person1 instanceof Object; // true
person2 instanceof Object; // true
缺点:
每个方法都要在实例重新创建一遍,其实没有必要,方法作用一样但是要创建好多次,没啥必要。
person1.sayName == person2.sayName // false
当然,可能说
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName() {
alert(this.name);
}
这样所有实例就都用了全局的方法,但是这个全局方法却只能给Person用,这特么还叫全局?要是有更多的方法呢?那岂不是要维护更多的这种只给某个class用的全局了呢?
等等等等,新模式出现
原型模式
function Person() {
}
Person.prototype.name = "Tom";
Person.prototype.age = 29;
Person.prototype.job = 'Engineer';
Person.prototype.sayName = function() {
alert(this.name);
}
这样一遍一遍的写prototype很麻烦,所以进化一下
function Person() {
}
Person.prototype = {
name: 'Tom',
age: 29,
job: 'engineer',
sayName: function() {
alert(this.name);
}
}
这样写有个 问题:
constructor属性不再指向 Person了。
正常情况每创建一个函数,会同时创建它的prototype对象,这个对象会自动获得constructor属性,而这种声明式完全重写了默认的prototype对象,因此constructor属性变成了新对象的constructor的属性(指向Object构造函数),不再指向Person函数,尽管instanceof操作符还能返回正常值,但是通过constructor已经无法确定对象的类型了
var friend = new Person();
friend instanceof Object; // true
friend instanceof Person; // true
friend.constructor == Person; // false
friend.constructor == Object; // true
如果
真的需要constructor值,那就加上就好
function Person() {
}
Person.prototype = {
constructor: Person,
name: 'Tom',
age: 29,
job: 'engineer',
sayName: function() {
alert(this.name);
}
}
又有缺点contructor本来是不可枚举的,这样一来就变得可以枚举了。
再改,用上defineProperty
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value: Person,
})
除此之外,原型模式还有其他缺点
- 在初始化的时候,所有实例都会获得相同的属性值
- 如果属性是引用类型,那就问题很大了
function Person() {
}
Person.prototype = {
constructor: Person,
name: 'Tom',
age: 29,
job: 'engineer',
friends: ['jack', 'John'],
sayName: function() {
alert(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push('Julia');
alert(person1.friends); // ['jack', 'John', 'Julia']
alert(person2.friends); // ['jack', 'John', 'Julia']
alert(person1.friends === person2.friends) // true
构造函数模式和原型模式的结合
属性用构造函数模式
方法用原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['jack', 'julia'];
}
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name);
}
}
var person1 = new Person('Tom', 11, 'doctor');
var person2 = new Person('Marry', 43, 'teacher');
person1.friends.push('mack');
alert(person1.friends); // ['jack', 'julia', 'mack']
alert(person2.friends); // ['jack', 'julia']
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true
这是个最广泛的创建'class'的方法,但是我写一个类要分开两段写,很不舒服,那能不能写到一个里面? 当然可以!下面这种动态原型模式
动态原型模式
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);
}
}
}
这个方法在构造函数运行的时候会执行一次,而且仅执行一次,后续就再也不执行了,但是生成的实例都会动态获得这个方法!
寄生构造函数模式
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 friends = new Person('Nick', 29, 'worker');
friends.sayName(); // 'Nick'
乍一看,这特么就是工厂模式吧?
其实不是,寄生构造模式是需要用new去调用的,而工厂模式是直接一个函数。
可能会问,这特么有啥用?
个人看了书上的实例,可能最大的作用在于,我可复写并且扩展原来的基本类?
function specialArray() {
// 创建数组
var avlues = new Array();
// 添加值
values.push.apply(values, arguments);
// 添加方法
values.toPipedString = function() {
return this.joiin('|');
}
// 返回数组
return values;
}
var colors = new SpecialArray('red', 'blue');
alert(colors.toPipedString()); // 'red'|'blue'
缺点:
也是不能用instanceof确定对象类型
稳妥构造函数模式
大佬提出的稳妥对象概念:
- 没有公共属性
- 其方法也不引用this 稳妥构造函数与寄生构造类似,但是有不同:
- 新创建对象的实例方法不引用this
- 不使用new操作符调用构造函数
这特么除了函数名不同,那跟工厂模式有啥区别呢? 个人感觉一模一样,
实际上观察name,发现name是直接用的传入的,
这样,变量person中保存的是一个稳妥对象,而除了调用sayName()方法外,没有别的方式可以访问其数据成员
function Person(name, age, job) {
var o = new Object();
// 这里定义私有变量和函数
// 添加方法
o.sayName = function() {
alert(name);
}
return o;
}
var friends = Person('Tom', 29, 'engineer');
friends.sayName(); // 'Tom'
不用new去创建实例,内部也不使用this,说这么做安全
缺点:
也是不能用instanceof确定对象类型