对一个知识点是否完全把握,最好的校验方法就是能否用自己的语言将其表述出来。
原型与原型链一直是学习 JS 绕不过的知识点,其中proto 与 prototype 最为让人头疼,这里简单的写下我自己的理解,从原型与原型链中拆解 proto 与 prototype ,希望能对大家有所帮助。
1,定义
在javascript中,函数可以有属性。 每个函数都有一个特殊的属性叫作原型(prototype)
注:prototype属性是函数特有,对象没有该属性
原型(prototype) 是什么东西呢,里面又有哪些属性呢?来,让我们拿个具体例子看下:
function fn() {};
console.dir(fn)
这里我们可以看出 原型(prototype) 是一个对象,对于当前例子来说,里面有 constructor 与 proto两个属性。那这个原型对象有什么作用呢?来,让我们继续往下看~
2,使用
现在我们知道了原型是一个对象,那原型对象有什么作用呢?实际上,原型是 ECMAScript 实现继承的过程中产生的一个概念。这里我们简单拿 ES5 的对象来举例子:
function Person(name) {
this.name = name;
}
Person.prototype.say = function() {
console.log(`My name is ${this.name}`);
}
let person = new Person('小明');
person.say(); // My name is 小明
让我们来逐步解释下这个例子:
我们发现,person可以调用其构造函数原型里的say方法,why?让我们看下 person 里都有什么:
实例 person 虽然自身没有 say 方法,但是通过 proto 属性访问到了其原型中的 say 方法。
为什么 proto 属性会指向其构造函数的原型 prototype 呢,他们之间是什么关系呢?让我们继续往下看~
1,__proto__
在介绍原型链前,让我们先看一个属性:proto,这是一个与原型 prototype 很相似的属性,这次让我们来彻底搞懂他们之间的关系。
让我们先来看MDN上的定义:
Object.prototype 的 proto 属性是一个访问器属性(一个getter函数和一个setter函数), 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)。
注:函数也是一种对象,所以其同时具有 prototype 与 __proto__ 两个属性
看不懂定义没关系,让我们举例说明:
let obj = {a: 1};
console.log(obj);
我们可以看出 proto 指向了一个对象,这个对象是什么呢?来,让我们继续看
class Parent {
constructor(name) {
this.name = name;
}
print() {
console.log(this.name);
}
}
let parent = new Parent('小明');
console.dir(parent);
console.dir(Parent);
咦,有没有发现 parent.__proto__ 与 Parent.prototype所指向的对象很相似,它们知否是同一个对象呢?
parent.__proto__ == Parent.prototype // true
结果是同一个引用,这个时候我们可以得出一个结论:实例的__proto__属性指向其构造函数的原型。那他们之间的这种关联关系有什么作用呢?这便涉及到了原型链。
2,原型链
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
注:因为对象没有prototyp属性,所以通过__proto__属性与原型prototype进行关联
文字描述很抽象,让我们通过ES5的继承来具体分析:
function Parent(name) {
this.name = name;
}
Parent.prototype.print = function() {
console.log(this.name);
}
function Child(name) {
Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
let child = new Child('小明');
console.log(child);
让我们来看下 child 里都有什么
这里可能有的同学对Object.create()这个函数不太熟悉,不明白其做了什么,不要急,让我们来看下其源码实现:
function create(proto) {
function F();
F.prototype = proto;
return new F();
}
声明了一个构造函数F,然后将其原型指向参数proto,返回构造函数的实例。好,让我们将整个过程串起来看一下:
1,声明 Parent 构造函数
2,声明 Child 构造函数,并手动绑定 this 指向
3,执行 Object.create(Parent.prototype):声明一个构造函数 F,更改其原型prototype指向(F.prototype = Parent.prototype),然后返回 F 的实例 f,注意这一步,实际上是 f.__proto__ == F.prototype
4,将 Child 的 prototype 指向 f.prototype
5,绑定 Child 的构造函数
让我们再来看下 child.print() 的调用过程
1,child对象里没有 print 函数,于是便在其原型上寻找:child.__proto__ ——> f.prototype
2,进入 f.prototype 中寻找 print 函数,发现没有,于是去其原型上寻找:f.__proto__ ——> F.prototype
3,F.prototype == Parent.prototype,于是便进入 Parent.prototype 中寻找 print 函数,有 print 函数,调用成功
怎么样,是不是豁然开朗!原型链其实就是通过 proto 与 prototype 的关联关系连接起来的,这样对象便可以寻找其原型上的方法与属性。详细的关系描述如下图:
实战
让我们来看一道面试题:
var F = function() {};
Object.prototype.a = function() {
console.log('a');
};
Function.prototype.b = function() {
console.log('b');
}
var f = new F();
f.a();
f.b();
F.a();
F.b();
解题思路如下:
1. f.a() ——> 实例 f 调用 a 方法,自身没有,从其原型中查找:f.__proto__ == F.prototype
2. F.prototype中没有 a 方法,于是继续在其原型中查找,F.prototype.__proto__ == Object.prototype
3. Object.prototype中有a方法,无b方法,所以f.a()结果为a,f.b()调用会报错:f.b is not a function
4. F.a():构造函数调用 a 方法,自身没有,从其原型中查找:F.__proto__ == Funtion.prototype
5. Funtion.prototype中有 b 方法,所以F.b()的输出为b。没有 a 方法,继续在其原型中查找,Function.prototype.__proto__ == Object.prototype
6. Object.prototype中有 a 方法,所以F.a()的输出为a
JS是一个非常有魅力的语言也是一个比较烦人的语言,主要就是因为他的特殊性灵活性。JS的原型链,需要深刻的研究才能搞懂。不要纠结细节吧。实在不行就按这个死背住,慢慢就理解了。总之吧就是一句话万物皆对象。
不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。
不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。
在工作中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,但是我通过查阅相关资料,决定做一下总结加深自己的理解
在JS中,函数的本质就是对象,它与其他对象不同的是,创建它的构造函数与创建其他对象的构造函数不一样。那产生函数对象的构造函数是什么呢?是一个叫做Function的特殊函数,通过new Function 产生的对象就是一个函数。
JS中原型是为了实现代码重用的一种仿类机制,不过它跟类又完全不同。它通过给对象添加原型关系(即给某个对象添加__proto__属性)实现一个关联。把共有的方法和属性放到这个关联上即实现了JS的继承。简单来说就是一种委托机制
了解JavaScript中原型以及原型链只需要记住以下2点即可:对象都有__proto__属性,指向构造函数的prototype;构造函数\\函数都有prototype属性,指向构造函数的原型
对象:1,函数对象:有function创造出来的函数2,普通对象:除开函数对象之外的对象,都是普通对象**即普通对象obj是构造函数Object的一个实例,因此:
每个函数都有一个 prototype 属性,函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是这个例子中的 cat 和dog 的原型。
这里不介绍原型链。javascript 中有若干长得跟prototype,proto很想的属性/函数,这里简单总结一下他们都是啥,哪个是原型对象,哪个不是。[[Prototype]]这个对象的一个内置槽,对程序员是不可见
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!