关于面向对象概念以及做题理解面向对象

72 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

编程语言

  • 面向对象 OOP( java js php c++ 等等)
  • 面向过程 POP (c)

面向对象编程

面向对象编程主要包括三大类

  • 对象:万物皆对象(泛指)
  • 类:对“对象”的划分(按照其功能结构特点划分 比如 人可以分为 男人和女人)
  • 实例:类中的具体事物

JS 本身是基于面向对象开发出来的编程语言,所以我们使用 JS 的时候要有面向对象的思维方式处理问题

  • 内置类

    • 每一种数据类型都有一个所属的内置类:Number(每个数字,NaN 都是它的实例) Sting/Boolean / Array 等等。。。。

    • 每一种 DOM 元素也有自己所属的类

      window -> Window -> WindowProperties -> EventTarget -> Object

      document -> HTMLDocument -> Document -> Node -> EventTarget -> Object

      .......

      学习数组,首先分析一个数组(实例),研究清楚这个实例特征后(结构特点以及常用方法),再遇到其他数组,也按照相同的机制处理

  • 自定义类

    创建一个函数 fn

    • fn() 普通函数执行(堆栈机制)
    • new fn() 构造函数(堆栈机制 + 面向对象机制)
// 分析一下自定义类的机制过程
function Fn(x, y) {
  let total = x + y;
  this.x = x;
  this.y = y;
}
let result = new Fn(10, 20);
console.log(result);

stack_new的副本.jpg

new 函数(): 构造函数执行,和普通函数的区别

  • 相似点
    • 一样是把函数执行(传递参数也一样)
    • 形成私有上下文
    • 变量提升
  • 不同点
    • new 执行,浏览器会在当前上下文中,默认创建一个对象(实例对象)
    • 在初始化this的时候,this执行这个实例
      • 代码中编写 this.xxx = xxx 的操作,都是给实例对象设置私有属性
      • 除了this.xxx = xxx 其余的操作全部和实例对象没有关系
    • 函数如果没有返回值,或者返回值是基本类型,则默认返回创建的实例对象,如果返回的是引用类型,那么以返回的引用类型为主

构造函数执行(函数被称为类,返回结果被成为实例)

function Fn() {
    /*
     * EC(FN)
     *   初始创建Fn找个类的一个实例对象  0x000
     *   初始THIS:this->0x000
     */
    let total = 0; //上下文的私有变量  和实例对象没有必然的联系
    this.x = 10; //this.xxx=xxx 都是给实例对象设置的私有属性和方法
    this.y = 20;
    this.say = function () { //0x000.say=0x100   0x001.say=0x101
        console.log('SAY');
    };
    /* 如果不设置返回值,或者返回值是一个基本类型值,默认都会把实例对象 0x000 返回;如果手动返回的是一个引用数据类型值,则以自己返回的为主; */
    // return {
    //     name: 'zhufeng'
    // };
}
let f1 = new Fn(); //->0x000
let f2 = new Fn; //->0x001  new执行的时候,如果类不需要传递实参吗,可以不用加小括号(不加小括号,叫做无参数列表new;设置小括号,叫做带参数列表new;除了师是否传递参数的区别,在运算的优先级上也有区别? new Fn->19  new Fn()->20)
// 每一次new都是把函数重新执行(重新形成一个新的私有上下文、重新创建一个实例对象、代码重新执行...)
// console.log(f1, f2, f1 === f2); //=>false

检测成员

检测成员是否属于这个对象或者是否属于这个对象的私有属性

  • in :检测成员是否属于这个对象( 'dd' in window)检测公有属性和私有属性,如果存在则为true
  • hasOwnProperty:检测成员的私有属性(f1.hasOwnProperty('say'))只有私有属性才为true

如何检测一个成员是公有属性而不是私有属性

// obj:要检测的对象
// attr:要验证的成员
 function hasPubProperty(obj, attr) {
    // 思路一:是它的属性 但是还不是私有的,那么一定是公有的「BUG:如果某个属性即使私有的,也是公有的,则检测出来的结果是不准确的」
    // return (attr in obj) && (!obj.hasOwnProperty(attr));
    
    // 思路二:真正的思路应该是检测原型上的属性,因为原型上的属性都是公有的
    // Object.getPrototypeOf:获取当前对象的原型
    let proto = Object.getPrototypeOf(obj);
    while (proto) {
        // 依次查找原型链,直到找到Object.prototype为止
        if (proto.hasOwnProperty(attr)) {
            return true;
        }
        proto = Object.getPrototypeOf(proto);
    }
    return false;
} 

题目

第一题

function C1(name) {
  if (name) {
    this.name = name;
  }
}
function C2(name) {
  this.name = name;
}
function C3(name) {
  this.name = name || "join";
}
C1.prototype.name = "Tom";
C2.prototype.name = "Tom";
C3.prototype.name = "Tom";
alert(new C1().name + new C2().name + new C3().name);

第二题

let n = 10;
let m = n.plus(10).minus(5);
console.log(m); //=>15(10+10-5)

第三题

// 使用es6 语法 class 修改下面的代码
function Modal(x, y) {
  this.x = x;
  this.y = y;
}
Modal.prototype.z = 10;
Modal.prototype.getX = function () {
  console.log(this.x);
};
Modal.prototype.getY = function () {
  console.log(this.y);
};
Modal.n = 200;
Modal.setNumber = function (n) {
  this.n = n;
};
let m = new Model(10, 20);

第四题

let obj = {
  2: 3,
  3: 4,
  length: 2,
  push: Array.prototype.push,
};
obj.push(1);
obj.push(2);
console.log(obj);

第五题

var a = ?;
if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
}

答案和分析

// 第一题
// C1 new C1().name  没有传入name 因为有if判断,所以C1没有私有属性name,这个时候要向原型上查找  所以为 Tom
// C2 c2也没有传入name 但是还是设置this.name = undefined 所以 C2是undefined
// C3 C3添加了一个或 所以this.name = "join"
// 最后字符串拼接 答案是 Tomundefinedjoin
// 第二题
// 检查是不是数字
var validate = function validate(x) {
  x = +x;
  return isNaN(x) ? 0 : x;
};
Number.prototype.plus = function plus(x) {
  x = validate(x);
  // this都是对象数据类型的值  this->10/new Number(10)
  return this + x;
};
Number.prototype.minus = function minus(x) {
  x = validate(x);
  return this - x;
};

let n = 10;
let m = n.plus(10).minus(5);
console.log(m); //=>15(10+10-5)
// 第三题
class Modal {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  getX() {
    console.log(this.x);
  }
  getY() {
    console.log(this.xy);
  }
  static n = 200;
  static setNumber(n) {
    this.n = n;
  }
}
Modal.prototype.z = 10;
// 第四题
let arr = [10, 20];
arr.push(30);
// Array.prototype.push 每次都向数组最后一位添加一位,并且数组累加
Array.prototype.push = function (value) {
  // this == arr
  // 把value 放在数组最后一位 this[this.length] = value
  // length 累加
  // 返回累加后的数组长度
};

let obj = {
  2: 3,
  3: 4,
  length: 2,
  push: Array.prototype.push,
};
obj.push(1); // this == obj value=1  length长度是2因为对象里面有定义length  obj[2] = 1 obj.length = 3
obj.push(2); // this == obj value=2  obj[3] = 2 obj.length = 4
console.log(obj); // {2:1,3:2,length:4,push:function}
// 第五题 a = ? 才符合判断
/*
=== 绝对相等 必须类型和值都相等才可以相等
== 相等 左右两边类型不同,会先转化为相同的类型再比较
   对象 == 字符串 对象转字符串
   null == undefined 相等 但是和其他值都不相等
   NaN == NaN false NaN 和谁都不相等
   其他的都是转化为数字比较

对象 转 数字或者字符串
     先调取属性 Symbol.toPrimitive
     没有这个属性 再去调取valueOf 获取原始值(基本类型)
     没有原始值 再去调用toString 变成字符串
     如果最后是转化成数字 再去调用Number 把字符串转化为数字
*/
let obj = {};
var a = {
  i: 0,
};
// Symbol.toPrimitive 可以替换为 valueOF  或者 toString 本质上都一样
a[Symbol.toPrimitive] = function (hint) {
  // this==a
  return ++this.i;
};
a.valueOf = function (hint) {
  // this==a
  return ++this.i;
};
a.toString = function (hint) {
  // this==a
  return ++this.i;
};

// 数据劫持
//   + 在全局上下文中基于var/function声明变量,相当于给window设置对应的属性  -> window.a
//   + Object.defineProperty劫持对象中某个属性的获取和设置等操作
var i = 0;
Object.defineProperty(window, "a", {
  get() {
    // 获取window.a的时候触发getter函数
    return ++i;
  },
  // set(value) {
  //     // 设置window.a属性值的时候触发setter函数
  // }
});
if (a == 1 && a == 2 && a == 3) {
  console.log("OK");
}