个人博客:haichenyi.com。感谢关注
一. 目录
- 一–目录
- 二–原型
- 三–原型链
二. 原型
什么是原型? 每个JavaScript对象都有一个原型,这个原型也是一个对象。比方说
function Person(name) {
this.name = name;
}
let person = new Person("张三")
console.log(person )
如上图,我们创建的对象Person,除了有我们定义的属性name之后,还有一个我们没有定义的属性[[Prototype]],指向其原型对象。
原型有什么用呢? 我们新生成的对象会继承原型对象上的属性和方法。比方说
console.log(person.toString())
如上图,我直接使用刚才创建的对象调用toString方法,我命名没有定义toString方法。为什么这里能打印呢?原因是:当前对象没有toString方法,但是它的原型对象上有。
一个调用方法,先到自身上找,如果,自身上没有,就到原型对象上找,原型对象上没有,就到 原型对象 的 原型对象 上找,直到原型对象是null为止。
三. 原型链
什么是原型链? 链是什么?链条。我们知道Object是所有对象的基类。我们看一下object的原型是什么。
const obj = {};
console.log(obj);
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
- 所有对象最终指向 Object.prototype
- Object.prototype.proto 为 null,表示原型链终点。
说什么半天到底什么是原型链呢?别急,我再说说原型的继承
function Person(name) {
this.name = name;
}
//在Person的原型上挂载了一个sayHello方法
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
//
function Student(name, grade) {
Person.call(this, name); // 继承属性
this.grade = grade;
}
// 继承方法,将Student的原型对象指向Person的原型对象
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // 修复构造函数指向
//在Student的原型对象上挂载一个sayGrade
Student.prototype.sayGrade = function() {
console.log(`Grade: ${this.grade}`);
};
const bob = new Student('Bob', 10);
bob.sayHello(); // Hello, Bob(来自 Person.prototype)
bob.sayGrade(); // Grade: 10(来自 Student.prototype)
console.log(bob)
//也就是这两句代码的作用
// 继承方法,将Student的原型对象指向Person的原型对象
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student; // 修复构造函数指向
我们还在Student.prototype上挂载了一个sayGrade方法。
如上图,sayGrade方法。
我们还在Person.prototype上挂载了一个sayHello 方法
如上图,sayHello方法。
原型链的验证
console.log(bob.__proto__ === Student.prototype); // true
console.log(Student.prototype.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
我们上面这个例子:
- bob是Student的对象
- Student继承Person
- Person是Object的子类
当bob调用一个方法时,
- 先到自身找,也就是Student上找,找到了,就执行,就结束了。如果没有找到
- 就到父类去查找,也就是Student的原型上去查找,也就是Person。找到了,就执行,就结束。如果还是没有找到
- 就到父类去查找,也就是Person的原型上去查找,也就是Object上去查找。找到了,就执行,就结束了。如果还是没找到
- 就到Object的原型上去查找,也就是Object.prototype。找到了,就执行,就结束了。如果还是没有找到,就到Object.prototype的原型上去查找
- Object.prototype__proto__,这个时候,就会发现对象时null。整个查找就结束了,如果执行到了这里,然后报异常了。
以上整个调用方法的过程,一层一层往原型上面去查找,一直找到了Object.prototype。就像一个链条一样。这个就是原型链。
PS:整篇文章,精炼一下,如下
当访问对象的属性或方法时:
- 查找对象自身属性。
- 若未找到,沿 proto 向上查找原型链。
- 直到找到属性或到达 null(抛出 undefined)。
不过,继承,现在不会这样去使用。ES6的class与extends就可以了(底层还是原型),使用起来更方便。其他语言也是用的class与extends实现继承。