手撕祖传原型链图(下)从构建过程学习理解思路
2023-11-02 18:26:56
JavaScript中通过prototype
构建原型链,它将[[Prototype]]
内部属性指向另一个对象,prototype
一般存储着对象或函数共有的属性和方法,而共用prototype
的对象/函数被成为它的实例,因此,对象/函数通过这种方式实现了对其他对象/函数的继承,它们继承了prototype
属性和方法。
6. 构造函数和原型
当我们在JavaScript中定义一个类的时候,通常会使用class
或者通过function
创建构造函数
的方式,如下所示:
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
在JavaScript中,函数作为一种特殊的对象,它拥有自己的prototype
属性,而通过class
创建的类,实际执行的是一种特殊的函数定义,只是语法糖更易于阅读书写,其本质是一个函数,class
关键字之后用constructor
标记的方法就是该类的构造函数,它本身也是一个函数,例如:
function Person(name) {
this.name = name;
}
构造函数和prototype之间的关系是prototype
属性指向构造函数的实例(constructor.prototype),而prototype存储着类的属性和方法,如下所示:
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
7. 通过原型链访问和修改对象属性
对象可以访问和修改prototype
中的属性,就像它们是自己的属性一样,例如:
const person = new Person('John');
console.log(person.name); // 'John'
person.name = 'Bob';
console.log(person.name); // 'Bob'
当一个对象访问一个不存在的属性时,JavaScript会在其原型链中向上查找,直到找到该属性或达到原型链的顶部(即null
),如果在原型链中找到该属性,则会返回该属性的值;如果在原型链中没有找到该属性,则会返回undefined
。
8. 原型链中的方法调用和修改
对象可以调用其原型链中的方法,就像它们是自己的方法一样,例如:
const person = new Person('John');
person.greet(); // 'Hello, my name is John'
当一个对象调用一个不存在的方法时,JavaScript会在其原型链中向上查找,直到找到该方法或达到原型链的顶部(即null
),如果在原型链中找到该方法,则会调用该方法;如果在原型链中没有找到该方法,则会抛出一个错误。
同样,也可以修改原型链中的方法,例如:
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old`);
};
9. 函数的原型和实例的原型
函数也有一个prototype
属性,该属性指向函数的实例(function.prototype),而函数的实例也有一个prototype
属性,该属性指向Object.prototype
,如下所示:
console.log(Function.prototype); // function Function() { [native code] }
console.log(Function.prototype.prototype); // {}
console.log(new Function().prototype); // {}
console.log(new Function().prototype.prototype); // { ... }
10. 函数的call/apply/bind和原型链
call()
、apply()
和bind()
方法允许函数在不同的上下文(this
值)中调用,这三个方法都接受一个对象作为第一个参数,该对象将作为函数的this
值,以下是一个例子:
const person = {
name: 'John',
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
const anotherPerson = {
name: 'Bob'
};
person.greet.call(anotherPerson); // 'Hello, my name is Bob'
在上面的例子中,greet()
方法被调用了,this
值被设置为anotherPerson
,所以this.name
的值是'Bob'
。
11. 原型链断裂
在某些情况下,原型链可能会被断裂,例如:
const person = new Person('John');
delete person.__proto__;
当原型链被断裂后,对象将无法访问其原型链中的属性和方法,如果尝试访问一个不存在的属性或方法,则会抛出一个错误。
12. 原型链图和继承图
原型链图和继承图是两种不同的图,原型链图显示了对象如何继承属性和方法,而继承图显示了类如何继承属性和方法。
原型链图如下所示:
Object
|
Person
|
John
继承图如下所示:
Object
|
Person
|
John
|
Bob
在原型链图中,每个对象都指向其原型,直到达到原型链的顶部(即null
),而在继承图中,每个类都继承自其父类,直到达到继承图的顶部(即Object
)。
总结
本文介绍了JavaScript原型链的概念,包括构造函数和原型、通过原型链访问和修改对象属性、原型链中的方法调用和修改、函数的原型和实例的原型、函数的call()
、apply()
和bind()
方法和原型链、原型链断裂、原型链图和继承图等概念。通过本文,读者应该能够理解原型链是如何工作的,以及原型链在JavaScript中的作用。