【读书笔记】ES6 Class的基本语法
2018-12-02 06:14:11来源:博客园 阅读 ()
Class
- constructor()
- 实例对象
- 表达式
- 提升
- 私有方法和私有属性
- this
- name属性
- 取值函数和存值函数
- Generator方法
- 静态方法
- 静态属性和实例属性
- new.target属性
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ',' + this.y + ')'; } }
1.constructor是构造方法。
2.this关键字则代表实例对象。
3.定义“类”的方法的时候,前面不需要加上function关键字,直接把函数定义放进去了就可以了。
4.方法之间不需要逗号分隔。
class Point { } typeof Point // "function" Point === Point.prototype.constructor // true
5.类的数据类型就是函数,类本身就指向构造函数。
Point.protype = {
constructor() {},
toString() {},
toValue() {}
};
6.事实上,类的所有方法都定义在类的prototype属性上面。
b.constructor === B.prototype.constructor // true
7.在类的实例上调用方法,其实就是调用原型上的方法。
Object.assign(Point.prototype, {
toString() {},
toValue() {}
});
8.Object.assign方法可以很方便地一次向类添加多个方法。
Point.prototype.constructor === Point // true
9.prototype对象的constructor属性,直接指向“类”的本身。
Object.keys(Point.prototype); // [] Object.getOwnPropertyNames(Point.prototype); // ["constructor", "toString"]
10.类的内部所有定义的方法,都是不可枚举的。
let methodName = 'getArea';
class Square {
constructor(length) {
}
[methodName]() {
}
}
11.类的属性名,可以采用表达式。
类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式。
只要你的代码写在类或模块之中,就只有严格模式可用。
ES6实际上把整个语言升级到了严格模式。
constructor()
1.constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
class Point { } // 等同于 class Point { constructor() {} }
2.一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加。
class Foo { constructor() { return Object.create(null); } } new Foo() instanceof Foo // false
3.constructor方法默认返回实例对象,即this,完全可以指定返回另一个对象。
class Foo { constructor() { return Object.create(null); } } Foo(); // TypeError
4.类必须使用new调用,否则会报错。
这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
实例对象
class Point { ... } var point = Point(2, 3); // 报错 var point = new Point(2, 3); // 正确
1.生成类的实例对象的写法,与ES5完全一样,也是使用new命令。
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' +this.x + ', ' + this.y + 'y'; } } var point = new Point(2, 3); point.toString(); // (2, 3) point.hasOwnProperty('x'); // true point.hasOwnProperty('y'); // true point.hasOwnProperty('toString'); // false point._proto_.hasOwnProperty('toString'); // true
2.与ES5一样,实例的属性除非显式定义在其本身,即定义在this对象上,否则都是定义在原型上,即定义在class上。
hasOwnProperty用来检测是不是实例的属性。
var p1 = new Point(2, 3); var p2 = new Point(3, 2); p1._proto_ === p2._proto_ // ture
3.与ES5一样,类的所有实例共享一个原型对象。
var p1 = new Point(2, 3); var p2 = new Point(3, 2); p1._proto_.printName = function() { return 'Oops' }; p1.printName() // "Oops" p2.printName() // "Oops" var p3 = new Point(4,2); p3.printName() // "Oops"
4.可以通过实例的_proto_属性为“类”添加方法。
生产环境中,我们可以使用Object.getPrototypeOf方法来获取实例对象的原型。
表达式
const MyClass = class Me { getClassName() { return Me.name; } };
let inst = new MyClass(); inst.getClassName(); // Me Me.name // ReferenceError
const MyClass = class {...};
1.与函数一样,类也可以使用表达式的形式定义。
这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类。
如果类的内部没用到的话,可以省略Me。
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }('张三'); person.sayName(); // "张三"
2.采用Class表达式,可以写出立即执行的Class。
提升
new Foo(); // ReferenceError class Foo{}
1.类不存在变量提升。
{ let Foo = class {}; class Bar extends Foo {} }
2.必须保证子类在父类之后定义。
私有方法和私有属性
class Widget { // 公有方法 foo(baz) { this._bar(baz); } // 私有方法 _bar(baz) { return this.snaf = baz; } }
1.私有方法是常见需求,但ES6不提供,只能通过变通方法模拟实现。
一种做法是在命名上加以区别。
_bar方法前面的下划线,表示这是一个只限于内部使用的私有方法。
class Widget { foo(baz) { bar.call(this, baz); } ... } function bar(baz) { return this.snaf = baz; }
2.另一种方法就是索性将私有方法移除模块,因为模块内部的所有方法都是对外可见的。
const bar = Symbol('bar'); const snaf = Symbol('snaf'); export default class myClass { // 公有方法 foo(baz) { this[bar](baz); } // 私有方法 [bar](baz) { return this[snaf] = baz; } ... }
3.还有一种方法就是利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值。
this
class Logger { printName(name = 'there') { this.print(`Hello ${name}`); } print(text) { console.log(text); } } const logger = new Logger(); const { printName } = logger; printName(); // TypeError
1.类的方法内部如果含有this,它默认指向类的实例。
但是,必须非常小心,一点单独使用该方法,很可能报错。
如果将这个方法提取出来单独使用,this会指向该方法运行时所作的环境,因为找不到print方法而导致报错。
class Logger { constructor() { this.printName = this.printName.bind(this); } ... }
2.一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。
class Logger { constructor() { this.printName = (name = 'there') => { this.print(`Hellow ${name}`); }; } ... }
3.另一种解决方法是使用箭头函数。
function selfish(target) { const cache = new WeakMap(); const handler = { get(target, key) { const value = Refkect.get(target, key); if (typeof value !== 'function') { return value; } if (!cache.has(value)) { cache.set(value, value.bind(target)); } return cache.get(value); } }; const proxy = new Proxy(target, handler); return proxy } const logger = selfish(new Logger());
4.还有一种解决方法是使用Proxy,获取方法的时候,自动绑定this。
name属性
class Point {} Point.name // "Point"
1.由于本质上,ES6的类只是ES5的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。
name属性总是返回紧跟在class关键字后面的类名。
取值函数和存值函数
class MyClass { constructor() { ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: ' + value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop // 'getter'
1.与ES5一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
上面代码中,prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。
class CustomHTMLElement { constructor(element) { this.element = element; } get html() { return this.element.innerHTML; } set html(value) { this.element.innerHTML = value; } } var decriptor = Object.getOwnPropertyDescroptor(CustomHTMLElement.prototype, "html"); "get" in decriptor // true "set" in decriptor // true
2.存值函数和取值函数是设置在属性的Descriptor对象上的。
上面代码中,存值函数和取值函数是定义在html属性的描述对象上面。这与ES5完全一致。
Generator方法
class Foo { constructor(...args) { this.args = args; } * [Symbol.iterator]() { for (let arg of this.args) { yield arg; } } } for (let x of new Foo('hello', 'world')) { console.log(x); } // hello // world
1.如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。
Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。
Symbol.iterator方法返回一个Foo类的默认遍历器,for...of 循环会自动调用这个遍历器。
静态方法
class Foo { static classMethod() { return 'hello'; } } Foo.classMethod(); // "hello" var foo = new Foo(); foo.classMethod(); // TypeError
1.类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo { static bar() { this.baz(); } statoc baz() { console.log('hello'); } baz() { console.log('world'); } } Foo.bar(); // hello
2.如果静态方法包含this关键字,这个this指的是类,而不是实例。
另外,从这个例子还可以看出,静态方法可以与非静态方法重名。
class Foo { static classMethod() { return 'hello'; } } class Bar extends Foo { } Bar.classMethod(); // 'hello'
3.父类的静态方法,可以被子类继承。
class Foo { static classMethod() { return 'hello'; } } class Bar extends Foo { static classMethod() { return super.classMethod() + ', too'; } } Bar.classMehtod(); // "hello, too"
4.静态方法也是可以从super对象上调用的。
静态属性和实例属性
class Foo { } Foo.prop = 1; Foo.prop // 1
// 以下两种写法都无效 class Foo { // 写法一 prop: 2 // 写法二 static prop: 2 } Foo.prop // undefined
1.静态属性指的是Class本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
目前,只有这种写法可行,因为ES6明确规定,Class内部只有静态方法,没有静态属性。
class ReactCounter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } }
2.以前,我们定义实例属性,只能写在类的constructor方法里面。
new.target属性
function Person(name) { if (new.target !== undefined) { this.name = name; } else { throw new Eooro('必须使用new命令生成实例'); } } // 另一种写法 function Person(name) { if (new.target === Person) { this.name = name; } else { throw new Error(''); } } var person = new Person('张三'); // 正确 var notAPerson = Person.call(person, '张三'); // 报错
1.new是从构造函数生成实例对象的命令。
ES6为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。
如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
class Rectangle { constructor(lenght, width) { console.log(new.target === Rectangle); this.length = length; this.width = width; } } var obj = new Rectangle(3, 4); // true
class Rectangle { constructor(length, width) { console.log(new.target === Rectangle); ... } } class Square extends Rectangle { constructor(length) { super(legnth, length); } } var obj = new Square(3); // false
2.Class内部调用new.target,返回当前Class。
需要注意的是,子类继承父类时,new.target会返回子类。
class Shape { constructor() { if (new.target === Shape) { throw new Error('本类不能实例化'); } } } class Rectangle extends Shape { constructor(length, width) { super(); ... } } var x = new Shape(); // 报错 var y = new Rectangle(3, 4); // 正确
3.利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。
注意,在函数外部,使用new.target会报错。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- Jquery插件写法笔记整理 2020-03-29
- 详解Webstorm 新建.vue文件支持高亮vue语法和es6语法 2020-02-07
- manifest.json 解析--手机web app开发笔记(三-2) 2019-08-14
- es6学习笔记(二) 2019-08-14
- 前端笔记之微信小程序(四)WebSocket&Socket.io&am 2019-08-14
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash