面向对象编程

演讲者:交互开发课题组

内容提纲

  1. 概念解析
  2. 创建对象
  3. 属性操作
  4. 继承实现
  5. 案例实现

概念解析

  • 类和对象
    • 面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。
    • 每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。
    • 面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发。
  • 常见对象
    • 内置对象:由ECMAScript规范定义的对象,如数组、函数、日期等
    • 宿主对象:由JS所嵌入环境(web浏览器等)定义的对象,如window对象等
    • 自定义对象: 由运行中的JS代码所定义的对象。
  • 参考资料

创建对象

  • 对象直接量
  • 通过new创建对象
  • 构造函数和原型

对象直接量-创建对象

  • 对象直接量
//对象直接量创建对象
var book={
    name:"javascript入门",
    category:"fed",
    author:{
        name:"whqet",
        age:18
    }
};

通过new创建对象-创建对象

  • 通过new创建对象
//通过new创建对象
var book=new Object();
book.name="javascript入门";
book.category="fed";
book.saleNo=0;
book.sale=function(){
    this.saleNo++;
}
book.author={
    name:"whqet",
    age:18
}
console.log(book.author.age);  //18
book.sale();
console.log(book.saleNo); //1

通过构造函数和原型-创建对象

  • 通过构造函数和原型
//通过构造函数和原型
var Book=function(name,category){
    this.name=name;
    this.category=category;
}
var book=new Book("javascript入门","fed");
console.log(book.category);  //fed

通过构造函数和原型-创建对象

  • 通过构造函数和原型
//通过构造函数和原型
var Book=function(name,category){
    this.name=name;
    this.category=category;
}
Book.prototype.sale=function(){
    console.log("sale a book");
}
var book=new Book("javascript入门","fed");
book.sale();

利用原型修改内置对象

//利用原型的方式修改内置对象

var a=[1,2,3,4];
//修改了数组类
//慎重
Array.prototype.sum=function(){
    var sum=0;
    var arr=this;
    for (var i = 0; i <arr.length; i++) {
        sum+=arr[i];
    }
    return sum;
}
console.log(a.sum());

shim和polyfill

  • shim
    • shim是硬垫片
    • 不一定符合标准
    • 不同API封装成一种
  • polyfill
    • polyfill是软垫片,严格来讲是shim的一种
    • 符合标准
    • 让旧浏览器支持新特性

shim和polyfill

EventUtil

/**
 * @author [xiaoruo]
 * [EventUtil 跨浏览器事件对象]
 * @type {Object}
 */
var EventUtil = {
    /**
     * [addHandler 添加跨浏览器事件]
     * @param {[Object]} element [事件对象]
     * @param {[String]} type    [事件类型]
     * @param {[Function]} handler [事件函数]
     */
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },

    /**
     * [removeHandler 移除事件]
     * @param {[Object]} element [事件对象]
     * @param {[String]} type    [事件类型]
     * @param {[Function]} handler [事件函数]
     */
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    },

    /**
     * [getEvent 跨浏览器事件]
     * @param  {[Object]} event [事件对象]
     * @return {[Object]}       [事件对象]
     */
    getEvent: function(event) {
        return event ? event : window.event;
    },

    /**
     * [getTarget 事件目标]
     * @param  {[Object]} event [事件对象]
     * @return {[Object]}       [事件目标]
     */
    getTarget: function(event) {
        return event.target || event.srcElement;
    },

    /**
     * [getRelatedTarget 与事件目标相关的节点]这个属性只对mouseover和mouseout有用(mouseover是离开的那个节点或mouseout时进入的那个节点)
     * @param  {[Object]} event [事件对象]
    * @return {[Object]}       [相关节点]
     */
    getRelatedTarget: function(event) {
        if (event.relatedTarget) {
            return event.relatedTarget;
        } else if (event.toElement) {
            return event.toElement;
        } else if (event.fromElement) {
            return event.fromElement;
        } else {
            return null;
        }

    },

    /**
     * [preventDefault 取消默认事件]
     * @param  {[Object]} event [事件对象]
     */
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },

    /**
     * [stopPropagation 取消事件的冒泡或捕获行为]
     * @param  {[Object]} event [事件对象]
     */
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },


    /**
     * [getCharCode 获取键盘码]
     * @param  {[Object]} event [事件对象]
     * @return {[number]}       [键盘码]
     */
    getCharCode: function(event) {
        if (typeof event.charCode == "number") {
            return event.charCode;
        } else {
            return event.keyCode;
        }
    },

    /**
     * [getButton 获取鼠标按键]
     * @param  {[Object]} event [事件对象]
     */
    getButton: function(event) {
        if (document.implementation.hasFeature("MouseEvents", "2.0")) {
            return event.button;
        } else {
            switch (event.button) {
                case 0://没有按下按钮
                case 1://按下主鼠标按钮
                case 3://同时按下主次鼠标按钮
                case 5://同时按下主中间
                case 7://同时按下三个
                    return 0;//主
                case 2://按下了次鼠标按钮
                case 6://同时按下次中间
                    return 2;//中间
                case 4://按下鼠标中间按钮
                    return 1;//次
            }
        }
    },

    /**
     * [getWheelDelta 鼠标滚轮事件]
     * @param  {[Object]} event [事件对象]
     * @return {[Number]}       [鼠标滚轮数值]上滚为正下滚为负
     */
    getWheelDelta: function(event) {
        if (event.wheelDelta) {
            return (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);
        } else {
            return -event.detail * 40;
        }
    },


    /**
     * [getClipboardText 获取剪切板数据]
     * @param  {[Object]} event [事件对象]
     * @return {[String]}       [剪切板数据]
     */
    getClipboardText: function(event) {
        var clipboardData = (event.clipboardData || window.clipboardData);
        return clipboardData.getData("text");
    },


    /**
     * [setClipboardText 为剪切板赋予数据]
     * @param  {[Object]} event [事件对象]
     */
    setClipboardText: function(event, value) {
        if (event.clipboardData) {
            event.clipboardData.setData("text/plain", value);
        } else if (window.clipboardData) {
            window.clipboardData.setData("text", value);
        }
    }
};

shim和polyfill

EventListener Polyfill for IE

// EventListener | CC0 | github.com/jonathantneal/EventListener

this.Element && Element.prototype.attachEvent && !Element.prototype.addEventListener && (function () {
    function addToPrototype(name, method) {
        Window.prototype[name] = HTMLDocument.prototype[name] = Element.prototype[name] = method;
    }

    // add
    addToPrototype("addEventListener", function (type, listener) {
        var
        target = this,
        listeners = target.addEventListener.listeners = target.addEventListener.listeners || {},
        typeListeners = listeners[type] = listeners[type] || [];

        // if no events exist, attach the listener
        if (!typeListeners.length) {
            target.attachEvent("on" + type, typeListeners.event = function (event) {
                var documentElement = target.document && target.document.documentElement || target.documentElement || { scrollLeft: 0, scrollTop: 0 };

                // polyfill w3c properties and methods
                event.currentTarget = target;
                event.pageX = event.clientX + documentElement.scrollLeft;
                event.pageY = event.clientY + documentElement.scrollTop;
                event.preventDefault = function () { event.returnValue = false };
                event.relatedTarget = event.fromElement || null;
                event.stopImmediatePropagation = function () { immediatePropagation = false; event.cancelBubble = true };
                event.stopPropagation = function () { event.cancelBubble = true };
                event.target = event.srcElement || target;
                event.timeStamp = +new Date;

                var plainEvt = {};
                for (var i in event) {
                    plainEvt[i] = event[i];
                }

                // create an cached list of the master events list (to protect this loop from breaking when an event is removed)
                for (var i = 0, typeListenersCache = [].concat(typeListeners), typeListenerCache, immediatePropagation = true; immediatePropagation && (typeListenerCache = typeListenersCache[i]); ++i) {
                    // check to see if the cached event still exists in the master events list
                    for (var ii = 0, typeListener; typeListener = typeListeners[ii]; ++ii) {
                        if (typeListener == typeListenerCache) {
                            typeListener.call(target, plainEvt);

                            break;
                        }
                    }
                }
            });
        }

        // add the event to the master event list
        typeListeners.push(listener);
    });

    // remove
    addToPrototype("removeEventListener", function (type, listener) {
        var
        target = this,
        listeners = target.addEventListener.listeners = target.addEventListener.listeners || {},
        typeListeners = listeners[type] = listeners[type] || [];

        // remove the newest matching event from the master event list
        for (var i = typeListeners.length - 1, typeListener; typeListener = typeListeners[i]; --i) {
            if (typeListener == listener) {
                typeListeners.splice(i, 1);

                break;
            }
        }

        // if no events exist, detach the listener
        if (!typeListeners.length && typeListeners.event) {
            target.detachEvent("on" + type, typeListeners.event);
        }
    });

    // dispatch
    addToPrototype("dispatchEvent", function (eventObject) {
        var
        target = this,
        type = eventObject.type,
        listeners = target.addEventListener.listeners = target.addEventListener.listeners || {},
        typeListeners = listeners[type] = listeners[type] || [];

        try {
            return target.fireEvent("on" + type, eventObject);
        } catch (error) {
            if (typeListeners.event) {
                typeListeners.event(eventObject);
            }

            return;
        }
    });

    // CustomEvent
    Object.defineProperty(Window.prototype, "CustomEvent", {
        get: function () {
            var self = this;

            return function CustomEvent(type, eventInitDict) {
                var event = self.document.createEventObject(), key;

                event.type = type;
                for (key in eventInitDict) {
                    if (key == 'cancelable'){
                        event.returnValue = !eventInitDict.cancelable;
                    } else if (key == 'bubbles'){
                        event.cancelBubble = !eventInitDict.bubbles;
                    } else if (key == 'detail'){
                        event.detail = eventInitDict.detail;
                    }
                }
                return event;
            };
        }
    });

    // ready
    function ready(event) {
        if (ready.interval && document.body) {
            ready.interval = clearInterval(ready.interval);

            document.dispatchEvent(new CustomEvent("DOMContentLoaded"));
        }
    }

    ready.interval = setInterval(ready, 1);

    window.addEventListener("load", ready);
})();

(!this.CustomEvent || typeof this.CustomEvent === "object") && (function() {
    // CustomEvent for browsers which don't natively support the Constructor method
    this.CustomEvent = function CustomEvent(type, eventInitDict) {
        var event;
        eventInitDict = eventInitDict || {bubbles: false, cancelable: false, detail: undefined};

        try {
            event = document.createEvent('CustomEvent');
            event.initCustomEvent(type, eventInitDict.bubbles, eventInitDict.cancelable, eventInitDict.detail);
        } catch (error) {
            // for browsers which don't support CustomEvent at all, we use a regular event instead
            event = document.createEvent('Event');
            event.initEvent(type, eventInitDict.bubbles, eventInitDict.cancelable);
            event.detail = eventInitDict.detail;
        }

        return event;
    };
})();

属性操作

  • 删除属性
    • delete只能删除自有属性,不能删除继承属性
  • 检测属性
    • in运算符
    • hasOwnProperty()
    • propertyIsEnumrable()
  • 遍历属性
    • for in遍历
    • Object.keys()
    • Object.getOwnPropertyNames()
//删除属性
var book=new Object();
book.name="javascript入门";
book.category="fed";
delete book.name;
console.log(book.name);  //undefined
//检测属性
console.log('category' in book);
console.log(book.hasOwnProperty('category'));
console.log(book.propertyIsEnumerable('category'));

属性操作-遍历属性

//遍历属性
var o={
    x:1,
    y:2,
    z:3
};
for(p in o){
    //跳过继承的属性
    if(!o.hasOwnProperty(p)) continue;
    //跳过方法
    if(typeof o[p]==="function") continue;
    //输出属性和属性值
    console.log(p,o[p]);
}
//ES5的方式
//ES5的方式 Object.keys()
console.log(Object.keys(o));
//ES5的方式 Object.getOwnPropertyNames()
console.log(Object.getOwnPropertyNames(o));

继承实现

  • 直接继承prototype
  • 拷贝继承
//直接继承prototype

//Animal对象
function Animal(){ }
Animal.prototype.species = "动物";
//将Cat的prototype对象指向Animal的prototype对象,完成继承
function Cat(){}
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat();
console.log(cat1.species); // 动物

继承实现

  • 直接继承prototype
  • 拷贝继承
//拷贝继承

//Animal对象
function Animal(){ }
Animal.prototype.species = "动物";
//写个函数,实现拷贝继承
function extend2(Child, Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
  for (var i in p) {
    c[i] = p[i];
  }
  c.uber = p;
}
function Cat(){}
extend2(Cat, Animal);
var cat1 = new Cat();
console.log(cat1.species); // 动物

交互开发

旨为前端开发工程师的前端开发课程