一个JavaScript函数fn,被执行有三种途径:app
JavaScript中定义了一种对象,称之为ECMAScript对象,其内部实现包括如下:函数
注意:__prototype__是原型链,对全部对象都有的。prototype是原型,是函数才有的,就是一个普通的对象而已,目的是为了接受new后给生成的对象提供原型链的。this
执行fn就是调用__call__prototype
执行new fn()会进行如下简化过程:code
fn.call(obj)或者fn.apply(obj)就是将obj做为this,执行fn。本质是调用__call__,只是传入了obj做为this.对象
//定义一个函数,正常函数会具备__call__, __construct__ //同时Parent.__proto__指向Function.prototype function Parent() { this.sayAge = function() { console.log("age is: " + this.age); } } //原型上添加一个方法 Parent.prototype.sayParent = function(){ console.log("this is Parent Method"); } //定义另外一个函数 function Child(firstname){ //这里就是调用Parent的__call__, 而且传入this //而这里的this,是Child接受new时候生成的对象 //所以,这一步会给生成的Child生成的实例添加一个sayAge属性 Parent.call(this); this.fname = firstname; this.age = 40; this.saySomething = function() { console.log(this.fname); this.sayAge(); } } //这一步就是new的调用,按原理分步来看 //1. 新建了个对象,记做o //2. o.__prototype__ = Parent.prototype, 所以o.sayParent会访问到o.__prototype__.sayParent(原型链查找机制) //3. Parent.call(o), 所以o也会有个sayAge属性(o.sayAge) //4. Child.prototype = o, 所以 Child.prototype 经过o.__prototype__ 这个原型链具备了o.sayParent属性,同时经过o.sayAge 具备了sayAge属性(也就是说Child.prototype上具备sayAge属性,但没有sayParent属性,可是经过原型链,也能够访问到sayParent属性) Child.prototype = new Parent(); //这也是一步new调用 //1. 新建对象,记做s //2. s.__prototype__ = Child.prototype, 此时s会具备sayAge属性以及sayParent这个原型链上的属性 //3. Child.call(s), 执行后, 增长了fname, age, saySomething属性, 同时因为跑了Parent.call(s), s还具备sayAge属性, 这个属性是s身上的, 上面那个sayAge是Child.prototype上的, 即s.__prototype__上的。 //4. child = s var child = new Child("张") //child自己属性就有,执行 child.saySomething(); //child自己属性没有, 去原型链上看, child.__prototype__ = s.__prototype__ = Child.prototype = o, 这里没找到sayParent, 继续往上找, o.__prototype__ = Parent.prototype, 这里找到了, 执行(第二层原型链找到) child.sayParent();
原理来看写得有些繁琐,自己实际上是比较简单的东西。
重点是new的过程,原型prototype和原型链__prototype__
也正是new的原理,致使了原型链的继承,本质是生成的对象的__prototype__指向了函数的原型prototype继承
更复杂的调用继承之类的,均可以经过这个原理来理解。说白了,原型链继承就是复用了prototype而已。ip
本例来看,Child中的Parent.call(this)看似没有必要,但本质上是有区别的。若是去掉这一句,则Child的实例自己将没有sayAge属性,而Child.prototype具备sayAge属性,所以实例的__prototype__具备sayAge属性,所以还能够执行。原型链
但目的是为了继承,所以属性是须要对象上自己持有,而非是经过原型链上来访问,因此加上这一句是原理上的严谨要求。能够经过下面的例子来检验:原型
function Parent() { this.sayAge = function() { console.log("age is: " + this.age); } } Parent.prototype.sayParent = function(){ console.log("this is Parent Method"); } function Child(firstname){ Parent.call(this); this.fname = firstname; this.age = 40; this.saySomething = function() { console.log(this.fname); this.sayAge(); } } Child.prototype = new Parent(); var child = new Child("张") child.saySomething(); child.sayParent(); console.log(child.hasOwnProperty('sayAge')); // true child.sayAge(); //能调用,此时调用的是自身的属性 delete child.sayAge; // delete只能删除自身的属性,不能删除原型链属性 console.log(child.hasOwnProperty('sayAge')); // false,自身没有这个属性了 child.sayAge(); //还能调用,此时调用的是原型链上的方法
若是删掉Parent.call(this), 上面两句child.hasOwnProperty('sayAge'), 都将返回false