JavaScript面向对象设计

一、理解对象
    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.稳妥构造函数模型      没有公共属性,不使用this和new,只能定义获取值的方法
        ①用途:安全性
        ②源码
            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











最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容