构造函数/原型/继承/class类

50 阅读5分钟

class类 --> 语法糖, 构造函数 + 原型链 + 继承 JS执行方法的逻辑:先在当前对象方法上找 当前对象方法上没有就往原型上找 原型没有就再网上找

构造函数

JS中所有类型对象的来源(除了Null和Undefined), 所有的数据类型对象都是通过构造函数实例化产生的对象,也是一个生产对象的模具

构造函数分成两类

  • 内置构造函数(js本身提供的)
  • 自定义构造函数

构造函数的语法

  • 不要使用箭头函数和表达式来创造构造函数 //表达式 var fn = function(){}
  • 构造喊的函数名应该首字母大写, 使用驼峰
  • 普通函数通过函数名()的形式调用函数, 构造函数需要通过实例化来产生对象, 而不是调用
  • 普通函数函数体内直接写业务或者代码逻辑, 但是在构造函数中, 我们只做两件事:设置属性 设置方法给对象 属性 方法 --> 对象 用this
  • 普通函数中, 在函数的最后可以设置return, 在构造函数照片那个不需要设置return
function Person(){
   this.a = 100 //this代指未来产生的对象
   this.b = 200
   //写这个位置问题:每次实例化产生对象的时候,函数都要跑一次
   //因为写在了构造函数的位置 
   //为啥要把方法放到实例化对象的原型上: 1.这样每次不会都跑一次 2.当实例化对象想用到这个方法的时候 直接调用就行了 因为方法就在这个原型上面
// this.add = function(){
//    console.log(this.a + this.b)
//  }
}
   Person.prototype.add = function(){
      console.log(this.a + this.b)
  }
const p1 = new Person()
console.log(p1) //add a b 
//const obj = {a:xx, b:xx, add(){} } 
//obj.a obj.b
console.log(p1.a)
console.log(p1.b)
p1.add() //与以上obj. 区别:产生的方式不一样 但结果一样
p1.info = 'hello world'
console.log(p1)

原型和原型链

''字符串 --> String{} --> Object --> null

100 数组 --> Number{} --> Object --> null

  • JS中的原型都是对象 只有对象能当对象原型 一切皆对象 和构造函数没关系
  • 对象从诞生起 就被赋予了原型 具有对象的特征 --> Prototype

原型对象对于对象的意义

能够让你拥有祖先的特性 原型是对象的上一层 构造函数是对象的来源 构造函数没有原型 image.png

如何看当前对象的原型/构造函数

  • 新语法 可以使用Object.getPrototypeOf()来获取一个对象的原型
  • Object.getPtototypeOf(对象).constructor 获取一个对象的构造函数
  • 站在构造函数的层面去查看原型 查看的是实例化对象的原型(p1) 构造函数.prototype
const p1 = new Person()
console.log(p1.__proto__) //老版本 已废弃
// Object.getPrototypeOf()来获取一个对象的原型
console.log(Object.getPrototypeOf(p1))
// Object.getPtototypeOf(对象).constructor 获取一个对象的构造函数
const fn = Object.getPtototypeOf(p1)
console.log(fn.constructor)
// 如何查看当前对象的原型 构造函数.prototype
console.log(p1.__proto__ === Person.prototype) //true

继承

修改原型对象,但是不影响构造函数 创建一个构造函数

image.png 我们发现这一份代码有大量的重复, 应该如何避免重复?

image.png

//需求 Human变成Person的原型
function Human(){
  
 }
Human.prototype.sayName = function(){
 (this.Username)
 }
function Person(){

 } 
//Person.prototype --> p1的原型 --> {} --> new Human
Person.prototype = Object.create(new Human)
const p1 = new Person()
console.log(p1)

输出结果 image.png 发现Human上面Person身上没有构造函数 因为给Pserson的原型覆盖了 需要加上去

Person.prototype.constructor.Person

image.png

//被继承者-->父亲
function Human(username){
  this.username = username
}
Human.prototype.sayName = function(){
 console.log(this.username)
 }
 //继承别人 --> 儿子
function Person(username, male){
//改变this指向的代码必须写在最上面
  human.call(this.username)//把 Human中的this指向改写成了Person中的this, Preson中的this指向p1
  this.male = male
}
//Person.prototype --> p1的原型 --> {} --> new Human
Person.prototype = Object.create(new Human)
const p1 = new Person('zhangsan', '男 ')
p1.sayName() // zhangsan

继承的重点

  • 把父亲的指向变成自己的this指向
  • 把person实例化对象的原型 变成空对象 将空对象的原型变成父亲的实例化对象
  • 把儿子的实例对象的原型对象的构造函数设置为儿子的构造函数 以上为ES5继承, 接下来 ES6进行整体升了个级 class

class: 就是es5构造函数的语法糖

父亲 Human username属性 sayName方法 儿子 Person male属性

es6中不再需要写构造函数的形式, 构造函数只是借助了函数的外表而已

//父
class Human {
  //方法 constructor 方法名固定,不能改
  constructor(username){
  //所有写在constructor()函数中的内容 等同于es5写在构造函数里面
    this.username = username
    }
  //只要是constructor之外的内容,都等同于写在原型对象上的内容
   sayName(){
     console.log(this.username)
   }
  }
//让儿子继承自父亲
class Person extends Human{
 //任何一个class类中都默认包含一个constructor函数
 constructor(male, username){
 //super必须写在constructor函数的最上面 等同于 Human.call(this.username)
  super(username)
  this.male = male
}
//实例化的方式和以前一样
const p1 = new Person('男', '张三')
console.log(p1)

结果 image.png 对应写法 image.png

image.png

新版本class类语法细节

class Person {
  constructor(){
  //constructor构造函数会自动执行, 在实例化的一瞬间
  //console.log(123)
  this.username = 'zhangsan' //constructor中定义的叫属性之外的叫方法
  }
  //方法
  sayName(){
   //如果想要在方法中调用属性, 需要使用this
   console.log(this.username)
   //想要在一个方法中调用另外一个方法,仍然需要使用this
   this.add()
   }
   add(){
     console.log('this is add')
   }  
   //方法前面加上一个static该方法就变成了静态方法
   staic lei(){
    console.log('真他娘的累')
   }
 }
 const p1 = new Person()
 //p1.sayName()
 //Promise.resolve()静态方法, 静态方法不是属于构造函数产生的实例对象的,属于构造函数的  所以可以调用方法
p1.lei()//无法调用
//用构造函数调用
Person.lei()//真他娘的累
MDN内置对象也有很多静态方法

image.png 调用方法不一样

image.png

image.png

image.png

静态属性
class Person{
  static age = 30 //静态属性:记录一些不变的关键值
  }
console.log(Person.age)//30

私有属性和方法#

class Person {
私有属性和私有方法只能在类的内部去调用
 #money = 10000 
 constructor(){
 this.username = 'zhangsan' //constructor中定义的叫属性之外的叫方法
  }
  //方法
  sayName(){
   //如果想要在方法中调用属性, 需要使用this
   console.log(this.username)
   //打印私有变量
   this.#siyou()
   //想要在一个方法中调用另外一个方法,仍然需要使用this
   this.add()
   }
   #siyou(){
   console.log('我是私有方法')
 }
 const p1 = new Person()
 console.log(p1)//打印出所有包括#money
 p1.#money = 100 //打印失败
 p1.#siyou()//打印失败
 //如何解决这个问题?
 //取消打印p1
 //调用sayname 
 p1.sayname()//打印出私有方法