JavaScript面向对象设计

来源:互联网 发布:sql 数据库 培训 编辑:程序博客网 时间:2024/05/30 20:07
一、理解对象    1.创建        ①构造函数   new Object        ②对象字面量  var o = {};    2.属性类型        ①数据属性,对象属性有4个属性特征,默认都为true,可以通过Object.defineProperty()来修改属性特征            a.[[Configurable]]  表示能否通过delete删除重新定义,能否修改属性的特征,能否修改为访问权属性            b.[[Enumerable]]    表示能否通过for-in枚举            c.[[Writable]]      表示能否修改属性的值            d.[[Value]]         表示属性值            eg:                var o = {                    name : [1, 2, 3]                }                Object.defineProperty(o, "name", {                    configurable : false,       // 不能delete,不能修改,不能设置为访问器属性                    enumerable : false,         // 不能枚举                    writable :  false,          // 不能修改                    value :     [100, 200]      // 把值变成[100, 200]                });                // for(var v in o.name) {                    // alert(o.name[v]);    // 100, 200, 能枚举                // }                alert(o.propertyIsEnumerable("name"));      // false                 // o.name = "li";                // alert(o.name);           // 100, 200 不能修改                // delete o.name;                // alert(o.name);               // 100, 200 不能删除        ②访问器属性,4个访问器属性特征,可以通过Object.defineProperty()来修改属性特征            a.[[Configurable]]  表示能否通过delete删除重新定义,能否修改属性的特征,能否修改为访问权属性            b.[[Enumerable]]    表示能否通过for-in枚举            c.[[Get]]           表示在读取属性时调用的函数,默认为undefined            d.[[Set]]           表示在设置属性时调用的函数,默认为undefined            eg:                 var o = {                    name : [1, 2, 3]                }                Object.defineProperty(o, "name", {                    get : function () {                        alert("get");                    },                    set : function() {                        alert("set");                    }                });                o.name = "li";      // set,设置name值时,自动调用o.set()                o.name;             // get,读取name时,自动调用o.get()        ③定义多个属性 Object.defineProperties()来同时定义多个属性            eg:                var o = {}                Object.defineProperties(o, {                    name : {                        configurable : false,                        value : "zhang"                    },                    age : {                        get : function () {                            alert("get");                        },                        set : function() {                            alert("set");                        }                    }                });                alert(o.name);          // zhang                o.age;                  // get                o.age = "li";           // set        ④读取属性的特征    Object.getOwnPropertyDescriptor(objectName, propertyName)            eg:                var o = {}                Object.defineProperties(o, {                    age : {                        get : function () {                            alert("get");                        },                        set : function() {                            alert("set");                        }                    }                });                var descriptor = Object.getOwnPropertyDescriptor(o, "age");                for(var v in descriptor) {                    alert(v + " = " + descriptor[v]);       // 弹出访问器属性的4个属性特征                }二、创建对象    1.工厂模式                  ①抽象了创建对象的具体过程            eg:                 function createObject (name, age) {                var object = new Object();                object.name = name;                object.age = age;                object.sayName = function () {                    return object.name;                }                return object;            }            var p1 = createObject("zhang", 23);            var p2 = createObject("li", 33);            alert(p1.sayName());        // zhang            alert(p2.sayName());        // li            // 无法识别p1和p2            alert(p1);                  // [object Object]            alert(p2);                  // [object Object]        ②弊端     没有解决对象识别的问题        ③解决方法   构造函数模型    2.构造函数模式        ①创建模式            eg:                function Person(name, age) {                    this.name = name;                    this.age = age;                    this.getName = function () {                        return this.name;                    }                }                var p1 = new Person("zhang", 34);                var p2 = new Person("li", 23);                alert(p1.getName());        // zhang                alert(p2.getName());        // li                alert(p1 instanceof Person);        // true                alert(p2 instanceof Person);        // true, 解决了工厂模式的对象识别问题        ②问题 每个方法都要在每个实例上创建一遍,从而形成不同的作用域链,从而导致不相同            eg: alert(p1.getName == p2.getName);        // false            我们也可以将方法部分提取到构造函数之外,但这样就没有什么封装性可言了。            eg:                function Person(name, age) {                    this.name = name;                    this.age = age;                    this.getName = getName;                }                function getName () {                    return this.name;                }        ③解决方法   原型模型    3.原型模式        ①创建模式   原型对象(构造函数的prototype属性指向它)的好处:可以让所有对象实例共享它包含属性和方法            eg:                function Person(name, age) {                    this.name = name;                    this.age = age;                }                Person.prototype.getName = function () {                    return this.name;                }                var p1 = new Person("zhang", 34);                var p2 = new Person("li", 23);                alert(p1.getName == p2.getName);    // true,解决了方法共享的问题        ②理解原型                   a.函数Person.prototype指向原型                b.Person.prototype.constructor指回构造函数                c.p1、p2的prototype指向原型,且可调用原型中的方法,用Person.prototype.isPrototypeOf(p1)判断,也可以用Object.getPrototypeOf(p1)来获取原型                d.我们可以用原型访问属性的值,但是不能通过实例重写原型的值,因为对象实例的值会屏蔽原型属性的值。当我们用实例对象重写了原型中的值,只有删除实例对象的值,才能访问原型属性的值。                e.同样我们可以通过[实例.hasOwnProperty(propertyName)]来检测实例是否定义了自己的属性值                    eg:                        function Person() {}                        Person.prototype.name = "zhang";                        Person.prototype.getName = function () {                            return this.name;                        }                        var p1 = new Person();                        alert(p1.name);     // zhang                        p1.name = "li";                        alert(p1.name);     // li,实例中的值覆盖了原型中的值                        alert(p1.hasOwnProperty("name"));   // 判断实例p1是否定义了自己的属性name的值,true                        delete p1.name;     // 删除实例对象中的属性值                        alert(p1.name);     // zhang        ③原型与in操作符                   a.无论是属性值存在于原型中,还是实例对象中都返回true                eg:                    function Person() {}                    Person.prototype.name = "zhang";                    Person.prototype.getName = function () {                        return this.name;                    }                    // 判断是否为原型中的属性                    function hasPrototypeProperty(object, propertyName) {                        return propertyName in object && !object.hasOwnProperty(propertyName);                    }                    var p1 = new Person();                    p1.name = "li";                    alert(hasPrototypeProperty(p1, "name"));    // false                    delete p1.name;                    alert(hasPrototypeProperty(p1, "name"));    // true            b.枚举所有可枚举的属性和方法,用Object.key(原型/实例)                eg:                    function Person() {}                    Person.prototype.name = "zhang";                    Person.prototype.age = 11;                    Person.prototype.getName = function () {                        return this.name;                    }                    alert(Object.keys(Person.prototype));   // 枚举原型中的属性和方法                    var p1 = new Person();                    p1.name = "li";                    p1.getName = function () {}             // 枚举实例对象中的属性和方法                    alert(Object.keys(p1));            c.枚举所有的属性和方法,无论是否隐藏,用hasOwnPropertyNames(原型);                eg: alert(Object.getOwnPropertyNames(Person));  // prototype,length,name        ④更简单的原型方法            a.源码            eg: function Person() {}                Person.prototype = {                    constructor : Person,                    name : "zhang",                    getName : function () {}                }            b.问题    这样做可能会导致原型中的constructor属性的[Enumerable]为true,默认为false            c.解决方法  用Object.defineProperty()方法重新定义                eg: Object.defineProperty(Person.prototype, constructor, { enumerable : false});            e.实例化对象一定要后于对象的定义完毕        ⑤原型对象的问题        共享性,针对方法很好,针对属性也说的过去,但是针对那些包含了引用类型则不可            eg:                function Person() {}                Person.prototype = {                    constructor : Person,                    friends : [1, 2]        // 引用类型                }                var p1 = new Person();                var p2 = new Person();                p1.friends.push(3);                alert(p1.friends);                alert(p2.friends);      // 同时返回1,2,3        ⑥解决方法   取长补短,用构造函数模式定义属性,用原型模式定义方法    3.组合构造模式和原型模式        ①模式 取长补短,用构造函数模式定义属性,用原型模式定义方法        eg:             function Person(name) {                this.name = name;                this.friends = [1, 2]       // 引用类型            }            Person.prototype = {                constructor : Person,                name : "zhang",            }            var p1 = new Person("li");            var p2 = new Person("wang");            p1.friends.push(3);            alert(p1.friends);      // 1,2,3            alert(p2.friends);      // 1,2        ②小问题        感觉构造函数和原型分离,破坏了封装性        ③解决方法   使用动态原型模式    4.动态原型模式(基本完美)      将原型中方法封装到构造函数中去        eg:            function Person(name) {                this.name = name;                this.friends = [1, 2];      // 引用类型                if (typeof this.getName != "function") {                    Person.prototype.getName = {                        return this.name;                    }                }            }    5.寄生构造模式        ①基本思想:创建一个函数(对象),该函数用来封装代码,然后返回函数(对象)        ②模式            eg:                function Person(name, age) {                    var o = new Object();                    o.name = name;                    o.age = age;                    o.getName = function () {                        return o.name;                    };                    return o;                }                var p1 = new Person("zhang", 34);                alert(p1.getName());        // zhang                 alert(p1 instanceof Person);// false        ③问题:由于实例对象和构造函数完全分离,因此无法识别对象        ④案例:对于Array类型,我们可能在特殊情况在,对它进行添加属性和方法            eg:                function NewArray() {                    var array = new Array();                    array.push.apply(array, arguments);                    array.addFun = function () {                        return this.join("|");                    }                    return array;                }                var a1 = new NewArray("zhang", 22);                alert(a1.addFun());     // zhang|22    6.稳妥构造函数模型      没有公共属性,不使用thisnew,只能定义获取值的方法        ①用途:安全性        ②源码            eg:                function Person(name, age) {                    var o = new Object();                    o.getName = function () {                        return name;                    }                    return o;                }                var p = Person("zhang", 3);                p.name = 'li';          // 无效                alert(p.getName());     // zhang        ③特点 函数名首字母大写、对象里只定义方法且不用this、实例化时不用new        ④问题:由于实例对象和构造函数完全分离,因此无法识别对象三、继承    1.原型链        ①将父类的实例赋值给子类的原型。因为父类的实例指向父类的原型,因此子类的原型也指向父类的原型。        ②基本源码:            eg:                function SuperType(){                    this.property = true;                }                SuperType.prototype.getSuperValue = function(){                    return this.property;                };                function SubType(){                    this.subproperty = false;                }                //继承了SuperType                SubType.prototype = new SuperType();    // 将父类的实例赋值给子类的原型                SubType.prototype.getSubValue = function (){                    return this.subproperty;                };                var instance = new SubType();                alert(instance.getSuperValue()); //true,调用父类SuperType的方法getSuperValue()        ③别忘记了父类同样基础了祖类Object        ③确定原型和实例的关系 用instanceof和对象.isPrototypeOf(实例)        ④在子类重新或者添加父类的方法时,必须要在父类定义之后        ⑤原型链的问题 原型链中不能存在引用类型            eg:                function SuperType(){                    this.friends = [1,2];                }                function SubType(){}                //继承了SuperType                SubType.prototype = new SuperType();                var s1 = new SubType();                var s2 = new SubType();                s1.friends.push(3);                alert(s1.friends);      // 1, 2, 3                alert(s2.friends);      // 同上        ⑥解决方法   借用构造函数    2.借用构造函数    对于原型链中包含引用类型,我们可以在子类的构造函中调用父类的构造函数        ①源码案例, 即可以使用引用类型,还可以传递参数        eg:             function SuperType(name){                this.name = name;                this.friends = [1,2];            }            function SubType(){                SuperType.call(this, "abc");        // 传递参数            }            //继承了SuperType            SubType.prototype = new SuperType();            var s1 = new SubType();            var s2 = new SubType();            s1.friends.push(3);            alert(s1.friends);      // 1, 2, 3            alert(s2.friends);      // 1, 2            alert(s1.name);         // abc        ③问题 由于是在构造函数中定义,所以方法不能够共享        ④解决方法   组合继承    3.组合继承(虽然两次调用了父类,但是基本ok)        ①基本思想   将借用构造和原型链结合起来,借用构造定义属性,原型链定义方法            eg:                 function SuperType(name){                    this.name = name;                    this.friends = [1,2];                    if (typeof this.getName != "function") {                        SuperType.prototype.getName = function () {                            return this.name;                        }                    }                }                function SubType(name, age){                    SuperType.call(this, name);             // 第二次调用父类                    this.age = age;                    if (typeof this.getAge != "function") {                        SuperType.prototype.getAge = function () {                            return this.age;                        }                    }                   }                //继承了SuperType                SubType.prototype = new SuperType();        // 第一次调用父类                var s1 = new SubType("zhang", 23);                var s2 = new SubType("li", 24);                s1.friends.push(3);                alert(s1.friends);      // 1, 2, 3                alert(s2.friends);      // 1, 2                alert(s1.getName());    // zhang                alert(s2.getAge());     // 24    4.原型式继承        ①基本思想   借助原型可以基于已有的对象创建新对象,从而不必自定义对象            eg:                 function object(o) {                    function F() {};                    F.prototype = o;                    return new F();                }                var person = {                    name : "zhang",                    friends : [1, 2]                }                var p1 = object(person);                p1.name = "li";                p1.friends.push(3);                alert(p1.name);     // li                alert(p1.friends);  // 1,2,3                var p2 = object(person);                p1.name = "wang";                p1.friends.push(4);                alert(p2.name);     // wang                alert(p2.friends);  // 1,2,3,4        ②ECMAScript 5发展了道格拉斯·克罗克福德的原型链继承,用Object.create()方法            eg: 其中第二个参数和defineProperty()方法一致                var person = {                    name : "zhang",                    friends : [1, 2]                }                var p1 = Object.create(person, {                    name : {                        value : "zhang"                    }                });                p1.friends.push(3);                alert(p1.name);     // li                alert(p1.friends);  // 1,2,3                var p2 = Object.create(person, {                    name : {                        value : "wang"                    }                });                p1.friends.push(4);                alert(p2.name);     // wang                alert(p2.friends);  // 1,2,3,4        ③问题:    原型链共享问题,引用类型    5.寄生式继承        ①思想 基于原型式继承,创建一个新函数对象,添加新方法        eg:            function object(o) {                function F() {};                F.prototype = o;                return new F();            }            function createAnother(original) {                // 继承原来的对象原型                var clone = object(original);                // 添加新方法                clone.newFun = function () {                    return "new function";                }                return clone;            }            var person = {                name : "zhang",                friends : [1, 2]            }            var p = createAnother(person);            alert(p.name);      // zhang            alert(p.newFun());  // new function         ②问题 原型链共享问题,引用类型    6.寄生组合式继承        ①思想 在组合继承和原型式继承的基础上,不在子类的内部调用父类的构造函数,而是创建父类原型的副本        eg:             function object(o) {                function F() {};                F.prototype = o;                return new F();            }            function inheritPrototype(subType, superType) {                // 赋值proto为superType的原型                var proto = object(superType.prototype);                // 原型的contructor属性指向构造函数                proto.contructor = subType;                // superType的构造函数指向原型                subType.prototype = proto;            }            function SuperType(name){                this.name = name;                this.friends = [1,2];                if (typeof this.getName != "function") {                    SuperType.prototype.getName = function () {                        return this.name;                    }                }            }            inheritPrototype(SubType, SuperType);            function SubType(name, age){                SuperType.call(this, name);                this.age = age;                if (typeof this.getAge != "function") {                    SuperType.prototype.getAge = function () {                        return this.age;                    }                }               }            var s1 = new SubType("zhang", 23);            var s2 = new SubType("li", 24);            s1.friends.push(3);            alert(s1.friends);      // 1, 2, 3            alert(s2.friends);      // 1, 2            alert(s1.getName());    // zhang            alert(s2.getAge());     // 24
0 0