Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class #8

Open
yangdui opened this issue May 16, 2020 · 0 comments
Open

Class #8

yangdui opened this issue May 16, 2020 · 0 comments

Comments

@yangdui
Copy link
Owner

yangdui commented May 16, 2020

Class

介绍过原型,在 ES6 中为了规范,提出了 Class ,不过还是利用的原型的机制。

Class 基本语法

ES6 提供的 Class 更像传统语言的写法

class Person {
	constructor (name, age) {
		this.name = name;
		this.age = age;
	}
	
	getName () {
		return this.name;
	}
	
	getAge () {
		return this.age;
	}
}

我们使用 prototype 也能实现

function Person (name, age) {
	this.name = name;
	this.age = age;
}

Person.prototype.getName = function () {
	return this.name;
}

Person.prototype.getAge = function () {
	return this.age;
}

实际上 Class 利用的还是 prototype 机制:constructor 就是构造函数,类的方法定义在类的 prototype 对象上面。如果我们修改了 prototype 对象,Class 实例也会改变。

class Person {
	constructor (name) {
		this.name = name;
	}
	
	getName () {
		return this.name;
	}
}

let zhang = new Person('zhang');
console.log(zhang.getName());  // "zhang"

Person.prototype.getName = function () {
	return this.name + '三';
}

console.log(zhang.getName()); // "zhang 三"

但是 Class 的 prototype 对象是不可修改的(不可变绑定)。

class Person {
	constructor (name) {
		this.name = name;
	}
	
	getName () {
		return this.name;
	}
}

let zhang = new Person('zhang');
console.log(zhang.getName());  // "zhang"

Person.prototype = {
	getName: function () {
		return this.name + '三';
	}
};

console.log(zhang.getName()); // "zhang"

constructor

constructor 是类的默认方法,也是原型继承的构造函数。类必须有 constructor 方法,如果没有显示定义,会被默认添加一个空的 constructor。

constructor 默认返回实例对象(this),但是也可以返回另外对象,这个特性和构造函数一样。

class Person {
	constructor (name) {
		this.name = name;
		
		return 1;
	}
}

let zhang = new Person('zhang');

console.log(zhang instanceof Person) // true

因为返回的不是对象,所以还是返回实例对象。

let obj = {};

class Person {
	constructor (name) {
  	this.name = name;

 		return obj;
	}
}

let zhang = new Person('zhang');

console.log(zhang instanceof Person) // false
console.log(zhang === obj); // true

constructor 方法返回的是对象,所以 new Person 对象就是返回对象 obj。

Class 表达式

Class 表达式和函数表达式相似。

let ClassExpression = class Person {
	getClassName () {
		return Person.name;
	}
};

let res = new ClassExpression();

console.log(res.getClassName()); // "Person"

和函数表达式一样,Person 只能在类内部使用。

Class 表达式还是有匿名形式。

let ClassExpression = class {
	constructor (name) {
		this.name = name;
	}
};

let res = new ClassExpression('li');

console.log(res.name); // "li"

Class 方法

Class 方法是不可枚举的。

class Person {
	constructor (name, age) {
		this.name = name;
		this.age = age;
	}
	
	getName () {
		return this.name;
	}
	
	getAge () {
		return this.age;
	}
}

for (let key in Person.prototype) {
	console.log(key); // 没值
}

可以通过 Object.assign一次性向类的 prototype 添加多个方法。

class Person {
	constructor (name, age) {
		this.name = name;
		this.age = age;
	}
}

Object.assign(Person.prototype, {
	getName: function () {
		return this.name;
	},
	getAge: function () {
		return this.age;
	}
});

let zhang = new Person('zhang', 25);

console.log(zhang.getName()); // "zhang"
console.log(zhang.getAge()); // 25

静态方法和静态属性

类的静态方法时在方法前加上 static。

class Person {
	constructor (name) {
		this.name = name;
	}
	
	getName () {
		return this.name;
	}
	
	static classMethod () {
		return 10;
	}
}

let zhang = new Person('zhang');

console.log(Person.classMethod()); // 10
console.log(zhang.classMethod()); // TypeError: zhang.classMethod is not a function

静态方法只能类自身访问,实例对象不能访问。

ES6 规定类中只有静态方法,没有静态属性。可以通过 Person.staticPro 这样添加。在 ES7 中有静态属性的提案:直接写在类中的属性就是静态属性。

class Person {
	staticPro = 1;
}

getter 和 setter 方法

在类中也有 getter 和 setter 方法。

class Person {
	get height() {
		console.log('175cm');
	}
	
	set height(value) {
		console.log(`The height is ${value}cm`);
	}
}

let zhang = new Person();

zhang.height; // "175cm"
zhang.height = 185; // "The height is 185cm"

Class 继承

Class 继承是通过关键字 extends 完成的。

class Person {
	construnctor (name, age) {
		this.name = name;
		this.age = age;
	}
	
	getName () {
		return this.name;
	}
	
	getAge () {
		return this.age;
	}
}

class Athletes extends Person {
	constructor (name, age,interest) {
		super(name, age);
		this.interest = interest;
	}
	
	getInterest () {
		return this.interest;
	}
}

需要注意的是 super。super 可以当函数使用,也可以作为对象使用。

如果有继承关系,子类必须在 constructor 调用 super 函数,此时 super 代表父类的构造函数。在调用 super 函数之后才能使用 this 关键字。

super 作为对象使用时,指向父类的原型对象

class Person {
	getResult() {
		return 1;
	}
}

class Athletes extends Person {
	constructor() {
		super();
		console.log(super.getResult());
	}
}

let zhang = new Athletes(); // 1

new.target

如果是 new 调用构造函数,那么 new.target 指向构造函数

function Foo(name) {
	if (new.target === Foo) {
		this.name = name;
	} else {
		throw new Error('必须使用 new 调用');
	}
}

在 Class 中,new.target 指向当前 Class

class Person {
	constructor(name) {
		console.log(new.target === Person);
		this.name = name;
	}
}

let zhang = new Person('zhang'); // true

但是在继承时,会返回子类

class Person {
	constructor(name) {
		console.log(new.target === Person);
    console.log(new.target === Athletes);
		this.name = name;
	}
}

class Athletes extends Person {
  constructor() {
    super('zhang');
  }
}

let zhang = new Athletes('zhang'); 
// false
// true

所以根据这个特性,写出不能实例化的 Class

class Person {
	constructor(name) {
		if (new.target === Person) {
      throw new Error('Person 不能被实例');
    }
	}
}

let zhang = new Person('zhang'); // Error: Person 不能被实例

参考资料

MDN 类

new.target

super

ES6 class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant