尼采般地抒情

尼采般地抒情

尼采般地抒情

音乐盒

站点信息

文章总数目: 316
已运行时间: 1570

前言:JavaScript首先在ES6没有出来之前,利用一个叫原型的一系列机制来用一段很长的代码来实现类的继承,说白了就是在函数里面默认给你加个一个叫原型的对象属性,再利用一系列指向来完成继承。在ES6之后,才有了形式上的类class及其对象,以及一个单词extends就搞定的继承,虽说搞定,但这里面的机制还是原型相关知识,记录学习一下。

不管是原型也好,新加的class也罢,就一个目的——为了实现面向对象。从两个体系来展开详述。

体系一:原型+原型链

隐式原型__proto__

每个对象里面都有__proto__属性,这个叫对象原型,这个玩意指向构造函数的prototype对象

__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

比如说,有一个Person类,有一个Student类,Student类继承自Person类,Student有一个实例student_1,那么student_1会有__proto__对象属性,并且展开该__proto__,会发现里面是Person类的方法和属性

显式原型prototype

每个构造函数里面都有一个属性,这个属性叫prototype,指向另一个对象有什么用?在后面原型链就会发现有用了),并且这个属性是一个对象,叫做构造函数原型

这样可以解决一个问题,就是创建不同实例,这些事例所用的方法都是同一个内存下的方法,实现共享

constructor构造函数

  1. 对象原型( __proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身
  2. constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
  3. 一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor  就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

instanceof

a instanceof b

判断是true还是false

看a的constructor是什么

当b为 a的constructor或a的__proto__的constructor……

结果都是true

原型链和成员查找机制

原型体系中的继承

call方法可以改变一个函数的指向

  1. 继承父构造函数里面的属性
// 1. 父构造函数
 function Father(uname, age) {
   // this 指向父构造函数的对象实例
   this.uname = uname;
   this.age = age;
 }
  // 2 .子构造函数 
function Son(uname, age, score) {
  // this 指向子构造函数的对象实例
  // 3.使用call方式实现子继承父的属性
  Father.call(this, uname, age);
  this.score = score;
}
var son = new Son('刘德华', 18, 100);
console.log(son);
  1. 继承方法
// 1. 父构造函数
function Father(uname, age) {
  // this 指向父构造函数的对象实例
  this.uname = uname;
  this.age = age;
}
Father.prototype.money = function() {
  console.log(100000);
 };
 // 2 .子构造函数 
  function Son(uname, age, score) {
      // this 指向子构造函数的对象实例
      Father.call(this, uname, age);
      this.score = score;
  }
// Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
  Son.prototype = new Father();
  // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
  Son.prototype.constructor = Son;
  // 这个是子构造函数专门的方法
  Son.prototype.exam = function() {
    console.log('孩子要考试');
  }
  var son = new Son('刘德华', 18, 100);
  console.log(son);

体系二:类(class)【ES6】

在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象。其创建的方式和之前学过的语言很类似。

在 ES6 中类没有变量或是函数提升概念,所以必须先定义类,才能通过类实例化对象,这也是类和函数最大的不同。

原型方法和原型字段声明

原型方法:

  1. 构造函数方法
  2. get set
  3. 静态方法:通常作为类的工具函数
  4. 普通方法


原型字段:

  1. 私有字段
  2. 公有字段
class Tooltip {
  #test1 = 111; // 私有成员
  test2 = 222; // 公有成员
  static test3 = 333; // 静态成员(类内部不能访问 相当于类的属性)
  static fun1(a, b) {
    // 静态方法(类内部不能访问 相当于类的属性)
    return a + b;
  }
  constructor() {
    console.log('private member test1: ', ++this.#test1);
    console.log('public member test2: ', ++this.test2);
    console.log('origin class: ', this);
  }
}

class TextTooltip extends Tooltip {
constructor(quill, options) {
super(quill, options.bounds);
}
}

this.textToolTip = new TextTooltip(quill, options);
console.log(this.textToolTip,
Tooltip.test3,
TextTooltip.test3,
TextTooltip.fun1(333, 333));

export default TextTooltipToolbar;

继承

// 奥特曼类
class Aoteman {
    // 构造函数里面放共有属性、方法
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    Ability(abi) {
        console.log(this.name + " can " + abi);
    }
}

// 假 继承 奥特曼
class Jia extends Aoteman {
constructor (x, y) {
// 继承父类的构造函数
super(x,y);
// 方便写自己的函数
this.x = x;
this.y = y;
}
// 自己内部的函数
Chuiniu() {
console.log(this.x + " chuiniu");
}

}

// 创建迪迦对象
let dijia = new Aoteman(‘dijia’, 100);
console.log(dijia);
dijia.Ability("X-ray");
let dijia_jia = new Jia("dijia_jia", 50);
dijia_jia.Ability("xxx");
dijia_jia.Chuiniu();

多重继承

JavaScript对象的继承只能继承一个父类,因为继承的根本实现是基于原型链的,所以继承不能同时指向多个对象,所以实现多重继承有两个思路:

  1. 父类链式继承
class Parent1 extends Parent2 {}
class Parent2 extends Parent3 {}
class Child extends Parent1 {}
  1. 实现Mixin来继承
function mixin(...mixins) {
  class Mixin {
    constructor(...args) {
      mixins.forEach(
        mixin => copyProperties(this, new mixin(...args)) // 拷贝实例属性
      ) 
    }
  }
  mixins.forEach(
    mixin => {
      copyProperties(Mixin, mixin); // 拷贝静态属性
      copyProperties(Mixin.prototype, mixin.prototype); // 拷贝原型属性
    }
  )

return Mixin;
}

function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if ([‘constructor’, ‘prototype’, ‘name’].indexOf(key) < 0) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}

class Child extends mixin(Parent1, Parent2, Parent3) {}

对象的各种创建方式

字面量

属性和方法的调用:

属性可以用"."和["属性名"]来调用

方法用"."来调用

模式工厂

new一个空对象,let obj = new Object();

然后利用"."的方式给其添加属性和方法

内置的Object()构造函数

构造函数

构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

function 构造函数名(形参1,形参2,形参3) {
     this.属性名1 = 参数1;
     this.属性名2 = 参数2;
     this.属性名3 = 参数3;
     this.方法名 = 函数体;
}

let obj = new 构造函数名(实参1,实参2,实参3)


  • 构造函数约定首字母大写
  • 函数内的属性和方法前面需要添加 this ,表示当前对象的属性和方法。
  • 构造函数中不需要 return 返回结果
  • 当我们创建对象的时候,必须用 new 来调用构造函数

原型模式

下面详述什么是原型

对象的内置方法

参考教程:

Object.defineProperty

Object.defineProperty设置或修改对象中的属性

Object.defineProperty(对象,修改或新增的属性名,{
        value:修改或新增的属性的值,
        writable:true/false,//如果值为false 不允许修改这个属性值
        enumerable: false,//enumerable 如果值为false 则不允许遍历
    configurable: false  //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
})

删除对象属性

assign()

  • Object.assign后者覆盖前者,再返回前者
  • 常用于深拷贝浅拷贝数据

对象的遍历

for...in

语句用于对数组或者对象的属性进行循环操作。

其语法如下:

for (变量 in 对象名字) {
    // 在此执行代码
}
  • 语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为 k 或者 key。书里面建议用const来加持。
for (let k in obj) {
    console.log(k);      // 这里的 k 是属性名
    console.log(obj[k]); // 这里的 obj[k] 是属性值
}

Object.keys(obj)

获取属性名

var obj = {
     id: 1,
     pname: '小米',
     price: 1999,
     num: 2000
};
var result = Object.keys(obj)
console.log(result)//[id,pname,price,num]

参考

评论区