首页 > 代码库 > 深刻理解下js的prototype
深刻理解下js的prototype
参考 http://aralejs.org/class/docs/competitors.html, http://www.iteye.com/topic/248933,http://www.cnblogs.com/qiantuwuliang/archive/2011/01/08/1930548.html
1、使用Dog.prototype.__proto__
1 2 3 4 5 6 7 8 9 10 11 | function Animal() {} function Dog() {} // 要让 Dog 继承 Animal, 只需: Dog.prototype.__proto__ == Animal.prototype; // 实例化后 var dog = new Dog(); // dog.__proto__ 指向 Dog.prototype // dog.__proto__.__proto__ 指向 Animal.prototype // 原型链已成功建立起来,而且很清晰 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function Animal(name) { this .name = name; } Animal.prototype = { talk: function () {}, run: function () {} } function Dog(age,name) { //Animal.call(this,name) this .age=age; } // 要让 Dog 继承 Animal, 只需:__ 2个_ Dog.prototype.__proto__ = Animal.prototype ; // 实例化后 //Dog.prototype = new Animal ; //Animal的构造函数和自己的prototype也都放到Dog上 Dog.prototype.haha = function () {}; // Dog.prototype.haha.tata = 4; var dog = new Dog( "sdd" , "bbb" ); var animal = new Animal( "aaaa" ); console.log(dog); console.log(Dog.prototype); console.log(animal.__proto__); console.log( "11 " +(dog instanceof Animal)); console.log( "11 " +(animal instanceof Dog)); |
- name: "sdd"
__proto__指向了dog本身,并且有constructor,开始循环引用了。dog.__proto__=Dog.prototype。
dog本身的Dog.prototype没有被覆盖,一个标准的prototype,包括constructor和__proto__,我们只覆盖了这个标准的prototype的__proto__而已。
如果不加__proto__
function Animal(name) { this.name = name; } Animal.prototype = { talk: function() {}, run: function() {} } function Dog(name) { Animal.call(this,name) } // 要让 Dog 继承 Animal, 只需: Dog.prototype = Animal.prototype; // 实例化后 var dog = new Dog("sdd"); console.log(dog);
返回
- name: "sdd"
第二种原型链没有建立,没有constructor,只是简单的把方法赋值过来了。上面用 console.log(dog instanceof Animal ) 都返回ture。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function Animal(name) { this .name = name; } Animal.prototype = { talk: function () {}, run: function () {} } function Dog(name) { Animal.call( this ,name) } // 要让 Dog 继承 Animal, 只需: Dog.prototype = Animal.prototype; //有没有有__proto__都会被覆盖 Dog.prototype = { qqq: function () {}, wwww: function () {} } // 实例化后 var dog = new Dog( "sdd" ); console.log(dog); console.log(dog instanceof Animal ); |
Dog {name: "sdd", qqq: function, wwww: function} name: "sdd" __proto__: Object qqq: function () {} wwww: function () {} __proto__: Object
Dog.prototype被覆盖了,本身的constructor和__proto__不存在了,变 Animal.prototype了。
2,使用new
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function Animal(name) { this .name = name; } Animal.prototype = { talk: function () {}, run: function () {} } function Dog(age,name) { //Animal.call(this,name) this .age=age; } // 要让 Dog 继承 Animal, 只需:__ 2个_ //Dog.prototype.__proto__ = Animal.prototype ; // 实例化后 Dog.prototype = new Animal ; //Animal的构造函数和自己的prototype也都放到Dog上 Dog.prototype.haha = function () {}; var dog = new Dog( "sdd" , "bbb" ); var animal = new Animal( "aaaa" ); console.log(dog); console.log(dog.__proto__); console.log(animal.__proto__); console.log(dog.__proto__===Dog.prototype); |
Dog {age: "sdd", name: undefined, haha: function, talk: function, run: function} age: "sdd" __proto__: Animal haha: function () {} name: undefined __proto__: Object run: function () {} talk: function () {} __proto__: Object
new 是调用构造函数的,会把构造函数上的属性拿过来,没有constructor,只有new 带构造函数。__proto__是实例化后才有,他代表的原型链,可以共享的原型链,上图中方法haha 也放到__proto__中了。
Dog.prototype被覆盖了,本身的constructor和__proto__不存在了,变 new Animal了,于是构造函数都传过来了。
没有继承的情况下,如下图所示,prototype没有被覆盖,constructor保留,是Dog本身这个函数。dog.constructor === Dog.prototype.constructor
function Dog(age,name) { //Animal.call(this,name) this.age=age; this.getName = function (){}; } var dog = new Dog("sdd","bbb"); console.log(dog); //结果 Dog {age: "sdd", getName: function} age: "sdd" getName: function (){} __proto__: Dog constructor: function Dog(age,name) { __proto__: Object
constructor: function Dog(age,name) { arguments: null caller: null length: 2 name: "Dog" prototype: Dog __proto__: function Empty() {} <function scope>
按照《悟透javascript》书中说的,new形式创建对象的过程实际上可以分为三步:
第一步是建立一个新对象(叫A吧);
第二步将该对象(A)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象;
第三步就是将该对象(A)作为this 参数调用构造函数(就是Person),完成成员设置等初始化工作。
其中第二步中出现了一个新名词就是内置的原型对象,注意这个新名词跟prototype对象不是一回事,为了区别我叫它inobj,inobj就指向了函数Person的prototype对象。在person的prototype对象中出现的任何属性或者函数都可以在one对象中直接使用,这个就是javascript中的原型继承了。
总结: 1)Dog.prototype = Animal.prototype 或者Dog.prototype.__proto__ = Animal.prototype 时,dog instanceof Animal 都是true。Dog.prototype.__proto__ = Animal.prototype,实际实例化后是 dog.__proto__.__proto__=Animal.prototype。沿着A的原型链查找 如果有一个原型和B.prototype相等 则返回true , 如:A.__proto__.__proto__ === B.prototype 则返回true
js的 instanceof 是根据什么来判断两个对象的继承关系?参考:http://yxc-gdut.iteye.com/blog/1812766
js的instanceof是根据prototype来判断两个对象是否存在继承关系,A instanceof B, js会沿着A的原型链查找 直到找到A.__proto__ === B.prototype 返回true。
一句话__proto__一层层都是指向继承的,最终到基本类型上。没有继承的就是他的protoype,包含constructor: function () {},__proto__: Object,和他自己加的方法。
下图展示的new方法的,Dog.prototype = new Animal ;所以dog第一层__proto__有name, new把构造函数有传入了。只有有prototype chrome下才能展示出来下面的结构。
1 2 3 4 5 6 7 8 9 | Dog {age: "sdd" , name: undefined, haha: function , talk: function , run: function } age: "sdd" __proto__: Animal haha: function () {} name: undefined __proto__: Object run: function () {} talk: function () {} __proto__: Object |
2)理解下function ,如下图。function ,包括 arguments, caller,length,name ,prototype,__proto__,他的prototype,又分为constructor: function () {},__proto__: Object,然后constructor这个function 又继续循环。
__proto__他从别的原型链继承过来可以直接用的,prototype是他要加在自己原型链上的,供别人调用,或者直接实例化后,别人可以直接调用转成__proto__的。
run: function () {} arguments: null caller: null length: 0 name: "" prototype: Object constructor: function () {} __proto__: Object __proto__: function Empty() {}
1 2 3 4 5 6 7 | var AAA= function (name) { this .name=222; } AAA.dhj= "123" ; AAA.prototype.www= function (){}; var aaa= new AAA(); console.log(aaa); |
有继承关系的__proto__他指向的是指定的继承的那个,没有继承关系的__proto__依然保留constructor: function () {},__proto__: Object。
AAA {name: 222, www: function} name: 222 __proto__: Object constructor: function (name) { www: function (){} __proto__: Object
按照javascript的说法,function定义的这个是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。
下面分析下constructor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function Animal(name) { this .name = name; var a=1; } Animal.prototype.talk= function (){}; var animal = new Animal( "aaaa" ); function Dog(age,name) { //Animal.call(this,name) this .age=age; this .getName = function (){}; } // 要让 Dog 继承 Animal, 只需:__ 2个_ //Dog.prototype.__proto__ = Animal.prototype ; // 实例化后 Dog.prototype = animal ; //注意这里啊, Dog.prototype.constructor = Dog; //animal本身也有constructor,animal本身也指定到这里。引用传递。animal已经实例化了 |
1 | var dog = new Dog( "sdd" , "bbb" ); console.log(dog); console.log(dog.constructor);; console.log(animal); console.log(animal.constructor); |
Dog {age: "sdd", getName: function, name: "aaaa", constructor: function, talk: function} age: "sdd" getName: function (){} __proto__: Animal function Dog(age,name) { //Animal.call(this,name) this.age=age; this.getName = function (){}; } Animal {name: "aaaa", constructor: function, talk: function} constructor: function Dog(age,name) { name: "aaaa" __proto__: Animal function Dog(age,name) { //Animal.call(this,name) this.age=age; this.getName = function (){}; }