理解JS面向对象

72 阅读4分钟
  • 在编程的世界,程序通常可以分为:面向对象程序设计(OOP) 和 面向过程程序设计(POP)。
  • 就面向对象而言,是指万物皆对象,然后我们根据一定的思维方式,对对象进行特定的抽离,让其划分为具体的类。我们再拿出这个类的实例进行研究。从这个实例上可以映射出对应类的特性。

面向对象的分类

  • JS本身就是基于面向对象开发的语言。

  • 它可以划分为:内置类 和 自定义类

  • 我们所用的数据类型,每一种都对应一个自己的数据类型

    • 数字类:Number:每一个数字,包含NaN Infinity,这些都是Number类的实例
    • 字符串类、布尔...: String、Boolean、BigInt、Object(Array、Date...)、Function
    • Null、Undefined 这两个类是存在的,但是被浏览器保护起来了,是不让用
    • Symbol 这个类有点特别,不能new
  • DOM元素也存在有自己的类和实例

    • dir(div): DIV 元素对象 --> HTMLDivElement --> HTMLElement ---> Element --> Node ---> EventTarget --> Object
    • dir(a): A元素对象 ---> HTMLAnchorElement ---> HTMLElement --> ...
    • dir(document) document ---> HTMLDocument --> Document --> Node --> ...
    • dir(window) window ---> Window ---> WindowProperties ---> EventTarget --> ...
    • 节点集合 --> NodeList --> Object
  • 我们也可以设置 自定义类

    • 类名的首字母一般是大写
    • 每一个类都是函数数据类型的。
    function Fun() {
        this.xxx = xxx
    };
    Fun();
    let fn = new Func();
    log(fn);
    
    • 自定义类也是函数,所以也可以直接调用。直接掉哟个时候,就是普通函数执行。所以它有普通函数的特性:创建私有上下文、初始化作用域链、初始化THIS、初始化ARGUMENTS、形参赋值、变量提升、代码执行。
    • 使用new 执行时,会将该函数当作类执行,此时的函数叫做类,返回的结果 称为当前类的实例。是一个实例对象。

构造函数和函数执行的区别

  • 构造函数 和 普通函数 执行,大体是相同的。构造函数具备普通函数执行的业务链:初始化作用域链、初始化THIS、ARGUMENTS、形参赋值、变量提升、代码执行。
  • 构造函数 比 普通函数 执行,多了一项创建对象和指向的过程
    • 在初始化作用域链 和 初始化THIS 之间。构造函数执行会:
    • 默认创建一个对象
    • 让上下文中的THIS 指向这个对象
    • this.xxx=xxx; 是给这个实例 创建 私有属性 和 方法
    • 函数中的 RETRUN:如果是基本数据类型,那么返回这个创建的对象;如果返回引用类型,用这个类型代替创建出的对象。

检测方式

  • 检测返回的对象,是不是这个类的实例 instanceof f instanceof Func
  • 检测某个属性是否是对象的属性 [无论公有还是私有] in 属性 in 对象
  • 判断一个属性是当前对象的私有属性还是公有属性 hansOwnPrototype Obj.hasOwnPrototype(属性名)

prototype 和 __proto__

  • 任何一个对象,都有很多的属性和方法,放在自己堆内存中的 是私有属性和方法。如果是基于 __proto__ (原型链)找到对应 prototype(原型)上的属性和方法,这些是共有属性和方法。
  • 每一个函数(包含内置类)都是Function 的一个实例,所以他们的__proto__ 一定指向的是Function.prototype
  • 每一个对象(包含原型对象)都是Object 的实例,所以最后他们基于__ptoto__一定可以找到Object.prototype

Object.create 原理

  • 创建一个对象,然后让这个对象的 __proto__ 指向这个参数函数的prototype。
  • 创建一个对象,可以将其理解为创建一个类的实例。然后让这个类的原型指向参数
Object.create = function create(prototype) {
    function Func () {};
    Func.prototype = prototype;
    return new Func();
}

重写new

  • new 的执行过程:
    • 创建一个实例对象
    • 让类当作普通函数执行
    • THIS 指向这个实例对象
    • 函数执行是否存在 RETURN,如果有查看数据类型
function _new(Func, ...args) {
    
    let obj = Object.create(Func.prototype); // 创建实例对象,其this指向FUNC的prototype
    
    let result = Func.call(obj, ...args); // 让函数执行,同时THIS发生改变
 	
    if(result !== 'null' && /^(object|function)$/.test(typeof result)) { // 判断RETURN
        return result;
    }
    return obj;
}

来个题?

function Func (x,y) {
    let num = x+y;
    this.x = x;
    this.y = y;
} 
let f1 = Func(10,20);
let f2 = new Func(10,20);
  • Func(): 当作普通函数执行,num作为私有上下文的私有属性。this执行window。没有return,所以为 undefined
  • new Fun(): 当作构造函数执行。num依然是上下文的私有属性,new 会创造一个实例对象,this是给这个实例对象中赋予私有属性和方法。所以f2={x: 10, y: 20}