zoukankan      html  css  js  c++  java
  • js小记:对象、原型及原型链、面向对象编程

    一、js对象

    1、js对象 
    js对象是一种复合数据类型,它可以把多个(不同类型的)数据集中在一个变量中,并且给每个数据起名字。

    2、对象与数组 
    对象的每个数据有对应的名字(属性名),我们通过叫名字访问具体哪个数据; 
    数组的每个数据没有名字,我们只能通过编号来访问具体哪个数据。 
    从本质讲,数组就是对象的特殊形式,数组的每个索引实质就是特殊化的 对象属性名。举个例子:

    var a = [0,1,2,3];
    a['me'] = 1;
    a[-1] = '负数';    //负数 转换为 字符串
    a[1.23] = '小数';  //小数 转换为 字符串
    a['2.00'] = 'dad';//字符串 无法转换为 整数,所以仍为字符串
    a['4'] = 'dad';   //字符串 转换为 整数
    console.log(a);   //[0, 1, 2, 3, "dad", me: 1, -1: "负数", 1.23: "小数", 2.00: "dad"]
    
    //因为 a['4'] = 'dad';,所以length值加1
    console.log(a.length);  //5
    
    //上面例子说明数组的length属性,仅计算有 整数索引 的值的个数
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3、创建对象的两种方式

    1)var o = new Object();
    (2)var o = {};
    (3)var o = new Object({x:0,y:0,radius:2});
    (4)var ciclr = {x:0,y:0,radius:2};
    • 1
    • 2
    • 3
    • 4

    对象 通过 构造器 生成:

    • object(对象):下图左边的红圈(o、a、d···)
    • constructor(构造器):下图的右边的红圈(Object、Array···)

    注:Object、Array等这些都是系统自带的构造器,我们也可以自定义构造器(eg:下面深入使用的 构造函数) 
    这里写图片描述

    并不是所有函数都当构造器使用 
    eg: var o = new Math.min();

    4、初始化/访问 数据

    var book = new Object();
    book.title = "math";
    book.author = "bty";
    book.chapter1 = {
        title:"math 简介",
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5、删除对象属性

    1)delete book.chapter1;    //彻底删除
    (2)book.chapter1 = null;     //仅仅置空
    • 1
    • 2

    delete删除成功或删除的属性不存在返回true.

    • 数组:delete后 不是彻底删除,length不变
    • 对象:delete后 是 彻底删除,没有length属性
    //数组
    var a = [0,1,1,2];
    console.log(a.length);//4
    delete a[1];
    console.log(a.length);//4
    console.log(a);       //[0, empty, 1, 2]
    console.log(a[1]);    //undefined
    
    //对象
    var b = {aaa:1,bbb:2};
    console.log(b);  //{aaa: 1, bbb: 2}
    delete b['aaa'];
    console.log(b);  //{bbb: 2}
    console.log(b.length);  //undefined //说明对象没有length属性
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    6、遍历对象

    //key是obj里的属性名
    for(var key in obj){
        console.log(obj[key]);
        //console.log(obj.key);则会输出undefined
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    其次还有some(),every(),map(),forEach(),filter()等方法。 
    具体用哪个应场景而定。


    二、相关知识

    1、this

    下面讲的还是没这里好:Javascript中的this

    (1)全局环境中的this: 
    - 非严格模式:先指向undefined,然后再自动指向全局对象。 
    - 严格模式:指向undefined 
    以下均是 非严格模式。

    例1:

    //在浏览器中全局对象就是window。
    var a = 10;
    alert(this.a); //10
    • 1
    • 2
    • 3

    例2:

    var a = 12;
    var obj = {
        a:1,
        b:this.a + 1
    };
    console.log(obj.b);//13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    特殊: 
    new Function()里面的this,不论它是在构造函数中,还是函数调用中, 
    this都指向 全局对象。

    (function(){
        var f = new Function('alert(this)');
        f();
    })();
    
    //或
    function Foo(){
        this.bar = function(){
            var f = new Function('alert(this)');
            f();
        }
    }
    var foo = new Foo();
    foo.bar();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (2)构造函数中的this:指向新创建的对象(红圈部分) 
    例1: 
    这里写图片描述

    例2:

    var a = 1;
    var obj = {
        a:2,
        b:function(){
            function fun(){
                return this.a;
            }
            console.log(fun());
        }
    };
    obj.b();//1
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (3)函数中的this:指向函数的调用者(红圈部分) 
    这里的调用者就是创建的对象p1,输出:Hello,lily 
    这里写图片描述

    在例如

    var a = 1;
    var obj = {
        a:2,
        b:function(){
            return this.a; 
        }
    };
    //函数的调用者是obj
    console.log(obj.b());//2
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (4)eval()中的this:指向调用上下文中的this

    //下面的闭包,这里的this指向的是Globel
    (function(){
        eval('alert(this)');
    })();
    • 1
    • 2
    • 3
    • 4
    //这里的this是在bar函数里调用的,所以这里的this和bar函数里面的this一样。
    //而bar函数里的this指向的是其调用者foo对象
    function Foo(){
        this.bar = function(){
            eval('alert(this)');
        }
    }
    var foo = new Foo();
    foo.bar();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2、apply、call

    看下面场景:

    function Point(x,y){
        this.x = x;
        this.y = y;
        this.move = function(x,y){
            this.x += x;
            this.y += y;
        }
    }
    var point = new Point(0,0);
    point.move(1,1);//移动到(1,1)
    
    var circle = {x:0, y:0, r:1};
    
    //移动圆至(1,2)
    point.move.apply(circle,[1,1]);
    //或point.move.call(circle,1,1);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们首先new了一个point对象,初始坐标(0,0),之后又将该点移动到(1,1)处。 
    然后我们又定义了circle对象,圆心坐标(0,0),变径1,如下图: 
    这里写图片描述 
    问题来了:现在我们怎么通过Point的move方法来让 圆 移动到这个位置 
    这里写图片描述 
    通过下面两行任意一行代码即可: 
    point.move.apply(circle,[1,1]); 
    point.move.call(circle,1,1);

    apply和call实际只是将 Point里的this指针的指向做了改变,point.move.call后,move方法开始指向circle对象。

    call与apply区别: 
    两者区别仅在参数上。apply第二个参数必须是个数组(把函数调用的值依次传入),而call把参数依次传入时用的不是数组,直接用逗号隔开而已,当然call第二个参数也可以是数组

    3、原型与原型链
    function Teacher(){
        this.courses = [];//相当于私有变量
    }
    Teacher.prototype = { //相当于public
        constructor:Teacher,
        job:'teacher',
        setName = function(name){
            this.name = name;
        },
        addCourse = function(course){
            this.courses.push(course);
        }
    }
    
    var bill = new Teacher();
    bill.setName('Bill');
    bill.addCourse('math');
    
    var tom = new Teacher();
    tom.setName('Tom');
    tom.addCourse('physics');
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    上面代码原型链如下: 
    bill和tom都有一个隐式指针_ proto_,都指向Teacher.proptotype。 
    又因为Teacher是一个函数对象,而函数对象又是通过new Function()来建立的,Function也有一个protoptye。Function.prototype属性在js中是内置属性。 
    Teacher的_ proto_是个对象,同理推之如图。

    bill和tom是以Teacher.proptotype为原型,而Teacher.proptotype又以Object为原型。(这种原型继承的方式就叫原型链) 
    这里写图片描述


    原型链的操作

    • (1)属性查找
    • (2)属性修改
    • (3)属性删除

    我们继续用上面的例子 
    (1)属性查找:

    • 访问tom.toString() 
      首先js先从tom中找toString方法,发现没有;然后再沿着原型链往上找,到Teacher.prototype中查找toString,发现也没有,然后再到object.prototype中查找,发现有,然后调用toString方法

    (2)属性修改:

    • tom.name = ‘bty’; 
      首先js先在tom对象上查找有没有name属性,发现有,则直接将原来的’Tom’值改为’bty’。
    • tom.job = ‘assistant’; 
      首先js在tom对象上查找有没有job属性,发现没有,则直接在tom对象中添加job属性并赋值为’assistant’ 
      若想修改原型上的属性,则通过tom.prototype.job = 'assistant'修改,但这样会让所有Teacher创建出的对象job值都做修改。

    (3)属性删除

    • delete tom.job; 
      假设tom.job = ‘assistant’已经执行。delete tom.job后会直接删除tom上的job属性,但不会删除原型上的job属性,此时再访问tom.job,值为teacher
    • delete tom.job; delete tom.job; 
      道理结果同上

    判断对象是否有该属性: 
    tom.hasOwnProperty(‘job’); 
    请参考


    原型的继承

    原型的继承有两种方式:

    • 1、用构造器(具体参见 三、深入使用)
    • 2、用Object.create(); (低版本的ie浏览器不兼容)

    下面就详细说说Object.create(prototype, descriptors) 
    详细参见此处

    var teacher = {
        job:'teacher',
        courses:[],
        setName = function(name){
            this.name = name;
        },
        addCourse = function(course){
            this.courses.push(course);
        }
    };
    
    var bill = Object.create(teacher);
    
    bill.setName('Bill');
    bill.addCourse('math');
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    var bill = Object.create(teacher)即创建一个以teacher对象为原型的bill对象。 
    Object.create做的就是将bill的隐式指针_ proto_指向teacher,而不是指向teacher.prototype,这是一种新的原型继承的方式。如下图:

    这里写图片描述


    三、深入使用

    1、构造函数 
    创建构造器的三种形式:

    • function Person(){}
    • var Person = function(){}
    • var Person = new Function();

    例:

    //为了和普通函数区分,构造函数首字母要大写
    function Rect(w,h){
        this.width = w;this.height = h;
        this.area = function(){return this.width * this.height};
    }
    var r = new Rect(5,10);
    alert(r.area()); //50
    //过程:new一个对象出来,然后将对象交给了Rect这个函数;
    //new出来的这个对象在Rect中就叫this
    //换句话说,this指向的是新创建的对象
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    至于为什么不用下面代码,

    function Rect(w,h){
        this.area = function(){return w * h};
    }
    var r = new Rect(5,10);
    alert(r.area()); //50
    • 1
    • 2
    • 3
    • 4
    • 5

    是为了方便后续像r.width = 10;这样修改数值

    注意:

    function Person(name,age){
      this.name = name;
      this.age = age;
      return {};
      ///结尾加了个return {},此时new对象时就会返回空对象,所以下面打印undefined
    }
    
    var my = new Person('bty','12'); 
    console.log(my.age); //undefined
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、原型对象 
    (1)简单使用

    function Person(){
        Person.prototype.name = "Nicholas";
        Person.prototype.age = 20;
        Person.prototype.sayName = function(){
            alert(this.name);
        }
    }
    var person1 = new Person();
    var person2 = new Person();
    person1.sayName();   //Nicholas
    person2.sayName();   //Nicholas
    alert(person1.sayName === person2.sayName);//true
    
    person1.name = "Greg";
    person1.sayName();   //sam//from instance
    person2.sayName();   //bty//from prototype
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这里写图片描述
    (2)原型的问题 
    如果我们对原型属性修改的是简单变量类型(eg:数值或字符串),ok,没问题。但如果修改是对象(eg:数组)就会出现问题。

    function Person(){}
    Person.prototype = {
        //这些相当于 public 变量
        constructor:Person,
        name:"bty",
        age:20,
        friends:["amy","sam"]
    };
    var person1 = new Person();
    var person2 = new Person();
    person1.friends.push("ssh");
    alert(person1.friends);//amy,sam,ssh
    alert(person2.friends);//amy,sam,ssh
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们发现对person1.friends进行操作,person2.friends也跟着改变。说明他们是共享friends的(public变量),现在我们怎么将其变为 私有变量 呢?继续往下看。

    3、组合原生和构造方法

    function Person(name,age){
        //这些是每个对象拥有的属性,相当于 private 变量
        this.name = name;
        this.age = age;
        this.friends = ['amy','sam'];
    }
    Person.prototype = {
        //这些相当于 public 变量
        constructor:Person,
        sayName:function(){
            alert(this.name);
        }
    };
    var person1 = new Person("bty",20);
    var person2 = new Person("ssh",12);
    person1.friends.push('zsn');
    alert(person1.friends); //amy,sam,zsn
    alert(person2.friends); //amy,sam
    alert(person1.friends === person2.friends); //false
    alert(person1.sayName === person2.sayName); //true
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    四、全局对象(浏览器)

    1、浏览器的全局对象是window 
    2、浏览器中所有全局变量 实际就是 全局对象window的成员(属性)

    var value = 12;
    alert(window.value);//12
    • 1
    • 2

    3、window.document表示浏览器窗口中的HTML页面 
    4、document.write()将内容写入html页面

    for(x in window.document){
        document.write(x)
    }
    //for(x in document){```}也行
    • 1
    • 2
    • 3
    • 4

    五、面向对象编程

    • 全局变量
    • 封装
    • 继承
    1、全局变量

    定义方法: 
    (1)在全局环境中 var a = ‘hhh’; 
    (2)window.a = ‘hhh’; 
    (3)(function(){ a = ‘hhh’})() //变量声明提升

    注意:

    //这里test为全局变量。a为局部变量
    function todo(){
        var a = test = 'hhh';
    }
    //这里test2为全局变量,a2、b2为局部变量(注意看标点符号)
    function todo2(){
        var a2 = 'hhh',
            b2 = 'hhh';
        test2 = 'hhh2';
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2、封装

    通过prototype封装public变量;通过直接在Aaa上声明,封装私有变量,然后我们通过添加 this.XXX 方法,来访问私有变量。

    function Aaa(){
      //相当于私有属性
      var _config = ['A','B','C'];
      this.getConfig = function(){
        return _config;
      }
    }
    //相当于公有属性
    Aaa.prototype = {
      _step1:function(){},
      _step2:function(){},
      api:function(){}
    };
    var my = new Aaa();
    //外部无法访问config,必须通过this.XXX 方法以类似API的形式才能访问config
    console.log(my.config);//undefined
    console.log(my.getConfig());//["A", "B", "C"]
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    3、继承
    • 原型继承(JS固有的继承) 
      -原型的继承又两种方式:Object.create()和构造器
    • 类继承(模拟C、JAVA的继承)

    (1) 类继承(模拟C、JAVA的继承) 
    我们现在模拟:B继承A

    //(function(){})()是个闭包
    (function(){
        function ClassA(){
            var classMethod = function(){};
        }
        ClassA.prototype = {
            api:function(){}
        };
    
        function ClassB(){
            ClassA.apply(this,arguments);
        }
        ClassB.prototype = new ClassA();
        ClassB.prototype = {
            constructor:ClassB,//因为之前constructor是ClassA,所以要改成B
            api:function(){
                ClassA.prototype.api.apply(this,arguments);
            }   
        };
    
        var b = new ClassB();
        b.api();
    })()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这里写图片描述

    (2)原型继承(JS固有的继承)

    //
    (function(){
        var myproto = {
            action1:function(){},
            action2:function(){},
        };
        //obj的隐式指针_ proto_直接指向了myproto  
        var obj = Object.create(myproto);
    })();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    由于IE低版本浏览器不支持Object.create(),所以我们可以写个函数模拟下,如下:

    var clone = (function(){
        var F = function(){};
        return function(proto){
            F.prototype = proto;
            return new F();
        }
    })();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    解释:我们创建函数F,再为其指定原型,而F的原型就是传入的proto对象的原型。最后在new F(),即最后创建了个新的对象,而这个对象是以F.proto为原型,即以传入对象proto为原型

  • 相关阅读:
    MapReduce 中的Map后,sort不能对中文的key排序
    wordCount程序中MapReduce工作过程分析
    使用eclipse的快捷键自动生成的map或者reduce函数的参数中:“org.apache.hadoop.mapreduce.Reducer.Context context”
    "hadoop namenode -format"命令的作用和影响的文件
    伪分布模式下使用java接口,访问hdfs
    MySQL Server 5.5.44免安装版配置详解
    quartz Cron表达式一分钟教程
    【转载】jQuery弹出层始终垂直居中于当前屏幕
    LeetCode 151 翻转字符串里的单词
    LeetCode 43 字符串相乘
  • 原文地址:https://www.cnblogs.com/kkdn/p/9323918.html
Copyright © 2011-2022 走看看