• js面向对象学习


    纯属笔记,加强记忆,不是教程,欢迎纠错,没有逻辑,不太适合学习使用。

    --------------

    继承多态等太多概念难以理解,还是从实践中慢慢学吧!争取能大致看懂网上的开源的代码。

    --------------

    对象的组成:方法和属性

    属性关键词:静止的,状态

    方法关键词:动态的,过程,处理,带括号

    --------------

    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 = new Aaa();
    11         for (var attr in Aaa.prototype) {
    12             console.log(attr); //显示constructor  name  age
    13         }
    14     </script>

     instanceof 运算符,判断个体对象和原型函数 是否有原型链上的关系,返回true或false 如下

     1     <script type="text/javascript">
     2         function Man(name){
     3             this.name = name;
     4         }
     5         function Woman(name){
     6             this.name = name;
     7         }
     8         var zhanshan = new Man();
     9         console.log(zhanshan instanceof Man); //true
    10         console.log(zhanshan instanceof Object); //true
    11         console.log(zhanshan instanceof Woman); //false
    12     </script>

    用instanceof判断某变量是否是数组

    1     <script type="text/javascript">
    2         var a1 = [1,2,3,4,5];
    3         console.log(a1 instanceof Array); //true;
    4         
    5         var b1 = 'hi';
    6         console.log(b1 instanceof Array); //false;
    7     </script>

    toString 方法  把对象转成字符串 。 系统对象下面都是自带的,而自己创建的对象toString在object上

    如下

    1     <script type="text/javascript">
    2         var a1 = [1,2,3,4,5];
    3         console.log(a1.toString == Object.prototype.toString); //false 系统对象下面都是自带的,不是Object上的;
    4         console.log(a1.toString == Array.prototype.toString); //true 系统对象下面都是自带的,在Array上 不在Object
    5         function Bbb(){
    6         }
    7         var b1 = new Bbb();
    8         console.log(b1.toString == Object.prototype.toString); //true 自己创建的对象toString在object上
    9     </script>
    1     <script type="text/javascript">
    2         var a1 = [1,2,3,4,5];
    3         console.log(a1.toString()); //1,2,3,4,5
    4         console.log(typeof a1.toString()); //string
    5     </script>
     1     <script type="text/javascript">
     2         var a1 = [1,2,3,4,5];
     3         console.log(a1.toString()); //1,2,3,4,5
     4         console.log(typeof a1.toString()); //string
     5         
     6         //按照系统toString生成的1,2,3,4,5 如果不符合我们使用的格式的话,我们还可以自己去改写
     7         Array.prototype.toString = function(){
     8             return this.join('+'); //将数组内容按照加号连接
     9         }
    10         console.log(a1.toString()); //1+2+3+4+5
    11     </script>
    1     <script type="text/javascript">
    2         var num = 255;
    3         console.log(num.toString(16)); //ff, 参数16是16进制的意思,输出16进制的数据  rgb色彩可以用~~
    4         console.log(num.toString(2)); // 11111111  参数2是2进制的意思 
    5     </script>

    上面是利用toString转换数字进制

    toString还可以用来判断变量的数据类型,比typeof   instanceof  更精准,如下

     1     <script type="text/javascript">
     2         var num = 255;
     3         console.log(Object.prototype.toString.call(num)); // [object Number]
     4         console.log(Object.prototype.toString.call(num) == '[object Number]');  // true;
     5         
     6         var str = 'hello';
     7         console.log(Object.prototype.toString.call(str)); // [object String]
     8         var arr = [1,2,5];
     9         console.log(Object.prototype.toString.call(arr)); // [object Array]    
    10         
    11         console.log(Object.prototype.toString.call({name:'张三'})); //[object Object]
    12         
    13     </script>

     -------------

    对象的继承  继承要求原类不变,新出子类

    如何做?

     1     <script type="text/javascript">
     2         function CreateUser(name,age){
     3             this.name = name;
     4             this.age = age;
     5         }
     6 
     7         CreateUser.prototype.dosometing = function(){
     8             console.log(this.name+'dosometing');
     9         }
    10 
    11         var Zhanshan = new CreateUser('张三',39);
    12         Zhanshan.dosometing();
    13 
    14         function CreateStar(name,age,job){
    15             CreateUser.call(this,name,age); //这句话完成了属性的继承
    16             this.job = job;//这句话添加父级没有的新属性
    17         }
    18 
    19         CreateStar.prototype = CreateUser.prototype; //这句话完成了方法的继承,但是根据对象赋值可知,这里,子类对象的变化,父类对象亦变。不是完美的方式。
    20         
    21 
    22         var Lichen = new CreateStar('李晨',40,'演员');
    23         console.log(Lichen.name + '是个' + Lichen.job);
    24         Lichen.dosometing();
    25         
    26         console.log(Lichen.constructor); //CreateUser 因为直接CreateStar.prototype = CreateUser.prototype; 所以这里不是CreateStar
    27         
    28         console.log(CreateStar.prototype.constructor); //CreateUser
    29         console.log(CreateUser.prototype.constructor); //CreateUser
    30         console.log(CreateUser.prototype.constructor == CreateStar.prototype.constructor); //true;
    31         
    32     </script>
  • 相关阅读:
    并行编程——OPENMP
    并行编程——MPI/OPENMP混合编程
    C#中窗体间传递数据的几种方法
    开发人员一定要加入收藏夹的网站
    Web网站中从Sybase数据库读取的中文显示为乱码的解决方法
    数据空间和日志空间分离的操作方法
    双机集群中的数据库配置同步
    删除已损坏库方法
    RDLC报表中如何实现行交替颜色
    安装Sybase时安装界面为乱码的解决方法
  • 原文地址:https://www.cnblogs.com/html55/p/9757021.html
Copyright © 2020-2023  润新知