1. 构造函数
-
什么是构造函数:主要用来初始化对象,即为对象成员变量赋初始值,与new一起使用。
-
在构造函数中添加成员:
- 实例成员:实例成员是属于类的每个实例(对象)的属性或方法。当你创建了类的一个实例时,实例成员将成为该实例的一部分。每个对象都有自己独立的实例成员。
-
(1)实例成员通常在构造函数中初始化
-
(2)实例成员通过类的实例来访问
jsclass Student { constructor(name, age) { // 实例成员 this.name = name; // 每个对象都有自己独立的 name 属性 this.age = age; // 每个对象都有自己独立的 age 属性 } // 实例方法 sayHello() { console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`); } } const student1 = new Student("nini", 18); const student2 = new Student("zheng", 30); student1.sayHello(); // 输出:Hello, my name is nini and I'm 18 years old. student2.sayHello(); // 输出:Hello, my name is zheng and I'm 30 years old.
-
静态成员:静态成员是属于类本身的属性或方法,而不是类的实例。静态成员不依赖于类的实例对象,它们可以直接通过类名来访问。静态成员对于所有实例是共享的,它们在内存中只有一份。
-
静态成员不属于类的实例,而是属于类本身。
-
静态成员通过类名直接访问。
-
静态成员不能通过 this 来访问,它只能通过类名访问。
jsclass Car { constructor(make, model) { this.make = make; this.model = model; } // 实例方法 displayInfo() { console.log(`${this.make} ${this.model}`); } // 静态方法 static compareModels(car1, car2) { return car1.model === car2.model; } } const car1 = new Car("Tesla", "Model S"); const car2 = new Car("Tesla", "Model X"); console.log(Car.compareModels(car1, car2)); // 输出:false
-
在上面的代码中,compareModels 是 Car 类的静态方法。不能通过实例 car1.compareModels()来调用它,而是必须通过类名来调用Car.compareModels(car1, car2)
-
-
总结: 实例成员用于存储对象的特有数据,静态成员则用于存储与类本身相关的功能或常量。
-
- 实例成员:实例成员是属于类的每个实例(对象)的属性或方法。当你创建了类的一个实例时,实例成员将成为该实例的一部分。每个对象都有自己独立的实例成员。
-
构造函数存在的问题:让每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费,浪费内存。
2. 什么是原型
- 原型这个概念的诞生主要是为了解决构造函数存在一些问题。
- 原型是什么:原型是一个对象,我们也称prototype为原型对象。
- 原型的作用:共享方法。
- 原型关系:
-
每个class都有显示原型prototype
-
每个实例都有隐式原型__proto__
-
实例的__proto__指向对应class的prototype
js// class实际上是函数,可见是语法糖 typeof Student // 'function' typeof People // 'function' // 隐式原型和显示原型 console.log(nini.__proto__); console.log(Student.prototype); console.log(nini.__proto__ === Student.prototype); // true
-

3. 显式原型
- 显示原型即构造函数中的prototype属性。
- 每一个类的构造函数都有一个prototype属性。这个属性包含一个对象:prototype原型对象,所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
- 实例对象一旦创建,将自动引用prototype对象的属性和方法。只要修改了prototype对象,就会同时影响到多个实例对象。

4. 隐式原型
- 隐式原型即实例中的__proto__属性。
- 每一个实例都会有一个属性__proto__指向构造函数的prototype原型对象。
- 之所以我们在实例中可以使用构造函数prototype原型对象的属性和方法,就是因为实例有__proto__原型的存在。__proto__对象原型和构造函数的原型对象prototype是等价的。
- _proto__对象原型的意义就在于为对象的查找机制提供一个方向,实际开发中不可以使用这个属性,它只是内部指向原型对象prototype

5. 原型中的constructor属性
- 隐式原型(proto)和显式原型(prototype)里面都有一个属性constructor属性,它指回构造函数本身。
- constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
- 注意:写法
Student.prototype.doHomework=function(){}
等相当于在原型中添加方法,所以自动带有constructor。而写法Student.prototype={}
是相当于修改了原型对象,覆盖了之前的所有,所以要自己手动添加constructor构造函数,否则是没有构造函数的。
6. 基于原型的执行规则 (JS的成员查找机制)
- 当访问一个实例的属性(包括方法)时,首先查找这个对象自身有没有该属性(例如获取nini.name或者nini.sayHello()时)
- 如果找不到,则自动去该实例(nini)的__proto__隐式原型中查找(也就是__proto__隐式原型指向的prototype显式原型)
- 如果还没有找到,就查找这个显示原型的原型(Object原型对象)
- 以此类推,一直找到Object为止(null)
总结:__proto__隐式原型的意义就在于为实例成员查找机制提供一个方向,或者说一条路线。

7. 原型链
- 只要是实例,就有__proto__隐式原型,指向其类的构造函数的显示原型
- 构造函数的显式原型prototype里面的__proto__隐式原型指向的是Object.prototype
- Object.prototype显式原型里面的__proto__原型,指向为null
备注:instanceof:原理就是是否能找到某某的原型(即xxxxx.prototype),如果能找到就是true,找不到就是false
8. 扩展内置对象
- 通过显式原型,对原来的内置对象进行扩展自定义的方法。
- 比如给数组增加自定义求偶数和的功能:
- 注意:数组和字符串内置对象不能给显示原型覆盖操作Array.prototype={},只能是Array.prototype.xxx=function(){}的方式。第一种方式会覆盖原先的内置方法。