首先原型,原型链到底有什么用,为什么要去学习。 来看一段代码
const arr = [1,2,3];
arr.pop() //arr变为[1,2]
const obj = {name:"sdy"}
obj.toString() //res-> [object Object]
在这段代码中发现,对象与数组都可以调用一些方法来操作自己。但是当我们在控制台打印却又发现没有这些方法的存在。
所以我们这篇文章就是来说明这个问题。原型
在js这个语言中,所有的函数都是通过new Function来创建的,不论是我们自定义的函数,还是js内部的函数像:Object,Array等。
而每个函数都有一个prototype的对象,这个对象存储的就是函数的原型,当我们打印Object,Array的原型后,就会发现上面的toString,push方法都在里面的。
而在prototype里面又有一个constructor的属性,他又可以指向构造函数本身的
Object.prototype.constructor === Object //true
隐式原型__proto__
在所有的对象里面都有一个proto 的属性,而这个属性指向 创建该对象的构造函数的原型。
const obj = {};
obj.__proto__ === Object.prototype//true
//在这段代码中注意:在js中直接书写字面量对象,实际上就是通过new Object来创建的。
因此obj的构造函数就是Object
function Fun(){}
const obj1 = new Fun();
obj.__proto__ === obj1.prototype//true
理解上面代码后,这里也很容易
//关键的地方来了,既然所有的对象都有__proto__,那prototype也是对象啊,他也应该有__proto__,没错,prototype里面的__proto__,指向的就是Object.prototype
obj1.prototype.__proto__ === Object.prototype //true
原型链
当我们调用一个属性或方法的时候,首先会从创建自己的构造函数的prototype里面来调用,如果没找到的话,就会从prototype里面__proto__,指向的对象再去找,循环往复
上面那句话就是本篇文章的关键,特别重要
let arr = [1,2];
// 首先字面量的数组,都是通过new Array来创建的。
// 因此arr.__proto__,指向Array.protoype,而push方法就存放在Array.prototype里面的
看到这里我们最开始的疑问就基本解决了
这里有两点要特别注意1,Object.prototype.__proto__ 指向null。2,Function.__proto__ 指向Function.prototype.__proto__
圣杯模式
但是如果我们需要将自定义的2个函数,来实现继承该怎么办
//es5
function inherit(son,father) {
son.prototype = Object.create(father.prototype);
son.prototype.constructor = son;
son.prototype.uber = father.prototype;
}
//es5以前
var inherit = (function () {
var tmp = function (){};
return function (son, father) {
tmp.prototype = father.prototype
son.prototype = new tmp();
son.prototype.constructor = son;
son.prototype.uber = father;
}
})()