一、构造函数
1-1 实例成员、静态成员
- 实例成员:实例成员就是在构造函数内部,通过this添加的成员。实例成员只能通过实例化的对象来访问
静态成员:在构造函数本身上添加的成员,只能通过构造函数来访问 ```javascript function Person(name,age) {
//实例成员
this.name = name;
this.age = age;
} //静态成员 Star.sex = ‘女’;
let stars = new Person(‘小红’,18); console.log(stars); // Star {name: “小红”, age: 18} console.log(stars.sex); // undefined
console.log(Star.sex); // '女'
<a name="bEc8Y"></a>
#### 1-2 定义方法位置对比
- 在构造函数上直接定义方法(不共享)
```javascript
function Star() {
this.sing = function () {
console.log('我爱唱歌');
}
}
let stu1 = new Star();
let stu2 = new Star();
stu1.sing();//我爱唱歌
stu2.sing();//我爱唱歌
console.log(stu1.sing === stu2.sing); //false stu1 和 stu2 指向的不是一个地方。
缺点:如果方法在构造函数内部,每次生成实例,都会新开辟一个内存空间存方法。这样会导致内存的极大浪费,从而影响性能。
- 通过原型添加方法(共享)
function Star(name) {
this.name = name;
}
Star.prototype.sing = function () {
console.log('我爱唱歌', this.name);
};
let stu1 = new Star('小红');
let stu2 = new Star('小蓝');
stu1.sing();//我爱唱歌 小红
stu2.sing();//我爱唱歌 小蓝
console.log(stu1.sing === stu2.sing);//true
1-3 构造函数的原型链继承(常用)
function Animal() {
this.eat = function() {
console.log('animal eat')
}
}
function Dog() {
this.break = function() {
console.log('dog bark');
}
}
// Dog.prototype的__proto__指向Animal.prototype
Dog.prototype = new Animal();
var hashiqi = new Dog();
1-4 构造函数的特点
- 首字母必须为大写,用来区分普通函数
- 内部使用的 this 对象,来指向即将要生成的实例对象
- 使用 new 关键字来生成实例对象
二、class
2-1 类的定义
基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已 ```javascript // 构造函数和class的对比
// ES5 function Point(x, y) { this.x = x; this.y = y; }
Point.prototype.toString = function () { return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’; };
var p = new Point(1, 2);
// ES6 class Point { constructor(x, y) { this.x = x; this.y = y; }
toString() { return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’} // 等价于 // Point.prototype.toString = function () { // return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’; // }; static toString = function(){} // 等价于 // Point.toString = function(){} }
<a name="WXQk9"></a>
#### 2-2 类的特点
1. class中的 constructor 函数相当于 ES5 中的构造函数
1. class中定义方法是定义在类的 prototype 属性
1. class的内部定义的所有方法都是不可枚举的
1. class和模块内部默认采用严格模式
1. 子类继承父类以后,必须在 constructor 中调用时 super 方法,否则不能新建实例,因为子类没有属于自己的this对象,而是继承了父类的this对象对其进行加工
<a name="uLmNE"></a>
# 三、原型
<a name="fQjQA"></a>
#### 3-1 基本概念
- 每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,比如 [ ] 的 原型就是 Array.prototype
- 原型的作用就是共享方法,不用反复开辟内存
- 原型的 constructor 属性指向其构造函数,如果替换了原型对象之后,这个 constructor 属性就不准确,需要手动补充一下
![image.png](https://cdn.nlark.com/yuque/0/2021/png/12508812/1640226365660-c9061cb3-2011-4695-892c-380566eb8110.png#clientId=u586ce051-074d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=367&id=ua33c60cc&margin=%5Bobject%20Object%5D&name=image.png&originHeight=734&originWidth=1277&originalType=binary&ratio=1&rotation=0&showTitle=false&size=100352&status=done&style=none&taskId=u1b3ccabc-62a7-4a1e-9473-26d86e57cd4&title=&width=638.5)
<a name="nNpLM"></a>
#### 3-2 原型规则
- 所有引用类型(对象、数组、方法),除了 null ,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象,指向其构造函数的显式原型 prototype ,也就是指向该引用类型的原型
- 当访问一个对象的属性时,先在基本属性中查找,如果没有,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype )中寻找
- 所有的函数,都具有一个显式原型 prototype ,属性值也是一个普通对象
```javascript
function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
3-3 constructor
function Person() { }
var person = new Person();
console.log(person.constructor === Person); // true
众所周知,原型的 constructor 属性指向其构造函数,当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到 constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性
3-4 proto
绝大部分浏览器都支持使用 proto 这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)
四、原型链
- 原型与原型层层相链接的过程即为原型链
Object.prototype的原型 为 null 跟 Object.prototype 没有原型表达的是一个意思
4-1 原型链例子
// 父类
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat something`)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${this.name} 学号 ${this.number}`)
}
}
// 子类
class Teacher extends People {
constructor(name, major) {
super(name)
this.major = major
}
teach() {
console.log(`${this.name} 教授 ${this.major}`)
}
}
// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo instanceof Student) //true
console.log(xialuo instanceof People) //true ,People是Student的父类,也参与了xialuo的构造
console.log(xialuo instanceof Object) //true ,Object是所有类的父类
console.log([] instanceof Array) //true
console.log([] instanceof Object) //true
//instanceof 运算符用于检测构造函数的 prototype 属性是否出现在实例对象的原型链上。
4-2 Object.proto
定义1:访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着 proto 这条链向上找,这就是原型链。
- 所以 Object.toString 是会报错的 ,Object 找 toString 方法的时候会沿着 proto 这条链上找,因为Object.proto上没有toString方法,所以报错,必须使用 Object.prototype.toString
- 定义2:对象的 proto 指向其构造函数的 prototype ,所以 Object._proto 指向 Function.prototype
Object.__proto__ === Function.prototype // true