js基础之继承

本文介绍了如何使用JavaScript实现div元素的拖拽功能,分别展示了面向过程和面向对象的实现方法。接着讨论了当需要为两个div添加拖拽功能,其中一个需要限定范围时,如何通过继承扩展原有功能。文章详细讲解了原型链继承、借用构造函数继承、组合继承以及类继承的概念和实现,并通过代码示例展示了它们在实际问题中的应用。最后,通过一个具体的例子展示了如何通过类继承实现拖拽功能的限定范围。
摘要由CSDN通过智能技术生成

继承

我们现在呢,有一个拖拽的功能需要实现:假设有一个div,我们需要对它实现拖拽的功能,那么按照我们平常的方式,实现的方法如下:

// 面向过程
const mydiv = document.querySelector(".mydiv");
mydiv.onmousedown = e => {
    let ev = e || window.event;
    let x = ev.clientX - mydiv.offsetLeft;
    let y = ev.clientY - mydiv.offsetTop;
    mydiv.onmousemove = e => {
        let ev = e || window.event;
        let xx = ev.clientX;
        let yy = ev.clientY;
        mydiv.style.left = xx - x + "px";
        mydiv.style.top = yy - y + "px";
    }
    mydiv.onmouseup = function() {
        mydiv.onmousemove = "";
    }
}

// 面向对象
function Drag(ele) {
    this.ele = ele;
    this.downFn();
}
Drag.prototype.downFn = function () {
    this.ele.onmousedown = e => {
        let ev = e || window.event;
        let x = ev.clientX - this.ele.offsetLeft;
        let y = ev.clientY - this.ele.offsetTop;
        this.moveFn(x, y);
        this.upFn();
    }
}
Drag.prototype.moveFn = function (x, y) {
    this.ele.onmousemove = e => {
        let ev = e || window.event;
        this.setStyle(ev.clientX - x, ev.clientY - y);
    }
}
Drag.prototype.setStyle = function(leftNum,topNum){
    this.ele.style.left = leftNum + "px";
    this.ele.style.top = topNum + "px";
}
Drag.prototype.upFn = function () {
    this.ele.onmouseup =  () => {
        this.ele.onmousemove = "";
    }
}
let mydiv = document.querySelector(".mydiv");
new Drag(mydiv);

此时,我们新增了一个需求,有两个div,都需要实现拖拽功能,但是其中一个div是需要限定范围的。那么我们可以通过传参的方式区分来实现这个功能,也可以通过继承的方式,继承的好处在于:不需要去更改之前的代码,只需要新增自己的功能或重写。

继承实现新的需求:

function Drag(ele) {
    this.ele = ele;
    this.downFn();
}
Drag.prototype.downFn = function () {
    this.ele.onmousedown = e => {
        let ev = e || window.event;
        let x = ev.clientX - this.ele.offsetLeft;
        let y = ev.clientY - this.ele.offsetTop;
        this.moveFn(x, y);
        this.upFn();
    }
}
Drag.prototype.moveFn = function (x, y) {
    this.ele.onmousemove = e => {
        let ev = e || window.event;
        this.setStyle(ev.clientX - x, ev.clientY - y);
    }
}
Drag.prototype.setStyle = function(leftNum,topNum){
    this.ele.style.left = leftNum + "px";
    this.ele.style.top = topNum + "px";
}
Drag.prototype.upFn = function () {
    this.ele.onmouseup =  () => {
        this.ele.onmousemove = "";
    }
}
let mydiv = document.querySelector(".mydiv");
new Drag(mydiv);

function LimitDrag(ele) {
    Drag.call(this, ele);
}
// 组合继承
let Link = function () { };
Link.prototype = Drag.prototype;
LimitDrag.prototype = new Link();
LimitDrag.prototype.constructor = LimitDrag;
// 重写方法
LimitDrag.prototype.setStyle = function (leftNum, topNum) {
    leftNum = leftNum < 0 ? 0 : leftNum;
    topNum = topNum < 0 ? 0 : topNum;
    this.ele.style.left = leftNum + "px";
    this.ele.style.top = topNum + "px";
}
let mydiv2 = document.querySelector(".mydiv2");
new LimitDrag(mydiv2);

class继承实现:

class Drag {
    constructor(ele) {
        this.ele = ele;
        this.downFn();
    }
    downFn() {
        this.ele.onmousedown = e => {
            let ev = e || window.event;
            let x = ev.clientX - this.ele.offsetLeft;
            let y = ev.clientY - this.ele.offsetTop;
            this.moveFn(x, y);
            this.upFn();
        }
    }
    moveFn(x, y) {
        this.ele.onmousemove = e => {
            let ev = e || window.event;
            this.setStyle(ev.clientX - x, ev.clientY - y);
        }
    }
    setStyle(leftNum, topNum) {
        this.ele.style.left = leftNum + "px";
        this.ele.style.top = topNum + "px";
    }
    upFn() {
        document.onmouseup = () => {
            this.ele.onmousemove = "";
        }
    }
}
let mydiv = document.querySelector(".mydiv");
new Drag(mydiv);

class LimitDarg extends Drag {
    constructor(ele) {
        // 继承必须要执行一下,一种语法形式
        super(ele);
    }
    setStyle(leftNum, topNum) {
        // 重写方法
        leftNum = leftNum < 0 ? 0 : leftNum;
        topNum = topNum < 0 ? 0 : topNum;
        // 调用父类的被重写的方法
        super.setStyle(leftNum, topNum);
    }
}
let mydiv2 = document.querySelector(".mydiv2");
new LimitDrag(mydiv2);

那么,我们先来介绍一下继承的几种方式:

首先是比较基础的原型链继承,就是通过将父类的实例作为子类的原型来达到继承的目的。

  • 原型链继承
function Dad() {
    this.name = "唐三";
}
function Son() {}
// 子类原型等于父类实例
Son.prototype = new Dad();
let newSon = new Son();
console.log(newSon.name); // 唐三

原型链继承的缺点之一就是不能向父类构造函数传参,而借用构造函数继承是可以的,它是借用call等方式调用父类构造函数来实现继承。

  • 借用构造函数继承
function Dad(height) {
    this.name = "唐三";
    this.height = height;
}
function Son(height) {
    // 调用父类构造函数来实现继承
    Dad.call(this, height);
    // 这两种方式也都可以实现
    // Dad.apply(this,[height])
    // Dad.bind(this)(height);
}
let newSon = new Son("178cm");
console.log(newSon.height); // 178cm
console.log(newSon.name); // 唐三

但是勒,在借用构造函数继承方式中,并不能把父类的原型继承过来,如下的代码就报错了。

function Dad(height) {
    this.name = "唐三";
    this.height = height;
}
Dad.prototype.hobby = function() {
    console.log("高尔夫");
}
function Son(height) {
    Dad.call(this, height);
}
let newSon = new Son("178cm");
newSon.hobby(); // 报错

那么我们可以试想,把父类的原型赋值给子类的原型是否可以继承父类的原型。以下结果表明Son.prototype = Dad.prototype可以继承父类的原型,但同样存在问题,重写父类的方法会相互影响,会修改父类的方法。

function Dad(height) {
    this.name = "唐三";
    this.height = height;
}
Dad.prototype.hobby = function() {
    console.log("高尔夫");
}
function Son(height) {
    Dad.call(this, height);
}
Son.prototype = Dad.prototype;
Son.prototype.hobby = function() {
    console.log("篮球");
}
let newSon = new Son("178cm");
newSon.hobby(); // 篮球
let newDad = new Dad("179cm");
newDad.hobby(); // 篮球

那么我们可以让子类原型等于父类实例,这样不仅继承了父类的原型,也会继承父类的属性。将原型链继承和借用构造函数继承结合起来,就是组合继承了。

  • 组合继承
function Dad(height) {
    this.name = "唐三";
    this.height = height;
}
Dad.prototype.hobby = function() {
    console.log("高尔夫");
}
function Son(height) {
    Dad.call(this, height);
}
Son.prototype = new Dad()
// 但prototype里都有一个预定义属性constructor,所以我们还需要设置其指向自己的构造函数
Son.prototype.constructor = Son;
Son.prototype.hobby = function() {
    console.log("篮球");
}
let newSon = new Son("178cm");
newSon.hobby(); // 篮球
let newDad = new Dad("179cm");
newDad.hobby(); // 高尔夫

那么我们创建一个新的构造函数作为媒介,切断子类原型和父类原型的关系。这样原型的方法就不会相互影响。

function Dad(height) {
    this.name = "唐三";
    this.height = height;
}
Dad.prototype.hobby = function() {
    console.log("高尔夫");
}
function Son(height) {
    Dad.call(this, height);
}
// 或采用es6新增Son.prototype = Object.create(Dad.prototype)依然可以达到同样的效果
let Link = function(){};
Link.prototype = Dad.prototype;
Son.prototype = new Link();
Son.prototype.constructor = Son;
Son.prototype.hobby = function() {
    console.log("篮球");
}
let newSon = new Son("178cm");
newSon.hobby(); // 篮球
let newDad = new Dad("179cm");
newDad.hobby(); // 高尔夫

我们也可以使用深拷贝拷贝父类的原型来达到目的。

function deepCopy(obj) {
    let newObj = Array.isArray(obj) ? [] : {};
    for (let i in obj) {
        if (obj.hasOwnProperty(i)) {
            if (typeof obj[i] === "object") {
                if (obj[i] === null) {
                    newObj[i] = null;
                } else {
                    newObj[i] = deepCopy(obj[i]);
                }
            } else {
                newObj[i] = obj[i];
            }
        }
    }
    return newObj;
}

function Dad(height) {
    this.name = "唐三";
    this.height = height;
}
Dad.prototype.hobby = function() {
    console.log("高尔夫");
}
function Son(height) {
    Dad.call(this, height);
}
// 利用深拷贝继承父类的原型
Son.prototype = deepCopy(Dad.prototype)
Son.prototype.constructor = Son;
Son.prototype.hobby = function() {
    console.log("篮球");
}
let newSon = new Son("178cm");
newSon.hobby(); // 篮球
let newDad = new Dad("179cm");
newDad.hobby(); // 高尔夫
  • 类继承
class Dad {
    constructor(height){
        this.name = "唐三";
        this.height = height;
    }
    hobby() {
        console.log("高尔夫"); 
    }
}
class Son extends Dad {
    constructor(height){
        super(height);
    }
    hobby() {
        console.log("篮球"); 
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值