class继承

类的定义

  • ES6中的class可看作是一个语法糖。 只是为了更像面向对象编程的语法而已。让对象的原型的写法更清晰。
  • constructor方法、doThing方法都是定义在F.prototype对象上的。只不过constructor方法只允许通过new F() 来调用
  • constructor方法是默认的方法。new生成对象实例时,自动调用。一个类中必须有该方法。没有的话,一个空的constructor方法被默认添加
  • 且constructor方法默认返回实例对象(即this)。
  • 类内部的所有定义方法,都是不可枚举的。(这里与es5有区别。)
		function F(name, age) {
            this.name = name
            this.age = age
        }
        F.prototype.doThing = function() {
            console.log('something')
        }

        let f = new F('fjh', 20)
        console.log(f)
        f.doThing()
	
	// 对比于 es6的class
	
	    class F {
                constructor(name, age) {
                    this.name = name
                    this.age = age
                }
                doThing() {
                    console.log('something')
                }
            }
            let f = new F('fjh', 20)
            console.log(f)
            f.doThing()


在这里插入图片描述

	class F {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            doThing() {
                console.log('something')
            }
        }
        console.log(Object.keys(F.prototype))
        // [] 
        console.log(Object.getOwnPropertyNames(F.prototype)
        // ['constructor','doThing']

		function F(name, age) {
            this.name = name
            this.age = age
        }
        F.prototype.doThing = function() {
            console.log('something')
        }
        console.log(Object.keys(F.prototype))
        //['doThing']
        console.log(Object.getOwnPropertyNames(F.prototype))
        //['constructor','doThing']
  • 实例对象的__proto__指向当前实例对象的构造函数的原型对象。
  • 这里使用的getPrototypeOf() 。可以引申到原型链。(注意该方法获得的是__proto__所指向的)
	class F {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
        }
        let f = new F('fjh', 20)
        Object.getPrototypeOf(f).doThing = function() {
            console.log('something')
        }
        f.doThing()
        //something 
        /* 相当于f.__proto__.doThing = functiong(){}
        只不过__proto__是私有属性。不推荐对其操作。
        */
  • 取值函数(getter)和存值函数(seter):
    • 对某个属性设置。 这样访问时就可以拦截该属性的存取行为。
    • 且这两个函数是定义在属性的 Descriptor 对象上. 通过Object.getOwnPropertyDescriptor()可以获取到。
	class F {
            get name() {
                return 'fjh'
            }
            set name(value) {
                console.log('set---' + value)
                name = value
                /*
					这里不能用this.name = value 。否则会造成栈空间溢出。因为其相当于在set方法内调用了set方法。
				*/
            }
        }
        let f = new F()
        console.log(f.name)
        f.name = 'fff'
        
        /*
        	fjh
        	set---fff
        */ 
        
  • 属性表达式
    • 可以用作定义对象的可迭代属性。十分灵活。
	let func = 'doThing'
        class F {
            [func]() {
                console.log('something')
            }
        }
        let f = new F()
        f.doThing()
       	//something
   
   //通过Generator方法。可以和之前的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
	 */
}
  • this的指向
    • 类的方法内部的this。 默认指向类的实例 。但是this的指向可能会改变。
	class F {
            f() {
                console.log(this)
            }
        }
        let f1 = new F()
        let {
            f
        } = f1
        f()
        //undefined
        /*
        	这里的f函数是通过对象解构获得的。而class内部是严格模式,所以在调用f函数会输出undefined
        可以通过bind函数绑定this 或 使用箭头函数(箭头函数没有this,其this时函数定义时所处的上下文对象!!!)
        */
     
  • 静态方法(这个涉及到继承)

    • 在方法前,加上static关键词表示是个静态方法。不会被实例所继承。而是通过类来调用。
    • 且其this的指向,指向的是类,而不是实例。
    • 父类的静态方法可以被子类继承
  • 静态属性(类本身的属性,而不是实例对象上的属性)

    • 一种写法: 类名.属性名 = ‘…’ (在类外定义)
    • 另一种写法: static 属性名=‘…’ (在类中定义)
  • 私有方法和私有属性:

    • 只能在类的内部访问的方法和属性。外部不能访问。有利于封装:
      • 第一种:前加下划线(’ _ ') 。 表示只限于内部使用。 但是在类的外部还是可以调用的。
      • 第二种: 将私有方法移出类 。 因为类内部的所有方法都是对外可见的。 (在类内部使用call方法去调用)
      • 第三种:使用Symbol的唯一性。 将私有方法的名字命名为一个Symbol值
  • new.target

    • 属性一般用在构造函数中(用在函数外会报错) 。 返回new命令作用于的那个构造函数。确定构造函数如何调用的。
    • 子类继承父类时,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);  // 正确

class的继承

有两条原型链:
	一条是子类继承了父类的静态属性和方法。
	另一条是子类的实例继承了父类的原型上的方法。
	class F {
            static a = 1
            static b = () => console.log('b')
            c() {
                console.log('c')
            }
        }
        class S extends F {

        }
        let s = new S()
        console.log(S.a)	// 1
        console.log(s.a)	// undefined
        S.b()				//b
        s.b()				//报错。s.b is not a function
        S.c()				//报错。S.c is not a function
        s.c()				// c
  • 通过extends关键词实现继承 . 还是通过原型去理解。(每个对象都有__proto__属性,__proto__指向对应的构造函数的prototype属性 。 而构造函数对象在此基础上还有prototype属性。)
    • 子类的__proto__属性,表示构造函数的继承,总是指向父类。
    • 子类的prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性.
	class F {}
    class S extends F {}
    console.log(S.__proto__ === F)
   	console.log(S.prototype.__proto__ === F.prototype)
   	console.log(S.constructor === Function)
	/*
		true
		true
		true
	*/
  • 上述过程的模式实现:
	//S的实例继承F的实例
	Object.setPrototypeOf(S.prototype,F.prototype)
	//B继承A的静态属性
	Object.setPrototype(S,F) 
  • 子类必须在constructor方法中调用super方法。否则子类就得不到this对象。
    • es5中的继承:先创建子类的实例对象this。再将父类的方法添加到this上(father.apply(this)).
    • es6中的继承:先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this.
	function F(name) {
         this.name = name
    }

    function S(name, age) {
         F.call(this, name)
         this.age = age
    }

	class F {
        constructor(name) {
            this.name = name
        }
     }
     class S extends F {
        constructor(name, age) {
             super(name)
             this.age = age
         }
      }
      /*
		结合上面的话。 意思是通过super方法来获得this对象,然后才在this对象上定义子类实例自身的属性。
	  */
	  Object.getPrototypeOf(S) === F
	  	// true
	class Point {
	  constructor(x, y) {
	    this.x = x;
	    this.y = y;
	  }
	}
	
	class ColorPoint extends Point {
	  constructor(x, y, color) {
	    this.color = color; // ReferenceError
	    super(x, y);
	    this.color = color; // 正确
	  }
}
  • super关键词
    • 可以当函数使用,也可当对象使用。
    • 函数使用:代表父类的构造函数
	class F {}

	class S extends F {
	  constructor() {
	    super();
	  }
	}
	/*
		就和前面的类中方法定义串起来了。
		super() 等同于 F.prototype.constructor.call(this)
		this指向的是S的实例。
	*/
  • 当对象使用:
    • 普通方法中,指向父类的原型对象 ; 静态方法中,指向父类。
    • this指向
      • 子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例
      • 子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

阮一峰es6入门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值