当前位置: 首页 > news >正文

重学前端(六)-JavaScript中的class

一、什么是原型?

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

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

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

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

二、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() //10
new 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() // A
new 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

相关文章:

  • Spring Security 基础登录实例
  • PostgreSQL pgbench SQL RT 与 事务RT 浅析
  • 决策树 熵
  • Knative 核心概念介绍:Build、Serving 和 Eventing 三大核心组件
  • 洛谷P2676 超级书架
  • 讲清楚分布式系统中的这个算法,帮你从面试候选人中脱颖而出!
  • 我的第一天!
  • JAVA-基础(接口---抽象)
  • 从0开始学习 GITHUB 系列之「GITHUB 常见的几种操作」
  • 【JavaEE企业应用实战学习记录】struts2实现登录并获取各个范围的数据
  • [译] 2019 年了,为什么我还在用 jQuery?
  • RubyGem修改国内源
  • H5移动端页面常见兼容问题及解决方案
  • iOS开发中的内存分配与分区
  • 第一个微信项目
  • ----------
  • 深入了解以太坊
  • 【个人向】《HTTP图解》阅后小结
  • ➹使用webpack配置多页面应用(MPA)
  • Python连接Oracle
  • Quartz初级教程
  • windows下如何用phpstorm同步测试服务器
  • Yeoman_Bower_Grunt
  • Zepto.js源码学习之二
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 从setTimeout-setInterval看JS线程
  • 原生 js 实现移动端 Touch 滑动反弹
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • #Linux(帮助手册)
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (2020)Java后端开发----(面试题和笔试题)
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (ros//EnvironmentVariables)ros环境变量
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (剑指Offer)面试题34:丑数
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .Net Remoting(分离服务程序实现) - Part.3
  • .net 程序发生了一个不可捕获的异常
  • /var/spool/postfix/maildrop 下有大量文件
  • @javax.ws.rs Webservice注解
  • [ Linux Audio 篇 ] 音频开发入门基础知识
  • [android] 练习PopupWindow实现对话框
  • [Android]通过PhoneLookup读取所有电话号码
  • [C#] 如何调用Python脚本程序
  • [C++] new和delete
  • [CERC2017]Cumulative Code
  • [Firefly-Linux] RK3568修改控制台DEBUG为普通串口UART
  • [flume$2]记录一个写自定义Flume拦截器遇到的错误