博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
重学前端(六)-JavaScript中的class
阅读量:7064 次
发布时间:2019-06-28

本文共 3778 字,大约阅读时间需要 12 分钟。

一、什么是原型?

用原型来描述对象,一像二,一就是原型,二是对象。

对于原型的复制操作有两种

  • 一个是并不真的去复制一个原型对象,而是使得新对象持有一个原型的引用;

  • 另一个是切实地复制对象,从此两个对象再无关联。

二、JavaScript原型

  • 如果所有对象都有私有字段 [[prototype]],就是对象的原型;
  • 读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。

ES6以来,JavaScript提供了一系列的内置函数,以便于更直接的访问操作原型

  • Object.create 根据指定的原型创建新对象,原型可以是 null;
  • Object.getPrototypeOf 获得一个对象的原型;
  • Object.setPrototypeOf 设置一个对象的原型。
var water={	    color(){console.log('none-color')}    }    var sea={    	color(){console.log('blue-color')}    }    var waterRiver=Object.create(water);    console.log(waterRiver.color())//none-color    var waterSea=Object.create(sea)    console.log(waterSea.color())//blue-color复制代码

最早之前,语言使用者唯一可以访问 [[class]] 属性的方式是 Object.prototype.toString。

var o = new Object;    var n = new Number;    var s = new String;    var b = new Boolean;    var d = new Date;    var arg = function(){ return arguments }();    var r = new RegExp;    var f = new Function;    var arr = new Array;    var e = new Error;    console.log([o, n, s, b, d, arg, r, f, arr, e].map(v => Object.prototype.toString.call(v)));     //复制代码

var o = { [Symbol.toStringTag]: "MyObject" }    console.log(o + "");                       //[object MyObject]复制代码

创建了一个新对象,并且给它唯一的一个属性 Symbol.toStringTag,用字符串加法触发了 Object.prototype.toString 的调用,发现这个属性最终对 Object.prototype.toString 的结果产生了影响。

三、new

  • 以构造器的 prototype 属性(注意与私有字段 [[prototype]] 的区分)为原型,创建新对象;
  • 将 this 和调用参数传给构造器,执行;
  • 如果构造器返回的是对象,则返回,否则返回第一步创建的对象。

new 这样的行为,试图让函数对象在语法上跟类变得相似,但是,它客观上提供了两种方式,一是在构造器中添加属性,二是在构造器的 prototype 属性上添加属性。

function c1(){    this.p1 = 1;    this.p2 = function(){        console.log(this.p1);    }} var o1 = new c1;o1.p2();function c2(){}c2.prototype.p1 = 1;c2.prototype.p2 = function(){    console.log(this.p1);}var o2 = new c2;o2.p2();复制代码

第一种方法是直接在构造器中修改 this,给 this 添加属性。

第二种方法是修改构造器的 prototype 属性指向的对象,它是从这个构造器构造出来的所有对象的原型。

四、ES中的class

class Rectangle {  constructor(height, width) {    this.height = height;    this.width = width;  }  // Getter  get area() {    return this.calcArea();  }  // Method  calcArea() {    return this.height * this.width;  }}new Rectangle(2,5).calcArea() //10new Rectangle(2,6).area       //12复制代码

我们通过 get/set 关键字来创建 getter,通过括号和大括号来创建方法,数据型成员最好写在构造器里面。

**继承extends**

1、当做函数使用

class A {}class B extends A { constructor() {  super(); //ES6 要求,子类的构造函数必须执行一次super函数。 }}复制代码

super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。

class A { constructor() {  console.log(new.target.name); //new.target指向当前正在执行的函数 }}class B extends A { constructor() {  super(); }}new A() // Anew B() // B复制代码

2、当做对象使用

在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

class A { c() {  return 2; }}class B extends A { constructor() {  super();  console.log(super.c()); // 2 }}let b = new B();复制代码

上面代码中,子类B当中的super.c(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.c()就相当于A.prototype.c()。

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

class A { constructor() {  this.x = 1; } s() {  console.log(this.x); }}class B extends A { constructor() {  super();  this.x = 2; } m() {  super.s(); }}let b = new B();b.m() // 2复制代码

上面代码中,super.s()虽然调用的是A.prototype.s(),但是A.prototype.s()会绑定子类B的this,导致输出的是2,而不是1。也就是说,实际上执行的是super.s.call(this)。

由于绑定子类的this,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。

class A { constructor() {  this.x = 1; }}class B extends A { constructor() {  super();  this.x = 2;  super.x = 3;  console.log(super.x); // undefined  console.log(this.x); // 3 }}let b = new B();复制代码

上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined。

注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

class A {}class B extends A { constructor() {  super();  console.log(super); // 报错 }}复制代码

上面代码中,console.log(super)当中的super,无法看出是作为函数使用,还是作为对象使用,所以 JavaScript 引擎解析代码的时候就会报错。这时,如果能清晰地表明super的数据类型,就不会报错。

转载于:https://juejin.im/post/5cf48793f265da1b8c197471

你可能感兴趣的文章