一、构造函数
1-1 实例成员、静态成员
- 实例成员:实例成员就是在构造函数内部,通过 this 添加的成员。实例成员只能通过实例化的对象来访问
- 静态成员:在构造函数本身上添加的成员,只能通过构造函数来访问
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); // '女'
1-2 两种定义方法对比
- 在构造函数上通过this直接定义方法(不共享)
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 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
// 构造函数和 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(){}
}
2-2 类的特点
-
class 中的 constructor 函数相当于 ES5 中的构造函数
-
class 中定义方法是定义在类的 prototype 属性
-
class 的内部定义的所有方法都是不可枚举的
-
class 和模块内部默认采用严格模式
-
子类继承父类以后,必须在 constructor 中调用时 super 方法,否则不能新建实例,因为子类没有属于自己的this对象,而是继承了父类的this对象对其进行加工
三、原型
3-1 基本概念
-
每一个 JavaScript 对象( null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,比如 [] 的 原型就是 Array.prototype
-
原型的作用就是共享方法,不用反复开辟内存
-
原型的 constructor 属性指向其构造函数,如果替换了原型对象之后,这个 constructor 属性就不准确,需要手动补充一下
3-2 原型规则
-
所有引用类型(对象、数组、方法),除了 null ,都有一个隐式原型
__proto__属性,属性值是一个普通的对象,指向其构造函数的显式原型 prototype ,也就是指向该引用类型的原型 -
当访问一个对象的属性时,先在基本属性中查找,如果没有,那么它会去它的隐式原型
__proto__(也就是它的构造函数的显式原型 prototype )中寻找 -
所有的函数,都具有一个显式原型 prototype ,属性值也是一个普通对象
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