JS高级

作者: CHEN_Erhui | 来源:发表于2017-05-20 15:28 被阅读0次

    1.面向对象的概念

    1.1什么是面向对象:

    1 .面向对象是一种思维方法
    2.面向对象是一种编程方法
    3.面向对象并不只针对某一种编程语言

    1.2面向对象和面向过程的区别

    1.面向过程过程侧重整个问题的解决步骤,着眼局部或者具体
    2.面向对象侧重具体的功能,让某个对象具有这样的功能。更加侧重于整体。

    1.3面向对象的实现方式

    1.基于类的面向对象
    2.基于原型的面向对象

    1.4访问对象的属性

    访问一个对象的属性,我们可以直接通过 对象.属性名对象[属性名] 来访问。

    alert(person.name);  // 访问person对象的 name属性值
    person.age = 30;  //修改person对象的 age 属性
    person.eat();  //既然是调用方法(函数) 则一定还要添加 ()来表示方法的调用
    alert(person["name"]);  //
    

    两种使用方式有一些不同的地方:

    1. 对象.属性名的方式,只适合知道了属性的名字,可以直接写。比如: person.age 。如果属性名是个变量,则这种方法无效, 对象.变量名 会出现语法错误。
    2. 对象[属性名],这种方式使用无限制。如果是字符串常量,则应该用""或''引起来,如果是变量,可以直接使用。
    person.age = 100; // ok
    var n = "age";
    person.a = 101; //  no ok 语法错误
    person["age"] = 102; // ok
    person[n] = 103;  //ok
    

    1.5给对象添加属性

    //给person对象的属性 girlFriend 赋值。在赋值的过程中,首先会判断这个属性在JavaScript中是否存在,如果存在就对这个
    //属性重写赋值。如果不存在,就给这个对象添加这个属性,并赋值。
    person.girlFrient = "小丽";  
    
    //给对象添加方法
    person.play = funcion(){
        alert("打击high起来");
    }
    

    1.6删除对象属性

    使用 delete 操作符

    // 使用delete操作关键字,删除person对象的属性age
    delete person.age;
    alert(person.age);  //弹出undefined。表示这个属性没有定义
    

    1.7使用for...in遍历对象的属性

    for...in 可以遍历对象的所有属性。

    // 在用for...in遍历的时候, in前面的变量pn指的是属性的名称。
    for (pn in person) {
        alert(pn + " " + person[pn]);
    }
    

    2.创建对象的方式

    2.1使用new Object()创建

    <script type="text/javascript">
        //使用object创建一个对象    完全等同于 var person = {};
        var person = new Object();
        //给对象添加属性
        person.name = "李四";
        //给对象添加方法
        person.eat = function () {
            alert("好好吃")
        }
    </script>
    

    2.2使用工厂模式创建

    <script type="text/javascript">
        function createPerson(name, age, job) {
            var o = new Object();
            o.name = name;
            o.age = age;
            o.job = job;
            o.sayName = function() {
                alert(this.name);
            };
            return o;
        }
        var person1 = createPerson("张三", 29, "js开发者");
        var person2 = createPerson("李四", 27, "java开发者");
    </script>
    

    2.3构造函数创建对象

    <script type="text/javascript">
        function Person (name, age, sex) {
            this.name = name;
          
            this.age = age;
            this.sex = sex;
            
            this.eat = function () {
                alert(this.name + "在吃东西");
            }
        }
        var p1 = new Person("张三", 20, "男");
        p1.eat();   //张三在在吃东西
        var p1 = new Person("李四", 30, "男");
        p1.eat();   //李四在在吃东西
        alert(p1 instanceof Person);    //
    </script>
    

    说明:

    1. 使用构造函数创建对象,必须使用关键字new ,后面跟着构造函数的名,根据需要传入相应的参数。
    2. 其实使用 new 构造函数() 的方式创建对象,经历了下面几个步骤。
      • 创建出来一个新的对象
      • 将构造函数的作用域赋给新对象。意味着这个时候 this就代表了这个新对象
      • 执行构造函数中的代码。 在本例中就是给新对象添加属性,并给属性初始化值。
      • 构造函数执行完毕之后,默认返回新对象。 所以外面就可以拿到这个刚刚创建的新对象了。

    2.3.1构造函数与普通函数的关系

    1. 他们都是函数。构造函数也是函数,也可以像普通的函数一样进行调用。 做普通函数调用的时候,因为没有创建新的对象,所以this其实指向了window对象。
    function Person(){
        this.name = "张三";   // 把name属性添加到了window对象上面
        alert(this === window);  //如果不作为构造方法调用,则 是true
    }
    Person();  // 把构造函数当做普通方法调用。这个时候内部的this指向了weindow
    alert(window.name);  //张三
    function Human(){
        this.name = "王五";
        alert(this instanceof window);  // false
        alert(this instanceof Human);  //true
    }
    var h = new Human();  //当做构造函数来调用,创建一个对象
    alert(h.name);
    
    1. 构造函数和普通函数仅仅也仅仅是调用方式的不同。也就是说,随便一个函数你如果用new 的方式去使用,那么他就是一个构造函数。
    2. 为了区别,如果一个函数想作为构造函数,作为国际惯例,最好把这个构造函数的首字母大写。


    3.理解原型

    3.1 函数的原型对象

    ​ 在JavaScript中,我们创建一个函数A(就是声明一个函数), 那么浏览器就会在内存中创建一个对象B,而且每个函数都默认会有一个属性 prototype 指向了这个对象( 即:prototype的属性的值是这个对象 )。这个对象B就是函数A的原型对象,简称函数的原型。这个原型对象B 默认会有一个属性 constructor 指向了这个函数A ( 意思就是说:constructor属性的值是函数A )。

    ​ 看下面的代码:

    <body>
        <script type="text/javascript">
            /*
                声明一个函数,则这个函数默认会有一个属性叫 prototype 。而且浏览器会自动按照一定的规则
                创建一个对象,这个对象就是这个函数的原型对象,prototype属性指向这个原型对象。这个原型对象
                有一个属性叫constructor 指向了这个函数
                
                注意:原型对象默认只有属性:constructor。其他都是从Object继承而来,暂且不用考虑。
            */
            function Person () {
                
            }       
        </script>
    </body>
    

    3.2使用构造函数创建对象

    ​ 当把一个函数作为构造函数 (理论上任何函数都可以作为构造函数) 使用new创建对象的时候,那么这个对象就会存在一个默认的不可见的属性,来指向了构造函数的原型对象。 这个不可见的属性我们一般用 [[proto]] 来表示,只是这个属性没有办法直接访问到。

    ​ 看下面的代码:

    <body>
        <script type="text/javascript">
            function Person () {
                
            }   
            /*
                利用构造函数创建一个对象,则这个对象会自动添加一个不可见的属性 [[proto]], 而且这个属性
                指向了构造函数的原型对象。
            */
            var p1 = new Person();
        </script>
    </body>
    

    观察下面的示意图:

    说明:

    1. 从上面的图示中可以看到,创建p1对象虽然使用的是Person构造函数,但是对象创建出来之后,这个p1对象其实已经与Person构造函数没有任何关系了,p1对象的[[ proto ]]属性指向的是Person构造函数的原型对象。
    2. 如果使用new Person()创建多个对象,则多个对象都会同时指向Person构造函数的原型对象。
    3. 我们可以手动给这个原型对象添加属性和方法,那么p1,p2,p3...这些对象就会共享这些在原型中添加的属性和方法。
    4. 如果我们访问p1中的一个属性name,如果在p1对象中找到,则直接返回。如果p1对象中没有找到,则直接去p1对象的[[proto]]属性指向的原型对象中查找,如果查找到则返回。(如果原型中也没有找到,则继续向上找原型的原型---原型链。 后面再讲)。
    5. 如果通过p1对象添加了一个属性name,则对p1对象来说就屏蔽了原型中的属性name。 换句话说:在p1中就没有办法访问到原型的属性name了。
    6. 通过p1对象只能读取原型中的属性name的值,而不能修改原型中的属性name的值。 p1.name = "李四"; 并不是修改了原型中的值,而是在p1对象中给添加了一个属性name。

    看下面的代码:

    <body>
        <script type="text/javascript">
            function Person () {        
            }
            // 可以使用Person.prototype 直接访问到原型对象
            //给Person函数的原型对象中添加一个属性 name并且值是 "张三"
            Person.prototype.name = "张三";
            Person.prototype.age = 20;
    
            var p1 = new Person();
            /*
                访问p1对象的属性name,虽然在p1对象中我们并没有明确的添加属性name,但是
                p1的 [[prototype]] 属性指向的原型中有name属性,所以这个地方可以访问到属性name
                就值。
                注意:这个时候不能通过p1对象删除name属性,因为只能删除在p1中删除的对象。
            */
            alert(p1.name);  // 张三
    
            var p2 = new Person();
            alert(p2.name);  // 张三  都是从原型中找到的,所以一样。
    
            alert(p1.name === p2.name);  // true
    
            // 由于不能修改原型中的值,则这种方法就直接在p1中添加了一个新的属性name,然后在p1中无法再访问到
            //原型中的属性。
            p1.name = "李四";
            alert("p1:" + p1.name);
            // 由于p2中没有name属性,则对p2来说仍然是访问的原型中的属性。    
            alert("p2:" + p2.name);  // 张三  
        </script>
    </body>
    

    3.3与原型有关的几个属性和方法

    3.3.1prototype属性

    prototype存在于构造函数中,它指向于构造函数的原型对象。

    3.3.2 constructor属性

    constructor属性存在于原型对象中,他指向了构造函数

    看下面的代码:

    <script type="text/javascript">
        function Person () {
        }
        alert(Person.prototype.constructor === Person); // true
        var p1 = new Person();
        //使用instanceof 操作符可以判断一个对象的类型。  
        //typeof一般用来获取简单类型和函数。而引用类型一般使用instanceof,因为引用类型用typeof 总是返回objece。
        alert(p1 instanceof Person);    // true
        alert(typeof p1); // object
    </script>
    

    3.3.3 hasOwnProperty()方法

    hasOwnProperty方法用来判断一个属性是否来自对象本身。

    <script type="text/javascript">
        function Person () {
            
        }
        Person.prototype.name = "志玲";
        var p1 = new Person();
        p1.sex = "女";
        //sex属性是直接在p1属性中添加,所以是true
        alert("sex属性是对象本身的:" + p1.hasOwnProperty("sex"));
        // name属性是在原型中添加的,所以是false
        alert("name属性是对象本身的:" + p1.hasOwnProperty("name"));
        //  age 属性不存在,所以也是false
        alert("age属性是存在于对象本身:" + p1.hasOwnProperty("age"));
        
    </script>
    

    3.3.4 in操作符

    in 操作符用来判断一个属性是否存在于这个对象中。但是在查找这个属性时候,先在对象本身中找,如果对象找不到再去原型中找。换句话说,只要对象和原型中有一个地方存在这个属性,就返回true

    <script type="text/javascript">
        function Person () {
            
        }
        Person.prototype.name = "志玲";
        var p1 = new Person();
        p1.sex = "女";
        alert("sex" in p1);     // 对象本身添加的,所以true
        alert("name" in p1);    //原型中存在,所以true
        alert("age" in p1);     //对象和原型中都不存在,所以false
        
    </script>
    

    回到前面的问题,如何判断一个属性是否存在于原型中:

    如果一个属性存在,但是没有在对象本身中,则一定存在于原型中。

    <script type="text/javascript">
        function Person () {
        }
        Person.prototype.name = "志玲";
        var p1 = new Person();
        p1.sex = "女";
        
        //定义一个函数去判断原型所在的位置
        function propertyLocation(obj, prop){
            if(!(prop in obj)){
                alert(prop + "属性不存在");
            }else if(obj.hasOwnProperty(prop)){
                alert(prop + "属性存在于对象中");
            }else {
                alert(prop + "对象存在于原型中");
            }
        }
        propertyLocation(p1, "age");
        propertyLocation(p1, "name");
        propertyLocation(p1, "sex");
    </script
    

    3.4组合使用原型模型和构造函数模型创建对象


    ​ 原型模式适合封装方法,构造方法模式适合封装属性,综合两种模式的优点就有了组合模式。

    <script type="text/javascript">
        //在构造方法内部封装属性
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        //在原型对象内封装方法
        Person.prototype.eat = function (food) {
            alert(this.name + "爱吃" + food);
        }
        Person.prototype.play = function (playName) {
            alert(this.name + "爱玩" + playName);
        }
        
        var p1 = new Person("李四", 20);
        var p2 = new Person("张三", 30);
        p1.eat("苹果");
        p2.eat("香蕉");
        p1.play("志玲");
        p2.play("凤姐");
    </script>
    

    3.5动态原型模式创建对象

    前面讲到的组合模式,也并非完美无缺,有一点也是感觉不是很完美。把构造方法和原型分开写,总让人感觉不舒服,应该想办法把构造方法和原型封装在一起,所以就有了动态原型模式。
    

    ​ 动态原型模式把所有的属性和方法都封装在构造方法中,而仅仅在需要的时候才去在构造方法中初始化原型,又保持了同时使用构造函数和原型的优点。

    看下面的代码:

    <script type="text/javascript">
        //构造方法内部封装属性
        function Person(name, age) {
            //每个对象都添加自己的属性
            this.name = name;
            this.age = age;
            /*
                判断this.eat这个属性是不是function,如果不是function则证明是第一次创建对象,
                则把这个funcion添加到原型中。
                如果是function,则代表原型中已经有了这个方法,则不需要再添加。
                perfect!完美解决了性能和代码的封装问题。
            */
            if(typeof this.eat !== "function"){
                Person.prototype.eat = function () {
                    alert(this.name + " 在吃");
                }
            }
        }
        var p1 = new Person("志玲", 40);
        p1.eat();   
    </script>
    

    说明:

    • 组合模式和动态原型模式是JavaScript中使用比较多的两种创建对象的方式。
    • 建议以后使用动态原型模式。他解决了组合模式的封装不彻底的缺点。

    4 继承

    相关文章

      网友评论

          本文标题:JS高级

          本文链接:https://www.haomeiwen.com/subject/gszhxxtx.html