A quick review of ES6 【class】【作用域】【继承】

 

目录

继承的方法

class

class的继承

super 关键字

Object.getPrototypeOf()

构造函数

new操作符原理

属性表达式

静态方法

new.target属性

super关键字(作为对象,作为函数)

存取器

把类当作接口

块级作用域

用扩展运算符代替apply方法

扩展运算符的运用


继承的方法

https://juejin.im/post/5bcb2e295188255c55472db0  原型链继承 组合继承 构造函数继承 class实现继承

class

class的继承

下面看用class实现继承的例子。注意几点:

  • 用extends关键字创建子类
  • 在子类的构造函数里访问 this的属性之前,要调用 super()
  • 用super.XX访问基类的方法、属性
  • 使用 new构造了 Student类的一个实例。 它会调用之前定义的构造函数,创建一个Student类型的新对象,并执行构造函数初始化它
class Test {
  insName: string;
  constructor(name: string) {
    this.insName = name;
    console.log(name, this.insName);
  }
}

class Person {
  insName: string;
  insAge: number;
  constructor(name: string, age: number) {
    this.insName = name;
    this.insAge = age;
    console.log("person", this.insName, name);
  }
  showName() {
    console.log("Person", this.insName, this.insAge);
  }
}

class Student extends Person {
  insGender: string;
  constructor(name: string, age: number, gender: string) {
    super(name, age); //调用父类的 constructor(name: string, age: number)
    this.insGender = gender; // 子类只有调用super()之后才可以使用this关键字
    console.log("student", this.insName, this.insAge, this.insGender, name);
    super.showName();
  }
}

  ngOnInit() {
    const test = new Test("lake");
    const student = new Student("lake", 19, "female");
  }

打印:

super 关键字

如果属性定义在父类的原型对象上,super就可以取到。

在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。

Object.getPrototypeOf()

Object.getPrototypeOf方法可以用来从子类上获取父类。

Object.getPrototypeOf(ColorPoint) === Point // true
Object.getPrototypeOf(ColorPoint) === Object //false 要直接继承的 间接的不算 

因此,可以使用这个方法判断,一个类是否继承了另一个类。


构造函数

// es5
function Keith(height) {
    this.height = height;
}

//es6
class Point {
    constructor(height) {
        this.height = height;
    }
    toString() {
        return '233'
    }
}

typeof Point // 'function'
Point === Point.prototype.constructor   // true

上面代码中,Keith就是构造函数,它提供模板,用来生成对象实例。(扩展:生成对象实例的几种方法)

构造函数的特点:

  a:构造函数的函数名的第一个字母通常大写。

  b:函数体内使用this关键字,代表所要生成的对象实例。

  c:生成对象的时候,必须使用new命令来调用构造函数。

constructor 方法是类的构造函数,是一个默认方法,通过 new 命令创建对象实例时,自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个默认的 consructor 方法会被默认添加。所以即使你没有添加构造函数,也是会有一个默认的构造函数的。一般 constructor 方法返回实例对象 this ,但是也可以指定 constructor 方法返回一个全新的对象,让返回的实例对象不是该类的实例。类的所有方法都定义在类的prototype属性上面。

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

在类的实例上调用方法,其实就是调用原型上的方法。


es6的构造函数用关键字constructor来实现。可以通过this(和java/C#一样代表对象实例的成员访问)关键字来访问当前类体中的属性和方法。 

class student{  //定义student类
  name:string;  //定义类的属性
  constructor(myname:string){ //定义构造函数
      this.name=myname;
  }
  study(){ //定义类的方法
           //定义该方法所要实现的功能
  }
}
// http://cw.hubwiz.com/card/c/55b724ab3ad79a1b05dcc26c/1/4/4/

new操作符原理

 new命令的作用,就是执行一个构造函数,并且返回一个对象实例。使用new命令时,它后面的函数调用就不是正常的调用,而是依次执行下面的步骤。

  a:创建一个空对象,作为将要返回的对象实例。

  b:将空对象的原型指向了构造函数的prototype属性。

  c:将空对象赋值给构造函数内部的this关键字。

  d:开始执行构造函数内部的代码。

也就是说,构造函数内部,this指向的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所谓构造函数,意思是这个函数的目的就是操作一个空对象(即this对象),将其构造为需要的样子。

属性表达式

类的属性名,可以采用表达式。

let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}

上面代码中,Square类的方法名getArea,是从表达式得到的。

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

new.target属性

new是从构造函数生成实例对象的命令。ES6 为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三');  // 报错

 上面这段代码确保构造函数只能通过new命令调用


super关键字(作为对象,作为函数)

在 子类的constructor 中必须调用 super 方法去继承父类的this对象。在 super() 执行时,它指向的是 子类 B 的构造函数,而不是父类 A 的构造函数。也就是说,super() 内部的 this 指向的是 B。

super 在普通方法之中,指向 A.prototype,所以 super.c() 就相当于 A.prototype.c()。【A:父类;B:子类】

通过 super 调用父类的方法时,super 会绑定子类的 this

存取器

在“类”的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。通过getters/setters来截取对对象成员的访问,能帮助你有效的控制对对象成员的访问。

当尝试设置属性时,set语法将对象属性绑定到要调用的函数。

下面来看如何把一个简单的类改写成使用 get和 set。 首先,我们从一个没有使用存取器的例子开始。

class Employee {
  fullName: string;
}

  const employee = new Employee();
  employee.fullName = "lake Go";
  console.log(employee.fullName);
// 打印:lake Go。

使用存取器之后:

let passcode = "Passcode";
class EmployeeFillter {
  private _fullName: string;
  get fullName() {
    return this._fullName;
  }
  set fullName(value: string) {
    if (passcode && passcode === "Passcode") {
      this._fullName = value;
    } else {
      console.log("Error: Unauthorized update of employee!");
    }
  }
}

    const employeeFillter = new EmployeeFillter();
    employeeFillter.fullName = "lake go by fillter";
    console.log(employeeFillter.fullName);
// 打印:lake go by fillter。 

把类当作接口

类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

作用域 作用域链

作用域决定了代码区块中变量和其他资源的可见性。

作用域:全局作用域,函数作用域,块级作用域(es6)

块级作用域

块级作用域可通过新增命令let和const声明,所声明的变量在指定块的作用域外无法被访问。

特点:

  • let/const 声明并不会被提升到当前代码块的顶部

  • 禁止重复声明

  • 循环中的绑定块作用域的妙用

  ngOnInit() {
    var a = [];
    for (var i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 10
    for (let i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 6
  }

  ngOnInit() {
    var x = 11;
    var obb = {
      x: 222,
      y: {
        x: 333,
        obc: function () {
          var x = 111;
          var obj = {
            x: 22,
            say: function () {
              console.log(this.x);
            },
          };
          obj.say();
        },
      },
    };
    obb.y.obc();
  }

// 打印22。因为es5里函数的this的指向有2种。当函数定义在全局,this指向全局作用域;
// 当函数作为对象的方法时,this指向当前这个对象。

 如果say()函数从 say: function () {console.log(this.x);} 变为  say: function () {console.log(x);} 时候,那么obb.y.obc();打印为111(而不是22);注释掉x = 111,那么obb.y.obc();打印为11(而不是333)。 ==》作用域链

当在内部函数中,需要访问一个变量的时候,首先会访问这个函数本身的变量对象,是否有这个变量。如果没有,则会继续沿作用域往上查找,直到全局作用域。如果在某个变量对象中找到则使用该变量对象中的变量值。变量的查找:是沿着作用域链来实现的。作用域和作用域链在这个变量被定义的时候决定的,和执行顺序无关。内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境的任何变量和函数。


数组的扩展

扩展运算符(spread)是三个点(...),它将一个数组转为用逗号分隔的参数序列。

用扩展运算符代替apply方法

由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。

// ES5 的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);

扩展运算符的运用

求数组中最大值,通过push函数将一个数组添加到另一个数组的尾部,扩展数组,合并数组等。https://es6.ruanyifeng.com/#docs/array#Array-from 【例子请见这个博客】


在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

上面的函数有两个代码块,都声明了变量n,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是 10。

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

const声明一个只读的常量。一旦声明,常量的值就不能改变。对于const来说,只声明不赋值,就会报错。const的作用域与let命令相同:只在声明所在的块级作用域内有效。

从ES6开始,JavaScript引入了解构赋值,可以同时对一组变量进行赋值。

数组的解构赋值:ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

不完全解构:即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

let [x, y] = [1, 2, 3];
x // 1
y // 2

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

解构赋值允许指定默认值。

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined

上面最后一个表达式之所以会报错,是因为xy做默认值时,y还没有声明。

let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。 

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

参数变量是默认声明的,所以不能用letconst再次声明。

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

参考

http://es6.ruanyifeng.com/  (阮一峰)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值