14.面向对象(二)

74 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

工厂函数

创建工厂函数

function creatObj(width,color,brand,lang){
  // 属性 - 构造属性
  this.width = width; // this 
  this.color = color;
  this.brand = brand;
  this.lang = lang;
  // 方法 - 构造方法
  this.run = function(){
    console.log( this.brand + '在路上行驶');
  }
  this.start = function(){
    console.log( this.brand + 'start');
  }
  this.stop = function(){
    console.log(this.brand + '刹不住车了!');
  }
    var byd = new creatObj('1500mm','red','比亚迪');
  var bmw = new creatObj('1500mm','白色','BMW');
​
  bmw.start();
  byd.start();
}

这样也可以创建一个对象,但是每次创建的对象的地址都是一个新的内存地址,内存地址是不同的

console.log(bmw.start == byd.start); // false

这样会更加浪费内存空间,需要讲方法放在原型中

// 工厂模式; 
function creatObj(width,color,brand,lang){ // 类
  // 属性 - 构造属性
  this.width = width; // this 
  this.color = color;
  this.brand = brand;
  this.lang = lang;
  // 方法 - 构造方法
  this.run = function(){
    console.log( this.brand + '在路上行驶');
  }
}
// 原型 prototype -  属性
creatObj.prototype.lang = this.lang;
​
// 原型 prototype -  方法
creatObj.prototype.start = function(){
  console.log( this.brand + 'start');
}
creatObj.prototype.stop = function(){
  console.log(this.brand + '刹不住车了!');
}
​
var byd = new creatObj('1500mm','red','比亚迪'); // 对象
var bmw = new creatObj('1500mm','白色','BMW');  // 对象
​
bmw.start();
byd.start();

案例:给数组添加sum求和方法

正常添加

var arr = new Array(1,2,3,4,5,6,7);
// sum 字定义了一个方法
arr.sum = function(){
  var res = 0;
  for(var i=0; i<this.length; i++){
    res += this[i];
  }
  return res;
}
console.log(arr.sum());  // 28
​
var arr2 = [1,2,3];
console.log(arr2.sum()); // undefined

当前sum方法只是添加给了arr这个对象,并没有添加给整个数组对象,所以如果是给已经存在的对象添加方法,需要使用prototype原型方法

var arr = new Array(1,2,3,4,5,6,7);
// sum 字定义了一个方法
Array.prototype.sum = function(){
  var res = 0;
  for(var i=0; i<this.length; i++){
    res += this[i];
  }
  return res;
}
console.log(arr.sum()); // 28
​
var arr2 = [1,2,3];
console.log(arr2.sum()); // 6

原型相关的方法

  1. Object.getPrototypeOf() 取得实例的原型对象。

Object.getPrototypeOf(person1);

  1. isPrototypeOf() 判断是不是一个实例的原型对象。

Person.prototype.isPrototypeOf(person1);

  1. hasOwnProperty() 检测一个属性是否存在于实例中

person1.hasOwnProperty("name");

  1. in 判断一个属性是否存在于实例和原型中。

"name" in person1;

面向对象就是一种全新的写法而已。用面向过程能实现的功能,面向对象依然能够实现,对于初学者来说优点麻烦

思考:给数组添加去重方法

方法一:函数方式实现

function arrayNoRepeat(arr) {
  var tempArr = [];
  for (var i = 0; i < arr.length; i++) {
    if (tempArr.indexOf(arr[i]) == -1) {
     tempArr.push(arr[i]);
    }
  }
  return tempArr;
}
var numArr = [9, 9, 3, 4, 8, 9, 3, 4, 8];
var relArr = arrayNoRepeat(numArr);
console.log(numArr);
console.log(relArr);

方法二:通过原型方法实现

Array.prototype.noRepeat = function () {
  for (var i = 0; i < this.length; i++) {
    for (var j = i + 1; j < this.length; j++) {
      if (this[j] == this[i]) {
        this.splice(j, 1);
        j--;
      }
    }
  }
}
console.log(numArr);
numArr.noRepeat();
console.log(numArr);

继承

面向对象语言的特点: 封装,  继承,  多态

面向对象中,有两个很重要的东西,属性和方法

所以继承的时候就是想办法把属性和方法继承过来就可以了

属性和方法

    构造属性/方法
​
    原型属性/方法

继承

    构造继承
​
    原型继承

1.继承-关键词

继承: 一个类拿到另一个类的属性/方法

JS 中的继承可以通过 call() 和 apply() 两个方法实现.call 和 apply 方法的本质是通过改变this指向来实现继承.

call语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])

apply语法:apply([thisObj[,argArray]]) argArray必须为数组格式

2. 构造继承

构造继承(如上述代码, 通过 call 和 apply 实现)

创建一个Student对象(姓名,性别,年龄,学号)

用.call()和.apply()将父类构造函数引入子类函数

特点:只能继承构造中的内容,不能继承原型中的内容

function Student(name,sex,age,stu_id){
    // 继承方法1: 让student 继承 person
    // Person.call(this,name,sex,age);
    Person.apply(this,[name,sex,age]);
​
    // 添加自己的构造属性
    this.stuID = stu_id;
​
    // 重写
    this.play = function(){
        console.log('蹦迪!!!');
    }
​
    // 添加学习方法
    this.study = function(){
        console.log('studey');
    }
}
// 原型
Student.prototype.ID= '1000001';
Student.prototype.eat = function(){
    console.log('减肥,不吃了');
}

3. 原型继承

实现的本质:重写原型对象,用一个新的类型的实例去替代

优点:

1, 简单粗暴的实现了继承, 非常纯粹的继承方式

2, 父类原型对象中的属性和方法都可以继承到

缺点:

1, 子类没法新增属性/方法

2, 无法实现多继承

var Student = function(){
}
Student.prototype = new Person();
​
var stu = new Student();
console.log(stu.ID); // 原型属性
stu.eat()// 原型方法

4. 实例继承

是一种变态的继承方式,通过在子类中实例化对象的形式,其实就是把父类给倒进来了

特点:

1, 不限制调用方式, 可以使用new 也可以不使用.

2, 子类实际上是父类的一个实例.

3, 不能实现多继承.

4, 不能给子类添加原型对象

function Student(name,sex,age){
    var per = new Person(name,sex,age);
    per.stuID= '123456789';
    per.getId = function(){
        console.log(this.stuID);
    }
    return per;
}
​
var stu = new Student('Jack','男',18);
stu.getId();

5. 拷贝继承

拷贝继承,并不是直接通过=进行赋值

特点:

1, 支持多继承

2, 效率低, 内存占用高

3, 无法获取父类中不可枚举的方法

function Student(name,sex,age,stuId){
    var per = new Person(name,sex,age);
    // 循环 - 将父级中的属性和方法进行遍历
    for(var key in per){
​
        // Student.name = jack
        // Student[key] =  per[key] 推的过程
​
        Student.prototype[key] = per[key];
        console.log(key); // 获取person中的键
        console.log(per[key]); // 获取person中的值
    }
​
​
​
    Student.prototype.study = function(){
        console.log('好好学习');
    }
​
    // 添加自己的属性
    this.stu_id = stuId;
}
var stu = new Student('Jack','男',18,'1001');
console.log(stu);

6. 组合继承

用构造继承方法(call)继承父级的普通属性和方法; 用原型继承,继承父级的原型属性和方法 重点:结合了两种模式的优点,传参和复用。 特点:

1、可以继承父类原型上的属性,可以传参,可复用;

2、每个新实例引入的构造函数属性是私有的。

缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

function Student(name,sex,age,stuID){
    // 继承构造 call apply
    // Person.call(this,name,sex,age);
    Person.apply(this,[name,sex,age]);
    // 添加新的构造
    this.stuID = stuID;
}
// 继承原型
Student.prototype = new Person();
Student.prototype.getID = function(){
    console.log(this.stuID);
}
​
var stu = new Student('Jack','男',18,'10010');
console.log(stu.name);
console.log(stu.stuID);
console.log(stu.ID);
stu.eat();
stu.getID();
console.log(stu);

7. 寄生继承

特点:

1, 融合了以上所有方式的优点

2, 实现复杂

function Student(name,sex,age,stuID){
    Person.call(this,name,sex,age);
    this.stuID = stuID;
    this.study = function(){
        console.log('我正在学习');
    }
}
(function(){
    // 创建一个super类,作为中间媒介,进行传递
    var Super = function(){};
    // 将该类原型执行person的原型
    Super.prototype = Person.prototype;
    // 子类的原型指向 super的实例
    Student.prototype = new Super();
})()
var stu = new Student('Jack','男',18,'10010');
console.log(stu.ID);
console.log(stu.name);
stu.play()

综合案例:使用面向对象实现选项卡效果

 <style>
        #div1 div{
            width:300px;
            height: 100px;
            border: 1px solid #f00;
            display: none;
        }
        .active{
            background-color: #f00;
            color: #fff;
        }
</style>
<div id="div1">
        <input class="active" type="button" name="" id="" value="歌星">
        <input type="button" name="" id="" value='诗人'>
        <input type="button" name="" id="" value='作家'>
        <div style="display: block;">林俊杰,周杰伦</div>
        <div>李白,杜甫</div>
        <div>朱自清,鲁迅</div>
</div>
<script>
    var oBtn = null;
    var aDiv = null;
​
    // TabSwitch 构造函数
    function TabSwitch(){
        var _this = this; // 对象
        var oDiv = document.querySelector(id);
        this.oBtn = oDiv.getElementsByTagName('input');
        this.aDiv = oDiv.getElementsByTagName('div');
​
        for(var i=0;i<this.oBtn.length;i++){
            this.oBtn[i].index = i;
            this.oBtn[i].onclick = function(){
                _this.fnClick(this);
            };
        }
    }
    TabSwitch();
</script>