前面聊了一些对象的构造函数,前面如果了解的后就可以聊原型和原型链了,其实这个听着很瘆人,不过简单的了解然后换一个名词,觉得原来如此了。
原型(prototype)其实就是function的对象的一个属性,打印出来的话也就是一个对象,如下
function Objthis(name,year){ this.name=name; this.year=year; } console.log(Objthis.prototype)
既然如此其原型也是一个对象,那么他的对象可以添加属性吗?
Objthis.prototype.sex="女"; obj= new Objthis("张三",23); console.log(obj);
这样看似乎对其原型添加一个属性,对根据构造函数创建的对象似乎没什么影响啊?难道真的没有影响?
// 大胆搞一下 console.log(obj.sex)
神奇不,在原型上赋予sex属性的值,通过obj.sex竟然也可以打印出来。
这个时候又有疑问了,如果我为对象添加一个sex属性呢?那打印谁的?、
obj.sex="男"; console.log(obj.sex);
这个时候又有了一个大胆的想法
function Objthis(name,year,sex){ this.name=name; this.year=year; this.sex=sex; } Objthis.prototype.sex="女"; Objthis.prototype.address="中国"; obj=new Objthis("张三",23); obj1=new Objthis("李四",23,"男"); console.log(obj.sex,obj.address); console.log(obj1.sex,obj1.address);
总结:
通过上面的了解是否可以这样写:
function Objthis(name,year,sex){ this.name=name; this.year=year; this.sex=sex; }; Obj={ sex:"女", address:"中国", like:function(){// 为证明也可以继承方法,所以添加一个方法 console.log("喜欢看电视剧"); } }; // 声明对象的时,必须在这个后面,不然其prototype属性不会是Obj Objthis.prototype=Obj; obj= new Objthis("张三",23,"男"); obj.like();
前面说过一个constructor
function Objthis(name,year,sex){ this.name=name; this.year=year; this.sex=sex; }; obj= new Objthis("张三",23,"男"); // 打印其构造 obj.constructor
现在看一下原型的constructor。
可见常规情况下构造函数的constructor指向的是自己本身。
前面一直说原型,但是有没有发现一件事就,那就是构造函数默认是有prototype的,但是声明的对象,而对象却没有prototype属性,不过细心的回发现__proto__这个属性。
通过上面可以看出__proto__
和prototype
给关系以及区别
每个对象都有一个__proto__
属性,并且指向它的构造函数prototype
原型对象
每个构造函数都有一个prototype
原型对象
prototype
原型对象里的constructor
指向构造函数本身JavaScript中最常用的一句话,那就是万物皆对象,这句话,是否很神奇。
function test(){}
其实这个有点原型链的影子了,毕竟原型的原型是obejct,这个地方可以看出方法其实它的祖先也是一个object
前面一直说原型,和__proto__
属性,似乎没说原型链。原型链具体是什么?
# 方便演示原型链 function test(){} ; var j=new test();
这个图可以看出一点对象 j 的__proto__
下面还有一个__proto__
,当然最后当其构造方法变成你object()的时候就不在有了,可见obejet是所有__proto__
的重点。
当然__proto__
的构造函数也有自己的__proto__
,其终点也是指向object()。
原型链本质就是__proto__
指向了原型,而原型上也有一个__proto__
,这样一串的__proto__
等于prototype,所以称之为原型链。
前面聊原型和原型链,如果没有作用或者意义,那就没有什么出现的作用了。
如果有点java基础的,瞬间会联想到java中的父类和子类,以及其继承逻辑。而原型链也遵循这个逻辑。那就是继承属性。
老规矩先来一些代码:
function Country(){ this.countryName="中国"; this.countryFun=function(){ console.log("我来自的国家--",this.countryName); } } // 这个地方一定是new 对象 不然返回的不到继承的数据(后面解释) Province.prototype=new Country(); function Province(){ this.provinceName="河北"; this.provinceFun=function(){ console.log("我来自的省份--",this.provinceName); } } City.prototype=new Province(); function City(){ this.cityName="石家庄"; this.cityFun=function(){ console.log("我来自的城市--",this.cityName); } } var address= new City();
可以看出address 可以打印构造函数的属性以及方法,也可以待用原型链上的属性和方法。
Province.prototype=new Country();
这个地方为什么要new?
如果不new的话如下:
function Country(){ this.countryName="中国"; this.countryFun=function(){ console.log("我来自的国家--",this.countryName); } } Province.prototype= Country; function Province(){ this.provinceName="河北"; this.provinceFun=function(){ console.log("我来自的省份--",this.provinceName); } } City.prototype=Province; function City(){ this.cityName="石家庄"; this.cityFun=function(){ console.log("我来自的城市--",this.cityName); } } var address= new City();
contry上的属性和方法无法使用,为什么会这样?
前面说过对象__proto__
=构造函数的prototype
首先City.prototype=Province;
前面说过没有return this(new的作用说过)。而这个
简单来看就是
City.prototype=Province; function City(){ this.cityName="石家庄"; this.cityFun=function(){ console.log("我来自的城市--",this.cityName); } } var address= new City(); //具体效果如下 address.__proto__=function Province(){ // 这样函数就是一个空函数 return undefined } } //function Province() 不是对象 所以其prototype=Country
如果使用new的话:
City.prototype=new Province(); function City(){ this.cityName="石家庄"; this.cityFun=function(){ console.log("我来自的城市--",this.cityName); } } var address= new City(); //具体效果如下 address.__proto__=new Province(){ this.__proto__={//Country 的对象,有点套娃,所以不再写 } this.cityName="石家庄"; this.cityFun=function(){ console.log("我来自的城市--",this.cityName); } return this; } } // 这样一个逻辑 __proto__ 会一直下去从而完美的拥有了对象继承的属性
这个时候又有了一个疑问因为前面在Objthis.prototype.sex="女";
这样写的时候new 构造函数的时候就把prototype的属性继承给对象了啊?是咋回事呢?
var a={} a.name="李四"; 其实这个上面那种方式而已 Objthis.prototype.sex="女" 为Objthis.prototype创建一个属性为sex,然后赋值。等于直接赋值的。
不过一般这样用 function test() { this.testarr1 = []; this.testarr2 = []; } var testfather={ addarr: function(v) { this.testarr1.push(v); } }; test.prototype = testfather;// 直接prototype=实例化的对象 var t=new test(); t.addarr("1")
这个涉及到this指向,如果不了解可以看之前的博客,里面讲了this的指向以及如果修改this指向。
为什么一般说演示2是最常用的呢,很简单说一个需求
架构师写了一个祖先A。这个祖先构A里面的函数被很多人用
var A={ funA:function1(){} …………………… } function B(){ key:"value" }; B.prototype=A; var b =new B() ##一般都会将方法放在prototype中,而属性一般都会变,所以没有必要。
突然又发现,B继承了A的很多方法,而自己的方法又和C中常用都有,D没有相同的方法?
方法一: 将B的方法写入A var A={ funA:function(){} funB:function(){} …………………… } function B(){ key:"value" }; function C(){ key:"value" }; function D(){ key:"value" }; B.prototype=A; C.prototype=A; D.prototype=A; var b =new B(); var c =new C(); var d =new D(); # 上面看似解决问题,但是 一般总的类都不允许别人东的。但又想让方法变大可以重复利用咋办? 方法二: 方法一: 将B的方法写入A var A={ funA:function(){} …………………… } var A2={ funB:function(){} …………………… } A2.__proto__=A; function B(){ key:"value" }; function C(){ key:"value" }; function D(){ key:"value" }; B.prototype=A2; C.prototype=A; D.prototype=A2; var b =new B(); var c =new C(); var d =new D();
一般会用第二种方式将其中间添加一个对象,这样不会修改最顶端对象的方法他添加修改以及删除。
for (key in address){ console.log(key) }
可以看出可以得到整个原型链上的属性。
上面的for循环可以循环所有的属性,而hasOwnProperty可以判断整个属性是否是其是其构造函数的prototype上的属性。
for (key in address){ console.log(key , City.prototype.hasOwnProperty(key)) }
只能判断直系,如果隔代就无法判断。
var A={a:1}; // A 这个对象继承了Object.prototype上面的所有属性 // A 自身没有名为 hasOwnProperty 的属性 // hasOwnProperty 是 Object.prototype 的属性 // 因此 A 继承了 Object.prototype 的 hasOwnProperty // Object.prototype 的原型为 null // 原型链如下: // A ---> Object.prototype ---> null var arr = ["a", "b", "c"]; // 数组都继承于 Array.prototype // (Array.prototype 中包含 indexOf, forEach等方法) // 原型链如下: // arr ---> Array.prototype ---> Object.prototype ---> null function fun() { return 1; } // 函数都继承于Function.prototype // (Function.prototype 中包含 call, bind等方法) // 原型链如下: // fun ---> Function.prototype ---> Object.prototype ---> null
function test() { this.testarr1 = []; this.testarr2 = []; } var testfather={ addarr: function(v) { this.testarr1.push(v); } }; test.prototype = testfather;
Object.create
创建的对象 (ES5)var a = { k: 1 }; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.k); // 1 (继承而来) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null 因为d没有继承Object.prototype, 这个和 var d={}不一样下面看图
class
关键字创建对象 (ES6)这个以后单独聊ES6与ES5区别。下面这个也是一种方式。
class fatherFun { constructor(test) { this.test=test; } } class sonFun extends fatherFun { constructor(test) { super(test); } get fun1() { return this.test * this.test; } }