JavaScript学习笔记 || 使用prototype还是constructor添加成员函数

1,190 阅读3分钟

入门新手第一次写文,如有错误理性讨论🤗🤗🤗。

思路来源于摸鱼时在stack overflow上看到的一个问题:使用prototype和在constructor中添加成员函数,哪一个更好(javascript - Functions inside constructor vs prototype - Stack Overflow)在掘金搜了一下好像没人写这个,于是便捡过来,简简单单做个翻译+学习笔记。

首先看一个典中典:无论你是在线下还是b站还是p站学js,我们应该都知道,为一个对象添加属性可以有下面两种方式:

var Person = function() {
  this.firstName = null;
  this.lastName = null;
  
  this.fullName1 = function() {
    return this.firstName + this.lastName;
  };
};

Person.prototype.fullName2 = function() {
  return this.firstName + this.lastName;
}

在上面的代码中,fullName1与fullName2都可以添加fullName方法,在调用时似乎也不会感觉有什么不同,那么,这两种方法的区别在哪呢。

首先,使用this在对象体内声明函数,好处就是作用域清晰,this指向明了。成员函数自己就在对象的局部作用域内,构造器中的各种局部参数、函数啥的随便嚯嚯。

对于使用prototype的方法,好处就是代码职责明确,可读性强,省空间、效率高。

1.原理

针对上文的代码,我们可以有

   let a = new Person();
   let b = new Person();

   console.log(a.fullName1 == b.fullName1); // false
   console.log(a.fullName2 == b.fullName2); // true
   console.log(a.fullName2 == Person.prototype.fullName2) //true

可以看到,对于使用this创造的成员,在每次对对象进行实例化的时候也会被一起实例化,所以两个对象的fullName1()是由两个对象分别实例化出来的,并不相等。

但可以看到使用prototype时,两个实例中的函数fullName2()是相等的,因为使用prototype创建成员函数时,只会创造一次,调用时,所有实例都会共享原型上的这个函数。 这也就是上文所说使用prototype时,效率更高,性能更好的原因。(不过在极其先进、性能无敌的V8引擎中,通常对象数量得到1e3/4这个数量级时,两种方法的性能才会有比较明显的差距)

2.建议

说了这么多,在日常使用时,哪一种会比较好呢

通常情况下,使用prototype更好:(1)使用prototype可以避免你的constructor过于臃肿、庞大,如果把什么东西都塞到constructor里,代码可读性,关注点分离度都会下降,这都是我们在开发时应该注意避免的事; (2)上文提到过的一点:效率。使用prototype时,所有实例共享原型链上的函数; (3)使用prototype更方便进行对象扩展、继承(不过现在已经有class extends了)

当然,在需要时,你也可以使用第二种方法来实现对象的私有作用域,并在构造器中访问局部私有作用域,毕竟现在eslint还不支持‘用#表示私有成员’的es13新特性(虽然chrome内核已经支持,但提案现在还在stage3?),像下面的代码中的Recorder一样,想对自律的自己进行记录,同时不想让别人知道自己day0:

const Recorder = function(name) {
    this.name = name;
    let quitMasturbation = 0;
    
    this.success = function() {
        quitMasturbation++;
        alert( `${this.name},day:${quitMasturbation}`);
    };

    this.chong = function() {
        quitMasturbation = 0;
    };
};

Recorder.prototype.peek = function() {
    alert(this.name + quitMasturbation);
};

Recorder.prototype.judge = function() {
    alert(this.name + '顶级自律');
};

const me = new Recorder(WuTao)
me.success(); //day1
me.success(); //day2
me.chong();   //虽然day0,但不告诉别人
me.peek();   //Uncaught ReferenceError(不要窥探别人隐私,数据安全很重要)
me.judge();  //顶级自律

不过,最后的最后

JS现在已经添加了对Class的支持,上文所说的扩展与继承、性能优化啥的Class这个语法糖都帮我们做好了,ES13里估计也会添加对类的私有成员的支持。所以能用Class多用Class,少用prototype,所以此文仅用于帮助像我这样的新手更好的理解对象与原型链,至少在写完这篇文章以后,我自己对prototype和constructor的理解确实更深刻了,同时也鼓励各位starter,尝试自己写点东西,把一些学习收获记录下来,不要害怕,受益良多。

(封面图来源Eloquent JavaScript)