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):::函数的属性——prototype
JavaScript中所有函数都有一个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的使用位置有三个:
- 子类的构造函数
- 实例方法
- 静态方法
:::

