js编写面向对象的几种方法

谭佳成
2017-12-20
(317)

方法一、使用工厂方式,此处非工厂设计模式,而是说像工厂运作方式一样,原料、零件->加工->出厂

function CreatePerson(name,age){         //构造函数

    //1、原料,实例化一个空对象
    var obj = new Object();
    //2、加工
    obj.name = name;
    obj.age = age;

    obj.getname = function(){
        alert(this.name);
    }

    obj.getage = function(){
        alert(this.age);
    }
   //3、出厂
   return obj;
}

var obj1 = CreatePerson('张三',12);
obj1.getname();
obj1.getage();
var obj2 = CreatePerson("李四",22);
obj2.getname();
obj2.getage();
alert(obj1.getname === obj2.getname);//    ->false,非同一个类


此种方式是最最基本也最容易理解的方式,但是不推荐这样,因为这样会带来两个问题

1、没有new,对象不用new还叫啥对象

2、重复函数,浪费资源,每实例化一个对象就会占用新的内存资源


方法二、混合的构造函数/原型方式,具体使用原型链(prototype)

原型链呀,你可能在书上或网上看过相关解释,你真的弄懂了吗,这里打个小广告,在我没有看智能社的视频之前,我一直对这家伙没搞懂,什么玩意,直到看了视频,总结的很好,至少我是理解了。

请看下面的小例子

var arr1 = [1,2,3,4,5,6];
var arr2 = [4,5,6,7,8,9,10];
Array.prototype.sum = function(){//类比css中的class,对所有数组有效
//arr1.sum = function(){         //行间样式,只对当前arr1数组有效
    var result = 0;
    for (var i = 0; i < this.length; i++) {
        result += this[i];
    }
    return result;
}
alert(arr1.sum());
alert(arr2.sum());


解析下,代码片段中没注释的是使用原型链,相当于在js原生数组中添了一个sum方法,这样做以后,数组就有一个sum方法,供所有数组使用,好比css中,class对拥有该class的起作用

注释掉的,只是在arr1数组上添了一个方法,该方法只对arr1数组有效,arr2中是没有sum方法的。类比css中行间样式

好,下面进入正题,如何使用原型链编写js的面向对象

function CreatePerson(name,age){//构造函数

    //1、原料,实例化一个空对象
    //系统(浏览器、node环境,以及能运行js的宿主环境)默认创建空对象
    //var this = new Object();
    this.name = name;
    this.age = age;
    // this.getname = function(){
    //     alert(this.name);
    // }
    // this.getage = function(){
    //     alert(this.age);
    // }
    //3、出厂
    //系统(浏览器、node环境,以及能运行js的宿主环境)默认返回对象
    //return this;
}

//利用原型,一次给对象添加所有方法
CreatePerson.prototype.getname = function(){
    alert(this.name);
}

CreatePerson.prototype.getage = function(){
    alert(this.age);
}
var obj1 = new CreatePerson('张三',12);
var obj2 = new CreatePerson('李四',22);
obj1.getname();
obj2.getname();
//利用原型,方法是同一个
alert(obj1.getname === obj2.getname);//   -> true


现在不仅有了new,同时创建多个对象后,类的引用只有一个,这也是es6之前的编写js面向对象的最佳实践

可以看到现在这种写法,构造函数里只有属性赋值,而方法写到外面了,没错,是酱紫。

当然没有完美的东西,这种写法仍然也有缺点,无法避免方法被覆盖掉

还有,注意到没,这里出现了this,关于this、闭包,如果你还有点模糊,这里推荐一本书叫《你不知道的js上卷》,里面对this、闭包讲的很好。

我们都知道面向对象三大特性,封装、继承、多态。目前这里我们只看到了封装,继承和多态呢,别急,恕我娓娓道来,请看下面的小例子。

使用js实现拖拽功能

drag.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>drag</title>
    <style>
    #div1{
        width: 200px;
        height: 200px;
        background: yellow;
        position: absolute;
        }
        #div2{
        width: 200px;
        height: 200px;
        background: red;
        position: absolute;
        }
    </style>
</head>
<body>

    <div id="div1">普通拖拽</div>
    <div id="div2">限制范围</div>
    <script src="./parentdrag.js"></script>
    <script src="./limitdrag.js"></script>
    <script type="text/javascript">
        window.onload = function(){
            new Drag('div1');
            new LimitDrag('div2');
        }

    </script>
</body>
</html>


parentdrag.js

function Drag(id) {
    this.disX = 0;
    this.disY = 0;
    var _this = this;
    this.oDiv = document.getElementById(id);
    this.oDiv.onmousedown = function(ev) {
        _this.fnDown(ev);
        return false;
    };
}
Drag.prototype.fnDown = function(ev) {
    var oEvent = ev || event;
    this.disX = oEvent.clientX - this.oDiv.offsetLeft;
    this.disY = oEvent.clientY - this.oDiv.offsetTop;
    var _this = this;
    document.onmousemove = function(ev) {
        _this.fnMove(ev);
    };

    document.onmouseup = function(ev) {
        _this.fnUp(ev);
    };
}
Drag.prototype.fnMove = function(ev) {
    var oEvent = ev || event;
    this.oDiv.style.left = oEvent.clientX - this.disX + 'px';
    this.oDiv.style.top = oEvent.clientY - this.disY + 'px';
}
Drag.prototype.fnUp = function() {
    document.onmousemove = null;
    document.onmouseup = null;
}


limitdrag.js

//这里实现了类的继承,使用call
function LimitDrag(id){
    Drag.call(this,id);
}
for(var i in Drag.prototype){
    LimitDrag.prototype[i] = Drag.prototype[i];
}
//这里实现了类重写,限制范围
LimitDrag.prototype.fnMove = function(ev) {
    var oEvent = ev || event;
    var l = oEvent.clientX - this.disX;
    var t = oEvent.clientY - this.disY;
    if(l < 0){
        l = 0;
    }else if(l > document.documentElement.clientWidth-this.oDiv.offsetWidth){
        l = document.documentElement.clientWidth-this.oDiv.offsetWidth;
    }
    if(t < 0){
        t = 0;
    }else if(t > document.documentElement.clientHeight-this.oDiv.offsetHeight){
        t = document.documentElement.clientHeight-this.oDiv.offsetHeight;
    }
    this.oDiv.style.left = l + 'px';
    this.oDiv.style.top = t + 'px';
}


建议自己复制粘贴保存文件看看效果,具体实现的是,两个框框,一个div1任意拖拽不受屏幕限制,另一个div2只能在可视区域内拖拽。

一个父类parentdrag.js,一个子类limitdrag.js,子类通过call()方法实现继承。原理是call()该方法可以改变this的指向

多态和php一样,多态也是通过重写父类方法实现的,体现在limitdrag.js里重写fnMove方法。


第三种、es6写法,这才真的像面向对象嘛,引入了class

//类
    class User{
        constructor(name,age){//构造器
            this.name = name;
            this.age = age;
        }

        info(){//方法
            console.log(`hi,my name is ${this.name},have ${this.age} years old!`);
        }

        static desc(){//静态方法,只能是类调用
            console.log('im learning es6!');
        }
        set github(val){//设置属性
            this.githubname = val;
        }

        get github(){//获取属性
            return `https://github.com/${this.githubname}`;
        }
    }

    const person1 = new User('张三',20);
    const person2 = new User('李四',15);

    //类的继承
    class Animal{
        constructor(name){
            this.name = name;
            this.belly = [];
        }

        eat(food){
            this.belly.push(food);
        }

        speack(){
            console.log(`hi im ${this.name}`);
        }
    }
    
    //使用关键字extends
    class Dog extends Animal{
        constructor(name,age){
            //调用super
            super(name);
            this.age = age;
        }

        bark(){
            console.log('bark! bark');
        }
        
        //重写父类方法
        speack(){
            console.log(`bark!bark! im ${this.name}`);
        }
    }

    const lucky = new Dog('lucky',2);


可以看到es6中的面向对象写法逐渐像后台语言写法靠拢,还在不断完善中,敬请期待...

第四种、使用json单体

json始于JavaScript,现已被广泛用来做数据接口交互

var obj = {
    name:'张三',
    age:'12',
    speak(){//es6下function可省略
        alert(`hi,im ${this.name},have ${this.age}years old.`);
    }
}
obj.speak();


这是一个很简单的json对象,如果我想创建多个json对象怎么办,只能再写个全新的json

优点:简单

缺点:不能实例化多个对象,可以理解为单体对象

想想vue,一个app是不是对应一个Vue()json对象,要想再写个app,只能再new一个Vue()json对象



另外,关于命名空间

var Animal = {};
Animal.person = {};
Animal.bird = {};
Animal.fish = {};
Animal.person.move = function(){
    alert('Walk');
}
Animal.bird.move = function(){
    alert('fly');
}
Animal.fish.move = function(){
    alert('swim');
}
Animal.person.move();
Animal.bird.move();
Animal.fish.move();


可以让相同的函数同时存在



如无说明,本站文章均为原创,转载或引用注明来源:https://93jc.cn/article/140.html


上一篇:简单介绍webpack

下一篇:加密解密