前端值得一探究竟特辑 --- Class及面向对象

ES5面向对象

使用传统的构造函数。通过call和apply将父类的constructor赋值到子类上,再对父类的原型进行遍历赋值到子类原型上。

	function Person(name){
	    this.name = name;
	}
	Person.prototype.showName = function(){
	    console.log(this.name);
	}
	Person.prototype.reName = function(newName){
	    this.name = newName;
	}
	
	function Chinese(){
		Person.call(this);
	}

ES6面向对象

1. ES6 Class语法

为什么ES6要提出Class?
其实ES6并没有真正的将js变成了面向对象的语言,而是提供了新的一套语法糖,让开发者更像在写面向对象语言。
其次,在ES5中虽然也能够写继承,但是行为与内置对象表现并不一致,ES6的继承是进一步向内置对象靠齐。

ES6 Class语法说明:

  1. 声明的类,其实还是构造函数constructor。如果不写constructor将会默认添加一个constructor。
  2. 不能重复声明,会报错,相当于声明一个变量与let、const类似。
  3. 与ES5的构造函数行为一致,但原型里的方法不可以枚举Object.keys(Person.prototype)
  4. 共享属性写在constructor里,实例私有。
  5. 共享方法直接写在类里,会自动放到prototype中。
  6. 私有属性只能在class块之外,使用点操作符添加,可以被子类继承。
  7. 私有方法,在共享方法前添加static关键字,可以被子类继承。
    class Person {
        constructor(name) {
            this.name = name; // 共享属性,为实例私有
        }
        // 共享方法,其实跟Person.prototype是一样的,只是一个语法糖而已,实例通过继承得到,不可以枚举。
        showName() {
            console.log(this.name);
        }
        static goHome(){ // 私有方法,这里面的this是指向Class的,而不是实例
            console.log(`回${this.address}啦~`)
        }
    }
   	Person.address = '自贡'; // 私有属性
   	
test: 
	Person.address // 自贡
	Person.goHome(); // 回自贡啦~
	
	const liz = new Person('liz');
	liz.address // undefined
	liz.goHome(); // liz.goHome is not a function
2. 什么是new.target

new.target指向当前正在执行的函数,用于检查此类必须通过继承才能使用。

	class Person{
		constructor(){
			if(new.target === Person){
				throw error('Person不允许实例化,请继承后再使用');
			}
		}
	}
3. Class的get与set

公共属性的值进行便捷的获取和设置

	class Person {
	     constructor() {
	     }
	     get address(){
	         return '自贡'
	     }
	     set address(value){
	     	console.log(`你设置了地址${value}`);
	     }
	 }
	
	const liz = new Person('liz');
	console.log(liz.address) ; // 自贡
	liz.address = '成都'; // 你设置了地址成都
4. ES6 的继承

使用extends关键字进行继承。
使用super引入父类的构造函数,并将this指向子类,Father.prototype.constructor.call(this)

这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

如果父类构造函数有参数,还需要在super中传递进去。
子类依旧可以调用父类的私有方法

    class Chinese extends Person{
        constructor(...arg){ // 收集参数
            super(...arg); // 解构参数
        }
    }
5. super的使用
super方法

只能在子类的构造函数中调用,引入父类的构造函数,并将this指向子类,等同于Father.prototype.constructor.call(this)
注: new.target指向当前正在执行的函数。

	class Person {
	  constructor() {
	    console.log(new.target.name);
	  }
	}
	class Chinese  extends Person {
	  constructor() {
	    super();
	  }
	}
	new Person() // Person
	new Chinese () // Chinese 
super对象
  1. 在普通函数中调用
    super指向父类的原型对象,只能调用原型里的东西。
    如果调用的是方法,方法里的this指向子类的实例,如果在子类找不到此属性将会进行原型链搜索
    如果使用super去操作属性,此时super指向子类实例。这里实际实验与书本有误。

     class Person {
       constructor() {
              this.x = '父类的共享属性x'; 
          }
          showX() {
              console.log('父类的共享方法,且弹出', this.x);
          }
      }
      Person.x = '父类的私有属性x';
      Person.prototype.x = '父类的原型属性x';
    
      class Chinese extends Person {
          constructor() {
              super();
              this.x = '子类的共享属性x';
              super.showX();        // 父类的共享方法,且弹出 子类的共享属性x => 子类的原型属性x => 父类的共享属性x => 父类的原型属性x
              console.log(super.x); // 父类的原型属性x
              super.x = 'super就是this,指向子类实例';
              super.showX();  // 父类的共享方法,且弹出 super就是this,指向子类实例
          }
      }
      Chinese.x = '子类的私有属性x';
      Chinese.prototype.x = '子类的原型属性x';
      
      new Chinese(); 
      
    
  2. 在私有方法中调用
    super将指向父类,而不是父类的原型对象。方法里的this也是子类,而不是子类实例。

     class Person {
          constructor() {
              this.x = '父类的共享属性x';
          }
          static showX() {
              console.log('父类的私有方法,且弹出', this.x);
          }
      }
      Person.x = '父类的私有属性x';
      Person.prototype.x = '父类的原型属性x';
    
      class Chinese extends Person {
          constructor() {
              super();
              this.x = '子类的共享属性x';
          }
          static showX() {
              console.log('子类的私有方法,且弹出', this.x);// 子类的私有方法,且弹出 子类的私有属性x => 父类的私有属性x
              super.showX(); // 父类的私有方法,且弹出 子类的私有属性x => 父类的私有属性x
          }
      }
      Chinese.x = '子类的私有属性x';
      Chinese.prototype.x = '子类的原型属性x';
    
      Chinese.showX(); 
    
ES6里__proto__指向

ES6里为了规范化 __proto__的使用,提出了Object.setPrototypeOf(Son.prototype, Father.prototype)用于内部继承。
导致了:

  1. 子类的 __proto__指向父类,而不是Function.prototype
  2. 子类prototype属性的 __proto__表示方法的继承,指向父类的prototype。
 class Person{}
 class Chinese extends Person{
       constructor(){
           super()
       }
   }
   console.log(Chinese.prototype.__proto__ === Person.prototype) // true
   console.log(Chinese.__proto__ === Person); // true
   console.log(Chinese.__proto__ === Function.prototype) // false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值