js面向对象总结(学习笔记)

对象---一切属性和方法的加工厂
 
面向过程:按步骤一步一步亲历亲为,注重过程
面向对象:根据需求,将对象的属性和方法分离出来,最终调用属性或方法,实现需求,注重结果
 
一、创建对象的三种方式:
1.new的方式
 
var obj = new Object();
var arr = new Array();
var str = new String();
 
2.字面量方式
 
var person={
    name:"小明",
    age:"20",
    eat:function(){
        console.log("吃东西")
    }
}
 
3.构造函数方式
var Person(name,age){
    this.name = name;
    this.age = age;
    this.say = function(){
        console.log("您好!~")
    }
}
//创建实例
var person = new Person("小白",20);
person.say();
 
二、面向对象的特性
 
封装、继承、多态、抽象
 
封装性---将对象的属性和方法封装起来,供实例调用
继承性---子对象可以继承父对象的属性或方法
多态性---子类的方法会覆盖父类的方法,即表现的是多态性
抽象性---数据抽象、过程抽象
 
三、原型
 
原型的作用:
1.解决数据共享,节省内存空间
2.继承
 
原型是什么:
1.构造函数中一个属性prototype,该属性是一个对象,是个标准属性,给程序员用的,叫原型对象
2.实例对象中有一个属性__proto__,该属性是一个对象,是个非标准属性,给浏览器用的,叫原型对象
 
function Person(name, age) {
      this.name = name;
      this.age = age;
      this.say=function () {
        console.log("您好");
      };
 }
 //原型添加方法
Person.prototype.say = function () {
     console.log("您好");
};
var p1 = new Person("小明", 20);
var p2 = new Person("小花", 30);
//构造函数的protype与实例对象的__proto__相等
p1.__proto__ === Person.prototype
//原型对象的构造器constructor即为该原型对象的构造函数
Person.prototype.constructor===Person
 
四、构造函数、原型对象、实例对象之间的关系
1.实例对象是通过构造函数创建出来的;
2.构造函数中的prototype属性即为原型对象;
3.实例对象中的__proto__指向构造函数的prototype
 
五、this的指向
构造函数和原型对象中this指向当前的实例对象
 
六、原型链
原型链指的是实例对象和原型对象之间的关系
 
因为实例对象的__proto__指向构造函数的prototype,则对象的__proto__指向的肯定是某个函数的prototype
 
七、继承
 
1.改变原型指向实现继承
 
function Person(name,age){
    this.name=name;
        this.age=age;
}
 
//原型添加方法
Person.prototype.eat=function(){
    console.log("吃东西");
}
 
//学生的构造函数
function Student(sex){
    this.sex=sex;
}
 
//学生的原型
Student.prototype.play=function(){
    console.log("大家一起玩");
}
 
//通过改变原型指向实现继承方法
Student.prototype = new Person("小明",20);
 
//学生实例化
var stu = new Student("男");
 
总结:
  • 如果实例对象中的属性或者方法和原型对象中的重复了,那么此时使用的是实例对象中,但是是不会修改原型对象中的属性的值;
  • 实例对象访问属性的时候,先在该实例对象中查找,有则使用,没有就去原型中找,找到了则直接使用,如果没有找到,则是undefined;
  • 继承后,原型的指向改变,实例对象中的__proto__的指向也会发生改变,这样从原型对象中的属性和方法就继承过来了
 
2.借用构造函数实现继承
 
通过原型指向会引起属性一致的问题,故借用构造函数进行继承
 
   function Person(name,age) {
      this.name=name;
      this.age=age;
    }
    //原型添加方法
    Person.prototype.eat=function () {
      console.log("我要吃东西");
    };
    //学生的构造函数
    function Student(name,age,sex) {
      //借用构造函数,call方法是改变了this的指向
      Person.call(this,name,age);
      this.sex=sex;
    }
    //实例对象
    var stu1=new Student("小明",20,"男");
    var stu2=new Student("小花",30,"女");
    console.log(stu1.name,stu1.age,stu1.sex);
    console.log(stu2.name,stu2.age,stu2.sex);
    stu1.eat();//出错
 
总结:
  • 可以解决属性重复问题,但方法无法继承
 
3.组合继承
 
改变原型指向,并借用构造函数实现继承
 
    function Person(name,age) {
      this.name=name;
      this.age=age;
    }
    //原型添加方法
    Person.prototype.eat=function () {
      console.log("我要吃东西");
    };
    //学生的构造函数
    function Student(name,age,sex) {
      //借用构造函数,call方法是改变了this的指向
      Person.call(this,name,age);
      this.sex=sex;
    }
    //改变原型指向
    Student.prototype=new Person();
    Student.prototype.run=function () {
      console.log("来啊,我们一起奔跑啊");
    };
    //实例对象
    var stu1=new Student("小明",20,"男");
    var stu2=new Student("小花",30,"女");
    console.log(stu1.name,stu1.age,stu1.sex);
    console.log(stu2.name,stu2.age,stu2.sex);
    stu1.eat();
    stu1.run();
 
八、拷贝继承
 
分为浅拷贝和深拷贝
 
浅拷贝:复制对象
 
var obj1={
      age:20,
      cat:["大黄","加菲猫"]
};
var obj2=obj1;//浅拷贝继承
obj2.age=100;
 
深拷贝:找到一个对象的属性或方法,在另一个对象内重新开辟一个空间,存储到另一个对象中
 
var obj1={
      age:10,
      sex:"男",
      car:["奔驰","宝马","特斯拉","奥拓"],
      dog:{
        name:"大黄",
        age:5,
        color:"黄色"
      }
    };
 
    //空对象
    var obj2={};
    //a---当前的对象,b是一个目标对象,把a对象中所有的内容全部拷贝到b对象中
    function extend(a,b) {
       //遍历过程
      for(var key in a){
        //先获取a对象中的每个属性值
        var item=a[key];
        //判断这个属性的值是不是数组
        if(item instanceof Array){
          //如果数据是数组,那么就在b对象中添加一个新的属性,并且这个属性的值也是数组
          b[key]=[];
          //调用方法,把对象a中的数据一个一个的复制到b对象的这个数组中
          extend(item,b[key]);
        }else if(item instanceof Object){
          //如果这个数据是对象,那么b对象中添加一个属性,也是对象类型的
          b[key]={};
          extend(item,b[key]);
        }else{
          //如果属性的值是普通的数据,那么就直接复制到b对象的属性中
          b[key]=item;//直接赋值
        }
      }
    }
 
    //调用方法
    extend(obj1,obj2);
    obj2.age=100;
    console.log(obj1.age);
    console.log(obj2.age);
 
区别:
浅拷贝,第一个对象和第二个对象的地址是相同的,其中一个对象的属性值改变,另一个对象的属性值也会发生变化;
深拷贝:两个对象之间发生拷贝后,属性的值是不会相互影响的
 
九、对象冒充
 
    //构造函数
    function Animal(name,color) {
      this.name=name;
      this.color=color;
    }
    //通过原型添加方法
    Animal.prototype.eat=function () {
      console.log("今晚吃的是榴莲沾臭豆腐汁,还有啃了两头大蒜");
    };
    //构造函数
    function Dog(name,color,age) {
      this.age=age;
      //对象冒充----仅仅是盗用了别人的构造函数实现属性的继承,并不能实现方法的继承
      this.Animal=Animal;
      this.Animal(name,color);
      delete this.Animal;//删掉了
    }
    //实例化
    var dog=new Dog("大黄","黄色",20);
    console.log(dog.name,dog.age,dog.color);
    dog.eat();
 
十、原型的不同写法
 
1.通过原型添加属性或方法
 
Person.prototype.name="乔峰";
Person.prototype.play=function () {
      console.log("走,出去玩儿");
 };
 
2.将属性和方法直接写到原型对象中
 
Person.prototype={
      constructor:Person,//一定要把这个加上
      name:"卡卡西",
      age:20,
      sex:"男",
      eat:function () {
        console.log("吃");
      },
      play:function () {
        console.log("玩");
      }
};
var p=new Person();
 
十一、动态原型
会让这种写法看起来更像是面向对象语言中的类的写法
 
    function Person(name, age) {
      this.name = name;
      this.age = age;
 
      //每次把原型的方法写在构造函数外部很麻烦,所以,可以这么写
      //在构造函数初始化的时候直接添加原型方法(如果构造函数没有初始化,那么原型方法也不会添加(只要实例化对象那么就会有原型方法))
      if (typeof Person.init == "undefined") {
        //原型方法
        Person.prototype.sayHi = function () {
          console.log("Hi~~");
        };
        Person.init = true;//标识
      }
    }
    var p = new Person("小白", 20);
    p.sayHi();
 
十二、通过原型为系统对象添加方法
沙箱的形式:函数自调用,内部变量的变化不会影响函数外相同名称的变量的变化
 
(function(){
    String.prototype.myTrim = function(){
       return this.replace(/\s*/ig,"");   
    }
})();
 
var str="    欢迎大家    来到我的博客 ";
console.log("==="+str.myTrim()+"===");
posted @ 2019-10-23 14:52  Katherine蓝羽  阅读(54)  评论(0)    收藏  举报