零JS筆記 | 面向對象 | 組件

   提高对象的复用性
如何配置参数和默认参数
   对面向对象的深入应用(UI组件,功能组件)
   将 配置参数、方法、事件,三者进行分离
创建自定义事件
   有利于多人协作开发代码
   如何去挂载自定义事件与事件函数

組件開發,多組對象;像兄弟之間的關係 (代碼利用的一種形式;)
推理過程:

    function show(opt){
    }
    show({
        id:'div1',
        toDown:function(){},
        toUp:function(){}
    })

    var a = { name:'小明' }  //配置參數   1、優先級較高 2、key值一定要相同 ;
    var b = { name:'小強' }  //默認參數

    extend(b,a) //b的name被a的name覆蓋了;

    console.log(b.name);  //小明,(小強被小明蓋了)

    function extend(o1,o2){
        for(var attr in o2){
            o1[attr] = o2[attr]
        }
    }

重點:寫配置參數---> 寫默認參數--->用配置覆蓋默認---->調用默認方法;

所以,搭架子


var f1 = new Fn();
f1.init({

});

function Fn(){
    
    this.settings = {
            
    }
}
Fn.prototype.init = function(opt){
    extend(this.settings,opt)
}

function extend(o1,o2){
    for(var attr in o2){
        o1[attr] = o2[attr]
    }    
}
//改寫:定義配置參數;
        d1.init({
            id:'div1'
        })

        d2.init({
            id:'div2',
            _down:function(){
                document.title = 'down'
            }
        })
        d3.init({
            id:'div3',
            _down:function(){
                document.title = 'down-down-down'
            },
            _up: function () {
                document.title='up-up-up'
            }
        })
        d4.init({
            id:'div4',
            _up:function(){
                document.title = 'bybybyby'
            }
        })

//沒有值的情況:

    function Drag() {
        this.obj = null;
        this.disX = 0;
        this.dixY = 0;
        this.settings = {    //配置覆蓋默認,然後去調用默認的值;
            _down: function () {},
            _up: function () {}
        }
    }

//執行:

    Drag.prototype.init = function (opt) {
        this.obj = document.getElementById(opt.id)
        var _this = this;

        extend(this.settings, opt)   //使this.settings的值 == opt的值,如果opt沒有,則找默認的

        this.obj.onmousedown = function (ev) {
            var ev = ev || event;
            _this.fnDown(ev)  //ev要傳進來,下面才不用定義 ;

            _this.settings._down()  //調用默認

            document.onmousemove = function (ev) {
                var ev = ev || event;
                _this.fnMove(ev)
            }
            document.onmouseup = function () {
                _this.fnUp();

                _this.settings._up() //調用默認

            }
            return false //最後加阻止默認事件;
        }
    }


//其他:

    Drag.prototype.fnDown = function (ev) {
        this.disX = ev.clientX - this.obj.offsetLeft;
        this.disY = ev.clientY - this.obj.offsetTop;
    }
    Drag.prototype.fnMove = function (ev) {
        this.obj.style.left = ev.clientX - this.disX + 'px'
        this.obj.style.top = ev.clientY - this.disY + 'px'
    }
    Drag.prototype.fnUp = function () {
        document.onmousemove = document.onmouseup = null;
    }
    function extend(o1, o2) {
        for (var attr in o2) {
            o1[attr] = o2[attr]
        }
    }

核心:

//1、寫默認
        this.settings = {
            _down: function () {},
            _up: function () {}
        }
//3、改寫默認,即把外面調用的函數賦值給默認 覆蓋默認,所以再執行如果外面有,則執行,如無則執行默認

        extend(this.settings, opt) 

//2、調執行默認
        _this.settings._down()
        _this.settings._up()

組件彈窗

window.onload = function () {
    var _inp = document.getElementsByTagName('input')
    _inp[0].onclick = function () {
        var d1 = new Dialog();
        d1.init({ //配置參數
            cur: 0,
            title: '登入'
        });
    }
    _inp[1].onclick = function () {
        var d2 = new Dialog();
        d2.init({
            cur: 1,
            w: 100,
            h: 400,
            dir: 'right',
            title: '公告'
        })
    }
    _inp[2].onclick = function () {
        var d2 = new Dialog();
        d2.init({
            cur: 2,
            mask: true
        })
    }
}
function Dialog() {
    this._login = null;
    this._mask = null;
    this.settings = {  //默認參數
        w: 300,
        h: 300,
        dir: 'center',
        title: '',
        mask: false
    }
}
Dialog.prototype.json = {};             //目的:只能彈一次: 先定義空json
Dialog.prototype.init = function (opt) {

    extend(this.settings, opt)

    if (this.json[opt.cur] == undefined) { //第一次走這裏;
        this.json[opt.cur] = true;
    }

    if (this.json[opt.cur]) {       //為true時就走創建
        this.create();
        this.fnClose();
        if (this.settings.mask) {
            this.createMask();
        }
        this.json[opt.cur] = false; //後fase跟undefined也等於false,也不會走上面;
    }
}
Dialog.prototype.create = function () {
    this._login = document.createElement('div');
    this._login.className = 'login'
    this._login.innerHTML = '<div class="title"><span>' + this.settings.title + '</span><span class="close">X</span></div><div class="content"></div>'

    document.body.appendChild(this._login);
    this.setDate();
}
Dialog.prototype.setDate = function () {
    this._login.style.width = this.settings.w + 'px'
    this._login.style.height = this.settings.h + 'px'
    if (this.settings.dir == 'center') {
        this._login.style.left = (viewWidth() - this._login.offsetWidth) / 2 + 'px'
        this._login.style.top = (viewHeight() - this._login.offsetHeight) / 2 + 'px'
    }
    else if (this.settings.dir = 'right') {
        this._login.style.left = (viewWidth() - this._login.offsetWidth) + 'px'
        this._login.style.top = (viewHeight() - this._login.offsetHeight) + 'px'
    }
}
Dialog.prototype.fnClose = function () {
    var _this = this;
    var _close = this._login.querySelector('.close')
    _close.onclick = function () {
        document.body.removeChild(_this._login)
        if (_this.settings.mask) {
            document.body.removeChild(_this._mask)
        }
        _this.json[_this.settings.cur] = true; //關閉後再設回;
    }
}
Dialog.prototype.createMask = function () {
    this._mask = document.createElement('div')
    this._mask.id = 'mask'
    document.body.appendChild(this._mask)
    this._mask.style.width = viewWidth() + 'px'
    this._mask.style.height = viewHeight() + 'px'
}
function viewWidth() {
    return document.documentElement.clientWidth;
}
function viewHeight() {
    return document.documentElement.clientHeight;
}
function extend(o1, o2) {
    for (var attr in o2) {
        o1[attr] = o2[attr]
    }
}

See the Pen avezwe by elmok (@elmok) on CodePen.


<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

零JS筆記 | 面向對象 | 繼承

继承:在原有對象的基础上,略作修改,得到一个新对象,不影响原对象的功能;
如何添加?
属性继承: 调用父类的构造函数;父类.call(this,参数,参数。。。);
方法继承: 父类的原型直接给子类的原形

function CreatePerson(name, sex) {
    this.name = name;
    this.sex = sex;
}
CreatePerson.prototype.showname = function () {
    console.log(this.name);

}
var p1 = new CreatePerson('小明', '男');
p1.showname();

///CreateStar繼承CreatePerson

function CreateStar(name, sex, job) { //子
//調用相當於執行;调用父类构造函数 ,不加call,则this指向window,必须使用this指向实例;
    CreatePerson.call(this, name, sex) 
    this.job = job;
}
CreateStar.prototype = CreatePerson.prototype

var p2 = new CreateStar('张敬轩', '男', '歌手');

p2.showname()

以上存在问题:一个原型给另一个原型,即对像给对像,所以出现了引用(修改一个会影响另一个;)



一、拷貝繼承;

如何添加?
属性继承: 调用父类的构造函数;父类.call(this,参数,参数。。。);
方法继承: for in形式實現 extend()函數,jq

function CreatePerson(name, sex) {
    this.name = name;
    this.sex = sex;
}
CreatePerson.prototype.showname = function () {
    console.log(this.name);

}
var p1 = new CreatePerson('小明', '男');
p1.showname();

///CreateStar繼承CreatePerson

function CreateStar(name, sex, job) { //子
//調用相當於執行;调用父类构造函数 ,不加call,则this指向window,必须使用this指向实例;
    CreatePerson.call(this, name, sex) 
    this.job = job;
}
//for in繼承
  extend( CreateStar.prototype ,CreatePerson.prototype)

var p2 = new CreateStar('张敬轩', '男', '歌手');

p2.showname()

    function extend(o1,o2){
        for(var attr in o2){
            o1[attr] = o2[attr];
        }
    }
//函數的的特點不能修改,只能重新賦值,所以雖然函數也是對象,但不會出問題;

拖拽繼承小例;

window.onload = function () {
    var d1 = new Drag('div1')
    d1.init()
    var d2 = new ChildDrag('div2')
    d2.init()
}
function Drag(id) {
    this.obj = document.getElementById(id)
    this.disX = 0;
    this.dixY = 0;
}
Drag.prototype.init = function () {
    var _this = this;
    this.obj.onmousedown = function (ev) {
        var ev = ev || event;
        _this.fnDown(ev)  //ev要傳進來,下面才不用定義 ;
        document.onmousemove = function (ev) {
            var ev = ev || event;
            _this.fnMove(ev)
        }
        document.onmouseup = function () {
            _this.fnUp();
        }
        return false //最後加阻止默認事件;
    }
}
Drag.prototype.fnDown = function (ev) {
    this.disX = ev.clientX - this.obj.offsetLeft;
    this.disY = ev.clientY - this.obj.offsetTop;
}
Drag.prototype.fnMove = function (ev) {
    this.obj.style.left = ev.clientX - this.disX + 'px'
    this.obj.style.top = ev.clientY - this.disY + 'px'
}
Drag.prototype.fnUp = function () {
    document.onmousemove = document.onmouseup = null;
}

function ChildDrag(id) {
    Drag.call(this, id) //指向與傳參數
}

//繼承Drag類
extend(ChildDrag.prototype, Drag.prototype);

//繼承後重新改寫move限制範圍;
ChildDrag.prototype.fnMove = function (ev) {
    var L = ev.clientX - this.disX;
    var T = ev.clientY - this.disY;
    if (L < 0) {
        L = 0;
    }
    else if (L > document.documentElement.clientWidth - this.obj.offsetWidth) {
        L = document.documentElement.clientWidth - this.obj.offsetWidth
    }
    this.obj.style.left = L + 'px'
    this.obj.style.top = T + 'px'

}
function extend(o1, o2) {
    for (var attr in o2) {
        o1[attr] = o2[attr]
    }
}



一、類繼承;
利用構造函數的方式;

b1找showName,b1下沒有,但因為賦值(Bbb.prototype = new Aaa();),則通過原型鏈找Bbb.prototype 相當於找 new Aaa(),new Aaa()再通過原型鏈找Aaa.prototype

    //父
    function Aaa() {
        this.name = '小明'
    }
    Aaa.prototype.showName = function () {
        console.log(this.name);
    }
    //子
    function Bbb() {

    }
    //子原型,= 誰去給值給子類原型:父類創建出來的對象 ;
    Bbb.prototype = new Aaa(); //其實也是引用

    var b1 = new Bbb();
    b1.showName()

問題一:constructor

 console.log(b1.constructor);  //變成function Aaa(){}了;修改了;

//修正
 Bbb.prototype = new Aaa(); //其實也是引用
 Bbb.prototype.constructor = Bbb; //修正指向;

問題二:引用時,值的改變會相互影響 ;所以,完整個類繼承的寫法是:

    
    function Bbb(){
        Aaa.call(this)
    }
    
    var F  = function () {}
    F.prototype = Aaa.prototype; //只會給showName,不會接收到屬性;,所以需要設置接收屬性;
    Bbb.prototype = new F();
    Bbb.prototype.constructor = Bbb; //修正指向;

推斷過程:

function Aaa() {
//        this.name = '小明'
    this.name = [1,2,3]
}
Aaa.prototype.showName = function () {
    console.log(this.name);
}
//子
function Bbb() {
    Aaa.call(this)
}
//子原型,= 誰去給值給子類原型:父類創建出來的對象 ;
//  Bbb.prototype = new Aaa(); //其實也是引用
//  Bbb.prototype.constructor = Bbb; //修正指向;

var F  = function () {}
F.prototype = Aaa.prototype; //只會給showName,不會接收到屬性;,所以下面需要設置接收屬性;
Bbb.prototype = new F();
Bbb.prototype.constructor = Bbb; //修正指向;
//屬性與方法的繼承要分開繼承;
var a1 = new Aaa()
var b1 = new Bbb();
b1.showName()
//   console.log(b1);
//b1找showName,b1下沒有,則通過原型鏈找Bbb.prototype 相當於找 new Aaa(),new Aaa()再通過原型鏈找Aaa.prototype

//如何用一句話來完成繼承;
//Bbb.prototype = new Aaa(); 但不坑

b1.name.push(4)
var b2 = new Bbb()
console.log(b1.name);
console.log(b2.name); //也變成1,2,3,4;
console.log(b1.constructor);  //變成function Aaa(){}了;修改了;

所以,属性继承使用call,使用F只继承方法



三、原型繼承

var a = {
    name: '小強'
}
var b = cloneObj(a)

b.name = '小強修改'  //相當於在b下面添加了;所以不需要向後找,
console.log(a.name); //則不會受影響了;

console.log(b.name);
function cloneObj(obj) {
    var F = function () {}
    F.prototype = obj;
    return new F
}

解析:f1 (new出來)通過原型鏈F.pro == obj -->name在obj下面,然後直接return f1 ,則f1 == b;
所以 b找name,相當於b 找f1 ,f1找不到則通過原型鏈F.pro == obj,找到name = '小強'
當給b.name = '小強修改',時,相當於給b下面添加了一個屬性name,則再彈b時不需要沿原型鏈找,所以a.name也不會受影響



繼承三種方法:拷貝、類式、原型;

拷貝:通用型,有new無new都可以;

類式:new構造函數情況;

原型:無new的對象;

當然也有其他繼承:寄生式、、、