构造函数浅析

78 阅读4分钟

构造函数实例化的时候做了哪些操作

  1. 创建一个新对象:并将这个对象作为新实例实例对象
  2. 设置原型链接:js中对象具有原型链,可以继承其他对象的属性和方法。构造函数有一个protpotype属性,该属性指向了一个对象,新创建的实例对象会通过原型与该对象链接起来,以便继承构造函数的属性和方法。
  3. 执行构造函数代码:构造函数是一个普通js函数,它可以接受参数,并且可以在构造实例时执行一些初始化操作。构造函数内部的代码将被执行,通常这些代码会初始化实例的属性和方法
  4. 返回实例对象:通常构造函数不需要显式返回实例对象,js会自动返回这个新创建的实例对象,这样就可以在构造函数内设置属性和方法,并且不需要return语句来返回实例对象(如果有显式返回return,如下分析)

构造函数内部显示return会出现什么情况

  1. 如果构造函数内返回一个普通js对象(!==null or !== undefined),这个对象将取代默认创建的空对象作为构造函数的实例,而不是返回默认创建的实例对象,这意味着构造函数中的属性和方法设置不会影响到最终的实例对象
  2. 如果return返回一个原始值(string/number/boolean)则这个返回值将被忽略,仍然返回默认创建的实例对象
  3. 如果return返回null or undefined,则依旧会返回默认创建的实例对象
总之,构造函数内部的return可以改变到实例化的结果。通常情况下,最好不要在构造函数内使用return语句返回结果,以免引发混淆和不一致的行为。最佳实践是在构造函数内初始化属性和方法,而不是返回其他对象

构造函数实例化过程

// 构造函数化使用new关键字,简易new的实现
function _new(fn, args) {
    // 创建一个新对象
    const obj = {};
    // 通过原型与构造函数prototypr进行链接
    obj._proto_ = fn.prototype;
    // 执行构造函数
    const result = fn.apply(this, args);
    // 判断result是否属于对象,如果是,则直接返回,否则返回obj
    return result instanceof Object ? result : obj;
}

instanceof浅谈

instanceof是检查对象是否是某个构造函数的实例的运算符

object instanceof constructor
// object是要被检查的对象
// constructor是要被检查的构造函数
// instanceof会在其原型链上检查是否包含constructor.prototype,如果是,返回true,否则返回false
// 与typeof不同,instanceof主要用于检查对象的原型链,而typeof用于检查数据类型。
// typeof只能检查数据原始类型(数字、字符串、布尔值等)及函数,而不能检查对象是否是特定构造函数的实例

箭头函数解析及为何不能用做构造函数

箭头函数特点及用法

  1. 更简洁:箭头函数的语法比传统函数的定义更为简洁,尤其是对于单一函数体
  2. 没有自己的this:箭头函数没有自己的this上下文,this在箭头函数书写位置就已经确定,后续也不再可以通过显示绑定等方式改变,这个特性可以减少this绑定问题的发生
  3. 不能用做构造函数:不能使用new来实例化,也不能做为构造函数创建新对象。

为什么箭头函数不能用做构造函数

  1. 缺少prototype属性:构造函数通常具有一个prototype属性,用于定义构造函数创建实例的原型。这个原型允许实例继承构造函数的属性和方法。
  2. this绑定:箭头函数的this是在定义时捕获的,总是指向外部函数的this值。这意味着箭头函数无法动态绑定this值,而是固定绑定到定义箭头函数的上下文

动态改变this指向(显示绑定)

call & apply

// call & apply用法基本一致,绑定时会立即执行,区别是call支持多个传参,appy只支持两个参数
// 实现一个apply
Function.prototype._apply = function(context = window, ...args) {
    context.fn = this;
    const result = context.fn();
    delete context.fn;
    return result;
}

bind

// 返回一个新函数,并不会立即调用它
Function.prototype._bind = funtion(context) {
    const self = this;
    const ars = arguments.slice(1);
    
    return function() {
        const newArg = [...arguments];
        return self.apply(context, arg.concat(newArg));
    }
}

// 考虑到bind返回返回可能会被用做构造函数,做如下调整
Function.proptotype._bind = function(context) {
    const originFun = this;
    const arg = arguments.slice(1);
    return function() {
        const newArg = [...arguments];
        // 判断是否是new关键字调用
        const isNew = this instanceof originFun;
        if(isNew) {
            // 如果是new调用 则绑定到新创建的实例上
            const instance = Object.create(originFun.prototype);
            const result = originFun.apply(instance, arg.concat(newArg));
            return Object(result) === result ? result : instance;
        } else {
            return originFun.apply(context, arg.concat(newArg));
        }
    }
}