ES6的类Class基础知识点

原始生成实例对象的方法是通过构造函数:

function Person(name, age) {
  this.name = name;
  this.age = age
}
Person.prototype.sayName = function () {
  console.log(this.name);
}
var person = new Person('wang', 18);
person.sayName();

  

ES6引入了类的概念,通过class关键字用来定义类。
// es6
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name)
  }
}

  

说明:constructor() {} 为类的构造方法。而this代表实例对象。  constructor之外用来定义方法。注意:方法之间不需要用逗号或是分号分隔。
 
实际上,Person就是就是构造函数的语法糖:
console.log(typeof Person);  // function
console.log(Person === Person.prototype.constructor);  // true

  

 
注:所有类的自身属性,都定义在constructor里,在constructor之外定义的方法,都是在类的prototype上;
 
和es5的构造函数类的区别:
1. es5的prototype中的属性和方法是可枚举(enumerable)的:
console.log(Object.keys(Person.prototype)); // ["sayName", "name"]

  

    es6的prototype中的属性和方法是不可枚举的(non-enumerable)的:
console.log(Object.keys(Person.prototype)); // []

  

2.es6的类的内部,默认使用严格模式,es5不是
3.es6必须使用new调用,否则会报错;es5可以直接用
4.es6不存在变量提升(let变量声明也不会提升),es5因为是普通的函数,所以存在变量提升
 
 
(hasOwnProperty可以用来判断类的属性是否在类自身还是类的原型上)
(由于__proto__并不是js本身拥有,而是浏览器单方面实现的私有属性,因此不建议在开发中使用,替代方法为:Object.getPrototypeOf(person),参数为实例对象,可以得到实例对象的类的原型:
console.log(Object.getPrototypeOf(person) === Person.prototype) // true

  

)
 
注:es6有提案,用来实现私有属性和方法(现有能用的方法是加下划线表示私有属性和方法,或者使用Symbol生成独特的仅供内部使用的属性和方法),即在属性或方法之前加#;
 
this指向问题:
es6类的方法内部如果含有this,则默认指向类的实例。但是,如果将类的方法提取出来用,this将会指向其运行的环境,而不是类的实例。解决方法就是绑定this(.bind(this))或是使用箭头函数;
例如:
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name)
  }
}
var person = new Person('wang', 12);
person.say();
var { say } = person;
say();  // Uncaught TypeError: Cannot read property 'name' of undefined

  

 
Class的取值(getter)和存值(setter):
与es5一样,在类的内部可以使用get,set关键字,对某个属性设置存值函数和取值函数,在相应操作中会执行get或set函数:
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    // console.log(this.name)
  }
  get name() {
    console.log('get name');
  }
  set name(value) {
    console.log('set name = ' + value)
  }
}
var person = new Person('wang', 12);
person.name = 'wangpei';  // set name = wangpei
person.name;  // get name
 
Class可以在内部使用Generator方法
在某个方法之前加上*,就表示该方法是一个Generator函数
 
Class的静态方法
说明:即只能通过类本身来调用,不能在实例化对象中调用。但是可以被子类继承,子类同样不能在实例中调用
注意:和私有方法(即只能在类内部调用,不能通过类本身调用,例如在类的内部定义var a = 1;这个变量a就只能在类内部调用,外部无法使用)的概念区别
注:在es5中,静态方法是直接定义在构造函数自身的方法:
Person.write = function(){}

  

es6中:需要通过static关键字定义,表示该方法不会被实例继承,但会被子类继承;
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    // console.log(this.name)
  }
  static write() {
    console.log(this.name);
  }
  get name() {
    console.log('get name');
  }
  set name(value) {
    console.log('set name = ' + value)
  }
}
var person = new Person('wang', 12);
Person.write();  // Person
person.write();  // Uncaught TypeError: person.write is not a function

  

注意:静态方法内部无法访问到类的实例对象的属性和方法。只能访问到类本身的属性和方法
注意:es6规定,Class的内部只有静态方法,没有静态属性,即类的内部不能通过static为属性定义值(static name: 'wang')
 
Class的实例属性:
Class的实例属性也可以写在constructor方法外部,相当于在constructor内部写入this.xxx = xxx;
例: 
class Person {
  sex=33;
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}
var person = new Person('wang', 12);
console.log(person.sex);  // 33

  

注:这个特性是es7的新提案。目前需babel转码器支持,浏览器暂不支持
 
构造函数(非Class)独有的命令:new.target
new.target 命令一般用于构造函数中,用于判断构造函数是怎么调用的;如果构造函数不是通过new调用的,会返回undefined,否则会返回构造函数:如果有子类继承父类,它总是返回子类构造函数;利用这个特性,可以写出不能独立使用,必须继承后才能使用的类(构造函数)
注:只能在构造函数内部使用,不能在构造函数原型上使用(undefined)
例如:
function Person(name, age) {
  console.log(new.target);  // Person函数本身
  this.name = name;
  this.age = age
}
Person.prototype.sayName = function () {
  console.log(new.target);
}
var person = new Person('wang', 18);
person.sayName();  // undefined

  

 

Class的继承

语法:
// es6
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name);
    // console.log(this.name)
  }
  static write() {
    console.log(this.write);
  }
}
var person = new Person('wang', 12);
person.say();
class Student extends Person {
}
var student = new Student('yao', 24);
student.say(); // yao
Student.write(); // write函数本身

  

注:子类的内部如无constructor,则默认会添加constructor()和super()函数;
如子类定义了constructor,则必须同时定义super()方法(否则报错),因为子类没有自己的this对象,而是靠父类继承的this对象,再对其进行加工。如果不调用super方法,子类就得不到this对象;
由此可以理解,super是用来返回父类的实例对象的,然后才能在父类的实例上进行加工,从而构建子类的实例。
因此,子类使用this时,必须在super()之后,此时子类才会拥有父类实例的this对象
例如:
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name);
    // console.log(this.name)
  }
  static write() {
    console.log(this.write);
  }
}
var person = new Person('wang', 12);
person.say();
class Student extends Person {
  constructor() {
  }
}
var student = new Student('yao', 24);  // Uncaught ReferenceError: this is not defined

  

值得注意的是:
super()虽然返回的是父类的实例对象,但是子类中的this指向的仍是子类,而不是父类。super()相当于Person.prototype.constructor.call(this)
 
super也可以作为对象来使用:(作为函数使用时,只能在constructor()方法中使用)
在普通方法中,super指向父类的原型对象,在静态方法中,super指向父类
例如:
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name);
    // console.log(this.name)
  }
  static write() {
    console.log(this.write);
  }
}
class Student extends Person {
  constructor() {
    super();
    this.class = 1
    console.log(super.say)
  }
  run() {
    console.log(super.say)
  }
}
var student = new Student('yao', 24);
student.run(); // function say(){...}

  

注:作为对象时,不能直接使用super对象,只能使用super上的属性或方法(否则报错:Uncaught SyntaxError: 'super' keyword unexpected here);因为super指向的是父类的原型对象,因此可以取得父类的原型上的属性或方法,但不能取到父类实例的属性或是方法;
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name);
    // console.log(this.name)
  }
  static write() {
    console.log(this.write);
  }
}
class Student extends Person {
  constructor() {
    super();
    this.class = 1
  }
  run() {
    console.log(super.say);  // function say(){...}
    console.log(super);  // 报错
    console.log(super.name);  // undefined
  }
  static eat() {
    console.log(super.name);  // Person
  }
}
var student = new Student('yao', 24);
student.run();
Student.eat();

  

**注意:es6规定,super()虽然指向的是父类的原型或实例,但是this指向的却是子类,如下:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name);
  }
}
class Student extends Person {
  constructor() {
    super();
    this.class = 1
    this.name = 'dong'
  }
  run() {
    console.log(super.say());  // dong
  }
}
var student = new Student('yao', 24);
student.run();

  

 
子类获取父类的方法:
Object.getPrototypeOf(subClass);  // 父类
 
类的prototype属性和__proto__属性
__proto__是指向父类的指针
(1)子类的__proto__,表示构造函数的继承,指向父类
(2)子类prototype属性的 __proto__属性,表示方法的继承,总是指向父类的prototype属性
(3)子类原型的原型,是父类的原型,即子类的__proto__属性的__proto__属性,指向父类实例的__proto__属性。
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
  }
}
class Student extends Person {
  constructor() {
    super();
  }
}
console.log(Student.__proto__ === Person);  // true
console.log(Student.prototype === Person);  // false
console.log(Student.prototype.__proto__ === Person.prototype);  // true

  

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2017-08-16 15:51  汪培  阅读(791)  评论(0编辑  收藏  举报