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

上一篇下一篇

猜你喜欢

热点阅读