6-面向对象开发

92 阅读4分钟

核心就是 对象,类,实例三者之间的关系

js中万物皆对象

单例模式远古时期

  • 把描述同一个事物的属性和方法放在同一个对象中,起到分组作用
    • 说白了就是一个破对象
let person1 = {
  name: "小白",
  age: 19
}
let person2 = {
  name: "甜甜",
  age: 18
}
// => person1和person2也叫做"命名空间"

工厂模式早期方式

  • 单例模式虽然解决了分组的问题,但不能实现批量的生产,属于手工作业模式
  • 于是诞生了"工厂模式",实现"低耦合高内聚"
//=>把实现同一个事件的相同代码放到一个函数中,以后再想实现这个功能,只需要执行当前的函数即可->函数的封装
function createJsPerson(name, age){
    var obj = {};
    obj.name = name;
    obj.age = age;
    obj.write = function(){
        console.log('my name is' + this.name + 'i can write js!');
    }
    return obj
}

var p1 = createJsPreson('王玉', 18);
p1.write();
var p2 = createJsPreson('婷婷', 18);
p2.write();
  • 所有的编程语言都是面向对象开发的->类的继承/封装/多态
  • 继承:子类继承父类中的属性和方法
  • 多态: 当前方法的多种形态,在后台语言中包含 (重载和重写)
    • 重载:方法名相同,传的参数数据类型不同,就是2个方法都可以用(JS中没有重载)
      • 在JS中会覆盖的(所以JS没有重载)
    • 重写:子类重写父类的方法
//Js中有一个类似重载的方法但不是重载:我们可以根据传递参数的不一样,实现不同的功能
function sum(num){
    if(typeoof num === 'undefined'){
        return 0;
    }
    return num;
}
sum(100);
sum();

构造函数

  • 目的: 就是为了创建一个自定义类,并且创建这个类的实例
  • 构造函数构造原理:
    • 1.在函数体的最前面隐式的加上 this = {}
    • 2.执行this.xxx = xxx;
    • 3.隐式的返回 return this;

构造函数模式 和 工厂模式的区别?

    1. 执行的时候
    • new CreatePerson() -> 通过 new执行后 CreatePerson 就是一个类了
    • 相同点:
      • 都是形成一个私有的作用域,经历形参赋值 -> 预解释 -> 代码从上到下执行(类也有普通函数的一面)
      • 通过new执行变成了一个类,但是它本身也是普通的函数
    • 不同点:
      • 在代码执行前,不用自己在手动的创建对象,浏览器会默认的创建一个this = {}
      • 不用手动return,构造函数会自己return this
      • 如果不传参数 可以不用小括号如 new Fn;
  • JS中所有的实例都是对象数据类型的
  • 构造函数: 手动返回基本类型的值还是默认返回this,return 对象才会改变
function CreatePerson (name, age) {
  this.name = name;
  this.age = age;
  this.say = function() {
    console.log(`姓名:${this.name}-年龄:${this.age}`)
  }
}
const p1 = new CreatePerson("沐沐", 18);
p1.say();// this -> p1
let ss = CreatePerson("rr", 12);
console.log(ss.name);// undefined this -> window

检测某个实例是否是这个类

funtion Fn() {
  this.x = 100;
  this.getX = function() {
    console.log(this.x);
  }
}
let f1 = new Fn;
let f2 = new Fn;
// => 检测某个实例是否是这个类
console.log(f1 instanceof Fn);//=> true
console.log(f1 instanceof Array);// => false
console.log(f1 instanceof Object);// => true 
// => 所有的实例都是对象数据类型的,而每一个对象数据类型都是Object这个内置对象的实例

console.log(f1.getX === f2.getX);//false
// in: 检测某一个属性是否属于这个对象 attr in object
console.log('getX' in f1);// => true 是它的一个属性

//=> hasOwnProperty: 检测某个属性是否为某个对象"私有属性",这个方法只能检测私有属性
console.log(f1.hasOwnProperty('getX'));// => true

// => 检测某一个属性是否是该对象的"共有属性"
function hasPubOwnProperty (obj, attr) {
  // 是他的属性,并且不是私有属性
  return (attr in obj) && !obj.hasOwnProperty(attr) === false;
}
  • 例子1
function Fn(x, y) {
  let sum = 10;
  this.total = x + y;
  this.say = function() {
    console.log('总数为' + this.total);
  }
}

let res = Fn(10, 20);//普通函数 非严格模式下this -> window
let f1 = new Fn(10, 20);//构造函数执行
let f2 = new Fn(10, 20);
console.log(f1.sum);// undefined
console.log(f1.total);// 30
console.log(f1 === f2)// false
  • 例子2
function Fn() {
    this.x = 100;
    this.y = 200;
    this.getX = function() {
      console.log(this.x);
    }
}

Fn.prototype.getX = function() {
    console.log(this.x);
}
Fn.prototype.getY = function() {
    console.log(this.y);
}
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);// false
console.log(f1.getY === f1.getY);// true
console.log(f1.__proto__.getY === Fn.prototype.getY); // true
console.log(f1.__proto__.getX === Fn.prototype.getX);// true
console.log(f1.getX === Fn.prototype.getX); // false
console.log(f1.constructor)// Fn
console.log(Fn.prototype.__proto__.constructor);// Object
f1.getX();// 100
f1.__proto__.getX();// undefined
f2.getY();// 200;
f2.__proto__.getY();//undefined

图解流程图如下: image.png

  • 例题3
    • 阿里的面试题
function Dog(name) {
  this.name = name;
}

Dog.prototype.bark = function() {
  console.log('wangwang');
}

Dog.prototype.sayName = function() {
  console.log(`my name is ${this.name}`);
}


function _new (ctor, ...args) {
  // => 根据后面的结果完成你的代码
  // 1.创建空对象, 对象.__proto__ = ctor.protoType
  obj = {}
  obj.__proto__ = ctor.prototype;

  // 2. 像普通函数一样执行,但是需要让函数中的this指向创建的实例对象(实参也要传进去)
  let result = ctor.call(obj, ...args);
  // 3. 监听方法的返回值,如果是原始值类型则把创建的实例返回...
  if(typeof result !== 'object' || result === null) {
    return obj;
  }
  return result;
}
let sanmao = _new(Dog, '三毛');
sanmao.bark();// => wangwang
sanmao.sayName();// => "my name is 三毛"
console.log(sanmao instanceof Dog);// => true

优化写法

function _new (ctor, ...args) {
  // => 根据后面的结果完成你的代码
  if(!ctor.prototype || ctor === Symbol || ctor === BigInt) {
    throw new TypeError('ctor is not a contructor function');
  }

  // 1.创建空对象, 对象.__proto__ = ctor.protoType
  let obj = Object.create(ctor.prototype);
  // 2. 像普通函数一样执行,但是需要让函数中的this指向创建的实例对象(实参也要传进去)
  let result = ctor.call(obj, ...args);
  // 3. 监听方法的返回值,如果是原始值类型则把创建的实例返回...
  if (result !== null && /^(object|function)&/.test(typeof result)) return result;
  return obj;
}