jquery 链模式调用简易实现

   <script>
    // 定义一个名为A的构造函数,接受selector和context参数
    var A = function (selector, context) {
        // 返回一个新的A.fn.init实例
        return new A.fn.init(selector, context);
    }

    // 设置A的原型和fn属性
    A.fn = A.prototype = { 
        // 强化构造器:
        // 当显式地重写 A.prototype(如通过 A.fn = A.prototype = {...} 赋值时),
        // 默认情况下 A.prototype.constructor 会丢失,变为指向 Object.
        // 因此,需要手动将 constructor 重新指向 A,以保持逻辑的正确性。
        constructor: A,
        // 初始化方法,接受selector和context参数
        init: function (selector, context) {
            // 初始化length属性为0
            this.length = 0;
            // 设置默认上下文为document
            context = context || document;
            // 根据selector的不同情况,执行不同的DOM查询
            if (~selector.indexOf('#')) {
                // 如果是ID选择器
                this[0] = context.getElementById(selector.slice(1));
                this.length = 1
            } else if (~selector.indexOf('.')) {
                // 如果是类选择器
                const elements = document.getElementsByClassName(selector.slice(1));
                for (let i = 0; i < elements.length; i++) {
                    this[i] = elements[i];
                }
                this.length = elements.length;
            } else {
                // 如果是标签选择器
                const elements = context.getElementsByTagName(selector);
                for (let i = 0; i < elements.length; i++) {
                    this[i] = elements[i];
                }
                this.length = elements.length;
            }
            // 设置selector和context属性
            this.selector = selector;
            this.context = context;
            // 返回当前实例
            return this;
        },
        // 返回实例的长度
        size: function () {
            return this.length;
        },
        // 初始化length属性
        length: 0,
        // 增强数组。
        push: [].push,
        splice: [].splice,
        sort: [].sort
    }

    // 设置A.fn.init的原型为A.fn
    // 在 JavaScript 中,构造函数的实例会继承构造函数的原型 (prototype),
    // 这使得实例可以访问构造函数原型上的方法。

    // A.fn = A.prototype = { ... } 设置了 A 构造函数的原型为 A.fn,
    // 使得 A 的实例可以访问 A.fn 中定义的方法(如 init 方法、size 方法等)。
    // 但 init 是作为 A.fn.init 构造函数的 prototype 属性来定义的,而不是直接在 A.fn 上定义的。
    // 所以,A.fn.init.prototype = A.fn; 是为了确保通过 new A.fn.init() 创建的实例能够继承 A.fn 中的所有方法。
    A.fn.init.prototype = A.fn;

    // 扩展方法,用于扩展A或A.fn
    // 对 A 的方法扩展是 静态方法,直接用A.XXX() 调用。
    // 对 A.fn 的方法扩展是 实例方法。用 obj = new A(); obj.xxx() 实例对象调用。
    A.extend = A.fn.extend = function () {
        var i = 1, len = arguments.length, target = arguments[0], j;
        // 如果只有一个参数,则将target设为this
        if (i == len) {
            target = this;
            i--;
        }
        // 遍历参数对象,将属性复制到target
        for (; i < len; i++) {
            for (j in arguments[i]) {
                target[j] = arguments[i][j];
            }
        }
        // 返回扩展后的对象
        return target;
    }

    // 添加事件监听方法
    
    A.fn.extend({
        on: (function () {
            // 根据浏览器支持情况,选择事件监听方法
            if (document.addEventListener) {
                return function (type, fn) {
                    var i = this.length - 1;
                    for (; i >= 0; i--) {
                        this[i].addEventListener(type, fn, false);
                    }
                    return this;
                }
            } else if (document.attachEvent) {
                return function (type, fn) {
                    var i = this.length - 1;
                    for (; i >= 0; i--) {
                        this[i].attachEvent('on' + type, fn);
                    }
                    return this;
                }
            } else {
                return function (type, fn) {
                    var i = this.length - 1;
                    for (; i >= 0; i--) {
                        this[i]['on' + type] = fn;
                    }
                    return this;
                }
            }
        })()
    })

    // 驼峰命名方法,用于将CSS属性名转换为驼峰命名
    A.extend({
        camelCase: function (str) {
            return str.replace(/-(\w)/g, function (all, letter) {
                return letter.toUpperCase();
            })
        }
    })

    // CSS方法,用于设置或获取CSS属性
    A.fn.extend({
        css: function () {
            var args = arguments, len = args.length;
            // 根据参数数量和类型,执行不同的逻辑
            if (this.length < 1) {
                return this;
            }
            if (len === 1) {
                if (typeof args[0] === 'string') {
                    if (this[0].currentStyle) {
                        return this[0].currentStyle[A.camelCase(args[0])];
                    } else {
                        return getComputedStyle(this[0], false)[A.camelCase(args[0])];
                    }
                } else if (typeof args[0] === 'object') {
                    for (var i in args[0]) {
                        for (var j = this.length - 1; j >= 0; j--) {
                            this[j].style[A.camelCase(i)] = args[0][i];
                        }
                    }
                }
            } else if (len === 2) {
                for (var j = this.length - 1; j >= 0; j--) {
                    this[j].style[A.camelCase(args[0])] = args[1];
                }
            }
            return this;
        }
    })

    // attr方法,用于设置或获取DOM元素的属性
    A.fn.extend({
        attr: function (name, value) {
            var arg = arguments, len = arg.length;
            if (this.length < 1) {
                return this;
            }
            if (len === 1) {
                if (typeof arg[0] === 'string') {
                    return this[0].getAttribute(arg[0]);
                } else if (typeof arg[0] === 'object') {
                    for (var i in arg[0]) {
                        for (var j = this.length - 1; j >= 0; j--) {
                            this[j].setAttribute(i, arg[0][i]);
                        }
                    }
                }
            } else if (len === 2) {
                for (var j = this.length - 1; j >= 0; j--) {
                    this[j].setAttribute(arg[0], arg[1]);
                }
            }
            return this;
        }
    })

    // html方法,用于设置或获取DOM元素的HTML内容
    A.fn.extend({
        html: function (html) {
            var arg = arguments, len = arg.length;
            if (len === 0) {
                return this[0] && this[0].innerHTML;
            } else {
                for (var j = this.length - 1; j >= 0; j--) {
                    this[j].innerHTML = arg[0];
                }
            }
            return this;
        }
    })
</script>

问题:为什么在 A 的构造函数中要返回 A.fn.init(selector, context) 的实例?

A 是一个构造函数,而 A.fn.init 是实际的构造函数,用来执行选择器的初始化和 DOM 操作。A 的构造函数中返回 new A.fn.init() 的实例,主要目的是为了创建一个可以处理 DOM 元素的对象实例,并确保可以使用类似 jQuery 的链式调用方式。下面详细分析为什么要这么做:

1. 为了支持链式调用

  • 在现代 JavaScript 库(如 jQuery)中,通常会支持链式调用,允许开发者对同一个对象连续调用多个方法。
  • A.fn.init 实际上是构造函数,它执行 DOM 查询并返回一个具有相关功能的对象。通过 new A.fn.init() 创建的实例会附带一个方法集合(比如 .size().push() 等),可以继续在同一个实例上调用其他方法。
  • 比如,你可以写如下代码:
    var obj = new A('#id').size().push('item');
    这个链式调用依赖于 A.fn.init 的构造函数返回的对象保持对实例的引用,允许连续调用 A.fn.initA.fn 上定义的其他方法。

2. AA.fn.init 的关系

  • A 是外部可用的构造函数,用来初始化和返回一个处理 DOM 元素的对象实例。
  • A.fn 是一个空对象,表示 A 构造函数的原型,存放 A.fn.init 等实例方法。
  • A.fn.init 是实际负责 DOM 查询和实例化的构造函数。new A.fn.init() 用来执行选择器逻辑,并返回一个包含 DOM 元素的实例对象。

3. 避免重复代码

  • A.fn.init 被设计成执行所有与 DOM 查询和元素操作相关的逻辑。通过返回 new A.fn.init(selector, context),我们避免了在 A 构造函数中重复编写查询和初始化代码。A 仅仅作为外部接口,将具体的查询逻辑委托给 A.fn.init 来处理。

4. 保持对象的正确性

  • 在构造函数 A 内部使用 new A.fn.init(selector, context) 是为了确保返回的对象是 A.fn.init 的实例,并且它具有正确的原型链。
  • A.fn.init.prototype = A.fn; 这行代码确保了 A.fn.init 的实例(即 new A.fn.init() 创建的对象)能够访问 A.fn 中的方法。这样,无论是通过 new A() 还是 new A.fn.init() 创建的对象,都能够继承 A.fn 上的方法,保持一致的行为。

5. 处理 selectorcontext 参数

  • A.fn.init 的构造函数负责根据选择器和上下文来初始化 DOM 查询。它会根据选择器字符串(如 #id.classdiv)来选择 DOM 元素,并将其存储在实例对象的 length 属性及索引属性中。
  • 返回 new A.fn.init(selector, context) 的实例能够确保 selectorcontext 正确传递并处理,且每次实例化都能返回一个新对象。

总结:

返回 new A.fn.init() 的实例有几个核心目的:

  • 支持链式调用:通过返回一个包含相关方法的对象实例,允许开发者对返回的对象进行连续操作。
  • 分离关注点A 构造函数负责创建实例并返回,而实际的初始化逻辑(如 DOM 查询)则委托给 A.fn.init
  • 保持原型链一致性:通过正确的原型设置,确保返回的实例能够继承并访问到定义在 A.fn 上的方法。
  • 复用逻辑:避免代码重复,通过 new A.fn.init() 委托给 A.fn.init 进行处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值