function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
//上下两段代码意义一样
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
//-------------------------------------------------------------------------ES5中
var Point = function (x, y) {
// ...
};
Point.prototype.toString = function () {
// ...
};
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
1.constructor方法是类的默认方法,在使用 new 命令生成实例时会自动调用此方法,如果constructor方法没有被定义,则默认添加一个空的constructor方法
2.constructor方法默认返回实例对象(也就是this),也可以自己设置返回的对象
下面的代码设置了constructor方法的返回值,所以通过new操作符生成的对象不是Foo的实例
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false
3.其实实例属性可以不写在constructor内,也可以写在类的顶层,此时_count属性与取值函数value以及increment在同一级,不需要在前面加this。与在constructor中定义意义一样,但看起来比较简洁
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
1.不使用new操作符调用会报错
this
对象上),否则都是定义在原型上(即定义在class
上)//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.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
/*
x,y两个属性定义在this上,因此hasOwnProperty返回true
而toString方法在原型链上,因此hasOwnProperty返回false
*/
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__
//true
get
和set
关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为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'
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
/*
这个方法用于返回一个对象上的自有属性的属性描述符
这个方法接受两个参数,第一个为要查询的对象,第二个为查询对象上的属性名称
这个方法返回有两种结果,如果指定的属性在对象上存在,则返回他的属性描述符,否则返回undefined
*/
var descriptor = Object.getOwnPropertyDescriptor(
CustomHTMLElement.prototype, "html"
);
// in操作符,如果指定的属性在指定的对象或其原型链上,则返回true
"get" in descriptor // true
"set" in descriptor // true
new Foo(); // ReferenceError
class Foo {}
1.类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
2.如果在静态方法中使用到了this,这个this指向类本身,而不是实例(且静态方法与非静态方法可以重名)
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
1.有时有一些属性和方法我们希望只允许在类的内部使用,此时他们就是私有属性和私有方法
可以在属性或方法前加#,此时该属性或方法只能在类中被取到
2.私有属性和私有方法也可以被设置setter和getter,也可以在之前加static被设置为静态的私有属性和私有方法
//私有属性
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
const counter = new IncreasingCounter();
counter.#count // 报错
counter.#count = 42 // 报错
//私有方法
class Foo {
#a;
#b;
constructor(a, b) {
this.#a = a;
this.#b = b;
}
#sum() {
return this.#a + this.#b;
}
printSum() {
console.log(this.#sum());
}
}
1.有时定义的一些属性依赖于另外一个静态属性,此时可以把他放在class中的静态块中,静态块中的代码只会在创建类时执行一次,用于对静态属性进行初始化。解决了传统初始化方法中将逻辑写在类外部或将初始化代码放在constructor中每次实例化类时都会被执行一次的问题
//使用静态块
class C {
static x = ...;
static y;
static z;
static {
try {
const obj = doSomethingWith(this.x);
this.y = obj.y;
this.z = obj.z;
}
catch {
this.y = ...;
this.z = ...;
}
}
}
//不使用静态块
class C {
static x = 234;
static y;
static z;
}
try {
const obj = doSomethingWith(C.x);
C.y = obj.y
C.z = obj.z;
} catch {
C.y = ...;
C.z = ...;
}
2.静态块中不能有return语句
3.静态块中可以使用类名或this指代当前类
class C {
static x = 1;
static {
this.x; // 1
// 或者
C.x; // 1
}
}
1.new.target命令可以用于判断一个构造函数是否是被new操作符执行的。若是被new或Reflect.construct()
调用的,则它返回这个构造函数,否则返回undefined。
function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
// 另一种写法
function Person(name) {
if (new.target === Person) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三'); // 报错
2.在class内部调用new.target会返回当前的class,但若是子类继承父类时,会返回子类
class Rectangle {
constructor(length, 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, width) {
super(length, width);
}
}
var obj = new Square(3); // 输出 false
3.妙用:可以使用new.target写出只能被继承而不能被实例化的class
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); // 正确