面向对象

2017-02-26  本文已影响0人  012ca771a446

面向对象三大特性


对象的几种创建方式

字面量的方式:

适用场景: 只需简单创建单个对象
如需要创建多个对象,那么代码的冗余度太高(重复代码太多)
举例

var obj = {
   属性名:属性值,
   方法名:function(){函数体}
}
内置构造函数的方式:

内置构造函数包括:Object Function Number String Boolean Array Date ...
同样的问题是,如需要创建多个对象,代码冗余度高(重复代码多)
举例

var obj = new Object();
    obj.name = "name";
    obj.showName = function() {
    console.log(this.name)
}
工行函数的方式创建对象

特点:把固定的部分提取作为函数体,把变化的部分提取作为参数传递
问题是:如果创建多个不同类型的对象,那么我们无法分辨.
举例

    //01 提供函数(工厂函数)
    function createBook(){
        //02 创建空的对象
        var o = new Object();
        //03 设置属性和方法
        o.name = "默认的名称";
        o.author = "默认的作者";
        o.logDes = function(){
            console.log("作者是:" + this.author);
        }
        //04 返回新创建的对象
        return o;
    }

自定义构造函数的方式创建对象

自定义构造函数创建对象的内容实现细节:
01 我们在使用new关键字调用构造哈函数的时候,内部默认会创建一个空的对象 02 默认会把这个空的对象赋值给this 03 通过this来设置新对象的属性和方法 04 在构造哈函数的最后,默认会把新创建的对象返回
自定义构造函数和工厂函数对比
01 函数的名字不一样,构造函数首字母要大写.
02 自定义构造函数创建对象的方式,内部会自动生成空的对象并赋值给this
03 默认会return新创建的对象
返回值:
没有显示return,默认会把新创建的对象返回.

显示执行了return语句,则要看具体情况:
如返回的是值类型,那么会直接忽略返回,把新创建的对象返回.
如返回的是引用类型,则会覆盖掉新创建的对象,直接返回引用数据类型的值.
function Dog(name)
        {
            this.name = name;
            //return "demo";  忽略
            //return function (){};
        }

        var dog = new Dog("阿黄");
        console.log(dog);

函数的调用&this

函数的调用

new:创建对象,并在最后返回该对象
构造函数:用于初始化对象

加入以普通的函数方式来调用构造函数,那么内部的this指向window.

   function Person(name)
    {
        if(this instanceof Person)
        {
            this.name = name;
        }else
        {
            return new Person(name);
        }
    }

    var p1 = new Person("哔哩哔哩");

    //构造函数本身是一个函数,在调用可以直接调用
    var p2 =  Person("哗啦哗啦");  //这是一个错误的演示(不要这样写代码)
    console.log(p2);  //undefined

函数传值
   function Student(number,className,log){
        this.number = number;
        this.className = className;
        this.log = log;
    }
    var stu1 = new Student("201701","九阴真经修炼01班",function(){
       console.log("学号:" + this.number);
    });
    var stu2 = new Student("201702","九阴真经修炼01班",function(){
        console.log("班级名称:" + this.className);
    });
    stu1.log();
    stu2.log();

instanceof 对象类型判断
<script>
    function Person(){};
    function Dog(){};

    var p1 = new Person();
    var dog1 = new Dog();

    //关键字 instanceOf 用来判断当前对象是否是某个类型的实例(检查某个对象是否是使用指定构造函数创建的)
    //语法: 对象 instanceOf 构造函数(类型)
    console.log(p1 instanceof Person);
    console.log(p1 instanceof Dog);
    console.log(dog1 instanceof Person);
    console.log(dog1 instanceof Dog);
</script>
构造器属性的获取:constructor
<script>
    function Person(){};
    function Dog(){};
    var p1 = new Person();
    var dog1 = new Dog();

    //在所有的对象中,都拥有一个构造器属性:constructor
    console.log(p1.constructor);
    console.log(dog1.constructor);
</script>

构造函数的原型对象

01 什么是原型对象

在使用构造函数创建对象的时候,默认会生成一个与构造函数相关联的对象,这个对象就是原型对象.默认情况下该对象是个空对象:{}

02 原型对象的作用

使用构造函数创建的对象,自动拥有(可以使用)原型对象中所有属性和方法

03 如何访问原型对象

构造函数.prototype
对象.__proto__ 注: 在开发中不建议使用该属性,因为这个属性不是ECMA标准.

04 如何设置原型对象

可以像设置普通对象一样利用对象的动态特性设置属性和方法
使用字面量的方式设置原型对象(直接替换)

05 约定

正确的说法:该对象的构造函数的原型对象
构造函数的原型
构造函数的原型对象
对象的原型对象
对象的原型
以上四种说法,她们的意思是一样的,都是该对象的构造函数的原型对象

<script>
    //01 提供构造函数
    function Dog(name)
    {
        //02 设置属性和方法
        this.name = name;
        this.color = "绿色";
    }

    //设置原型对象
//    Dog.prototype.sayWangWangWang = function(){
//        console.log("汪汪汪");
//    };

    //03 创建对象
    var dog1 = new Dog("阿黄");
    var dog2 = new Dog("拉拉");

    dog1.sayWangWangWang();
    dog2.sayWangWangWang();

</script>

什么是实例化

通过构造函数创建对象的过程就叫做实例化

什么是实例

通过构造函数创建的对象被称为该构造函数的实例.


原型的使用方法

01 利用对象的动态特性来设置原型对象
<script>
    function Person(){
        this.name = "默认的名称";
    }
    //设置原型对象
    //成员= 属性|方法
    //01 增加成员
    Person.prototype.des = "我是直立行走的人";
    Person.prototype.logName = function(){
        console.log(this.name);
    }

    //02 修改成员
    Person.prototype.des = "我是直立行走的人++++";
    //console.log(Person.prototype);

    var p1 = new Person();
    console.log(p1.des);
    p1.logName();

    //03 删除成员
    //delete关键字
    //语法  delete 对象.属性
    //console.log(delete p1.des);  //不能用这种方式删除原型对象上面的属性(删除的是自己的属性)

    delete Person.prototype.des ;
    console.log(p1.des);
</script>
02 字面量的方式 (替换原型对象)

注意点:
01 如果替换了原型对象, 那么在替换前后所创建的对象,它们所指向的原型对象并非同一个
02 构造器属性: 在替换之后创建的对象中,它的构造器属性指向的并非Person构造函数,而是Object的原型对象的构造器属性
建议: 在设置完原型对象之后 在同一创建对象
举例

 function Person(){
        this.age = 40;
    }
    var p1 = new Person();
    Person.prototype.sayHi = function(){
        console.log("hi");
    }

    //设置原型对象
    Person.prototype = {
        name:"默认的名称",
        showName:function(){
            console.log(this.name);
        }
    }

    var p2 = new Person();

//    p1.sayHi();  //可以
//    p2.sayHi(); //不可以
//    console.log(p1.name);  //undefined
//    console.log(p2.name); //默认的名称
//    p1.showName();         //报错
//    p2.showName();         //默认的名称
<script>
    function Person(){
        this.age = 40;
    }
    var p1 = new Person();

    //var obj = new Object();
    Person.prototype = {
        //注意:如果替换了原型对象,那么需要在原型对象中修正构造器属性
        constructor:Person,
        sayHi:function (){
            console.log("hi");
        }
    };
    var p2 = new Person();
    //p2.constructor = Person;   //在p2对象上添加了一个属性(constructor)

    //var p3 = new Person();


    //构造器属性
    console.log(p1.constructor == p2.constructor);  //false
//    console.log(p1.constructor);    //Person
//    console.log(p2.constructor);    //Object
//    console.log(p2.constructor == p2.__proto__.constructor);
//    console.log(p2.constructor == Object.prototype.constructor);

    console.log(p2);
</script>
构造器:

属性: constructor
值:与之关联的构造函数
注意点:constructor是在原型对象身上,我们通过(对象.constructor)访问得到的值,其实就是原型对象中对应的值.


使用原型对象的注意事项

01 如何访问原型对象的属性

对象.属性
构造函数.prototype.属性

<script>
    function Person(){
        this.des = "默认的描述信息"
    }
    Person.prototype = {
        constructor:Person,
        des :"描述信息",
        logDes:function (){
            console.log("嘲笑我吧");
        }
    }
    //01 对象.属性
    var p1 = new Person();
    console.log(p1.des);
    //console.log(Person.prototype.des);
    p1.logDes();
    console.log(p1.age);
</script>
02属性的访问(读取)原则

就近原则,在访问属性的时候,先在对象自身查找(实力属性和方法),如果找到就直接用.否则,就去它的原型对象上查找,如果有就调用,找不到则提示undefined或者报错

<script>
    function Person(){
    }
    Person.prototype = {
        constructor:Person,
        des :"描述信息",
    }
    var p1 = new Person();
    var p2 = new Person();
    //设置
    //左值 --- 右值
    //如果是左值,那么在设置对象的属性的时候,不会去原型对象上面去找(查找自己是否有,如果有就是修改,没有就是添加)
    //如果是右值,那么在读取值得时候,先查找自己,如果找不到,再去原型身上找

    //  p1.hello(左值) = "hi"(右值);
    Person.prototype.hello = "hello";
    p1.hello = "hi";
    console.log(p1.hello);  //hi
    console.log(p2.hello);  //hello
</script>
03 设置属性的原则

如果是左值,那么在设置对象的属性的时候,不会去原型对象上查找(查找自己是否有,如果有就修改,没有就添加)
如果是右值,那么在读取值的时候,先找自己,找不到在去原型对象上去找.
设置原型属性的方法:
只能通过(构造函数.prototype.属性)或者直接替换原型对象的方式来设置
如果原型对象的类型是引用类型,那么可以通过对象的方式来设置

    function Person(){};
    Person.prototype.className = "逍遥派修仙技能班01";
    Person.prototype.showClassName = function(){console.log(this.className)};
    Person.prototype.car = {type:"火车",price:"$ 44444.44"};

    var p1 = new Person();
    var p2 = new Person();
    p1.showClassName();

    p1.className = "洗衣服班级";
    console.log(p1);
    console.log(p2);

    //如果原型对象的属性是引用类型的
    p1.car.type = "飞船";

    console.log(p1.car.type);   //飞船
    console.log(p2.car.type);   //飞船

    //额外的补充
    p1.car = {type:"火箭"};
    console.log(p1.car.type);   //火箭
    console.log(p2.car.type);   //飞船

    p1.car = {};
    console.log(p1.car.type);   //undefined
    console.log(p2.car.type);   //飞船

in关键字

01 检查对象中是否有指定属性(实例属性+原型属性)
02 遍历对象
语法:k in obj

<script>

    //01 提供一个构造函数
    function Person(name) {
        this.name = name;
    }

    //02 设置构造函数的原型对象的属性
    Person.prototype.sayHello = function () {
        console.log("hello");
    }

    //03 创建对象
    var p1 = new Person();

    //04 使用in关键字判断对象中是否存在以下属性:name age sayHello
    console.log("age" in p1);       //false
    console.log("name" in p1);      //true
    console.log("sayHello" in p1);  //true

</script>

hasOwnProperty

作用:检查对象中是否有指定属性(实例属性)
语法:`obj.hasOwnproperty.属性

<script>

    //01 提供一个构造函数
    function Person(name) {
        this.name = name;
    }

    //02 设置构造函数的原型对象的属性
    Person.prototype.sayHello = function () {
        console.log("hello");
    }

    Person.prototype.des = "默认的描述信息";

    //03 创建对象
    var p1 = new Person();

    //04 使用hasOwnProperty方法判断该属性是否是对象的实例属性
    console.log(p1.hasOwnProperty("age"));       //false
    console.log(p1.hasOwnProperty("name"));      //true
    console.log(p1.hasOwnProperty("sayHello"));  //false
    console.log(p1.hasOwnProperty("des"));       //false

</script>

isPrototype

判断是否是某个实例对象的原型对象

instanceof

检查是否是某个构造函数的实例

<script>
    function Person(){};
    var demo = {};
    var demo1 = {};
    Person.prototype = demo;
    Person.prototype.constructor = Person;
    var p1 = new Person();
    //判断某个对象是否是指定对象的原型对象
    console.log(demo1.isPrototypeOf(p1));
</script>

继承的实现

js的继承只支持实现继承,实现继承主要依赖原型链完成
js 中几种继承实现方式

混入式继承的实现方式

//创建一个空的对象
var o = {};
//提供一个已经有的对象 obj
var obj = {
    name: "对象属性",
    age:20,
    sayHolle: function(){
        console.log("Holle");
  } 
}
//混入式继承(把原有对象的所有属性和方法拷贝给新的对象)
for (var k in obj){
    o[k] = obj[k];
}
console.log(o);
//问题:  新对象的引用类型属性和就对象中对应的属性是否相等??
console.log(o.sayHolle == obj.sayHolle)//true
//答案是相等的,那么两个对象中其中一个修改了属性,则另外一个会受到影响.

原型链式继承

原型对象中的成员(属性和方法)可以被使用该构造函数创建出来的所有对象共享;
利用这一特性,可以实现原型式继承;

//提供一个构造函数
function Person(name, age){
    this.name = name;
    this.age = age;
}
//设置原型对象的属性
Person.prototype.className = "逍遥派1班";
//使用构造函数,创建原型对象;
var p1 = new Person("张三",10);
var p2 = new Person("李四", 20);
//04 打印p1和p2对象中的className属性
console.log(p1.className);
console.log(p2.className);
//结论:对象p1和p2继承了构造函数原型对象中的属性className
//但是这并不是严格意义上的继承

安全的扩展内置对象

过程:
01 提供一个构造函数
02 设置 构造函数的原型对象为内置构造函数创建出来的对象

<script>
    //Array
    function MyArray(){};
    //设置原型对象
    MyArray.prototype = new Array();
    MyArray.prototype.des = "描述信息";
    MyArray.prototype.logDes = function(){
        console.log(this.des);
    }

    //使用自己的构造函数来创建对象
    var myArr01 = new MyArray();
    var myArr02 = new MyArray();
    myArr01.push("123","abc");
    console.log(myArr01);
    console.log(myArr01.des);


    function OtherArray(){};
    OtherArray.prototype = new Array();
    OtherArray.prototype.des = "描述信息";
    OtherArray.prototype.logDes = function(){
        console.log("哈哈哈哈");
    }

    var otherArr01 = new OtherArray();
    var otherArr02 = new OtherArray();

    console.log(otherArr01.des);
    otherArr01.logDes();
</script>

原型链的结构

01 每个对象都是由构造函数创建出来的,因为每个对象都有构造函数
02 每个构造函数都有一个与之对应的原型对象
03 原型对象自身也是对象
04 因此,原型对象也有自己的构造函数
05 原型对象的构造函数也有自己的原型对象
06 原型对象的构造函数的原型对象也是对象,所以它也有自己的构造函数
07 原型对象的构造函数的原型对象的构造函数也有自己的原型对象。。。
以上形成一个链式结构,就称之为原型链
原型链的顶端是Object.prototype , Object.prototype本身也是一个对象,因此也有原型对象 : Object.__proto__打印出来为null.

原型链中对象属性的搜索规则

对象.属性的方法去访问属性的时候,先查找有没有对应的实例属性,如果有那么就直接使用
如果没有,那么就去该对象的原型对象上面去找,如果有那么就直接使用
如果没有,那么就接着查找原型对象的原型对象,如果有,那么就直接使用,
如果没有,那么就继续上面的搜索过程
直到搜索到Object.prototype为止,如果还是没有找到就返回undefined或者是报错

注意:

原型链搜索的路径越长,查询属性所花费的时间就越多
原则:就近原型

原型链继承
001 提供一个父构造函数
002 提供子构造函数
003 设置子构造函数的原型对象为父构造函数的一个实例对象
003 在这个实例对象上面设置属性和方法
<script>
    function A(){
        this.age = 55;
        this.showAge = function(){
            console.log(this.age);
        }
    };
    //设置A的属性和方法
    A.prototype.logDes = function(){
        console.log("des");
    }
    function B(){

    };
    //设置原型链继承
    B.prototype = new A();
    B.prototype.des = "des";
    B.prototype.show = function(){
        console.log("show");
    };
    var b1 = new B();
    console.log(b1.age);
    b1.showAge();
    b1.logDes();
    console.log(b1);
</script>

原型链继承注意点:

<script>
    function Person(){};
    function Student(){};

//    Student.prototype.sayHello = function(){
//        console.log("hello");
//    }

    //设置原型链继承
    Student.prototype = new Person();
    Student.prototype.constructor = Student;
    Student.prototype.sayHello = function(){
        console.log("hello");
    }

    //创建对象
    var stu1 = new Student();
    console.log(stu1.constructor);
    //stu1.constructor = Student;    //添加属性

    stu1.sayHello();
    console.log(stu1.name);

</script>
原型链继承的问题:

01 无法对父构造函数传递参数
02 继承过来实例属性会成为当前对象的原型属性,会被创建出来的多个对象共享.

Object.create

作用:创建对象,并设置该对象的原型对象
问题:存在兼容性问题,是ES5推出的

<script>
    var obj = {name:"张浩瀚"};
    if(typeof Object.create == "function") {
        var o = Object.create(obj);
    }else
    {
        Object.create = function(){
            function F(){};
            F.prototype = obj;
            var o = new F();
        }
    }
    Object.create(); //只需要调用该方法就可以创建对象
</script>

call&apply

都是Function原型对象上的方法
作用:借用其他对象的方法
参数:
第一个参数是调用该方法的对象(函数内部的this绑定的对象)
后面的参数:
call:参数列表
apply:数组
对象1.方法.call(调用方法的真正的对象,参数1,参数2,参数3);
对象1.方法.apply(调用方法的真正的对象,[参数1,参数2,参数3...])

this -- >总是指向一个对象

函数的调用方式
001 作为对象的方法来调用 this--->当前的对象
002 作为普通的函数调用 this--->window
003 作为构造函数和new使用 this--->构造函数内部新创建的对象
004 被call或者是apply调用(函数上下文调用) this--->第一个参数

问题 -- this的丢失
<script>
   window.name = "能够看见我吗";
   var demo01 = {
       name:"小偷",
       age:20,
       showName:function(param1,param2){
           console.log(this.name,param1,param2);
       }
   }
   var getName = demo01.showName;

   //调用函数
   demo01.showName();    //以对象的方式来调用   this-->当前的对象
   //函数的调用方式发生了改变,导致this丢失
   getName("S1","S2");  //以普通的函数调用      this--->window
</script>

借用构造函数实现继承的基本写法

<script>
    function Person(name,age){
        this.name = name;
        this.age = age;
    };
    function Boy(bookName,name,age){
        this.bookName = bookName;
        //Person.call(this,"悟空",600);  //借用构造函数
        Person.call(this,name,age);
    }

    //创建对象
    var boy = new Boy("水煮三国","悟空",600);
    var boy02 = new Boy("大话西游","云风",40);
    console.log(boy);
    console.log(boy02);
</script>

组合继承的基本写法

01 借用构造函数实现继承:可以获得父构造函数中的实例属性 Person.call(this,name,age)
02 获得原型属性和方法 ( Boy.prototype = Person.prototype)
组合继承:原型 + 借用构造函数

<script>
    function Person(name,age){
        this.name = name;
        this.age = age;
    };
    Person.prototype.des = "描述信息";
    Person.prototype.logDes = function(){
        console.log(this.des);
    };

    function Boy(bookName,name,age){
        this.bookName = bookName;
        //Person.call(this,"悟空",600);  //借用构造函数
        Person.call(this,name,age);
    }

    //设置原型继承
    //Boy.prototype = new Person();
    Boy.prototype = Person.prototype;

    //创建对象
    var boy = new Boy("水煮三国","悟空",600);
    var boy02 = new Boy("大话西游","云风",40);
    console.log(boy);
    console.log(boy02);
    boy.logDes();

    var p1 = new Person();
    p1.logDes();

    Boy.prototype.des = "描述信息-bOY";
    boy.logDes();
    p1.logDes();
</script>

浅拷贝&深拷贝

#####浅拷贝
var obj = {
    name = "小花脸",
    car:{
      type:"客车"
  }
}
var o = {};
for (var k in obj){
    o[k] =obj.[k];
}
console.log(o);
o.car.type = "银河战舰";
console.log(obj);

深拷贝

  var obj = {
        name:"小花脸",
        car:{
            type:"客车",
        },
        friends:["1","2","3"]
    };

    var o = {};
    //deepCopy(o,obj);
    function deepCopy(obj1,obj2) {
        obj1 = obj1 || {};
        for (var i in obj2)
        {
            if (obj2.hasOwnProperty(i))
            {
                if( typeof obj2[i] == "object")
                {
                    //判断是数组还是对象
                    obj1[i] = Array.isArray(obj2[i])?[]:{};
                    //引用类型
                    deepCopy(obj1[i],obj2[i]);   //函数调用
                }else
                {
                    //值类型
                    obj1[i] = obj2[i];
                }
            }
        }
    }

    deepCopy(o,obj);
    console.log(o);

    //共享问题
    //o.car.type = "小货车";
    console.log(obj);

通过深拷贝来实现继承

比较完整的继承方案

<script>
    if(typeof Array.isArray != "function") {
        Array.isArray = function(obj){
            return Object.prototype.toString.call(obj) == "[object Array]";
        }
    }
    function deepCopy(obj1,obj2) {
        obj1 = obj1 || {};
        for (var i in obj2)
        {
            if (obj2.hasOwnProperty(i))
            {
                if( typeof obj2[i] == "object")
                {
                    //判断是数组还是对象
                    obj1[i] = Array.isArray(obj2[i])?[]:{};
                    //引用类型
                    deepCopy(obj1[i],obj2[i]);   //函数调用
                }else
                {
                    //值类型
                    obj1[i] = obj2[i];
                }
            }
        }
    }
    function Person(name,age){
        this.name = name;
        this.age = age;
    };
    Person.prototype.des = "描述信息";
    Person.prototype.car = {
        type:"汽车"
    }
    Person.prototype.logDes = function(){
        console.log(this.des);
    };

    function Boy(bookName,name,age){
        this.bookName = bookName;
        //Person.call(this,"悟空",600);  //借用构造函数
        Person.call(this,name,age);
    }
    //设置原型继承
    //Boy.prototype = Person.prototype;
    deepCopy(Boy.prototype,Person.prototype);  //把父构造函数原型对象上面的属性和方法全部拷贝一份给Boy
    //创建对象
    var boy01 = new Boy("水煮三国","悟空",600);
    var boy02 = new Boy("大话西游","云风",40);
    Boy.prototype.car.type = "火车";
    console.log(Boy.prototype);
    var p1 = new Person();
    console.log(p1.car.type);
</script>
上一篇下一篇

猜你喜欢

热点阅读