背景
最近睡前习惯翻会书,重温了《JavaScript权威指南》。这本书,文字小,内容多。两年了,我才翻到第十章。因为书太厚,平时都充当电脑支架。
JavaScript 类
话说当年类、原型、继承,差点给我绕晕。
在JavaScript中,类使用基于原型的继承。如果两个对象从同一个原型继承属性(通常是以函数作为值的属性,或者方法),那我们说这些对象是同一个类的实例。
可能是秋季的清爽,又或者藕粉的甜蜜,这次我感觉自己开「神行千里」的buff,快速读完整章,丝滑多了。
构造函数和new.target
先来介绍一下new.target(MDN文档)。
new.target表达式可以判断函数是否作为构造函数被调用了。
- 如果new.target的值是undefined,表示函数是作为普通函数被调用的,没有使用new关键字;
- 如果new.target的值有定义,说明函数是作为构造函数,通过new关键字调用的。
注:使用class关键字创建的类不允许不适用new调用它们的构造函数,所以从根本上class创建的类用不到new.target。
所以我的检查功能库了又多了一条记录。
function Foo() {
if (!new.target) throw 'Foo() must be called with new';
console.log('Foo instantiated with new');
}
Foo(); // => throw 'Foo() must be called with new'
new Foo(); // => Foo instantiated with new
构造函数、原型、实例的关系
先来看一段代码
function Range(from, to) {
this.from = from;
this.to = to;
}
Range.prototype = {
constructor: Range,
includes: function (x) {
return x >= this.from && x <= this.to;
},
toString: function () {
return '(' + this.from + '~' + this.to + ')';
},
};
let r1 = new Range(2, 5);
let r2 = new Range(10, 15);
console.log(r1.includes(3)); // => true
console.log(r2.toString()); // => (10~15)
console.log(r1.constructor === Range); // => true
关系图如下:
私有字段
如果想在类实例上定义字段,必须在构造函数或某个方法中定义。可定义三种类型字段,公有、私有和静态字段。
其中,定义私有字段的方式蛮有意思的。如果在字段前面加上#(通常不是合法的JavaScript标识符字符),则该字段就只能是类体中(带着#前缀)使用,对类体外部的任何代码都不可见、不可访问(因此无法被修改)。如下代码就定义了一个私有字段name,该字段通过goodName函数只允许被外部读取。
class GetGood {
#name = '背包';
goodName = function () {
return this.#name;
};
}
let g1 = new GetGood();
console.log(g1.goodName());
委托而不是继承
如果当前要写的类中,有部分行为和另一个类相似,一种方式是通过创建子类来继承行为。还有一种方式是在要写的类中创建另一个类的实例,并在需要时委托该实例去实现想要的功能,更方便也更灵活。
这种委托策略常常被称为“组合”,也是面向对象编程领域奉行的一个准则,即开发者应该“能组合就不继承”。
总结
总结一下类的知识点,ES6新增了class关键字来定义类,但是底层仍然是构造函数和原型机制在起作用。
今天也特别有收获的一天。
我发现我总是在不同的时间段反复爱上JavaScript,偶尔翻出来珍藏的技术书,都能或多或少的有点收获。
这次的宝藏图书《JavaScript权威指南》,也是一样,原来觉得枯燥的章节,最近读起来也很丝滑,比绸缎还丝滑。
关于作者
非职业「传道授业解惑」的程序媛叶一一,欢迎来稀土掘金关注我。
作者:叶一一
作者简介:「趣学前端」、「CSS畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
文章来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。