1. 引言
- js原型与原型链是js的一大基础知识,在面试中高频出现,在平时的学习中也尤为重要。故而总结了此篇文章,如有错误,请评论。
2. 普通对象与函数对象
-
在学习原型与原型链之前要先回顾一下普通对象与函数对象的概念,这对于我们理原型与原型链有至关重要的作用。
-
首先,在js中万物皆为对象。但对象与对象也是有区别的,分为普通对象与函数对象。其中Object和Function是js自带的函数对象。代码示例说明。
// Object和Function都属于对象(函数对象) console.log(Object instanceof Object); //true console.log(Function instanceof Object); //true // 普通对象 var o1 = {}; var o2 = new Object(); console.log(typeof o1); // object console.log(typeof o2); // object // 函数对象 function fn1() {}; var fn2 = function() {}; var fn3 = new Function(); console.log(typeof fn1); // function console.log(typeof fn2); // function console.log(typeof fn3); // function
在以上代码中可以看出o1,o2,o3都是普通对象,fn1,fn2,fn3都是函数对象。如何区分,其实很简单,只要是通过new Function()创建的都是函数对象,其他的都是普通对象。fn1,fn2归根结底也是由new Function()创建的。而 Function Object(相当于构造函数)也都是由new Function()创建的。
一定要分清 普通对象与函数对象。
3. 通过构造函数了解原型
-
构造函数示例代码
function Person(name, age) { this.name = name; this.age = age; } var p1 = new Person('jack', 20); console.log(p1.name); // jack console.log(p1.age); //20 console.log(p1.constructor == Person) // true
以上代码,Person是构造函数,p1为构造函数的实例,p1有一个constructor属性,该属性(是一个指针)指向Person。
由此总结出:实例的constructor属性指向构造函数。
4. 原型对象
-
在js中,每当定义一个对象的时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个
prototype
属性,这个属性指向函数的原型对象。使用原型对象的好处是所有对象实例都共享它的属性和方法。function Person() { Person.prototype.name = 'jack'; Person.prototype.say = function () { alert(this.name + ' say ...'); }; } var p1 = new Person(); var p2 = new Person(); p1.say(); // jack say ... p2.say(); // jack say ... console.log(p1.say == p2.say) // true
-
那什么是原型对象呢?
将上面的例子修改一下:
Person.prototype = { name: 'jack', say: function () { alert(this.name + ' say ...'); } }
原型对象就是一个普通的对象,即Person.prototype。
-
在上面,为原型对象Person.prototype添加了name,say两个属性,它还有一个默认属性:
constructor
在默认情况下,所有的原型对象都会自动获得一个consrtuctor(构造函数)属性,这个属性指向(是一个指针)指向 prototype 属性所在的函数(Person)
用代码解释上面的话:
console.log(Person.prototype.consrtuctor == Person) // true
故而得出结论:原型的constructor属性指向其构造函数
-
我们也可以这样理解原型对象:原型对象是构造函数的一个实例,构造函数先创建了一个实例,并将它赋值给了自己的prototype属性,之后的实例创建就以它为基准。用代码来展示:
// 此代码只作为理解,真实不能运行 var new = new Person(); Person.prototype = new; // 那么 console.log(new.constructor == Person) // true console.log(Person.prototype.constructor == Person) // true
这样我们也能解释通为什么原型的constructor属性指向构造函数了。
5. 什么是__proto__?
-
js在创建对象时(不论是普通对象还是函数对象)都有一个__proto__的内置属性,用于指向创建它构造函数的原型。
p.__proto__ == Person.prototype
要注意的是:关系链是直接存在于实例与原型之间,而不是与构造函数之间。
6. 原型链
-
简单的来说:原型链就是构造函数,原型与实例三者之间的关系链。
我们用代码描述:
// 构造函数 function Person () {} // 实例 var p = new Person(); // 对象的__proto__属性指向创建它的构造函数的原型,以此来查找原型链出口 console.log(p.__proto__ === Person.prototype); console.log(Person.__proto__ === Function.prototype); console.log(Function.__proto__ === Function.prototype); // 这样会出现循环,故而寻找Function.prototype的原型 // 上文说过原型对象就是一个普通的对象,故而可以将Function.prototype看作一个object console.log(Function.prototype.__proto__ === Object.prototype); console.log(Object.__proto__ === Function.prototype); // 再找下去又会进入循环,故输出Object.prototype的原型 console.log(Object.prototype.__proto__ ); // null // 此时就为原型链的出口 null
如果还不了解,那就参考下方图片,写写代码,相信你一定会的
6. 总结
- 原型和原型链是JS实现继承的一种模型。
- 原型链的形成是真正是靠__proto__ 而非prototype。
以上就是小编对于原型与原型链的理解了,如有错误,请各位指正,感谢!