从零开始学_JavaScript_系列(60)——class(1)基本概念
来源:互联网 发布:守望先锋的性能数据 编辑:程序博客网 时间:2024/06/05 02:06
1、概念
1.1、前注
如果对c++之类语言比较熟悉的话,上手class的难度会很低,比上手原型链的难度低不少。
目前来看,class的实现,依然是通过构造函数和原型链来实现的(相当于是一个语法糖,至少目前是这样的),而在Babel转码之后,也是通过构造函数和原型链来做到的。
这里对于较为简单的内容,不会费太多笔墨,如果某些地方不太明白,可以看阮一峰的博文。
1.2、基本形态
为了帮助理解,写一个构造函数版,和一个class版进行对比。
function Foo() { this.count = 0}Foo.prototype.add = function () { this.count++}let m = new Foo()
class Foo { constructor() { this.count = 0 } add() { this.count++ }}let m = new Foo()
二者的效果是一致的。
1.3、一些需要注意的地方
1、class的关键字是全部小写;
class Foo
2、class在定义的时候,每个属性都需要是函数,而不能是变量(如add)(不适用setter和getter来实现变量的情况下);
class Foo { count:0 // error!报错!}
3、如果需要创建变量,那么应放在constructor函数(构造函数)里声明);
constructor() { this.count = 0}
4、构造函数里可以执行其本身的方法(例如上面的add()),通过this.add()
这样来调用;
constructor() { this.count = 0 this.add()}
5、this指向class本身,在new出来之后,是实例本身;
// this当然指向自己啦
6、class声明的类,不能直接调用,例如Foo()
这样是会报错的,只能通过new关键字来生成实例,如new Foo()
;
Foo(); //Uncaught TypeError: Class constructor Foo cannot be invoked without 'new'
7、类的不同方法之间,不应用逗号来分隔,如果有,则会报错;
constructor() { this.count = 0} // 这里没有逗号add() { this.count++}
8、constructor就相当于类的构造函数,在new出来的实例的时候会被调用一次;
// 感觉这句话是废话
9、类有prototype属性,而constructor挂在在prototype属性下,并且类的constructor属性指向类本身;
Foo.prototype.constructor === Foo //true
1.4、不可枚举
es5给属性加了三个特性,分别是:writable、enumerable、configurable。表示能否可写,能否枚举,能否可设置。
class声明的方法,默认是可写、不可枚举、可设置的
Object.getOwnPropertyDescriptors()
方法可以获取对象的三个属性的值。
Object.getOwnPropertyDescriptors(Foo.prototype);// add: {writable: true, enumerable: false, configurable: true, value: ƒ}// constructor: {writable: true, enumerable: false, configurable: true, value: ƒ}
而class new出来的实例,三个属性都为true
Object.getOwnPropertyDescriptors(new Foo())// count: {value: 0, writable: true, enumerable: true, configurable: true}
相对而言,通过原型链声明的方法,或者通过构造函数声明的方法,他们当然是可以枚举的啦。
版本 声明的方法是否可枚举 es6之前版本 可枚举 class声明的方法 不可枚举1.5、使用表达式作为属性名
class的方法名可以直接使用表达式
let name = 'abc'class Foo { [name]() { console.log('abc') }}let m = new Foo()m.abc()// abc
1.6、严格模式
class的类和模块是严格模式,自带的。
例如
class Foo { constructor() { a = 1 }}new Foo()// Uncaught ReferenceError: a is not defined
对未声明的变量直接赋值,于是报错了。
1.7、constructor
constructor函数是class的构造函数,如果没有就会自己加一个空的。
因为是构造函数,所以会被自动执行。
如果constructor没有返回值,那么默认返回的是创造出来的实例本身。
如果constructor有返回值,那么该返回值只能是对象或者undefined。
1、当是undefined时,当做无返回值来处理;
2、当是对象时,那么返回值就是这个对象,而不是class的实例;
class Foo { constructor() { return {} } test() { console.log('test') }}let p = new Foo()p.test; // undefined
话说应该不会有人无聊的在constructor里返回一个其他对象吧……
当new Foo的时候,传的参数,会被作为constructor的参数。
class Foo { constructor(...args) { console.log(...args) }}new Foo(1, 2, 3) //1 2 3
2、实例和原型链上的属性
2.1、不同的位置
在constructor函数里,通过this.foo这种方式声明的变量,在创建出实例后,属于实例的属性。
而在class上声明的属性(各种函数),在创建实例后,位于实例的原型链上(实例的proto属性的属性)
class Foo { constructor() { this.a = 1; } test() { }}let p = new Foo()Object.keys(p); // ["a"]p; // {a: 1}p.__proto__; // {constructor: class Foo, test: ƒ test()}
2.2、通过原型链,给类添加新的方法
假如我声明了一个Foo类,现在又想给Foo类添加一个新的方法怎么办?
通过原型链来实现即可,如果熟悉原型链,一看就明白
class Foo { constructor() { this.a = 1; } getA() { return this.a }}Foo.prototype.log = function () { console.log(this.a)}let p = new Foo()p.getA(); // 1p.log(); // 1
3、一些细节
3.1、class表达式
之前都是声明式写法。
当然还有,表达式写法(赋值式):
标准写法如下:
let Foo = class { constructor() { console.log("Foo") }}new Foo() // "Foo"
class后面也可以跟类名,但该类名仅限于该class内使用
let Bar = class bar1 { //这个bar1只能在class里面用 constructor() { console.log(bar1.name) }}Bar.prototype.log = function () { console.log(bar1.name) //在这里用会报错}let p = new Bar() //bar1bar1; // Uncaught ReferenceError: bar1 is not definedp.log(); // Uncaught ReferenceError: bar1 is not defined
3.2、类无法实现变量提升
普通函数作为构造函数的时候,如果是声明式,那么是可以变量提升的(因为是函数)
只有赋值式(函数表达式)之类的,才是不行的。
new Bar() // "1"function Bar() { console.log('1')}
但是class无论哪种都不行。
new Foo() //Uncaught ReferenceError: Foo is not definedclass Foo { constructor() { console.log("Foo") }}
原因是继承的时候,肯定要先声明父类,子类再通过继承父类来生成实例。
如果存在变量提升,那么假如父类不是class,那么子类被提升到作用域顶部,但继承的时候会发现父类还没有声明,岂不是尴尬。
- 从零开始学_JavaScript_系列(60)——class(1)基本概念
- 从零开始学_JavaScript_系列(64)——class的继承(1)基本概念、继承构造函数和class
- 从零开始学_JavaScript_系列(53)——Generator函数(1)基本概念和示例
- 从零开始学_JavaScript_系列(61)——class(2)私有方法、this
- 从零开始学_JavaScript_系列(62)——class(3)setter和getter、Generator、async函数
- 从零开始学_JavaScript_系列(63)——class(4)静态方法和new.target
- 从零开始学_JavaScript_系列(30)——NodeList
- 从零开始学_JavaScript_系列(32)——事件广播
- 从零开始学_JavaScript_系列(43)——Symbol简述
- 从零开始学_JavaScript_系列(47)——Reflect
- 从零开始学_JavaScript_系列(58)——Thunk函数
- 从零开始学_JavaScript_系列(59)——async函数
- 从零开始学_JavaScript_系列(65)——class的继承(2)super、extends与多重继承
- 从零开始学_JavaScript_系列(27)——myblog的优化【1】样式表分离、localStorage
- 从零开始学_JavaScript_系列(48)——Promise(1)基础知识
- 从零开始学_JavaScript_系列(15)——js系列<3>(转为字符串,截取字符串)
- 从零开始学_JavaScript_系列(16)——js系列<5>(正则表达式)
- 从零开始学_JavaScript_系列(19)——js系列<6>闭包
- PHP(冒泡,快速,选择,插入)排序
- 滚动条消息(WM_VSCROLL和WM_HSCROLL)
- 今天开始写博客啦~
- IE8及以下版本,兼容rgba颜色的半透明背景
- spring容器和springMVC容器
- 从零开始学_JavaScript_系列(60)——class(1)基本概念
- RPC框架简介
- oracle表字段删除问题
- Coding Index
- easyui的datebox只显示到年月【转载】
- rem与px的转换
- SSH进阶之路,写的很细很好理解
- C# 中的委托和事件
- nyoj42 一笔画问题(欧拉图、dfs)