js的面向对象

1.面向对象语言都有一个标志,那就是类的概念。通过类可以创建任意多个具有相同属性的方法的对象。但是在ECMAScript中

没有相关类的概念。但是我们可以通过其他方式来模拟面向对象中的类。

  -工厂模式:不细说

  -构造函数模式:比如像ECMAScript中的Array,Object,Date等都是通过构造函数来创建的。

  -动态原型模式

  -寄生构造函数模式

  -稳妥构造函数模式

<script type="text/javascript" charset="utf-8">
            
            var obj = new Object();
            obj.name = 'zs';
            obj.sex = 'male';
            obj.sayName = function() {
                alert('my name is zs');
            };
            
            // 类的概念
            // 第一种形式  工厂模型  通过这个方法用来创建实例
            function createPerson(name, sex, age) {
                var obj = new Object();
                obj.name = name;
                obj.sex = sex;
                obj.age = age;
                obj.sayName = function(){
                    alert(this.name);
                };
                return obj;    
            }
            
            var p1 = createPerson(ls, male, 24);
            
            
            // 第二种形式,构造函数式, 函数的第一个字母大写,约定俗成的表示为一个类的模板
            function Person(name, age, sex) {
                this.name = name;
                this.age = age;
                this.sex = sex;
                this.sayName = function() {
                    alert(this.name);
                };
            }
            // 构造一个对象,一般使用new 关键字,并且传递参数 执行模板代码 返回对象
            var p2 = new Person('ww', 'famale', 20);
            alert(p2.constructor == Person);  // 返回结果为true
            alert(p2 instanceof Person);    // 返回结果为true
            alert(p1 instanceof Object);    // 返回结果为true
            
            // 创建对象的方式
            // 1.当做构造函数去使用  即new的方式
            var p3 = new Person('w1', 'male', 10);
            // 2.作为普通函数去调用    //在全局环境里定义属性并复制 直接定义在window上
            var p4 = Person('w2', 'famale', 30);
            // 3.在另一个对象的作用域中调用
            var o = new Object();
            Person.call(o, '11', 'male', 30);
        </script>

2.原型   

  -我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途包含可以由特定类型

的所有实例共享的属性和方法。

  -原型对象实际就是一个构造函数的实例对象,与普通的实例对象没有什么本质上的区别,js中每一个对象都有一个原型对象。不过

它比较特殊,该对象所包含的所有属性和方法能够供构造函数的所有实例共享,也就是其他编程语言所说的继承,而js通过原型对象来

继承,简称原型继承。静态原型继承:Object.prototype.[method field];

  -简单实现Array中的push,pop方法。

<script type="text/javascript" charset="utf-8">
            
            
            // 原型   prototype
            // 构造函数方式
            function Person(name, age){
                this.name = name;
                this.age = age;
                this.sayName = function() {
                    alert('i am name');
                };
            }
            
            function sayName() {
                alert(this.name);
            }
            
            var p1 = new Person('zs', 20);
            var p2 = new Person('ls', 21);
            alert(p1.sayName == p2.sayName);    //返回结果为false
            alert(p1.name == p2.name);    //false
            p1.sayName();
            alert(p1.sayName == p2.sayName);     // 将Person中的sayName方法去掉之后,返回结果为true
            
            // 但是定义过多的全局变量 显然是不好的,于是我们采用下面的方法。
            // 创建每一个函数的时候 都有一个Prototype属性,这个属性其实是一个指针。而这个指针总是指向一个对象。
            // (这个对象的用途就是将特定的属性和方法 包含在内,起到一个所有实例所共享的作用)
            
            function Person1() {
                
            }
            var obj = Person1.prototype;
            alert(obj.constructor);    // 返回值为function Person(){}
            obj.name = 'zs';
            obj.age = 20;
            obj.sayName = function() {
                alert(this.name);
            };
            var p3 = new Person();
            var p4 = new Person();
            alert(p3.name);    //zs
            alert(p4.name);    //zs
            alert(p3.age);    // 20
            alert(p4.age);    // 20
            
            // 构造函数  原型对象  实例对象
            // 构造函数.prototype = 原型对象
            // 原型对象.constructor = 构造函数
            // 实例对象.prototype = 原型对象
            obj.isPrototypeOf(p3);    // 结果为true
            
        </script>

 3.原型的详细方法

  

<script type="text/javascript" charset="utf-8">
            
            // isPrototypeOf()  判断原型的方法
            
            // Object.getPrototypeOf();根据实例对象获得原型对象
            function Person() {
                
            }
            Person.prototype.name = 'zs';
            Person.prototype.age = 20;
            Person.prototype.sayName = function() {
                alert('我是原型对象的方法');
            };
            
            var p1 = new Person();
            var prototypeObj = Object.getPrototypeOf(p1); // 得到原型对象
            
            
            var p2 = new Person();
            p2.name = 'w5';
            // 每次代码读取一个对象的属性的时候,会先进行一次搜索,搜索实例对象。如果没有,再去实例所对应的原型对象里搜索。
            alert(p2.name);    // 返回值为w5
            // 如果非要获得原型对象中的属性,可以将实例中的属性删除掉。
            delete p2.name;
            
            // 判断一个对象是原型属性还是实例属性
            var p3 = new Person();
            p3.hasOwnproperty('name');    //返回false 因为是原型属性
            
            // in操作符:for-in
            // in操作符是判断属性是否存在于实例对象和原型对象中。如果有一个存在,则返回true,如果都不存在,才返回false
            var p5 = new Person();
            alert('name' in p5); // 返回true 证明在p5对象中有name属性
            var p6 = new Person();
            p6.name = 'w3';
            alert('name' in p6);     //返回true
            
            // 判断一个属性是否存在原型中。第一个参数是当前对象,第二个参数是要判断的属性
            function hasPrototypeProperty(object, name) {
                return !object.hasOwnProperty('name') && name in object;
            }
            
            // ECMA5新特性 Object.keys
              // 可以将对象中所有的属性找到,并返回一个数组
              var m1 = new Person();
              m1.name = 'zs';
              m1.age = 20;
              var attribute = Object.keys(m1);
              alert(arrtibute);    // 返回name和age
              
              var attribute = Object.keys(Person.prototype); //返回 name age sayName
              
              // ECMA5中 constructor属性 不可以被枚举
              // Object.getOwnPropertyNames  方法的作用是 枚举出对象的全部属性,无论该属性能否被枚举
              alert(Object.getOwnPropertyNames(m1));// constructor name age sayName
              
              
        </script>

 4.模拟each方法

<script type="text/javascript" charset="utf-8">
            // Array each方法
            
            // ECMA5中,forEach方法。
            var arr = [1,2,3,4,5];    //只适合遍历一维数组
            arr.forEach(function(item, index, array) {
                alert(item);
            });
            
            // 自己实现一个Array的Each方法,可以遍历多维数组
            var arr1 = [1,2,3,[4,[5,[6]]]];
            Array.prototype.each = function(fn){
                try{
                    // 1.目的,遍历数组的每一项
                    // 计数器,记录当前遍历的元素位置
                    this.i || (this.i = 0);
                    if (this.length > 0 && fn.constructor == Function) {
                        // 循环遍历
                        while(this.i < this.length){
                            // 获取数组的每一项元素
                            var e = this[this.i];
                            // 如果当前元素获取到了,并且是一个数组
                            if (e && e.constructor == Array) {
                                // 递归
                                e.each(fn);
                            } else {
                                fn.call(e, [e]);
                            }
                            this.i++;
                        }
                        // 释放内存,垃圾回收
                        this.i = null;
                    }
                    
                    
                }catch(ex){
                    // do something
                }
                return this;
            };
            
            arr1.each(function(item){
                alert(item);
            });
        </script>

5.简单原型的使用
  直接通过对象字面来重写整个对象(会改变原型对象的构造器)。

  ECMA5中的Object.defineProperty()方法可以为原型对象重新加入构造器。

  原型的动态性(注意原型和创建实例的先后顺序)

<script type="text/javascript" charset="utf-8">
            // 简单原型
            function Person() {
                
            }
            
            Person.prototype = {
                name : 'zs',
                age : 18,
                job : 'doctor',
                say: function(){
                    alert('aa');
                }
            };
            
            var p1 = new Person();
            alert(p1.name);
            p1.say();
            
            alert(Person.prototype.constructor);    //不加constructor : Person这句话,Object的构造函数,加上之后可以枚举(这样不对)
            // ECMA5给远行对象重新设置构造器方法
            // 3个参数  参数1:重设构造器的对象  参数2:设置什么属性  参数3:option配置项
            Object.defineProperty(Person.prototype, 'constructor', {
                enumerable : false,
                value : Person
            });
            
            // 原型动态特性
            function A() {
                
            }
            var a = new A();
            A.prototype.say = function(){
                alert('bbb');
            };
            
            a.say();//返回bbb 没有问题
            
            
            
            function B() {
                
            }
            var b = new B();
            B.prototype = {
                name : 'zs',
                age : 18,
                job : 'doctor',
                say: function(){
                    alert('aa');
                }
            };
               b.say(); // say is not a function 必须把var b = new B(); 拿到后面去写(实例对象必须在原型对象之后创建)
               
        </script>

 6.原型对象存在的问题

  -原型对象虽然可以对所有实例的属性和方法共享,但是他的局限性也是很明显的。正因为共享的特性,也导致了原型存在

的最大问题。

  -我们一般组合使用构造函数式和原型模式,在实际开发中,这种模式也是应用的最稳广泛。

  -动态原型模式:就是把信息都封装到函数中,这样体现了封装的概念。

  -稳妥构造函数式:所谓稳妥模式就是没有公共属性,而且其他方法也不引用this对象。稳妥模式最适合在安全的环境中

使用。如果你的程序对于安全性要求很高,那么非常适合这种模式。

<script type="text/javascript" charset="utf-8">
            
            // 原型概念:原型对象里的所有属性和方法,被所有构造函数实例化出来的对象所共享
            function Person(){
                
            }
            Person.prototype = {
                constructor:Person,
                name:'zs',
                age:20,
                job:'doctor',
                friends:['ls', 'ww'],
                sayName:function(){
                    alert('my name!');
                }
            };
            
            var p1 = new Person();
            var p2 = new Person();
            p1.friends.push('zl');
            // p1增加friend之后,由于共享性,p2的friend也增加了,这样会存在问题。
            alert(p1.friends);
            alert(p2.friends);
            
            // 组合使用原型和构造函数式(开发常用定义类的方式)
            function Person1(name, age, friends, job) {
                this.name = name;
                this.age = age;
                this.friends = friends;
                this.job = job;
            }
            
            Person.prototype = {
                constructor : Person,
                sayName : function() {
                    alert(this.name);
                }
            };
            
            // 动态原型模式:(让代码 所有属性和方法都封装到一起)
            function Person2(name, age, friends, job) {
                this.name = name;
                this.age = age;
                this.friends = friends;
                this.job = job;
                
                if(typeof sayName != 'function'){
                    Person.prototype.sayName = function() {
                        alert(this.name);
                    };
                }
            }
            
            
            // 稳妥构造函数式:durable object(稳妥对象)
            // 1.没有公共属性
            // 2.不能使用this
            function Person3(name, age, job) {
                // 创建一个要返回的对象
                var obj = new Object();
                var name = name;
                
                obj.sayName = function(){
                    alert(name);
                };
                return obj;
            }
        </script>

7.继承
  - 我们都知道构造函数,原型和实例的关系,如果我们让原型对象等于另一个类的实例,此时的原型对象将

包含一个指向另一个原型的指针,相应的另一个原型也包含着一个指向另一个构造函数的指针。

  -原型链:利用原型让一个引用类型来继承另一个引用类型的属性和方法。

  -简单继承

  -类继承(模板继承或者借用构造函数继承)

  -混合使用继承

<script type="text/javascript" charset="utf-8">
            // js中如何实现继承 采用原型链的概念
            
            // 构造函数 原型对象  实例对象
            // 构造函数 .prototype = 原型对象
            // 原型对象.constructor = 构造函数模板
            // 原型对象.isprototypeOf(实例对象)  判断实例对象原型是否是该原型对象
            
            
            // 如果我们让原型对象等于另一个类的实例
            function Super(name) {
                this.name = name;
            }
            Super.prototype = {
                constructor : Sup,
                sayName : function() {
                    alert(this.name);    
                }
            };
            function Sub(age){
                this.age = age;
            }
            // 子类原型对象 等于父类的实例
            // 子类的原型对象的构造器  变成了父类的构造器
            Sub.prototype = new Sup();
        </script>
<script type="text/javascript" charset="utf-8">
            function Person(name, age){
                this.name = name;
                this.age = age;
            }
            Person.prototype.id = 10;
            
            function Boy(sex){
                this.sex = sex;
            }
            
            Boy.prototype = new Person('zs');
            var b = new Boy();
            alert(b.name);
            alert(b.id);
            
            
            // 类继承,只继承模板,不继承原型(借用构造函数方式继承)
            function Person1(name, age){
                this.name = name;
                this.age = age;
            }
            Person1.prototype.id = 10;
            
            function Boy1(name, age, sex) {
                // call apply
                Person1.call(this, name, age);
                this.sex = sex;
            } 
            
            var b1 = new Boy1('zs', 20, 'male');
            alert(b1.name);
            alert(b1.id);    // undefined 父类的原型对象并没有继承,这就是类继承的缺点
            
            
            // 混合继承 通过原型继承 借用构造函数继承  = 混合继承
            // 类继承,只继承模板,不继承原型(借用构造函数方式继承)
            function Person2(name, age){
                this.name = name;
                this.age = age;
            }
            Person2.prototype.id = 10;
            Person2.prototype.sayName = function(){alert(this.name);};
            
            function Boy2(name, age, sex) {
                // call apply
                Person2.call(this, name, age);
                this.sex = sex;
            }
            // 不传参数 ,父类的实例和子类的原型对象的关联
            // 目的只是为了继承父类的原型对象
            Boy2.prototype = new Person2();
        </script>

 8.模拟extjs底层继承方式

<script type="text/javascript" charset="utf-8">
            // 混合继承:原型继承和借用构造函数继承
            function Person(name, age){
                this.name = name;
                this.age = age;
            }
            Person.prototype = {
                constructor : Person,
                sayHello : function(){alert('hello world');}
            };
            function Boy(name, age, sex) {
                Person.call(this, name, age);        // 也可以写成Boy.superclass.constructor.call(this, name, age);
                this.sex = sex;
            }
            Boy.prototype = new Person();//这里其实也继承了父类的模板,而且继承了父类的原型对象。
            
            // 混合继承  做了3件事, 继承了两次父类模板,继承了一次原型对象。
            // 混合模板的缺点,如果父类的属性特别多,这样很不方便
            
            
            // 继承一次父类模板,继承一次父类原型对象,这样是最好的,我们自己写一个extend方法
            function extend(sub, super){
                // 目的:只继承父类的原型对象
                // 1.用一个空函数进行中转
                var F = new Function();
                // 2.空函数的原型对象和父类的原型对象转换
                f.prototype = super.prototype;
                // 3.将空函数的原型对象,给子类的原型对象
                sub.prototype = new F();
                // 4.还原子类的构造器
                sub.prototype.constructor = sub;
                // 5.保存父类的原型对象  原因:方便解耦     方便获得父类的原型对象
                sub.superclass = sup.prototype;// 利用子类的静态属性,存储父类的原型对象 
                // 判断父类的原型对象的构造器
                if (sup.prototype.constructor == Object.prototype.constructor) {
                    sup.prototype.constructor = sup;
                }
            }
            
            // 给子类加一个原型对象方法
            Boy.prototype.sayHello = function(){
                alert('hello sub!!');
            };
            b.sayHello(); //返回值为hello sub
            // 为了想要使用父类的sayHello方法
            Boy.superclass.sayHello.call(b);  // 这样就没问题了
            
        </script>
原文地址:https://www.cnblogs.com/yangfanasp/p/7082435.html