class
2019-10-09 本文已影响0人
田成力
Class
ES5中的类
使用ES5中的类特点:使用function
来进行模仿的
function Animal(name) {
if (!(this instanceof Animal)) {
//如果this不是自己的实例,则说明没有实例化而是直接调用的
throw new Error('不能直接调用,必须使用实例调用')
}
//实例属性:
this.name=name;
this.age=10;
}
//公共属性
Animal.prototype.eat=function(){
}
实例 & 类 & Object的关系
let dog=new Animal('张三');
dog.name//张三
name是dog实例的属性 找到name是通过this.找到的
eat是dog的共有属性 如何找到eat方法的呢?
dog中有一个属性 __proto__
用于指向Animal.prototype
而在Animal.prototype
对象中有一个constructor
指向Animal
例子:
function Animal(name) {
if (!(this instanceof Animal)) {
//如果this不是自己的实例,则说明没有实例化而是直接调用的
throw new Error('不能直接调用,必须使用实例调用')
}
//实例属性:
this.name=name;
this.age=10;
}
//公共属性
Animal.prototype.eat=function(){
console.log("in eat method");
}
let dog=new Animal('zhangsan');
console.log(dog.name);
dog.eat();
console.log(dog.__proto__===Animal.prototype);
console.log(dog.__proto__.constructor===Animal);
console.log(dog.__proto__.__proto__===Object.prototype);
类的继承
function Animal(){
this.type='哺乳动物'
}
Animal.prototype.eat=function(){
console.log("我在吃东西");
}
function Dog(name){
this.name=name;
Animal.call(this);
}
// Dog.prototype.__proto__=Animal.prototype;
// Object.setPrototypeOf(Dog.prototype, Animal.prototype)
Reflect.setPrototypeOf(Dog.prototype, Animal.prototype);
let dog=new Dog('zhangsan');
console.log(dog.name);
console.log(dog.type);
dog.eat();
//这三句话的意思是相同的:
// Dog.prototype.__proto__=Animal.prototype;
// Object.setPrototypeOf(Dog.prototype, Animal.prototype)
// Reflect.setPrototypeOf(Dog.prototype, Animal.prototype);
//原理:
//Animal.call(this);// 想要再dog中有Animal中的实例属性,只能改变this的指向
//在Dog中的this是dog 所以执行下Animal并修改Animal的this指向即可
//Dog.prototype.__proto__=Animal.prototype;
//如何才能找到Animal中的prototype中的方法呢?
//dog先找自己的实例方法发现没有eat,再通过它的__proto__找到Dog的prototype,发现还是没有eat方法
//这时我们希望 再去找Animal的prototype有误eat方法怎么办呢?
//当Dog.prototype中找不到时就会通过Dog.prototype.__proto__去找上一级,而我们改变了它的__proto__属性,让他指向Animal的prototype 这样就形成了一个原型链
继承的第二种方法
function Animal() {
this.type = '哺乳动物'
}
Animal.prototype.eat = function () {
console.log("我在吃东西");
}
function create(parentPrototype) {
function FN() {
}
FN.prototype = parentPrototype;
return new FN();
}
function Dog(name) {
this.name = name;
Animal.call(this);
Dog.prototype = Object.create(Animal.prototype, { constructor: { value: Dog } });
}
let dog = new Dog('zhangsan');
console.log(dog.name);
console.log(dog.type);
dog.eat();
Es6 Class
//这样的写法是 规范写法
class Animal {
constructor(){
this.name='name';
this.type='type';
}
}
//这种写法是es的实验性写法
//在Node环境中是不能运行的
//在webpack 需要插件的帮助转换@babel/plugin-proposal-class-properties
class Animal{
type='type';
name='name'
}
class Animal{
type="name";
getName(){
console.log(this)
}
}
let getName=new Animal().getName;
getName();//undefined ES6的规范
//只能先绑定
let animal=new Animal();
let getName=animal.getName.bind(animal);
getName();
如何将属性直接定义到原型上呢?
class Animal {
constructor(name) {
this.name = name;
}
getName(){
return name;
}
//将a属性保证成get方法
get a() {
return 1;
}
}
let animal=new Animal()
console.log(animal);
//发现: a属性不仅会出现在Animal.prototype上也会出现在Animal的实例属性上
静态属性
静态属性是直接定义到类上的属性
注意点: static age=18(静态属性)语法是ES7的语法,不能直接在Node环境中使用 需要webpack
的
@babel/plugin-proposal-class-properties
转换
但是 static getAge(){} (静态方法)是ES6的语法,可以直接使用
class Animal {
constructor(name) {
this.name = name;
}
getName() {
return name;
}
get a() {
return 1;
}
static age = 18;
}
let animal = new Animal()
console.log(animal);//打印结果发现,并没有age属性
console.log(Animal.age);//18 在类中
//如果不想使用ES7的语法但也想直接使用静态属性,如何使用呢?
class Animal{
constructor(name) {
this.name = name;
}
getName() {
return name;
}
static get age(){
return 18
}
}
ES6的继承
class Animal{
}
class Dog extends Animal{
}
//静态方法也会被这类继承
class Animal{
static flag=false;
}
class Dog extends Animail{
}
console.log(Dog.flag)//true
Super的用法
//调用父类的钩子函数
class Animal{
constructor(name){
this.name=name;
}
}
class Dog extends Animal{
constructor(name){
super(name);
}
}
//super的纸袋问题?
// super是指父类
//主动调用父类的方法(原型方法)
class Animal{
constructor(name){
this.name=name;
}
getName(){
console.log('parent getName')
}
}
class Dog extends Animal{
constructor(name){
super(name);
}
getName(){
console.log('son getName');
super.getName();//super指定的是 Animal.prototype
}
}
new的模拟
function Animal() {
this.name = "zhangsan"
this.age = 18;
}
Animal.prototype.say = function () {
console.log("in say");
}
function mockNew(parent) {
let obj = {};
Animal.call(obj);
obj.__proto__ = parent.prototype;
return obj;
}
// let dog=new Animal();
let dog = mockNew(Animal);
console.log(dog.name);
console.log(dog.age);
dog.say();
类的装饰器
//可以修饰类 也可以修饰类的属性
@type1
@type2
class Animal{
}
//1.type1 和 type2 都是函数
//function type1(Constrtuctor){}
//2.它的意思是 声明了Animal类,并且调用 type函数
//3.调用顺序是 就近调用 先调用的是type2再调用type1
装饰器的小例子:
function type(typeName) {
console.log(typeName);
return function (Constructor) {
Constructor.type=typeName;
console.log("in type inter");
}
}
function name(n) {
console.log(n);
return function (Constructor) {
Constructor.name=n;
console.log("in name inter");
}
}
@type('哺乳类')
@name('张三')
class Animal {
}
let dog=new Animal();
console.log(dog);
console.log(Animal.name);
//执行顺序是 type函数 name 函数 再执行 name的 inter 和 type的inter
使用装饰器装饰类的小例子:
//混合对象的属性和类
let obj = {
name: "zhangsan",
age: 18
}
@mixin(obj)
class Zhangsan {
}
function mixin(obj) {
return function (Constrcutor) {
Object.assign(Constrcutor.prototype, obj);
}
}
console.log(new Zhangsan().name);
使用装饰器装饰类的属性
class Circle{
@readonly type="circle"
}
//ClassPrototype: CirCle.prototype
//key: 要修饰的key
//descriptor: key的描述信息 configurable enumerable writable initializer
function readonly(ClassPrototype,key,descriptor){
console.log(descriptor);
//将该属性的writable设置为false,就不能被更改了
descriptor.writable=false;
}
let circle=new Circle();
//尝试修改type的值
circle.type="1122"
//打印出来的结果依然是circle
console.log(circle.type);
使用装饰器装饰类的方法
class Circle {
@readonly type = "circle"
@before getName() {
console.log("getName");
}
}
//ClassPrototype: CirCle.prototype
//key: 要修饰的key
//descriptor: key的描述信息 configurable enumerable writable initializer
function readonly(ClassPrototype, key, descriptor) {
console.log(descriptor);
//将该属性的writable设置为false,就不能被更改了
descriptor.writable = false;
}
function before(ClassPrototype, key, descriptor) {
//在装饰函数中descriptor.value就是函数本身,就像例子中的"getName"
let oldMethod = descriptor.value;
descriptor.value = function () {
console.log("in before method");
oldMethod();
}
}
let circle = new Circle();
//尝试修改type的值
circle.type = "1122"
//打印出来的结果依然是circle
console.log(circle.type);
circle.getName();
装饰模式
对原有的方式进行包装
既不破坏原函数的核心逻辑代码 又可以在其函数上添加逻辑
es6
的使用@
,但只能在class
上使用
let obj={
type:"人"
};
let obj2={
genor:"男"
}
@mixin(obj)
@mixin(obj2)
class Zhangsan{
}
#### EventLoop