入门新手第一次写文,如有错误理性讨论🤗🤗🤗。
思路来源于摸鱼时在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)