982 字
5 分钟
创建对象之构造函数模式

ES中的构造函数是用来创建特点类型的对象的,像Object和Array这样的原生构造函数,直接再环境中能用。

当然也可以自定义构造函数

就得自定义属性和方法了

定义构造函数#

例如下面是一个工厂模式的函数,将他改造成构造函数形式

function createPerson(name, age){
let o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(this.name);
}
return o;
}
// 改成构造函数
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name)
}
}
let per1 = new Person('weng', 20);
per1.sayName(); // weng

当然也可以这样定义和new构造函数

let Person = function(){
.....
}
let person1 = new Person; // 不传参可以不加括号

当然构造函数也是函数,调用方式也很多

Person('weng', '23'); // 这种也可以,但是这样构建出来的实例会被添加到window上去
window.sayName() // weng

也可以在另一个对象作用域中调用

let o = new Object();
Person.call(o, 'weng', 23);
o.sayName() // weng

这里要记住,调用一个函数没有明确的指定this的情况下(即没有作为对象的方法调用,或没有使用call或者apply转换this指向的话,this始终指向Global对象(浏览器中式window)

构造函数与工厂模式区别#

在上面例子中只是用Person()构造函数替代了createPerson()工厂函数。实际上内部是和createPerson一样的。

区别在于

  1. 没有显示的去new一个object
  2. 属性和方法都直接赋值了this
  3. 没有return

1. 以new操作符去调用构造函数#

会执行以下操作

  1. 在内存中创建一个新对象

  2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype

  3. 构造函数内部的this被赋值为这个新对象(就this指向了第一步的新对象)

  4. 执行构造函数内部代码

  5. 如果构造函数返回非空对象,则返回该对象;否者返回刚创建的新对象

    这里如果返回值是一个非空对象,那这个对象的行为会默认覆盖构造函数在第四步对创建的内存对象进行操作得出的行为,如果返回其他类型值是不会的;

    这块设计到原型和继承方面的东西,在原型继承那边文章可以看

2. 自定义构造函数确保实例被标识为特定类型#

相比于工厂模式,这是一个很大的好处

例如:

const person1 = new Person();
console.log(person1.contructor == Person); // true
console.log(person1 instanceof Person) // true;

创建出来的person1的protype会指向它的原型对象,这个原型对象里头有个属性叫做contructor是回指向Person构造函数的,可以用person.contructor和person1 instanceof Person来判断

相当于有个标识,告诉这个实例是哪个构造函数创建出来的

但是一般来说用instanceof来判断对象类型是更可靠的方式

构造函数的问题#

问题很明显

就是在实例上定义的方法会在创建每个实例的时候都会再去创建一次

let per1 = new Person();
let per2 = new Person();
console.log(per1.sayName === per2.sayName) // false

因为都是调用同一种行为的函数,可能有些时候参数不同,但是行为一样的就没必要再去创建一个

这个时候会考虑把公共函数提出去到外部

function sayName(){
console.log(this.name)
}
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = sayName;
}

这样解决了不重复创建new Function的问题,因为每个实例sayName指针都是指向外部同一个函数;

但是这样缺陷也很明显啊

全局作用域乱了,如果内部有很多方法,那不得再全局再创建多个函数

所以这时候需要原型模式,另一篇文章看

创建对象之构造函数模式
https://nollieleo.github.io/posts/创建对象之构造函数模式/
作者
翁先森
发布于
2021-05-28
许可协议
CC BY-NC-SA 4.0