js原型链 原型 继承 Function

版权声明: https://blog.csdn.net/dashoumeixi/article/details/89049981

一句话:js中的继承用原型链实现.

先说一下原型 , 每个function都有一个prototype属性,称为原型,其实就一个Object对象,这玩意用来存放函数以及共享的变量,

就像是其他语言中的class:  只想说js中的东西比c++简洁多了

访问原型的方式 : 对象.__proto__   /  构造函数.prototype , js中任何一个对象都有__proto__, 是一个函数就有prototype

dom节点中的原型: 

<script type="text/javascript">
window.onload = function(){
				var div = document.getElementById("id");
				var pro = div.__proto__;
				while(pro){
					console.log(pro.constructor)
					pro = pro.__proto__
				}
			}
</script>

<body>
		<div id = "id"></div>
	</body>

通过js内置对象中的prototype 看看这东西:

            //调用String 构造函数
			var s = new String("nihaoa");
			
			//有没有indexOf属性呢?
			console.log(s.hasOwnProperty("indexOf"));//false
			
			//明明可以调用, 为什么没有这个属性?
			console.log(s.indexOf("o"))
			
			//同上,明明可以调用,为什么自身没有这个属性?
			//那么这些函数藏在哪里了?
			var d = new Date();
			console.log(d.getDate())
			console.log(d.hasOwnProperty("getDate"))

上面明明可以调用的函数为什么在自身对象中找不到 ?

他们都藏在了prototype中 . prototype指向的是一个对象. 这个对象默认就是一个new Object();

访问这个原型对象的方式一般就2种:

第一种: 通过构造函数的名字 ,比如 : String.prototype

扫描二维码关注公众号,回复: 6063011 查看本文章

第2种 : 通过一个对象 __proto__ ( 非标准, 即每个浏览器可能有自己变量名 ), 下面全部使用__proto__,如果你的浏览器不支持

__proto__ ,自己去查一下;

例子 , 沿用上面的代码:


            //通过对象的方式 , __proto__ 属性不一定在每个浏览器都适用, 非标准
            console.log(s.__proto__.hasOwnProperty("indexOf"))
			

            //通过构造函数名来访问 , String.prototype 
			console.log(String.prototype.hasOwnProperty("indexOf"))

            //Object.getOwnPropertyNames  枚举对象的属性
            console.log(Object.getOwnPropertyNames(String.prototype))

现在知道这些函数/方法都藏在了prototype指向的一个对象中,

给原型中添加一个函数:

            //给原型添加1个函数, 原型本身就1个Object ,因此随意添加即可;
            String.prototype.print_to_console = function(){
				console.log(this)
			}
			 
			var s = new String("12345");

            //调用这个刚刚添加的函数
			s.print_to_console()	

可以看到通过修改原型,可以随意添加任意函数/方法

自定义构造函数:

function x(){
}
console.log(x.prototype) //输出一个Object

console.log(x.prototype.constructor)  //输出了自己的构造函数

prototype : 指向一个Object 对象,其实就跟你 var o = new Object() 类似. 每个function对象都有这么一个属性 ,比如String.prototype , 只要是一个函数对象都会有这个属性,指向一个原型;

原型对象中一般情况下都有一个  constructor  与 一个 __proto__;

constructor. 指回自身构造函数,啥意思 ? 

function x(){
}

			console.log(x.prototype.constructor)

            
            //以下模拟 原型
            //自己创建一个对象
			var like_prototype = {};
            //添加一个属性 ,指向x
			like_prototype.constructor = x;
			console.log(like_prototype.constructor)

这玩意用来干嘛 ? 

用来维持实例对象与构造函数的关系的

	        function x(){}
			
			var o = new x();

            //这个对象是否通过这个构造函数生成的?
			console.log(o.constructor == x) // o.__proto__.constructor 

原型长什么样?

默认情况下有2个属性, 一个指向构造函数 , 还有一个__proto__属性,每个对象都有一个__proto__属性指向原型或用于继承(后面说);

从这里开始, 唯一需要记住的是, 每个对象中都隐藏着一个属性 __proto__ ,指向一个原型;

var obj = {constructor:x , __proto__ : object};

__proto__ 是什么? 先简单看一下, 指向原型

			function x(){
			}
			var obj = new x();
			console.log(obj.__proto__)
			console.log(x.prototype)
			console.log(x.prototype === obj.__proto__)

如果要在实例中访问原型则使用__proto__  , 与 prototype 指向同一对象;

__proto__ 太难看,我想改名怎么办?

			function x(){
				this.nihaoma = this.__proto__;
			}
			var obj = new x();
			console.log(x.prototype === obj.nihaoma)

总之,不论是__proto__  还是 prototype 都指向一个对象 , 这个对象用于添加函数及存放共享的变量.

对于有其他语言背景的人来说原型就是一个class,  不过不同的是这个class是一个对象,不仅仅是一般意义上的class

原型用来干嘛? 添加函数及共享的变量 ( 其他语言中的静态变量)

			function x(){
			}

            //给原型中添加属性
			x.prototype.name = "名字";
            //加个函数
			x.prototype.say_name = function(){
				console.log(this.name);
			}

			var o1 = new x();
			o1.say_name()  // 由于o1本身(this)中没有name, 就往__proto__中找
			var o2 = new x();
			o2.name = "nihao";  //陷阱, 此时o2添加了一个name属性
			o2.say_name()       //o2本身有name了,停止继续往__proto__中找
			o1.say_name()       //还是__proto__中的name, 并没有被修改

属性的查找: 先找自身,没有 -> 找原型 (__proto__) 中有没有  , 没有 -> 再找原型的原型(这部分在原型链中) ,还没? 直到

找到__proto__为空如果还没找到, 则报错; 先简单理解, 总之

自身对象有就用,没则去原型找;

一个使用原型的例子 , 给window对象添加一个产生随机数:  这个例子直接解释了为什么我们可以直接使用String, Array .....

第一种写法:

            (function(){
                //构造函数
				function rand(){
				}
                //给原型添加一个函数
				rand.prototype.get_rand = function(min){
					return Math.floor( Math.random() *min);
				}
            
                //给window对象添加1个属性 = 创建1个对象
                //此 this === window; 如果不太了解this, 可直接改成window.rand = new rand();
				this.rand = new rand();


			})();
			
			console.log(window.rand.get_rand(10))   

第二种写法: 这种最为常见,  模仿了String , Array 等 作为 window 这个全局对象的属性

            (function(){
				function rand(){
					
				}
				rand.prototype.get_rand = function(min){
					return Math.floor( Math.random() *min);
				}
				this.rand = rand;  //不再创建对象, 而是指向一个构造函数;
			})();

                
			//此时 window.rand 是一个构造函数;
			var r = new rand();     // 相当于  window.rand
			console.log(r.get_rand(10))

            //这种写法也是js中最常见的. 比如说String, Array;
    			console.log("rand" in window)
			console.log("String" in window)   //String === window.String
			console.log("Array" in window)    //window.Array === Array

想看看对象或原型中到底有什么属性:

			function x(){
			}
			x.prototype.name = "名字";
			x.prototype.say_name = function(){
				console.log(this.name);
			}
			var o1 = new x();
			var o2 = new x();
			o2.name = "nihao";

            //o1自身属性
			console.log(Object.getOwnPropertyNames(o1))
            //o2自身属性
			console.log(Object.getOwnPropertyNames(o2))
			
            //原型中的属性
			console.log(Object.getOwnPropertyNames(o1.__proto__))

上面有2个对象, 他们的原型是否一致呢? 当然: 

console.log(o1.__proto__ === o2.__proto__)

对于有其他语言的人来说你可以把原型就当成是一个class.

原型既然是一个Object ,而且里面就只有一个constructor . 是否可以自定义呢? 可以:

			function x(){
			}
			x.prototype = {
				name :"我是原型呀",
				talk:function(){
					console.log(this.name);
				},
			}
			var o1 = new x();
			o1.talk()

上面使用字面量创建一个原型. 

但有些问题:

            console.log(o1 instanceof x)  // true
			console.log(o1.constructor === x) //false

忘记加了构造函数的指向了  , 所以构造函数指到另一个地方去了.指去哪了呢? Object的构造函数, 这是原型链中会说的;

因此加一个构造函数的指向就ok:

x.prototype = {
				name :"我是原型呀",
				talk:function(){
					console.log(this.name);
				},
				constructor :x
			}

需要注意的一点 , 先确定原型 , 再 new , 换个位置看看情况:

			function x(){
			}
			var o1 = new x();
			
			x.prototype = {
				name :"我是原型呀",
				talk:function(){
					console.log(this.name);
				},
				constructor :x
			}
			
			o1.talk()  // 你确定能执行??????

此时, 在 new 时, o1的原型已经确定了 , 是默认的Object 对象, 后来又通过 x.prototype 修改了原型,但是, o1.__proto__不会动态的

改变,还是指向默认的Object. 可以查看一下:

console.log(x.prototype === o1.__proto__)

一般情况使用的构造函数与原型的混合模式 ,兼顾了构造函数传参与原型自定义:

			function x(name , age){
				this.name = name;
				this.age  = age;
			}
			
			x.prototype = {
				name :"我是原型呀,就像其他语言的static变量一样",
				talk:function(){
					console.log(this.name);
				},
				constructor :x
			}
			
			var o = new x("我是实例属性呀",10);
			o.talk()

关于Object: 拿Object 与 我们自定义的对象做类比:

			function Person(){
			}
					
			var o = new Person();
			//默认原型为Object对象 . 相当于 o.prototype = new Object();
			console.log(Person.prototype)
			
			//通过原型对象可以找到我们自己的构造函数
			console.log(Person.prototype.constructor)
			
			//Object的构造函数. 可以看到 Object也是一个 function ; function Object;
			console.log(Object.prototype.constructor)
			
			//Object与我们自定义的差不多;能否证明Object也有一个原型对象呢?
			console.log(Object.hasOwnProperty("toString")) // false , 既然自己没有,就在原型中咯;
			
			//在Object的原型中去找,找到了!
			console.log(Object.prototype.hasOwnProperty("toString"))
			
			//由此看来Object 与我们自定义的差不多嘛,有构造函数,也有原型;
			//我们自己的原型对象默认是一个object对象,由于只要是一个对象就有__proto__,那么原型对象中的__proto__ 指向谁呢?
			console.log(o.__proto__.__proto__ == Object.prototype) //true;			

这一部份看不懂没关系,下面原型链会说:

		
			var o = {}
			o.talk = function () {
				console.log("talk")
			}
			console.log("原型对象: " +o.__proto__ )
			console.log("2个原型是否一样? " + ( o.__proto__  === Object.prototype ))
			
			console.log("是否有talk? "+ o.hasOwnProperty("talk"))
			console.log("为什么能调用toString ? " + o.toString +"\n 是否有toString? " + o.hasOwnProperty("toString"));
			
			console.log("toString是否在原型中 ? " + (o.__proto__.hasOwnProperty("toString")));

最后,总结一下 原型就是一个对象, 用来存放函数以及共享变量的,与其他语言中的class类似;

原型链: 

先看看 Object的原型 , Object 构造函数, 对象 , 以及原型都与我们自己创建的差不多:

            var o = new Object();
			console.log(Object.prototype === o.__proto__) 

那么我们自己创建的函数对象的prototype  === new Object() , 这个原型对象也有一个__proto__ 指向Object的原型;

在看下面代码前,应该要有原型的基础, 否则你很可能看不懂哦.

			function x(){
			}
			var o1 = new x();
            
            //为什么能用toString() ????
			console.log(o1.toString())

            // 看看自身有没有toString , 好像没加过这个函数呀...
			console.log(o1.hasOwnProperty("toString"))  // false


            // 来查查看自己有什么属性 
			console.log(Object.getOwnPropertyNames(o1)) //哎呀没有,空的

问题来了.

toString这倒霉孩子,

哪来的? 继承来的

 从哪继承来的? Object 

能通过现有的知识查出来吗? 可以!

用什么办法 ? 原型链!

原型链好高深? 不, 你已经学了原型,原型链就一概念.不高深,就像链表,反正就连连连呗,简单的很.

从之前的讲解中可得知 , 我们既可以使用x.prototype ,也可以使用实例的 __proto__属性来找到原型, 也知道原型默认情况就是一个

Object 对象, 里面就一个constructor属性. 但Object中还有个属性没显示出来 __proto__ ,只是他还没办法通过

Object.getOwnPropertyNames枚举出来.( 我们自己的实例有__proto__ 属性, Object对象中当然也有, 就是从Object继承来的)

			function x(){
			}
			
            //枚举属性
			console.log(Object.getOwnPropertyNames(x.prototype)) 
			

            //看看是啥?
			console.log(x.prototype.__proto__) //object

(有其他编程语言的可以把原型对象当成class , 但不完全是.)

我们知道x.prototype 是一个原型对象,既然是对象内部肯定会有1个__proto__ . 那么他里面有一个__proto__ 意味着什么? 还有一个原型对象??

正解!

查看一下里面有啥?

			function x(){
			}
			
/*
            x.prototype  是我们自己的原型对象.
            x.prototype.__proto__  是我们的原型对象的原型对象
            x.prototype.__proto__  是谁呢? 先看看下面输出
*/

            //好多东西呢 ....!!! Object 中所有的函数都有
			console.log(Object.getOwnPropertyNames(x.prototype.__proto__))


            //看看构造函数..
			console.log(x.prototype.__proto__.constructor)



            //看看Object的原型中的构造函数与我们是否一致?? true!!
            console.log(Object.prototype.constructor === x.prototype.__proto__.constructor) 

x.prototype 是 x 自己的原型对象 , x.prototype.__proto__ 所指向的是Object的原型对象 ;

一个默认的原型对象, __proto__ 默认指向的就是 Object.prototype ,

这也是为什么我们创建的任何一个对象都可以使用Object中的方法;

如之前所说, 我们自己的原型对象不过就是一个 new Object() 里面多了一个constructor, 与 每个对象都有的__proto__;

现在看一下Object 的原型与对象的关系:

 构造函数,对象与原型的关系与我们自己的对象完全一致


            /*
                Object 本身就是一个构造函数
                function Object(){
                    ...
                }
            */

            // 这个 o 相当于 x.prototype
			var o = new Object();
			console.log(o.__proto__ === Object.prototype)

再次回顾之前提到的 如果在对象自身找不到属性, 则去__proto__ 中找, 如果找不到再去 __proto__ 找;

把上面的话转成这个代码该怎么解释呢?

			function x(){
			}
			
			var o = new x();

            //去哪找????  自己没有, 去__proto__ 中找
			o.toString()

            //看看__proto__中有吗?
            console.log(o.__proto__.hasOwnProperty("toString")) //false

            
            // 去原型对象的__proto__再去找
            console.log(o.__proto__.__proto__.hasOwnProperty("toString")) //true

o.__proto__.__proto__ 是Object的原型对象, 因此找到了toString.

Object的原型对象有没有__proto__ ??

    
    //Object的原型对象 有__proto__吗?
    console.log(o.__proto__.__proto__.hasOwnProperty("__proto__")) //true

    //看看是啥??
    console.log(o.__proto__.__proto__.__proto__) // null , 没了.

现在比如:

			o.xxxx(); //没有这个函数 : Uncaught TypeError: undefined is not a function
            

为什么会报错?

还是一样, 首先会找 自身有没有 , 如没有到__proto__ ,再没有再去__proto__,直到 __proto__ 为null ,如果还没找到就报错啦.

ok , 这个就叫原型链 , 找啊找啊找, 一层层找__proto__;

总之:原型链就是找东西, 以上面的代码为例, 先从 o 自身找, 没有则找 o 的原型, o的原型再没有则找o原型的原型,而这个原型就是Object.prototype,要是还没有,Object.prototype.__proto__ 就为null,也就停止查找.

以上唯一需要理解的是一个原型对象就是一个 new Object() (多了 constructor ) , 并且这个对象的__proto__默认指向了Object.prototype

继承: js中的继承依靠原型的改变;

            function Person(name){
				this.name = name;
			}

            //给默认原型添加函数
			Person.prototype.talk = function(){
				console.log( this.name+" talk");
			}
			Person.prototype.havesex = function(){
				console.log( this.name+" havesex")
			}
			

            
			function SuperMan(skill){
				this.skill = skill;
			}

            //改变默认原型的指向, 指向了一个 Person 对象
			SuperMan.prototype = new Person("超人");
			var super1 = new SuperMan("发射激光");
            
            //这样就可以使用Person中的函数了
			super1.havesex()

附注: 既然默认原型就是一个new Object() . 那我们可以给任意一个对象的prototype 赋值一个自定义对象,

通过原型链的方式层层往上找调用函数;

        //super1.__proto__ 就是 new Person()对象, 此对象中的__proto__ === Person.prototype;
        console.log(super1.__proto__.__proto__ == Person.prototype)

继承的通用方式:

            function Person(name){
				this.name = name;
			}
			Person.prototype.talk = function(){
				console.log( this.name+" talk");
			}
			
			
			function SuperMan(name,skill){
				this.skill = skill;
				Person.call(this,name);  //借用了Person构造函数,初始化this的属性
			}

            //new Person 不再传参数了,否则所有SuperMan对象的属性都一致了
			SuperMan.prototype = new Person();

            //通过借用构造函数来初始化
			var super1 = new SuperMan("超人","发射激光");
			super1.talk()
			

最后说一下 Function :

对于一个普通函数, 由new Function 来完成,即所有的函数都是Function的实例.

call,apply -> 改变this的指向. 

        //对于x, 本身即是一个函数对象, 也是一个构造函数
			function x(msg){
				console.log(msg)
			}
			x("函数调用") // 1. 作为函数调用 
			new x("构造函数") //2. 产生一个实例对象
			
			//因此对于x而言,既有x.prototype , 也有x.__proto__
			//x.prototype 是给new x() 来使用的
			//比如:
			x.prototype.test_func = function(){
				console.log(this.__proto__ === x.prototype)
			}
			//此时,obj这个对象的__proto__ === x.prototype,2个原型相等;
			var obj = new x("构造函数");
			obj.test_func();
			
			
			//对于函数本身可以使用call,apply. 为什么?
			x.call(null , "call这个函数在哪?") //null 相当于 window
			
			//在x.__proto__ 这个原型中. 
			console.log(Object.getOwnPropertyNames(x.__proto__))
			
			//x.__proto__ 就是Function的原型,因此可以找到call
			console.log(x.__proto__ === Function.prototype)

bind: 相当于c++ 中的std::bind , python 中的functools.partial , 用于参数绑定;

bind将返回一个可调用函数:

            function x(x,y){
				console.log(x +":" +y);
			}
			
			//bind,返回一个函数, bind,第一个参数与call,apply一样,后面的都是函数参数
			var ff = x.bind(null,20); //20 绑定到 x
			console.log( typeof ff)  // function
			ff(10); //调用ff(10), 相当于 x(20,10);

        
            

bind的第一个参数与call,apply一样:

			function x(){
				this.name = "构造函数";
			}
			 
			x.prototype.test = function(){
				console.log( "test:" + this.name)
			}
			var o = {name:"我是obj"};
			var obj = new x();
			//把 o 这个对象作为上下文传递给this,此时 this === o;
			var func = obj.test.bind(o); 
			func();

猜你喜欢

转载自blog.csdn.net/dashoumeixi/article/details/89049981