一些构造函数的学习整理

Posted by eric on 2017-02-06

转载需注明:前端唐小胖

最近对于前端的学习主要还是以熟悉公司技术栈及开发经验,对于javascript的一些基础知识有些遗忘,我于是重新看起了红宝书,结合几个月的撸码有了一些自己新的思考,在这里总结一下。

javascript是一个面向对象的语言,但是直到ES6才有了class的概念,这是他区别与其他oop语言的一个特点。

理解javascript中的对象

这是最原始的创造对象的方法:
使用Object构造函数创建

1
2
3
let person = new Object();
person.name = 'eric';
persion.age = '23';

现在常见的创造对象的方式对象字面量表示

1
2
3
4
5
6
7
let person = {
name:"eric",
age:"29",
sayname:function(){
console.log(this.name)
}
}

虽然Object构造函数和对象字面量都可以创造单个对象,但是我们利用单个接口创造的对象会产生大量重复的代码造成环境的效率低下,为了解决这个问题我们的前辈们想出了各种套路来解决,
我来梳理一下常用的几种套路。

1.工厂模式


抽象创建对象的过程
用函数来封装以特定接口创建对象的细节

优点:解决了代码重复书写的麻烦
缺点:不能识别对象(不知道对象的类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
function createPerson(name,age,sex){
let obj = {
name:name,
age:age,
sex:sex,
skill:function(){
console.log(this.name+" is a coder!")
}
}
return obj;
}
let person1 = createPerson('eric',23,'man');
let person2 = createPerson('tang');

2.自定义构造函数模式


不显式的创建对象,使用new操作符
优点:解决了对象的识别问题,可以将构造函数的实例对象标记为特定类型
缺点:多次复用的方法在每个实例对象中反复创建,导致相同方法产生在不同作用域链和多次标示符解析,简单来说导致内存被无谓的消耗。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.skill = function(){
console.log(this.name+" is a coder!")
}
}
let person1 = new Person('eric',23,'man');
let person2 = new Person('tang',23,'man');

//对象类型名称为myObj,解决了识别的问题,用下面方法可以验证
console.log(person1.constructor === Person && person2 instanceof Person && person2 instanceof Object);//true

3.原型模式


我们创造的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含了可以由特定类型的实例对象共享的属性和方法。于是我们可以不在
构造函数中定义对象实例的信息,而是将其加入原型链中。
优点:复用方法挂在原型对象上面,所有的构造函数实例对象都可以共享原型下的属性和方法,使代码空间和内存得到优化
缺点:实例对象失去本身的特点,公用一套属性和方法。

1
2
3
4
5
6
7
8
9
function Person(){
//空空如也
}
Person.prototype.name ='eric';
Person.prototype.age = '23';
Person.prototype.method = function(){
console.log('be a coder')
}
let person1 = new Person();

4.最常用的模式:构造函数+原型模式


最为常用的创建对象的方式,结合两种模式的优点,
实现对象属性的封装和对象方法的复用,
平衡了对象的独立性和多态复用节省内存的问题。
几乎成为最广泛的定义构造函数方法,广泛使用于工作中。

1
2
3
4
5
6
7
8
9
10
11
function Person(name,age){
this.name =name;
this.age = age
}
Person.prototype.skill = function(){
console.log('be a coder')
}
var person1 = new Person('eric',23);
var person2 = new Person('tang',23);
//相同方法的复用,避免作用域的反复声明
console.log(person1.skill == person2.skill);

5.动态原型模式


该方法把所有信息都封装在构造函数中,通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name,age){
this.name = name;
this.age = age;
if(typeof this.skill != "function")
{
Person.prototype.skill = function(){
console.log("be a coder");
}
}
}

let person1 = new Person('tang',23);
person1.skill();

只有当该方法不存在的时候才会将其添加到原型对象上面,而后其所有的实例对象都可以享用这个方法,任何实例对象都可以立即使用这个方法。是一种非常灵活的方案。

6.寄生构造函数模式


其实就是工厂模式+new操作符的一种变体,由于这个方案返回的对象与其构造函数和原型对象都无关系,不能使用instanceof来确认对象的类型,因此这个方法不被红宝书推荐,我觉得仅仅在
某些原生构造函数不能修改的时候可以用这个方法在原构造函数下增加一种类工厂模式的构造函数,这大概就是其寄生的含义。

7.稳妥构造函数


所谓稳妥对象,指的是一些安全环境中(禁用this和new)构造函数的实例对象的名称。可使用工厂模式符合这一要求,工作中几乎很难用到,所以不展开介绍了。

(完)转载需注明:前端唐小胖