五条原型规则:
- 所有的引用类型(数组,对象,函数),都具有对象特性,即可自由扩展属性(除了null之外)(null是object类型,但不具备可扩展属性)
- 所有的引用类型(数组,对象,函数),都具有_proto_(隐式原型)属性,属性值是一个普通的对象
- 所有的函数,都有一个prototype(显式原型)属性,属性值是一个普通的对象
- 所有的引用类型(数组,对象,函数),_proto_属性值指向它的构造函数的 “prototype” 属性值
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中寻找
var obj = {}; obj.num = 100;
var arr = []; arr.num = 100;
function fun() {}; fun.num = 100;
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fun.__proto__);
console.log(fun.prototype);
console.log(obj.__proto__ === Object.prototype);
下面举例说明(Student、Teacher 继承People类):
class People {
// 构造函数
constructor(name) {
this.name = name;
}
eat = () => {
console.log(
`${this.name} eat something`
)
}
}
class Student extends People {
constructor(name, number) {
// 继承 People 的属性
super(name)
this.number = number;
}
sayHi = () => {
console.log(
`姓名:${this.name},学号:${this.number}`
)
}
}
class Teacher extends People {
constructor(name, major) {
super(name)
this.major = major;
}
sayHi = () => {
console.log(
`姓名:${this.name},课程:${this.major}`
)
}
}
const Melody = new Student('Melody', 21);
console.log(Melody.name, Melody.number); // Melody 21
Melody.eat(); // Melody eat something
Melody.sayHi(); // 姓名:Melody,学号:21
const Coco = new Teacher('Coco', 'English');
console.log(Coco.name, Coco.major); // Coco English
Coco.eat(); // Coco eat something
Coco.sayHi(); // 姓名:Coco,课程:English
原型 与 原型链 图解
1、原型关系:
- 每个 class 都有显式原型 prototype
- 每个实例都有隐式原型_proto_
- 实例的 _proto_指向对应 class 的 prototype
2、基于原型的执行原则
- 获取属性 xialuo.name 或执行方法 xialuo.sayHi() 时
- 先在自身属性和方法中寻找
- 如果找不到则自动去_proto_中查找
3、原型图解
4、原型链图解
写一个贴近实际开发原型链继承的例子
function Elem(id) {
this.elem = document.getElementById(id);
}
Elem.prototype.html = function (val) {
var elem = this.elem;
if (val) {
elem.innerHTML = val;
return this // 链式操作
} else {
return elem.innerHTML;
}
}
Elem.prototype.on = function (type, fn) {
var elem = this.elem;
elem.addEventListener(type, fn);
return this; //链式操作
}
var div1 = new Elem('mainBox');
// console.log(div1.html())
div1.html('<p>hello imooc</p>').on('click', function() {
alert('clicked');
}).html('<p>javascript</p>');
补充:
ES6 引入了 Class(类)这个概念作为对象的模板。通过 class 关键字可以定义类。
- constructor 方法是类的默认方法,通过 new 命令生成对象实例时自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。
- 类必须使用 new 来调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
- 类不存在变量提升(hoist),这一点与ES5完全不同。
- Class 可以通过 extends 关键字实现继承
- 子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。
- ES5 的继承实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(People.apply(this)。
- ES6 的继承机制完全不同,实质是先创造父类的实例对象 this(所以必须先调用 super 方法),然后再用子类的构造函数修改 this。