js面向对象学习1
Posted html55
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js面向对象学习1相关的知识,希望对你有一定的参考价值。
纯属笔记,加强记忆,不是教程,欢迎纠错,没有逻辑,不太适合学习使用。
--------------
继承多态等太多概念难以理解,还是从实践中慢慢学吧!争取能大致看懂网上的开源的代码。
--------------
对象的组成:方法和属性
属性关键词:静止的,状态
方法关键词:动态的,过程,处理,带括号
--------------
js中的面向对象不是其他语言那样的面向对象。
1 <script type="text/javascript"> 2 //定义arr为一个数组(数组也是一个对象实例,所以arr是个对象实例) 3 var my_arr=[]; 4 //以前变量这样用,var number=10; 5 //现在定义属性,arr.number = 10; 就是用“点”即可! 6 my_arr.number = 10; 7 //定义对象的方法 8 my_arr.test = function(){ 9 console.log(this); 10 //这里this就是这个数组,直接用数组名也可以 11 //alert(my_arr.number); 12 //alert(this.number); 13 } 14 //这样使用方法 15 my_arr.test(); 16 </script> 17 18
这里需要注意的是,能不能将第6行放在第3行前面?
不可以,没有到第3行赋值,my_arr还是undefined,而undefined是不能有特性的。
1 <script type="text/javascript"> 2 var a =\'ddd\'; 3 a.kkk = \'eee\'; 4 console.log(a.kkk); //火狐,打印出undefined ,可见,a不是一个对象类型的数据时,还不可以直接加“点”呢 5 a.ka = function (){ 6 console.log(\'aaaaaaaaaaaaaaaaaa\'); 7 } 8 a.ka(); //报错,a.ka is not a function 可见,a不是一个对象类型的数据时,还不可以直接加“点”赋予方法呢 9 </script>
1 <script type="text/javascript"> 2 var a =\'ddd\'; 3 String.prototype.kkk = \'eee\'; 4 a.kkk = \'eeeddd\'; 5 console.log(a.kkk); //火狐,打印出eee ,可见,a不是一个对象类型的数据时,还不可以直接加“点”呢,但是从字符串的原型上可以加 6 String.prototype.ka = function (){ 7 console.log(\'aaaaaaaaaaaaaaaaaa\'); 8 } 9 a.ka(); //aaaaaaaaaaaaaaaaaa 10 </script>
结果是数组有个number属性和test方法,但是数组内容为空,length为0,但是这样alert(my_arr[\'number\']);也可以弹出10
//TODO 以后再研究数组,但是可见,数组的内容是内容,属性和内容不是一回事,内容是数组里面存了啥,属性是这个数组实例有什么属性。如同,一个是汽车里装了谁谁,一个是汽车有车大灯的感觉。
------------
新建一个纯净的对象实例
1 <script type="text/javascript"> 2 var my_obj = new Object(); //新建一个对象实例my_obj,换成 var my_obj = {}; 也行,一个意思 3 my_obj.name = \'张三\'; //加个属性 4 my_obj.test = function(){ //加个方法 5 this.test2(); //对象的A方法中用到对象的B方法 6 } 7 my_obj.test2 = function(){ 8 console.log(this.name); 9 } 10 my_obj.test(); //使用 11 </script>
对象中思考变量作用域的问题
以前学到变量作用域,都是函数和变量,现在加入对象,看看是怎么回事
1 <script type="text/javascript"> 2 console.log(my_obj); // 显示undefined,说明解析器中和其他类型一样,根据var,先放个undefined再说 3 var my_obj = {}; 4 console.log(my_obj); // 显示 Object {} 5 my_obj.name = \'张三\'; 6 console.log(my_obj); // 显示Object {name:"张三"} 7 </script>
其实可以这样理解,js解析器,预解析,根据var找到了my_obj,赋值undefined,如果放到不用对象的时候,出个name,需要var name一下,这里由于对象var了,对象下各个目前未定义的属性就相当于已经var但没赋值了,一旦测试都是undefined。第4行,由于还没有赋予name属性,如果查看my_obj.name的话,就是undefined,而第5行赋值,第6行再看,就是“张三”了。
1 <script type="text/javascript"> 2 var my_obj = {}; 3 my_obj.name = \'张三\'; 4 console.log(my_obj.test); //undefined 5 my_obj.test(); //浏览器提示出错,说test不是一个函数。可见my_obj.test = function(){...} 只是一个赋值,并没有因为是function,就扔到预解析仓库中 6 my_obj.test = function(){ 7 console.log(this.name); 8 } 9 </script>
1 <script type="text/javascript"> 2 var my_obj = {}; 3 my_obj.name = \'张三\'; 4 my_obj.test = xxx; 5 my_obj.test(); //这样写是可以执行的,因为函数xxx扔到预解析仓库了,上一行又赋予test这个函数了,显示张三 6 function xxx(){ 7 console.log(this.name); 8 } 9 </script>
<script type="text/javascript"> var my_obj = {}; my_obj.name = \'张三\'; my_obj.test = function xxx(){ console.log(this.name); }; my_obj.test(); // 正常运行 </script>
1 <script type="text/javascript"> 2 var my_obj = {}; 3 my_obj.name = \'张三\'; 4 my_obj.test = function xxx(){ 5 console.log(this.name); 6 }; 7 xxx(); //浏览器报错,火狐认为,xxx未定义,也就是说,在等号后面的函数不会被预解析,这里不求预解析,连解析都没有 8 </script>
1 <script type="text/javascript"> 2 var my_obj = { 3 name : "张三", 4 test : function(){ 5 //对象也是个变量作用域,这里面是可以使用对象外面的函数的 6 //console.log(name); 不可以这样用,不能因为name和test平级,就不加this,属性名和变量是不同的,要加上对象名才能用 7 console.log(this.name); //console.log(my_obj.name); 这样也行 8 } 9 }; 10 my_obj.test(); //显示张三 11 console.log(my_obj.name); //想使用对象的属性,就要加对象名 12 13 </script>
-----------------------
这里新建对象不够灵活,如果要建的对象名不是张三呢,而是好几个,张三,李四,王二,麻子等很多人,那么就要用到工厂方式
还按照上面的方式就是如下代码
1 <script type="text/javascript"> 2 var my_obj1 = { 3 name: "张三", 4 test: function() { 5 console.log(this.name); //如果是console.log(my_obj2.name); 将显示李四,也就是用到了其他对象的属性。 6 } 7 }; 8 9 var my_obj2 = { 10 name : "李四", 11 test : function(){ 12 console.log(this.name); 13 } 14 } 15 my_obj1.test(); //显示张三 16 my_obj2.test(); //显示李四 17 </script>
js并不是其他语言那样的面向对象,这里的对象,其实是其他语言中 对象实例的意思。
上面代码怎么精简呢,共同部分用一个函数来生成即可,也就是所谓的工厂模式,如下
1 <script type="text/javascript"> 2 function CreatePerson(name){ //这个function就相当于一个生产对象的工厂 3 var obj = new Object(); 4 obj.name = name; 5 obj.test = function() { 6 console.log(this.name); 7 } 8 return obj; 9 } 10 11 var my_obj1 = CreatePerson(\'张三\'); //开始生产了 12 var my_obj2 = CreatePerson(\'李四\'); 13 my_obj1.test();//使用工厂生产出来的对象 14 my_obj2.test(); 15 </script>
在js中,用new去调用一个函数的话,这个函数中的this就是创建出来的对象,而且函数的返回值直接就是this 隐式返回
难以理解这句吧,就是js规定的,函数加new, 函数中的this就是对象实例本身,并且函数返回该对象实例(不用你操作了,js帮你搞定)。
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 console.log(this); //打印出的this就是张三 这里就相当于js帮你做了 var my_obj1 = new Object(); 最后return my_obj1; 5 } 6 var my_obj1 = new CreatePerson(\'张三\'); 7 console.log(my_obj1); //打印出的my_obj1就是张三 8 </script>
这样调用
var my_obj1 = new CreatePerson(\'张三\'); 是不是很像 var date = new Date();
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 this.test = function(){ 5 console.log(this.name); 6 } 7 } 8 9 var my_obj1 = new CreatePerson(\'张三\'); 10 my_obj1.test(); 11 </script>
但是这样还是有问题,每个对象实例的方法,都是在内存总新建的,用prototype来对相同的对象实例方法进行定义,使之共用内存。改造如下
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 } 5 6 CreatePerson.prototype.test = function(){ 7 console.log(this.name); 8 } 9 10 var my_obj1 = new CreatePerson(\'张三\'); 11 var my_obj2 = new CreatePerson(\'李四\'); 12 13 alert(my_obj1.test == my_obj2.test); //true 14 my_obj1.test(); // 张三 15 my_obj2.test(); // 李四 16 17 </script>
这里面的对象这样去记忆理解容易点。 my_obj1和my_obj2是具体的某个人,是对象的实例。而CreatePerson是人这一类,是构造函数。在CSS中,有行内样式 <div style="...."></div> 或者写成 <div class="..."></div>这里。用构造函数加上prototype的方式,类似于class,能广泛用于很多个个体对象实例,而直接加在个体上的方法就只能用于个体,同时,二者有重名冲突的时候,个体上的方法优先级较高。
1 <script type="text/javascript"> 2 function CreatePerson(name){ 3 this.name = name; 4 } 5 6 CreatePerson.prototype.test = function(){ 7 console.log(this.name); 8 } 9 10 var my_obj1 = new CreatePerson(\'张三\'); 11 var my_obj2 = new CreatePerson(\'李四\'); 12 13 //为my_obj1这个个体又单独加个test。prototype定义的test对该个体来说失效 14 my_obj1.test = function(){ 15 console.log(this.name+\'111\'); 16 } 17 18 alert(my_obj1.test == my_obj2.test); //false 19 my_obj1.test(); // 张三111 20 my_obj2.test(); // 李四 21 22 </script>
再看看最开始怎么定义my_obj1,直接 var my_obj1 = new Object(); 或者写 var obj = {};这也是js的面向对象,到现在用构造函数的方式,一个是直接定义到个体,一个是先定义一个种类,然后再出个体。
使用原型再举个例子,求数组中元素的和,如下
1 <script type="text/javascript"> 2 var arr_1 = [1,9,3,5]; 3 Array.prototype.sum = function(){ 4 var result = 0; 5 for (var i=0;i<this.length;i++) { 6 result += this[i]; 7 } 8 return result; 9 } 10 11 console.log(arr_1.sum());//18 12 </script>
原型要加在构造函数上
1 <script type="text/javascript"> 2 var arr_1 = [1,9,3,5]; 3 var arr_2 = [3,5,7]; 4 Array.prototype.sum = function(){ 5 var result = 0; 6 for (var i=0;i<this.length;i++) { 7 result += this[i]; 8 } 9 return result; 10 } 11 arr_1.sum = function(){ 12 return \'我要覆盖prototype的方法!\'; 13 } 14 15 console.log(arr_1.sum());//我要覆盖prototype的方法! 16 console.log(arr_2.sum());//15 17 </script>
----------------
js是基于原型的程序
每个基本类型数据都有一个原型,字符串 String 数字 Number 布尔值 Boolean 数组 Array
尽量不要去修改系统对象下面的方法和属性。
例子:重写数组的push方法
1 <script type="text/javascript"> 2 var arr = [1,2,3]; 3 Array.prototype.push = function(){//这里系统自带的push就被覆盖了 4 for(var i=0; i<arguments.length;i++){ 5 this[this.length] = arguments[i]; 6 } 7 return this.length; 8 } 9 10 arr.push(9,8,7,6,5); 11 console.log(arr); 12 </script>
---------------
包装对象:基本类型(除了null 和 undefined)都有自己对应的包装对象,如String Number Boolean,这里也就知道了,null空对象和undefined未定义是不能再添加属性和方法的
<script type="text/javascript"> var str1 =\'hello123\'; var str2 = new String(\'hello123\');//通过new创建的都是对象 console.log(typeof str1);//string console.log(typeof str2);//object </script>
1 <script type="text/javascript"> 2 var str1 = \'hello123\'; 3 var str2 = new String(\'hello123\'); //通过new创建的都是对象 4 5 console.log(typeof str1); //string 6 console.log(typeof str2); //object 7 str2.lastvalue = function() { 8 return this.charAt(this.length - 1); 9 } 10 console.log(str2.lastvalue());//最后一个字符3 11 12 str1.lastvalue = function() { //报错,因为str1是字符串,不是对象,不能添加方法,只能使用其包装对象给予的方法 13 return this.charAt(this.length - 1); 14 } 15 console.log(str1.lastvalue()); 16 </script>
如果str1这个字符串类型数据,要添加方法,那可以添加到它的包装对象上。如下
1 <script type="text/javascript"> 2 var str1 = \'hello123\'; 3 String.prototype.lastvalue = function(){ 4 return this.charAt(this.length-1); 5 } 6 console.log(str1.lastvalue()); 7 </script>
-----------------------------
原型链:个体实例和原型之间的链接 , 原型链的最外层是Object.prototype
举例:个体人物:张三 / 原型:男人
张三具有属性:姓名:张三;
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = name; 4 } 5 Man.prototype.name = \'男人\'; 6 Object.prototype.name = \'人类\'; 7 8 var Zhanshan = new Man(\'张三\'); 9 console.log(Zhanshan.name); //显示张三,如果没有第3行代码,则根据原型链,显示为男人,如果连第5行也没有,则显示为人类 10 //console.log(Man.prototype.name); //显示男人,若无第5行,则显示为人类 11 </script>
也就是注意层级关系。没有找到的属性就往原型链上找,优先级当然是内层优先。
-------------------
------------------
面向对象的一些属性和方法
hasOwnProperty : 对象个体实例自身是不是有某个属性?如果是个体自身的属性返回true; 如果属性是原型下的,返回false;
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = name; 4 } 5 Man.prototype.name = \'男人\'; 6 7 var Zhanshan = new Man(\'张三\'); 8 console.log(Zhanshan.hasOwnProperty(\'name\')); //true 9 </script>
1 <script type="text/javascript"> 2 function Man(name){ 3 //this.name = name; 4 } 5 Man.prototype.name = \'男人\'; 6 7 var Zhanshan = new Man(\'张三\'); 8 console.log(Zhanshan.hasOwnProperty(\'name\')); //false 9 </script>
<script type="text/javascript"> function Man(name){ //this.name = name; } Man.prototype.name = \'男人\'; var Zhanshan = new Man(\'张三\'); Zhanshan.name = \'张三三\'; console.log(Zhanshan.hasOwnProperty(\'name\')); //true </script>
<script type="text/javascript"> function Man(){ this.name = \'张三\'; } Man.prototype.name = \'男人\'; var Zhanshan = new Man(\'张三\'); console.log(Zhanshan.hasOwnProperty(\'name\')); //true 虽然新建个其他的个体实例,name也会是张三,但是该个体实例自身有name属性,所以还是true </script>
constructor : 查看对象的构造函数
1 <script type="text/javascript"> 2 function Man(name){ 3 this.name = \'张三\'; 4 } 5 Man.prototype.name = \'男人\'; 6 7 var Zhanshan = new Man(\'张三\'); 8 console.log(Zhanshan.constructor == Man); //true 9 </script>
1 <script type="text/javascript"> 2 var a1 = [1,2,3]; 3 console.log(a1.constructor = Array); //true 4 </script>
当我们写一个函数时,程序自动会生成constructor
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //注意,下面这句话是系统内部自动实现的,这里写出来,好看到系统做了什么,只要定义一个函数,系统都会"自动生成"下面一句 5 //Aaa.prototype.constructor = Aaa; //这里可以自行更改,将覆盖,如Aaa.prototype.constructor = Array;但是乱改后将影响使用 6 console.log(Aaa.prototype); 7 //只有constructor是函数原型自身的,hasOwnProperty这个方法是Object上的,是通过原型链找到的 8 </script>
现在知道不要更改constructor,但是有时候,不注意我们就更改了,如下:
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 Aaa.prototype.name = \'张三\'; 5 Aaa.prototype.age = 30; 6 7 var a1 = new Aaa(); 8 console.log(a1.constructor); //Aaa 9 </script>
上面我们知道,Aaa.prototype也是一个对象,上面的改写为如下形式,就更改了constructor
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //这里更改了,不是更改属性,而是重新对Aaa.prototype进行了赋值,也就弄丢了constructor属性 5 Aaa.prototype = { 6 name:"张三", 7 age :30 8 } 9 var a1 = new Aaa(); 10 console.log(a1.constructor); //Object 11 12 </script>
所以,当我们按照上面这张方法写的时候,要加上修正的,变成如下
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //弄丢的constructor属性再找回来 5 Aaa.prototype = { 6 constructor:Aaa, //注意不要加引号,注意这句话就是为了修正对象constructor属性 7 name:"张三", 8 age :30 9 } 10 var a1 = new Aaa(); 11 console.log(a1.constructor); //Aaa 12 </script>
还有一个地方注意:用for in循环对象的属性的时候,系统自带的属性是循环不到的
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 Aaa.prototype.name = \'张三\'; 5 Aaa.prototype.age = 30; 6 Aaa.prototype.constructor = Aaa; 7 var a1 = new Aaa(); 8 for (var attr in Aaa.prototype) { 9 console.log(attr); //显示了name和age,但是不显示constructor,即使还单独写一次 10 } 11 </script>
但是,如果用= 重新赋值的方式,再循环打印是可以打印出来的哦!如下
1 <script type="text/javascript"> 2 function Aaa(){ 3 } 4 //这种方式是重新赋值 5 Aaa.prototype = { 6 constructor:Aaa, //注意不要加引号,注意这句话就是为了修正对象constructor属性 7 name:"张三", 8 age :30 9 } 10 var a1 = js面向对象学习1