面向对象基础「构造函数执行的机制」

本文介绍了JavaScript中的面向对象基础,包括构造函数的执行机制,如创建实例对象、this指向和返回值处理。接着详细讲解了原型和原型链的概念,区分了具备和不具备prototype的函数类型,并探讨了对象数据类型值。此外,还讨论了如何基于内置类原型扩展方法,以及私有和公有属性的检测方法,如hasOwnProperty和in操作符的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象基础

   面向对象:object oriented programming OOP编程/设计思想
      + JS
      + JAVA
      + Python
      + PHP
      + C#
      + C++
      + GO
      + ...
   面向过程:procedure oriented programming POP
      + C语言

面向对象是基于类和实例来管理一门语言的;
@1 在JS中所有要学习和研究的内容都可以被称为“对象”、JS把其划分为很多类、我们平时就是创造这些类的实例进行开发的;
@2 每个实例之间有一些自己的私有属性和方法,也具备类赋予他们的公共的属性和方法;
@3 JS语言本身就是基于 “面向对象” 这种思想设计的语言,所以我们学习和开发也应该按照这种思想来进行处理!!

构造函数执行的机制

JS中的内置类
@1 数据类型所属内置类
+ Number 数字类 每一个数字都是他的一个实例「含NaN」
+ String
+ Boolean
+ Symbol
+ BigInt
+ Object
+ Function
+ Array
+ RegExp
+ Date
+ Error
+ …
@2 DOM元素对象也有自己所属的内置类
+ 每一种HTML标签都有自己所属的内置类 HTMLDivElement / HTMLAnchorElement … -> HTMLElement -> Element -> Node -> EventTarget -> Object
+ document实例 -> HTMLDocument「和 XMLDocument」 -> Document -> Node …
+ window实例 -> Window -> WindowProperties -> EventTarget -> Object
+ 元素集合 -> HTMLCollection -> Object
+ 节点集合 -> NodeList -> Object

示例:

const Fn = function Fn(x, y) {
    let sum = 10;
    this.total = x + y;
    this.say = function () {
        console.log(`我计算的和是:${this.total}`);
    };
   let res1 = Fn(10, 20); //普通函数执行
console.log(res1); //undefined
console.log(window); //window.total、window.say
let f1 = new Fn(10, 20); //构造函数执行「面向对象思维处理:创造类和实例  基于NEW执行,Fn是类,f1是实例」
console.log(f1);
let f2 = new Fn(100, 200);
console.log(f2);
console.log(f1.say === f2.say); //false  构造函数体中 this.xxx=xxx 给实例设置的私有属性方法 

在这里插入图片描述

new执行(构造函数执行)也会和普通函数一样创建一个私有上下文(相同点)
区别:
1、会创建当前类、构造函数的一个空的实例对象
2、让构造函数中的this指向这个实例对象
3、代码执行完,观察函数的返回值

  • 如果return的一个对象则以自己返回的为主
  • 如果返回的是一个原始值或者是没有返回return则默认返回的是这个实例对象

二、「原型和原型链」

大部分“函数数据类型”的值都具备“prototype(原型/显式原型)”属性,属性值本身是一个对象「浏览器会默认为其开辟一个堆内存,用来存储实例可调用的公共的属性和方法」,在浏览器默认开辟的这个堆内存中「原型对象」有一个默认的属性“constructor(构造函数/构造器)”,属性值是当前函数/类本身!!

1、函数数据类型

  • 普通函数(实名或者匿名函数)
  • 箭头函数
  • 构造函数、类(内置类、自定义类)
  • 生成器函数

2、不具备prototype的函数

  • 箭头函数
  • 基于es6给某个成员赋值函数值得快捷键操作
    在这里插入图片描述

每一个“对象数据类型”的值都具备一个属性“proto(原型链/隐式原型)”,属性值指向“自己所属类的原型prototype”

3、对象数据类型值

  • 普通对象
  • 特殊对象:数组、正则、日期、Math、Error…
  • 函数对象
  • 实例对象
  • 构造函数.prototype

示例:

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);
console.log(f1.getY === f2.getY);
console.log(f1.__proto__.getY === Fn.prototype.getY);
console.log(f1.__proto__.getX === f2.getX);
console.log(f1.getX === Fn.prototype.getX);
console.log(f1.constructor);
console.log(Fn.prototype.__proto__.constructor);
f1.getX();
f1.__proto__.getX();
f2.getY();
Fn.prototype.getY(); 

在这里插入图片描述
例如:

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));

最终结果:

'Tom' + undefined + 'join' => "Tomundefinedjoin"

三、基于内置类原型扩展方法

内置类原型指向图:arr -> Array.prototype -> Object.prototype

在这里插入图片描述

在这里插入图片描述
数组去重:

// 基于内置类原型扩展方法「实例可以直接调用」:设置的名字不要和内置的方法名冲突{一般自己会设置前缀 myXxx}
Array.prototype.unique = function unique() {
    // this -> 我们要处理的数组
    return Array.from(new Set(this));
};

let arr = [1, 3, 2, 4, 3, 2, 2, 3, 4, 2, 5, 6, 3, 2, 1, 4, 5];
arr = arr.unique().sort((a, b) => a - b);
console.log(arr);

在这里插入图片描述

 调用所属类原型上的内置属性方法  实例.方法() 
   + 方便
   + 实现链式写法「执行完一个方法,返回的结果还是当前类的实例,这样就可以继续调用,类为其提供的其它方法执行」
 arr.sort((a, b) => a - b).map(item => item * 10).filter(item => item < 50).push('zhufeng'); 

四、基于内置类原型扩展方法——面试真题

1、装箱

装箱的操作:浏览器首先会把原始值变为对象类型的实例「new Number(10) 或者 Object(10)」,然后再继续调用toFixed等方法…
10 是个数字,它本身是不具备键值对的,但是它是Number类的实例,可以调用Number.prototype上的方法

2、拆箱

把对象类型的实例变为原始值类型的值{Number(对象)} Symbol.toPrimitive -> valueOf -> toString -> 变为数字

3、真题

真题描述: let n = 10; let m = n.plus(10).minus(5); console.log(m);
//=>15(10+10-5)

(function (proto) {
    // 检测num是否是合法数字
    const checkNum = num => {
        num = +num;
        return isNaN(num) ? 0 : num;
    };

    proto.plus = function plus(num) {
        num = checkNum(num);
        return this + num;
    };

    proto.minus = function minus(num) {
        num = checkNum(num);
        return this - num;
    };
})(Number.prototype);

五、私有和公有属性的检测

1、Object.prototype.hasOwnProperty 私有检测是自己的私有属性

在这里插入图片描述
在这里插入图片描述

2、in 私有检测是自己的属性(公有 私有)

在这里插入图片描述

3、自己重构:Object.prototype.hasPubProperty

思路一:是它的属性,但是还不是私有的,这样只能是公有的属性

Object.prototype.hasPubProperty = function hasPubProperty(attr) {
    return (attr in this) && !this.hasOwnProperty(attr);
};

但是会有一个问题
let arr = [10, 20, 30];
arr.push = ‘push’;
console.log(arr.hasPubProperty(‘push’)); //false
例如这种情况push即是私有属性有事共有属性的时候返回的是fasle 按理说应该是true所以这个时候就出现了bug

所以这种思路是不靠谱的

思路二:跳过私有的查找「无论私有有没有,都无所谓」,直接找公共的「所属类的原型 先找自己所属类的原型,没有基于__proto__再继续向上找,直到Object.prototype为止;如果中间找到返回true,找到头都没有就是false!」

Object.prototype.hasPubProperty = function hasPubProperty(attr) {
    let self = this,
        prototype = Object.getPrototypeOf(self);
    while (prototype) {
        if (prototype.hasOwnProperty(attr)) {
            return true;
        }
        prototype = Object.getPrototypeOf(prototype);
    }
    return false;

这样才能满足上面的要求,完美的方案!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值