JavaScript构造函数
构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数; 在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法; 但是JavaScript中的构造函数有点不太一样;  
在JavaScript中,构造函数就是一个普普通通的函数,从表现形式来看,和其他函数没有区别。当被new关键字所调用时,就变成了构造函数。
new操作调用函数时的流程
- 在内存中开辟一块新的空间(空对象)。
- 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性。
- 构造函数内部的this会指向新创建出来的对象。
- 指向内部函数代码
- 如果没有返回非空对象,则会把创建出来的对象给返回。
对象与函数的原型对象的原型——[[prototype]]JavaScript每一个对象都有一个特殊的内置属性[[prototype]](proto),这个特殊的对象指向另外一个对象。 :::warning__proto__是早期浏览器自己添加的,可能存在浏览器等兼容性问题,也是历史遗留下的问题。在现代的js中,推荐使用[Object.getPrototypeOf()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf)或者[Reflect.getPrototypeOf()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf):::函数的属性——prototypeJavaScript中所有函数都有一个prototype属性。
 该prototype属性可以被重写。[[prototype]]和prototype两者的区别:::info[[prototype]]是对象的原型prototype是函数的属性 :::
new一个对象时的内存表现

修改prototype(重写prototype)
function Foo() {}
Foo.prototype = {
msg: "hello",
x: 100,
};
const foo = new Foo();
console.log(foo);
console.log(foo.__proto__);
console.log(foo.msg);
console.log(foo.x);

内存表现
给prototype添加属性
function Foo() {}
Foo.prototype.msg = "hello";
Foo.prototype.x = 100;
const foo = new Foo();
console.log(foo);
console.log(foo.__proto__);
console.log(foo.msg);
console.log(foo.x);
函数与prototype结合使用
直接在构造方法上添加方法的方式。
function Person(name, age) {
this.name = name;
this.age = age;
this.eat = function () {
console.log("eat");
};
}
:::warning
通过这种方式创建一个对象有一个弊端,就是每次创建一个新的Person对象的时候,对象上的eat()函数会被重复创建。非常影响性能。
:::
在函数原型上添加方法,这样通过该方式,所有创建出来的Person对象,可以共享一个eat函数。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.eat = function() {
console.log("eat");
}
prototype.constructor
每一个函数都有一个prototype,而prototype上都会有一个constructor属性,这个constructor指向该函数。 
Object是所有类的父类
通过var foo = new Foo(),会将Foo的原型对象Foo.prototype赋值给foo的__proto__,而Foo.prototype中的__proto__默认指向Object.prototype,也就是[Object: null prototype]。
因为无论是通过显式原型prototype还是隐式原型[[prototype]],它的值只能是对象或者null。当其值为对象时,该对象肯定又会有一个隐式原型,如此无论怎样都会最终指到Object。
ES5中对象的继承方案
寄生组合式继承
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.eat = function () {
  console.log(this.name + " is eating...");
};
function Student(name, age, stuNumber) {
  // 借用构造函数
  Person.call(this, name, age);
  this.stuNumber = stuNumber;
}
// 继承prototype
Student.prototype = Object.create(Person.prototype);
// 重定义constructor
Object.defineProperty(Student.prototype, "constructor", {
  value: Student,
  writable: true,
  enumerable: false,
  configurable: true,
});
Student.prototype.study = function () {
  console.log(this.name + " is studying...");
};
Object、Function及自定义函数之间的关系

在JavaScript中,任何一个函数本质上都是通过new Function()出来的,所以它们的[[prototype]]都指向Function.prototype,每个函数中的prototype中的[[prototype]]默认指向Object.prototype,也就是说默认继承自Object。
ES6中的class类
通过前面的方式创建一个类,因为形式上与普通的function过于类似,而且代码不容易理解。在ES6中,添加了一种新的方式定义一个类,但本质上依然是构造函数,只是一种语法糖。 :::tips 如果想查看转化成ES5的代码,可以通过babel工具。 :::
类和构造函数的区别
本质上依然是构造函数,只是一种语法糖。
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}
const person = new Person("abc", 18);
console.log(person);
console.log(person.__proto__);
console.log(person.__proto__ === Person.prototype);
console.log(Person.prototype);
console.log(Person.prototype.__proto__);
console.log(typeof Person);
类的构造函数
每个类都有自己的构造方法,名称为constructor,且每个类只能有一个,如果有多个就会报错。
该构造方法与构造函数的运行原理一致。
类的实例方法
eat、run即为实例方法,与function.prototype.eat方式基本一致。
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  eat() {
    console.log(`${this.name} is eating~`);
  }
  run() {
    console.log(`${this.name} is running~`);
  }
}
类的访问器方法——getter、setter
通过get和set修饰符即可添加getter和setter函数
class Person {
  constructor(name, age) {
    this._name = name;
    this.age = age;
  }
  set name(name) {
    this._name = name;
  }
  get name() {
    return this._name;
  }
}
类的静态方法——static
通过static修饰,可以直接通过类.静态方法调用。
class Person {
  constructor(name) {
    this.name = name;
  }
  static count() {
    return 10;
  }
}
console.log(Person.count());
类的继承——extends
通过extends关键字可以帮助快速实现继承。
class Person {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log(`${this.name} is eating~`);
  }
}
class Student extends Person {
  constructor(name, score) {
    super(name); // super()调用父类的构造方法
    this.score = score;
  }
  study() {
    console.log(`${this.name} is ${this.score}`);
  }
}
:::warning
在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数!
 super的使用位置有三个:
- 子类的构造函数
- 实例方法
- 静态方法
 :::

 
 
 
                         
                                

